JuliusBrussee/caveman · error
awscreds: refusing plaintext IMDS endpoint at host
Error message
awscreds: refusing plaintext IMDS endpoint at host %q (allowed: loopback, 169.254.169.254, fd00:ec2::254)
What it means
checkIMDSEndpoint rejects an IMDS endpoint (AWS_EC2_METADATA_SERVICE_ENDPOINT) that uses plain http:// to a host not on its allowlist (loopback, 169.254.169.254, fd00:ec2::254). Like the container check, it prevents IMDS tokens/credentials from being sent over plaintext HTTP to arbitrary hosts.
Solutions
- Use the default endpoint (http://169.254.169.254/latest) or set AWS_EC2_METADATA_SERVICE_ENDPOINT to exactly that host.
- Serve the metadata endpoint over https:// if it is remote.
- Bind a custom IMDS simulator to 127.0.0.1 and point the env var at the loopback URL.
- Unset AWS_EC2_METADATA_SERVICE_ENDPOINT when running on real EC2 instances.
Example fix
// before
os.Setenv("AWS_EC2_METADATA_SERVICE_ENDPOINT", "http://imds.lab.internal")
// after
os.Setenv("AWS_EC2_METADATA_SERVICE_ENDPOINT", "http://127.0.0.1:8080") // simulator on loopback Defensive patterns
Strategy: validation
Validate before calling
u, _ := url.Parse(os.Getenv("AWS_EC2_METADATA_SERVICE_ENDPOINT"))
imdsOK := map[string]bool{"169.254.169.254": true, "127.0.0.1": true, "::1": true, "fd00:ec2::254": true}
if u != nil && u.Scheme == "http" && !imdsOK[u.Hostname()] {
return fmt.Errorf("IMDS endpoint %q not allowed over plaintext http", u.Host)
} Try / catch
if err != nil && strings.Contains(err.Error(), "refusing plaintext IMDS") {
log.Fatal("use the default IMDS endpoint or bind your simulator to loopback")
} Prevention
- Leave AWS_EC2_METADATA_SERVICE_ENDPOINT unset on real EC2 instances
- Point IMDS-mock tooling at 127.0.0.1 only
- Review any endpoint override in code review for plaintext http on non-loopback hosts
When it happens
Trigger: AWS_EC2_METADATA_SERVICE_ENDPOINT is set to an http:// URL with a hostname other than loopback or the EC2 metadata link-local addresses; runs before fromIMDS makes any request.
Common situations: Redirecting IMDS at a test/staging metadata simulator on a LAN hostname over http; typos in the endpoint; a custom IMDS proxy bound to a non-loopback address; leftover endpoint config from a hybrid-cloud setup.
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 container credentials endpoint…
- awscreds: AWS_EC2_METADATA_SERVICE_ENDPOINT is not a valid…
- awscreds: AWS_CONTAINER_CREDENTIALS_FULL_URI is not a valid…
- awscreds: build imds token request
- awscreds: build request
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/a61cd7792e4e77dd.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/awscreds/awscreds.go:531
}
// 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":
if plaintextHostAllowed(u.Hostname(), imdsHosts) {
return nil
}
return fmt.Errorf("awscreds: refusing plaintext IMDS endpoint at host %q (allowed: loopback, 169.254.169.254, fd00:ec2::254)", u.Hostname())
default:
return fmt.Errorf("awscreds: unsupported IMDS endpoint scheme %q", u.Scheme)
}
}
func (p *Provider) fromIMDS(ctx context.Context) (*result, error) {
if strings.EqualFold(p.env("AWS_EC2_METADATA_DISABLED"), "true") {
return nil, nil
}
base := p.env("AWS_EC2_METADATA_SERVICE_ENDPOINT")
if base == "" {
base = defaultIMDSBase
}
if err := checkIMDSEndpoint(base); err != nil {
return nil, err
}
base = strings.TrimSuffix(base, "/")
View on GitHub (pinned to 3ee70a1026)