EC2 Instance Metadata Service (IMDS) Best Practices: IMDSv2, IAM Roles & Real-World Security Threats

If you’ve ever wondered how your EC2 instance “knows” its own identity, region, or how it magically gets temporary IAM credentials without you hardcoding secrets — the answer is IMDS. And if you’re not securing it properly, attackers already know how to exploit it.

This guide breaks down exactly how EC2 Instance Metadata Service works, why IMDSv1 has caused real-world breaches, and how to lock down your instances using IMDSv2 and proper IAM design. Whether you’re prepping for AWS Security Specialty or hardening production workloads, this is the deep-dive you need.


What Is EC2 Instance Metadata Service (IMDS)?

Every EC2 instance has access to a special internal HTTP endpoint that exposes information about itself. This is the Instance Metadata Service, or IMDS.

Think of it like this: IMDS is the identity card drawer inside your server. Your application opens the drawer, pulls out credentials, and uses them to talk to AWS services. No hardcoded keys required.

Here’s what IMDS provides:

  • Instance IDi-0abc123def456
  • Region and Availability Zoneus-east-1a
  • AMI ID — the image your instance launched from
  • Security group names
  • IAM role credentials — temporary STS tokens

That last one is critical. When you attach an IAM role to an EC2 instance, the instance retrieves temporary credentials from IMDS. This is how your applications authenticate to S3, DynamoDB, KMS, and other AWS services without embedding long-term access keys.

⚠️ Security Risk: If an attacker can reach IMDS, they can steal those credentials.


How IMDS Works Internally (Under the Hood)

IMDS runs at a link-local address: 169.254.169.254. This IP is not routable over the internet or across VPCs. It’s only accessible from within the instance itself.

When your application makes an HTTP request to http://169.254.169.254/latest/meta-data/, it receives instance metadata. For IAM credentials, the path looks like this:

http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>

The response contains:

  • AccessKeyId
  • SecretAccessKey
  • Token (session token)
  • Expiration timestamp

These are temporary STS credentials, typically valid for 1–6 hours. AWS rotates them automatically before expiration, so your application always has fresh credentials.

Important distinction:

  • Metadata (/meta-data/) — instance identity, network config, IAM credentials
  • User Data (/user-data/) — bootstrap scripts you passed at launch

⚠️ Critical Point: IMDS is NOT protected by Security Groups. Security Groups control network traffic between instances and external resources, but IMDS is a local service running on the hypervisor. No firewall rule blocks it.


IMDSv1 vs IMDSv2 (Critical Interview Topic)

This is where security interviews get serious. AWS initially launched IMDS with a simple request-response model — now called IMDSv1. It worked, but it had a fatal flaw.

Why IMDSv1 Is Insecure

IMDSv1 accepts simple GET requests with no authentication. Any process on the instance — or any application that can make HTTP requests — can retrieve credentials.

Here’s the attack:

  1. Attacker finds an SSRF (Server-Side Request Forgery) vulnerability in your web application
  2. Attacker crafts a request that makes your server call http://169.254.169.254/latest/meta-data/iam/security-credentials/
  3. Your server fetches the role name
  4. Attacker follows up with another SSRF to get the actual credentials
  5. Attacker now has valid AWS credentials

This isn’t theoretical. This exact technique was used in the Capital One breach (2019), where an attacker exploited SSRF in a WAF misconfiguration to steal credentials from EC2 metadata, accessing over 100 million customer records.

How IMDSv2 Fixes This

IMDSv2 introduces a session-based token system. Before retrieving metadata, you must first obtain a token using a PUT request.

# Step 1: Get a session token (PUT request required)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")

# Step 2: Use token to fetch metadata (GET request)
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id

Why this defeats SSRF:

Most SSRF vulnerabilities only allow GET requests. Attackers cannot issue PUT requests through typical SSRF vectors. Even if they could, the token has a TTL and requires custom headers that SSRF payloads usually cannot inject.

Best Practice: Always enforce IMDSv2. There is no valid reason to keep IMDSv1 enabled in production.


Real-World Security Incidents Caused by IMDS Misuse

Let me be direct: most EC2 credential leaks are IMDS-related, not IAM misconfiguration.

