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
- Open the EC2 Console at https://console.aws.amazon.com/ec2
- Ensure you are in the us-east-1 region (top-right corner)
- In the left sidebar, click Instances
- Select the instance you want to modify (check the box next to it)
- Click Actions > Instance settings > Modify instance metadata options
- Under IMDSv2, select Required
- Ensure Metadata accessible is set to Enabled (unless you want to disable metadata entirely)
- Optionally, set Metadata response hop limit to 1 (recommended for non-containerized workloads)
- 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:
- Open the EC2 Console at https://console.aws.amazon.com/ec2
- Navigate to Instances
- Select your instance
- Click the Details tab
- Scroll to Instance metadata options
- 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
- AWS Documentation: Configure the instance metadata service
- AWS Documentation: Use IMDSv2
- AWS CLI Reference: modify-instance-metadata-options
- AWS Blog: Defense in depth using IMDSv2
- Terraform: aws_instance metadata_options
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-defaultsAPI. - Existing instances: This change only affects running and stopped instances. For terminated instances, you must configure IMDSv2 when launching new instances.