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

  1. Serve or consume the credentials endpoint over https:// instead of http://.
  2. If ECS/EKS, use the standard endpoint http://169.254.170.2/v2/credentials (or 169.254.170.23 for EKS Pod Identity) exactly.
  3. Bind the local credentials proxy to loopback (127.0.0.1/::1) if it must stay plaintext.
  4. 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

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


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)