Here’s how attacks typically unfold:

  1. Initial Access — Attacker finds SSRF, XXE, or command injection in an application
  2. Credential Theft — Attacker queries IMDS to retrieve IAM role credentials
  3. Privilege Escalation — If the role has broad permissions (like AdministratorAccess), attacker gains full AWS access
  4. Data Exfiltration — Attacker accesses S3 buckets, DynamoDB tables, Secrets Manager, or KMS keys

The Capital One breach is the most cited example, but this pattern repeats constantly in AWS environments. Attackers specifically look for SSRF vulnerabilities because they know IMDS credentials are often overprivileged.

⚠️ Security Risk: An over-permissioned IAM role combined with IMDSv1 is the most common root cause of cloud breaches on EC2.


EC2 IMDS Best Practices (Core Section)

Here’s exactly how to secure IMDS in your environment.

✅ Enforce IMDSv2 Only

Disable IMDSv1 completely. No exceptions.

AWS Console:

  1. Select your EC2 instance
  2. Actions → Instance Settings → Modify instance metadata options
  3. Set IMDSv2 to Required

AWS CLI:

aws ec2 modify-instance-metadata-options \
  --instance-id i-0abc123def456 \
  --http-tokens required \
  --http-endpoint enabled

Terraform:

resource "aws_instance" "secure_instance" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"

  metadata_options {
    http_tokens   = "required"  # Enforces IMDSv2
    http_endpoint = "enabled"
  }
}

✅ Set Hop Limit = 1

The hop limit controls how many network hops the metadata request can traverse. Setting it to 1 ensures that only processes running directly on the instance can reach IMDS — containers or forwarded requests cannot.

aws ec2 modify-instance-metadata-options \
  --instance-id i-0abc123def456 \
  --http-put-response-hop-limit 1

Terraform:

metadata_options {
  http_tokens                 = "required"
  http_endpoint               = "enabled"
  http_put_response_hop_limit = 1
}

✅ Disable IMDS Where Not Required

If your instance doesn’t need IAM credentials from IMDS (maybe it uses hardcoded credentials for legacy reasons, or no AWS API calls at all), disable IMDS entirely.

aws ec2 modify-instance-metadata-options \
  --instance-id i-0abc123def456 \
  --http-endpoint disabled

✅ Use Least-Privilege IAM Roles

Never attach AdministratorAccess or PowerUserAccess to EC2 instances. Define exactly what permissions your application needs.

