Skip to main content

EC2 Instance IMDSv2 Enforcement

Overview

This check verifies that your EC2 instances require IMDSv2 (Instance Metadata Service version 2) or have the metadata service disabled entirely. IMDSv2 uses session-oriented authentication with tokens, making it significantly more secure than the older IMDSv1 which allowed unauthenticated requests.

Risk

When EC2 instances allow IMDSv1 or have optional token configuration:

  • Credential theft via SSRF: Server-Side Request Forgery attacks can easily retrieve temporary IAM credentials from the metadata service
  • Privilege escalation: Stolen credentials can be used to access other AWS resources, potentially leading to full account compromise
  • Lateral movement: Attackers can use compromised credentials to pivot to other resources in your infrastructure
  • Container escape risks: In containerized environments, IMDSv1 makes it easier for compromised containers to access the host's IAM role

IMDSv2 mitigates these risks by requiring a session token that cannot be easily obtained through SSRF attacks.

Remediation Steps

Prerequisites

  • AWS account access with permissions to modify EC2 instance metadata options
  • The instance must be in a running or stopped state
Required IAM permissions

To modify instance metadata options, you need:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:ModifyInstanceMetadataOptions",
"ec2:DescribeInstances"
],
"Resource": "*"
}
]
}

AWS Console Method

  1. Open the EC2 Console at https://console.aws.amazon.com/ec2
  2. Ensure you are in the us-east-1 region (top-right corner)
  3. In the left sidebar, click Instances
  4. Select the instance you want to modify (check the box next to it)
  5. Click Actions > Instance settings > Modify instance metadata options
  6. Under IMDSv2, select Required
  7. Ensure Metadata accessible is set to Enabled (unless you want to disable metadata entirely)
  8. Optionally, set Metadata response hop limit to 1 (recommended for non-containerized workloads)
  9. Click Save

The change takes effect immediately. No instance restart is required.

AWS CLI (optional)

Require IMDSv2 on a single instance

aws ec2 modify-instance-metadata-options \
--instance-id <YOUR_INSTANCE_ID> \
--http-tokens required \
--http-endpoint enabled \
--region us-east-1

Replace <YOUR_INSTANCE_ID> with your instance ID (e.g., i-0123456789abcdef0).

Require IMDSv2 and set hop limit to 1

aws ec2 modify-instance-metadata-options \
--instance-id <YOUR_INSTANCE_ID> \
--http-tokens required \
--http-endpoint enabled \
--http-put-response-hop-limit 1 \
--region us-east-1

Disable metadata service entirely

If your instance does not need access to metadata:

aws ec2 modify-instance-metadata-options \
--instance-id <YOUR_INSTANCE_ID> \
--http-endpoint disabled \
--region us-east-1

Bulk update all instances in a region

# List all instance IDs
INSTANCE_IDS=$(aws ec2 describe-instances \
--query 'Reservations[*].Instances[*].InstanceId' \
--output text \
--region us-east-1)

# Update each instance
for INSTANCE_ID in $INSTANCE_IDS; do
echo "Updating $INSTANCE_ID..."
aws ec2 modify-instance-metadata-options \
--instance-id "$INSTANCE_ID" \
--http-tokens required \
--http-endpoint enabled \
--region us-east-1
done
CloudFormation (optional)

When launching new instances, configure IMDSv2 in your CloudFormation template:

AWSTemplateFormatVersion: '2010-09-09'
Description: EC2 instance with IMDSv2 required

Parameters:
InstanceType:
Type: String
Default: t3.micro
Description: EC2 instance type
AmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
Description: Amazon Linux 2 AMI ID
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: Subnet to launch the instance in

Resources:
SecureInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
ImageId: !Ref AmiId
SubnetId: !Ref SubnetId
MetadataOptions:
HttpTokens: required
HttpEndpoint: enabled
HttpPutResponseHopLimit: 1
Tags:
- Key: Name
Value: SecureInstance

Outputs:
InstanceId:
Description: Instance ID
Value: !Ref SecureInstance

Deploy with:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name secure-ec2-instance \
--parameter-overrides SubnetId=subnet-12345678 \
--region us-east-1

For Launch Templates

Resources:
SecureLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: imdsv2-required-template
LaunchTemplateData:
MetadataOptions:
HttpTokens: required
HttpEndpoint: enabled
HttpPutResponseHopLimit: 1
Terraform (optional)

Single EC2 instance with IMDSv2

variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}

variable "subnet_id" {
description = "Subnet ID for the instance"
type = string
}

data "aws_ami" "amazon_linux_2" {
most_recent = true
owners = ["amazon"]

filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}

resource "aws_instance" "secure" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = var.instance_type
subnet_id = var.subnet_id

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

tags = {
Name = "SecureInstance"
}
}

output "instance_id" {
description = "Instance ID"
value = aws_instance.secure.id
}

Launch Template with IMDSv2

resource "aws_launch_template" "secure" {
name = "imdsv2-required-template"

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

Deploy with:

terraform init
terraform plan
terraform apply

Verification

After making changes, verify IMDSv2 is required:

  1. Open the EC2 Console at https://console.aws.amazon.com/ec2
  2. Navigate to Instances
  3. Select your instance
  4. Click the Details tab
  5. Scroll to Instance metadata options
  6. Confirm IMDSv2 shows Required
Verify with AWS CLI
aws ec2 describe-instances \
--instance-ids <YOUR_INSTANCE_ID> \
--query 'Reservations[*].Instances[*].{InstanceId:InstanceId,HttpTokens:MetadataOptions.HttpTokens,HttpEndpoint:MetadataOptions.HttpEndpoint}' \
--output table \
--region us-east-1

Expected output for a compliant instance:

------------------------------------------------------
| DescribeInstances |
+-------------+---------------+----------------------+
| HttpEndpoint| HttpTokens | InstanceId |
+-------------+---------------+----------------------+
| enabled | required | i-0123456789abcdef0 |
+-------------+---------------+----------------------+

If HttpTokens shows optional, the instance is not compliant.

List all non-compliant instances

aws ec2 describe-instances \
--filters "Name=metadata-options.http-tokens,Values=optional" \
--query 'Reservations[*].Instances[*].[InstanceId,Tags[?Key==`Name`].Value|[0]]' \
--output table \
--region us-east-1
Test IMDSv2 from the instance

SSH into the instance and test metadata access:

This should work (IMDSv2):

# Get a token
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null)

# Use the token to get metadata
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/instance-id

This should fail (IMDSv1) when IMDSv2 is required:

curl http://169.254.169.254/latest/meta-data/instance-id
# Should return 401 Unauthorized

Additional Resources

Notes

  • No restart required: Changing metadata options does not require stopping or restarting the instance.
  • Application compatibility: Before requiring IMDSv2, ensure your applications and SDKs support it. AWS SDKs released after mid-2019 support IMDSv2. Older SDKs may need updates.
  • Container workloads: For containers on EC2, consider using a hop limit of 2 instead of 1 to allow container access to metadata. Alternatively, use IAM Roles for Service Accounts (IRSA) in EKS.
  • Auto Scaling Groups: Update your Launch Templates or Launch Configurations to require IMDSv2 for all new instances.
  • Account-level defaults: You can set IMDSv2 as the default for all new instances in your account using the modify-instance-metadata-defaults API.
  • Existing instances: This change only affects running and stopped instances. For terminated instances, you must configure IMDSv2 when launching new instances.