EC2 Instance Metadata and IMDSv2
Introduction¶
Every EC2 instance can query http://169.254.169.254/ for metadata about
itself — instance ID, region, and crucially the temporary IAM credentials
for its instance profile. That last part is why the service is a security-
sensitive target.
What's there¶
/latest/meta-data/instance-id
/latest/meta-data/placement/region
/latest/meta-data/iam/security-credentials/<role-name> <- STS creds, rotated
/latest/dynamic/instance-identity/document <- signed identity doc
/latest/user-data <- your bootstrap script
The SDKs and CLI read the credentials path automatically when no other credential source is configured — that's how "an EC2 instance just has AWS access".
The IMDSv1 problem¶
IMDSv1 is a plain unauthenticated GET. If your app has a server-side request
forgery (SSRF) bug — it fetches a URL an attacker controls — the attacker
points it at http://169.254.169.254/latest/meta-data/iam/security-credentials/
and exfiltrates the instance's IAM credentials. This has caused real,
large breaches.
How IMDSv2 fixes it¶
IMDSv2 requires a session token obtained with a PUT (which most SSRF
primitives can't do) that also carries a X-aws-ec2-metadata-token-ttl-seconds
header. Responses to PUT and the token requirement mean a naive
fetch(attacker_url) SSRF can't reach it, and the low default hop limit (1)
stops the token leaving the instance.
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/instance-id
Enforce it¶
At launch, or on a running instance:
aws ec2 modify-instance-metadata-options \
--instance-id i-0abc123 \
--http-tokens required \ # IMDSv2 only; IMDSv1 requests now fail
--http-endpoint enabled \
--http-put-response-hop-limit 1
In a launch template / ASG, set MetadataOptions.HttpTokens=required. There's
an account-wide default you can set so new instances get it automatically, and
an Organization SCP / Config rule to catch drift.
The hop limit and containers¶
http-put-response-hop-limit is the IP TTL on metadata responses. Default 1
means the response dies before leaving the instance — good.
But a container adds a network hop (the bridge). A pod/container reaching IMDS needs hop limit 2. Trade-off: hop limit 2 slightly widens the blast radius. Better options for containers:
- EKS: IAM Roles for Service Accounts (IRSA) or EKS Pod Identity — the
pod gets its own role via a projected token, and you can block pod access to
IMDS entirely (
--http-put-response-hop-limit 1+ a NetworkPolicy / iptables rule dropping 169.254.169.254). - ECS: the task metadata endpoint + task roles; disable container access to instance IMDS.
Verification and troubleshooting¶
# Is IMDSv1 still allowed? (should fail if enforced)
curl -s -o /dev/null -w "%{http_code}\n" http://169.254.169.254/latest/meta-data/
# 401 = IMDSv2 required (good). 200 = IMDSv1 still works.
aws ec2 describe-instances --instance-id i-0abc123 \
--query 'Reservations[].Instances[].MetadataOptions'
# CloudWatch metric: MetadataNoToken > 0 means something still uses IMDSv1
- App breaks after enforcing IMDSv2 — an old SDK (pre-2018) or a hand-rolled metadata client that only does IMDSv1. Update the SDK; all current ones speak v2.
- Container can't get credentials — hop limit is 1. Raise to 2, or (better) use IRSA / Pod Identity / task roles.
MetadataNoTokenmetric nonzero — find the offender before enforcing: it's usually a monitoring agent, a CI tool, or an old base image's SDK.- Credentials work then expire mid-job — the instance profile creds are temporary (~6h) and auto-rotate; a long job that cached them at start needs to re-read. SDKs handle this; custom code must.
403fetchinguser-data— there is no user-data, or it's been consumed; not an error condition per se.- Latency calling IMDS from many threads — the endpoint rate-limits. SDKs cache the token; don't fetch a new token per request.
Related tools and reading¶
- On-site: Port Checker.
- Related posts: AWS IAM policy evaluation logic, ECS Fargate task networking.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Cloud & AWS
AWS WAF: The Rules That Matter
AWS WAF filters HTTP requests at the ALB/CloudFront/API Gateway edge. Here's a sensible baseline Web ACL, how to tune with count mode, and the false-positive traps.
October 24, 2026An S3 Static Site Behind CloudFront
The right way to host a static site on AWS: a private S3 bucket, CloudFront with OAC in front, HTTPS via ACM, and SPA fallback routing. Plus the 403s that trip everyone.
October 24, 2026CloudWatch Logs and Metric Filters
CloudWatch Logs is cheap to write and expensive to forget about. Here's retention, turning error patterns into metrics and alarms, and Logs Insights for ad-hoc queries.
October 24, 2026