JuliusBrussee/caveman · error
awscreds: refusing plaintext container credentials endpoint…
Error message
awscreds: refusing plaintext container credentials endpoint at host %q (allowed: loopback, 169.254.170.2, 169.254.170.23, fd00:ec2::23)
What it means
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.
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.
Example fix
// before
os.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "http://creds-proxy.internal:9000/creds")
// after
os.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "https://creds-proxy.internal:9000/creds") Defensive patterns
Strategy: validation
Validate before calling
u, _ := url.Parse(fullURI)
allowed := map[string]bool{"169.254.170.2": true, "169.254.170.23": true, "127.0.0.1": true, "::1": true}
if u.Scheme == "http" && !allowed[u.Hostname()] {
return fmt.Errorf("plaintext http endpoint %q not allowed; use https or loopback/link-local", u.Host)
} Try / catch
if err := p.Credentials(ctx); err != nil && strings.Contains(err.Error(), "refusing plaintext") {
log.Fatal("switch container credentials endpoint to https or the ECS/EKS link-local address")
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- awscreds: refusing plaintext IMDS endpoint at host
- awscreds: AWS_CONTAINER_CREDENTIALS_FULL_URI is not a valid…
- awscreds: AWS_EC2_METADATA_SERVICE_ENDPOINT is not a valid…
- awscreds: build container credentials request
- awscreds: unsupported container credentials scheme
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/a2d3bdc1fec1f44c.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/awscreds/awscreds.go:509
}
// checkContainerURI applies the SDK rule for a caller-supplied credential
// endpoint: TLS anywhere, plaintext only to loopback or the fixed ECS/EKS
// credential addresses. Without it, AWS_CONTAINER_CREDENTIALS_FULL_URI is a
// request to hand a task role's Authorization token to an arbitrary host.
func checkContainerURI(raw string) error {
u, err := url.Parse(raw)
if err != nil {
return errors.New("awscreds: AWS_CONTAINER_CREDENTIALS_FULL_URI is not a valid URL")
}
switch u.Scheme {
case "https":
return nil
case "http":
if plaintextHostAllowed(u.Hostname(), containerCredentialHosts) {
return nil
}
return 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())
default:
return fmt.Errorf("awscreds: unsupported container credentials scheme %q", u.Scheme)
}
}
// checkIMDSEndpoint is checkContainerURI for AWS_EC2_METADATA_SERVICE_ENDPOINT.
// That variable was taken verbatim and then dialled with p.link — the client
// that deliberately ignores every proxy setting — so any host named there became
// a proxy-bypassing outbound request with the IMDSv2 token attached.
func checkIMDSEndpoint(raw string) error {
u, err := url.Parse(raw)
if err != nil || u.Host == "" {
return errors.New("awscreds: AWS_EC2_METADATA_SERVICE_ENDPOINT is not a valid URL")
}
switch u.Scheme {
case "https":
return nil
case "http":View on GitHub (pinned to 3ee70a1026)