If your app only reads from one S3 bucket, your role policy should look like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::my-app-bucket/*"
    }
  ]
}

✅ Monitor Metadata Access Patterns

Set up alerts for unusual IMDS access. More on this in the monitoring section below.


✅ IMDS Hardening Checklist

Use this quick reference when securing EC2 instances:

Security ControlStatus
IMDSv2 required (http-tokens = required)
Hop limit set to 1
Least-privilege IAM role attached
GuardDuty enabled for credential exfiltration
IRSA configured for EKS workloads
IMDS disabled if not needed

IMDS + IAM Roles: Security Design Patterns

Here’s the key insight: IAM roles on EC2 are secure when IMDS is secured.

Under the hood, IMDS is simply a delivery mechanism — the actual credentials are issued by AWS Security Token Service (STS). When your instance requests credentials from the metadata endpoint, AWS calls STS behind the scenes to generate temporary tokens tied to your IAM role.

The credentials delivered through IMDS are temporary STS tokens. They expire, they rotate automatically, and they’re tied to the instance’s identity. This is fundamentally safer than long-term access keys.

But the design only works if you:

  1. Enforce IMDSv2 to prevent credential theft
  2. Scope the role to minimum required permissions
  3. Set appropriate session duration (default is 1 hour, max is 12 hours)

Bad Design:

  • IAM role with AdministratorAccess
  • IMDSv1 enabled
  • Application has SSRF vulnerability

Good Design:

  • IAM role with specific S3 and DynamoDB permissions only
  • IMDSv2 required
  • Hop limit set to 1
  • Regular security scanning for SSRF

The session duration matters too. If an attacker steals credentials, shorter sessions limit the window of exploitation. AWS defaults to 1 hour, which is reasonable for most workloads.


IMDS with Containers, Docker & Kubernetes

Containers introduce additional IMDS risks that many teams overlook.

The Problem:

When you run Docker containers on EC2, those containers can access IMDS by default. The container inherits network access to 169.254.169.254, meaning any container on the host can retrieve the EC2 instance’s IAM credentials.

This becomes dangerous in multi-tenant scenarios or when running untrusted workloads.

Container Escape + IMDS Attack Path:

  1. Attacker compromises a container
  2. Container has access to IMDS
  3. Attacker retrieves EC2 instance credentials
  4. Attacker uses credentials to attack other AWS resources

Solutions:

For Docker on EC2:
Block IMDS access using iptables:

iptables -A OUTPUT -m owner ! --uid-owner root \
  -d 169.254.169.254 -j DROP

Or set hop limit to 1, which prevents containers from reaching IMDS in most configurations.

For EKS (Kubernetes on AWS):

Use IAM Roles for Service Accounts (IRSA) instead of relying on node-level IMDS. IRSA assigns IAM roles directly to Kubernetes pods using OIDC federation. Each pod gets its own credentials without touching IMDS.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/MyAppRole

⚠️ Security Risk: If you’re running EKS and haven’t implemented IRSA, your pods are probably using node-level credentials through IMDS. This violates least-privilege principles.


Monitoring & Detection

If someone is querying IMDS from unexpected contexts, you need to know.

CloudTrail:

When IMDS credentials are used to call AWS APIs, those calls appear in CloudTrail. Look for API calls where the userIdentity shows an assumed role from an EC2 instance, especially if the calls are unusual for that workload.

GuardDuty:

AWS GuardDuty has specific findings for credential exfiltration:

  • UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS — credentials used from an IP outside AWS
  • UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS — credentials used from a different AWS account or region

Enable GuardDuty. These findings are often the first indicator of IMDS-based attacks.

VPC Flow Logs:

Flow logs won’t show IMDS traffic directly (it’s local to the instance), but they can reveal suspicious outbound connections after credential theft occurs.

Interview Question: “How would you detect IMDS credential theft in production?”

Answer: Enable GuardDuty and monitor for InstanceCredentialExfiltration findings. Cross-reference CloudTrail logs for unusual API calls from EC2 instance roles. Alert on any use of EC2 credentials from outside expected network ranges.


Common IMDS Misconfigurations to Avoid

Leaving IMDSv1 enabled — There’s no excuse for this in modern AWS environments. Enforce IMDSv2 everywhere.

Using AdministratorAccess roles — If your EC2 instance has admin access and gets compromised, you lose everything.

Forgetting hop-limit in containerized workloads — Default hop limit of 2 allows containers to reach IMDS. Set it to 1.

Assuming Security Groups protect IMDS — They don’t. IMDS is a local service.

Not monitoring for credential exfiltration — GuardDuty detects this. Turn it on.


Interview Questions You WILL Be Asked

Q: Why is IMDSv2 more secure than v1?

A: IMDSv2 requires a session token obtained via PUT request before any metadata can be retrieved. This blocks SSRF attacks because most SSRF vulnerabilities only allow GET requests and cannot inject the required custom headers.

Q: How does IMDS relate to STS?

A: When an EC2 instance has an IAM role attached, IMDS provides temporary credentials generated by STS (Security Token Service). These credentials include AccessKeyId, SecretAccessKey, and a Session Token, all of which expire and rotate automatically.

Q: How do attackers exploit IMDS?

A: Attackers exploit application vulnerabilities like SSRF to make the server request IMDS endpoints. They retrieve IAM credentials from the metadata service, then use those credentials to access AWS resources like S3, DynamoDB, or Secrets Manager.

Q: How do you secure EC2 metadata in Kubernetes?

A: Use IAM Roles for Service Accounts (IRSA) to assign IAM permissions directly to pods. Block IMDS access at the node level or set hop limit to 1. Never rely on node-level credentials for pod workloads.

Q: Should IMDS ever be disabled?

A: Yes, if the instance doesn’t need AWS API credentials (no IAM role required, or using alternative credential sources), disable IMDS entirely to eliminate the attack surface.


Frequently Asked Questions (FAQs)

What is EC2 Instance Metadata Service (IMDS)?

EC2 Instance Metadata Service (IMDS) is an internal HTTP service available to every EC2 instance at IP address 169.254.169.254. It provides instance-specific information including the instance ID, region, AMI ID, network configuration, and most importantly, temporary IAM role credentials. Applications running on EC2 use IMDS to authenticate to AWS services without storing long-term access keys.

What is the difference between IMDSv1 and IMDSv2?

IMDSv1 accepts simple HTTP GET requests without authentication, making it vulnerable to SSRF attacks. IMDSv2 requires a session token obtained through a PUT request before any metadata can be retrieved. This two-step process blocks most SSRF exploits because attackers typically cannot issue PUT requests or inject custom headers through SSRF vulnerabilities. AWS strongly recommends enforcing IMDSv2 on all EC2 instances.

How do I enable IMDSv2 on EC2?

To enable IMDSv2, use the AWS CLI command: aws ec2 modify-instance-metadata-options --instance-id i-0abc123def456 --http-tokens required --http-endpoint enabled. In Terraform, set http_tokens = "required" within the metadata_options block. Through the AWS Console, select your instance, go to Actions → Instance Settings → Modify instance metadata options, and set IMDSv2 to Required.

Why is IMDS a security risk?

IMDS becomes a security risk when attackers can access it through vulnerabilities like SSRF (Server-Side Request Forgery). If an application is vulnerable to SSRF and IMDSv1 is enabled, attackers can make the server request IMDS endpoints to steal IAM role credentials. These stolen credentials can then be used to access S3 buckets, databases, secrets, and other AWS resources. The Capital One breach in 2019 resulted from exactly this attack pattern.

Can Security Groups block IMDS access?

No, Security Groups cannot block IMDS access. IMDS runs as a local service on the hypervisor at the link-local address 169.254.169.254. Security Groups only control network traffic between instances and external resources over the VPC network. To restrict IMDS access, you must use IMDSv2 enforcement, hop limit settings, or disable IMDS entirely.

What is the IMDS hop limit and why does it matter?

The hop limit controls how many network hops a metadata request can traverse before being rejected. The default value is 2, which allows containers to reach IMDS through the host’s network stack. Setting hop limit to 1 ensures only processes running directly on the instance can access IMDS, blocking access from containers, forwarded requests, and NAT configurations. This is critical for containerized workloads on EC2.

How do I disable IMDS completely on EC2?

To disable IMDS entirely, run: aws ec2 modify-instance-metadata-options --instance-id i-0abc123def456 --http-endpoint disabled. Only disable IMDS if your instance doesn’t need to retrieve IAM credentials or other metadata. Instances without IAM roles or those using alternative credential sources (like Secrets Manager for static credentials) are candidates for complete IMDS disablement.

How do I secure IMDS in Kubernetes (EKS)?

For EKS workloads, implement IAM Roles for Service Accounts (IRSA) instead of relying on node-level IMDS. IRSA assigns IAM roles directly to Kubernetes pods using OIDC federation, giving each pod its own scoped credentials. Additionally, set the hop limit to 1 on worker nodes and consider blocking IMDS access at the node level using iptables rules or network policies.

How do I detect IMDS credential theft?

Enable AWS GuardDuty to detect credential exfiltration. GuardDuty generates specific findings when EC2 instance credentials are used from unexpected locations: UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS for external use and InsideAWS for unusual internal use. Additionally, monitor CloudTrail for API calls from EC2 instance roles that don’t match expected application behavior.

What credentials does IMDS provide?

IMDS provides temporary credentials issued by AWS Security Token Service (STS) when an IAM role is attached to the EC2 instance. These credentials include an AccessKeyId, SecretAccessKey, and Session Token, along with an expiration timestamp. The credentials typically last 1-6 hours and are automatically rotated by AWS before expiration, eliminating the need for manual credential management.


Conclusion

IMDS isn’t dangerous — misconfigured IMDS is.

The metadata service is a core part of how EC2 instances integrate with IAM roles and AWS services. It’s elegant, it’s necessary, and it’s been exploited in some of the largest cloud breaches on record.

Your job is simple:

  1. Enforce IMDSv2
  2. Set hop limit to 1 for containerized workloads
  3. Use least-privilege IAM roles
  4. Monitor for credential exfiltration

Do these four things, and you’ve eliminated the most common EC2 attack vector in production environments.


👉 lab : IAM Roles for EC2 — Secure Access Without Access Keys


Written by Srikanth Ch, Senior DevOps Engineer at thedevopstooling.com

Similar Posts

Leave a Reply