Retrieving EC2 Instance ID Safely: IMDSv2 vs IMDSv1 Explained
When writing automation scripts or bootstrap logic on an EC2 instance, you often need the instance's own ID — for tagging, logging, or calling AWS APIs. The built-in Instance Metadata Service (IMDS) is the canonical way to get it, but how you query it determines whether your instance is a security liability or not.
TL;DR
| Aspect | IMDSv1 (Legacy) | IMDSv2 (Recommended) |
|---|---|---|
| Auth Model | None — open GET request | Session-oriented token required |
| SSRF Vulnerability | Fully exploitable | Mitigated by token requirement |
| Request Flow | 1 step (GET metadata) | 2 steps (PUT token → GET metadata) |
| AWS Recommendation | Deprecated for new workloads | ✅ Enforce via instance config |
| Core Takeaway | Always use IMDSv2. Enforce it at launch with --metadata-options HttpTokens=required. | |
Why IMDS Exists
Every EC2 instance has access to a special link-local IP address — 169.254.169.254 — that serves instance-specific metadata: instance ID, AMI ID, IAM role credentials, region, and more. This endpoint is only reachable from within the instance itself (it is not routable over the internet), making it the authoritative self-discovery mechanism for any script running on the host.
The endpoint path for the instance ID is:
http://169.254.169.254/latest/meta-data/instance-id
The Security Problem with IMDSv1
IMDSv1 is a simple, stateless HTTP GET. No authentication. No session. Any process — or any HTTP request that can be proxied through the instance — can retrieve sensitive metadata, including temporary IAM credentials attached to the instance profile.
Analogy: IMDSv1 is like a building's master key cabinet with no lock. Anyone who walks into the lobby — even someone who snuck in through a side door — can grab the keys. IMDSv2 adds a locked cabinet: you must first prove you're physically inside the building (the instance) to get a time-limited key, and only then can you open the cabinet.
The "side door" in this analogy is a Server-Side Request Forgery (SSRF) attack. If your application has an SSRF vulnerability (e.g., a URL-fetching feature that an attacker can manipulate), the attacker can instruct your server to fetch http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name> and return the result — handing over live AWS credentials.
How IMDSv2 Closes the Gap
IMDSv2 introduces a session-oriented, token-based flow. The key defense is that the token request uses an HTTP PUT method with a custom header (X-aws-ec2-metadata-token-ttl-seconds). Most SSRF vulnerabilities exploit simple GET requests; proxying a PUT with custom headers is significantly harder and blocked by many web frameworks and proxies by default.
Header: X-aws-ec2-metadata-token-ttl-seconds: 21600 IMDS-->>Script: Returns TOKEN string Note over Script,IMDS: Step 2 — Query Metadata (GET + Token) Script->>IMDS: GET /latest/meta-data/instance-id
Header: X-aws-ec2-metadata-token: TOKEN IMDS-->>Script: i-0abc1234def56789
The token is valid only for the TTL you specify (in seconds) and is bound to the instance. An attacker exploiting SSRF cannot easily replicate the PUT + custom header combination through a naive proxy.
Implementation: Retrieving Instance ID with IMDSv2
Bash (Shell Script)
#!/bin/bash
# Step 1: Get a session token (TTL = 6 hours)
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Step 2: Use the token to fetch the instance ID
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/instance-id)
echo "Instance ID: $INSTANCE_ID"
Python (boto3-free, using requests)
💻 [Click to expand] Python: IMDSv2 Instance ID Retrieval
import requests
IMDS_BASE = "http://169.254.169.254"
def get_imdsv2_token(ttl_seconds: int = 21600) -> str:
"""Acquire a session token from IMDSv2."""
response = requests.put(
f"{IMDS_BASE}/latest/api/token",
headers={"X-aws-ec2-metadata-token-ttl-seconds": str(ttl_seconds)},
timeout=2
)
response.raise_for_status()
return response.text
def get_instance_id() -> str:
"""Retrieve the EC2 instance ID using IMDSv2."""
token = get_imdsv2_token()
response = requests.get(
f"{IMDS_BASE}/latest/meta-data/instance-id",
headers={"X-aws-ec2-metadata-token": token},
timeout=2
)
response.raise_for_status()
return response.text
if __name__ == "__main__":
print(f"Instance ID: {get_instance_id()}")
Enforcing IMDSv2 at the Infrastructure Level
Relying on scripts to "use the right version" is fragile. The correct approach is to disable IMDSv1 entirely at instance launch, so no code path — intentional or accidental — can fall back to the insecure version.
AWS CLI — At Launch
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--metadata-options HttpTokens=required,HttpEndpoint=enabled
Enforce on an Existing Instance
aws ec2 modify-instance-metadata-options \
--instance-id i-0abc1234def56789 \
--http-tokens required \
--http-endpoint enabled
Terraform
🛠️ [Click to expand] Terraform: EC2 with IMDSv2 Enforced
resource "aws_instance" "app_server" {
ami = "ami-0abcdef1234567890"
instance_type = "t3.micro"
metadata_options {
http_endpoint = "enabled"
http_tokens = "required" # Enforces IMDSv2
http_put_response_hop_limit = 1 # Prevents container-escape attacks
}
tags = {
Name = "app-server"
}
}
Note on hop limit: Setting http_put_response_hop_limit = 1 ensures the token PUT request cannot be forwarded beyond the instance itself — a critical defense when running containers on EC2, preventing a compromised container from reaching the host metadata endpoint.
Architecture: Request Flow Comparison
IAM & Security Notes
- IMDS itself requires no IAM permissions — it is a local HTTP endpoint, not an AWS API call.
- However, the IAM credentials returned by IMDS (for the attached instance profile) are what attackers target. Enforcing IMDSv2 protects those credentials from SSRF exfiltration.
- Use AWS Config Rule
ec2-imdsv2-checkto audit all instances in your account for IMDSv1 exposure at scale.
Wrap-up & Next Steps
IMDSv2's session-token model is a simple, zero-cost upgrade that eliminates an entire class of credential-theft attacks — enforce HttpTokens=required on every instance and never rely on IMDSv1 again.
Next Steps:
- 📖 AWS Docs: Configure Instance Metadata Service
- Run
aws ec2 describe-instances --query 'Reservations[].Instances[].{ID:InstanceId,IMDSv2:MetadataOptions.HttpTokens}'to audit your fleet today. - Enable the AWS Config managed rule
ec2-imdsv2-checkfor continuous compliance monitoring.
Glossary
- IMDS (Instance Metadata Service): A local HTTP endpoint (
169.254.169.254) on every EC2 instance that serves instance-specific configuration and credentials without requiring AWS API calls. - IMDSv2: The session-oriented version of IMDS that requires a PUT-based token acquisition step before any metadata can be read.
- SSRF (Server-Side Request Forgery): An attack where a malicious actor tricks a server into making HTTP requests to internal endpoints (like IMDS) on their behalf.
- Link-local address: An IP address in the
169.254.0.0/16range that is only reachable within a single network segment — never routed over the internet. - Instance Profile: An IAM role attached to an EC2 instance, whose temporary credentials are accessible via IMDS and are the primary target of SSRF-based metadata attacks.
Comments
Post a Comment