{"record":{"id":"a2d3bdc1fec1f44c","repo":"JuliusBrussee/caveman","slug":"awscreds-refusing-plaintext-container-credentials-endpoint","errorCode":null,"errorMessage":"awscreds: refusing plaintext container credentials endpoint at host %q (allowed: loopback, 169.254.170.2, 169.254.170.23, fd00:ec2::23)","messagePattern":"awscreds: refusing plaintext container credentials endpoint at host %q \\(allowed: loopback, 169\\.254\\.170\\.2, 169\\.254\\.170\\.23, fd00:ec2::23\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/awscreds/awscreds.go","lineNumber":509,"sourceCode":"}\n\n// checkContainerURI applies the SDK rule for a caller-supplied credential\n// endpoint: TLS anywhere, plaintext only to loopback or the fixed ECS/EKS\n// credential addresses. Without it, AWS_CONTAINER_CREDENTIALS_FULL_URI is a\n// request to hand a task role's Authorization token to an arbitrary host.\nfunc checkContainerURI(raw string) error {\n\tu, err := url.Parse(raw)\n\tif err != nil {\n\t\treturn errors.New(\"awscreds: AWS_CONTAINER_CREDENTIALS_FULL_URI is not a valid URL\")\n\t}\n\tswitch u.Scheme {\n\tcase \"https\":\n\t\treturn nil\n\tcase \"http\":\n\t\tif plaintextHostAllowed(u.Hostname(), containerCredentialHosts) {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"awscreds: refusing plaintext container credentials endpoint at host %q (allowed: loopback, 169.254.170.2, 169.254.170.23, fd00:ec2::23)\", u.Hostname())\n\tdefault:\n\t\treturn fmt.Errorf(\"awscreds: unsupported container credentials scheme %q\", u.Scheme)\n\t}\n}\n\n// checkIMDSEndpoint is checkContainerURI for AWS_EC2_METADATA_SERVICE_ENDPOINT.\n// That variable was taken verbatim and then dialled with p.link — the client\n// that deliberately ignores every proxy setting — so any host named there became\n// a proxy-bypassing outbound request with the IMDSv2 token attached.\nfunc checkIMDSEndpoint(raw string) error {\n\tu, err := url.Parse(raw)\n\tif err != nil || u.Host == \"\" {\n\t\treturn errors.New(\"awscreds: AWS_EC2_METADATA_SERVICE_ENDPOINT is not a valid URL\")\n\t}\n\tswitch u.Scheme {\n\tcase \"https\":\n\t\treturn nil\n\tcase \"http\":","sourceCodeStart":491,"sourceCodeEnd":527,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/awscreds/awscreds.go#L491-L527","documentation":"checkContainerURI rejects a container credentials endpoint that uses plain http:// to a host not on the security allowlist (loopback, 169.254.170.2, 169.254.170.23, fd00:ec2::23). This is a deliberate SSRF/credential-sniffing guard: credentials sent over plaintext HTTP off-link could be intercepted.","triggerScenarios":"AWS_CONTAINER_CREDENTIALS_FULL_URI is set to an http:// URL whose hostname is not loopback or one of the two ECS/EKS link-local IPs; checkContainerURI runs before fromContainer issues the request.","commonSituations":"Pointing FULL_URI at a local credentials proxy on a LAN hostname over http; typo'd IP; using http instead of https for a remote metadata broker; running inside a custom sidecar exposing credentials on a non-allowlisted address.","solutions":["Serve or consume the credentials endpoint over https:// instead of http://.","If ECS/EKS, use the standard endpoint http://169.254.170.2/v2/credentials (or 169.254.170.23 for EKS Pod Identity) exactly.","Bind the local credentials proxy to loopback (127.0.0.1/::1) if it must stay plaintext.","Unset AWS_CONTAINER_CREDENTIALS_FULL_URI and let the provider use the default ECS address."],"exampleFix":"// before\nos.Setenv(\"AWS_CONTAINER_CREDENTIALS_FULL_URI\", \"http://creds-proxy.internal:9000/creds\")\n// after\nos.Setenv(\"AWS_CONTAINER_CREDENTIALS_FULL_URI\", \"https://creds-proxy.internal:9000/creds\")","handlingStrategy":"validation","validationCode":"u, _ := url.Parse(fullURI)\nallowed := map[string]bool{\"169.254.170.2\": true, \"169.254.170.23\": true, \"127.0.0.1\": true, \"::1\": true}\nif u.Scheme == \"http\" && !allowed[u.Hostname()] {\n    return fmt.Errorf(\"plaintext http endpoint %q not allowed; use https or loopback/link-local\", u.Host)\n}","typeGuard":null,"tryCatchPattern":"if err := p.Credentials(ctx); err != nil && strings.Contains(err.Error(), \"refusing plaintext\") {\n    log.Fatal(\"switch container credentials endpoint to https or the ECS/EKS link-local address\")\n}","preventionTips":["Default to the standard ECS/EKS endpoint instead of custom full URIs","Use TLS for any remote credentials broker","Bind local credential proxies to loopback only","Add an integration test asserting your endpoint host is on the allowlist"],"tags":["aws","security","ssrf","plaintext-http","env-config"],"backgroundTag":"invalid-config-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}