List EC2 instances making calls to the Instance Metadata Service using IMDSv1
A script that scans all EC2 instances in an AWS Account across all regions for their use of the IMDSv1 protocol within the past 15 months, based on two CloudWatch metrics: MetadataNoToken and MetadataNoTokenRejected.
The AWS Security Blog post "Get the full benefits of IMDSv2 and disable IMDSv1 across your AWS infrastructure" discusses the importance of transitioning from the Instance Metadata Service Version 1 (IMDSv1) to Version 2 (IMDSv2) for enhanced security in Amazon EC2 instances.
It provides detailed guidance on identifying IMDSv1-enabled instances, checking for IMDSv1 calls within your software, and systematically moving to IMDSv2-only environments.
That article covers various methods and tools to aid in this transition, including using the AWS Management Console, AWS CLI, AWS Config, and AWS Security Hub, as well as employing the IMDS Packet Analyzer and CloudWatch metrics.
However, it does not show an easy way to list all the EC2 instances making calls to the Instance Metadata Service using IMDSv1 (both when the instance's attribute marks IMDSv2 are optional or required).
A quick introduction to the Instance Metadata Service (IMDS)
The Instance Metadata Service (IMDS) is an on-instance component that code on the instance uses to securely access to instance metadata (for example, instance ID, public and private IP addresses, security groups,...). This service has two versions: Version 1 (IMDSv1) and Version 2 (IMDSv2).
Instance Metadata Service Version 1 (IMDSv1)
IMDSv1 is the original version of the Instance Metadata Service.
- It allows requests to the instance metadata without any session-oriented mechanism, which means you can retrieve metadata by simply sending a request to a specific endpoint from within the instance.
- Access to IMDSv1 is through a simple HTTP GET request to a specific URL (
http://169.254.169.254/latest/meta-data/
). - It does not require session tokens, making it less secure than IMDSv2 because it is susceptible to SSRF (Server Side Request Forgery) attacks if the instance's IAM role is overly permissive.
Instance Metadata Service Version 2 (IMDSv2)
- IMDSv2 enhances the security of the metadata service by requiring session-oriented requests.
- To access metadata with IMDSv2, a PUT request must first be made to create a session token. This token is then used in subsequent GET requests to retrieve metadata (
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
). - The session token is required and must be included in the HTTP header, significantly reducing the risk of SSRF attacks, as the attacker would need to obtain the session token before accessing the metadata (
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/
). - IMDSv2 provides options to configure the session duration and enforce IMDSv2 usage, ensuring higher security levels for accessing instance metadata.
The Script
The following script scans all EC2 instances in an AWS Account across all regions for their use of the IMDSv1 protocol within the past 15 months, based on two CloudWatch metrics: MetadataNoToken
and MetadataNoTokenRejected
.
For instances where either or both metrics have a sum greater than 0, indicating usage of IMDSv1, it constructs and prints a warning message that includes the occurrence count of each metric and specifies whether IMDSv2 is "required" or "optional" for that instance. If no IMDSv1 usage is detected, it prints a message stating that no attempt to use IMDSv1 was detected, along with the IMDSv2 requirement status (i.e., "required" or "optional").
#!/bin/bash
# Define the start time for 15 months ago in ISO 8601 format
start_time=$(date -u -d '15 months ago' +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u -v-15m +"%Y-%m-%dT%H:%M:%SZ")
# Define the end time as the current time in ISO 8601 format
end_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# List all regions
regions=$(aws ec2 describe-regions --query "Regions[].RegionName" --output text)
for region in $regions
do
echo "Checking region: $region"
# List all instances in the region along with HttpTokens setting
aws ec2 describe-instances --region "$region" \
--query 'Reservations[].Instances[].[InstanceId, MetadataOptions.HttpTokens]' \
--output text | while read instanceid httptokens
do
# Function to query a CloudWatch metric and return its sum as an integer
get_metric_sum() {
metric_name="$1"
metric_data=$(aws cloudwatch get-metric-statistics --metric-name "$metric_name" --namespace AWS/EC2 --statistics Sum --start-time "$start_time" --end-time "$end_time" --period 2592000 --dimensions Name=InstanceId,Value="$instanceid" --region "$region" --output text)
metric_sum=$(echo "$metric_data" | grep -oE '[0-9]+(\.[0-9]+)?' | head -n1)
if [ -z "$metric_sum" ]; then
echo "0"
else
# Converting the metric sum to an integer
printf "%.0f\n" "$metric_sum"
fi
}
# Retrieve metric sums as integers
no_token=$(get_metric_sum "MetadataNoToken")
no_token_rejected=$(get_metric_sum "MetadataNoTokenRejected")
# Initialize message
message=""
# Check the metrics and construct the message
if [ "$no_token" -gt 0 ] || [ "$no_token_rejected" -gt 0 ]; then
message="WARNING: Instance $instanceid in $region has "
if [ "$no_token" -gt 0 ] && [ "$no_token_rejected" -gt 0 ]; then
message+="$no_token occurrence(s) of the MetadataNoToken metric and $no_token_rejected occurrence(s) of the MetadataNoTokenRejected metric in the past 15 months"
elif [ "$no_token" -gt 0 ]; then
message+="$no_token occurrence(s) of the MetadataNoToken metric in the past 15 months"
else
message+="$no_token_rejected occurrence(s) of the MetadataNoTokenRejected metric in the past 15 months"
fi
message+=" - IMDSv2 is $httptokens."
else
message="No attempt to use IMDSv1 was detected from Instance $instanceid in $region in the past 15 months - IMDSv2 is $httptokens."
fi
echo "$message"
done
done
The Bash script can be run from CloudShell or from a physical or virtual machine configured to use an AWS CLI profile with the required permissions: EC2:DescribeInstances EC2:DescribeRegions and logs:DescribeMetricFilters.
Understanding the IMDSv2 attribute
The script scans all instances in all enabled regions in an AWS account. For each instance, the last part of the corresponding message shows if IMDSv2 is optional or required.
The HttpTokens attribute within an Amazon EC2 instance's MetadataOptions dictates the behavior of the Instance Metadata Service Version 2 (IMDSv2) with respect to the requirement of using session tokens. This attribute can take one of two values: optional or required.
optional: When set to optional, both IMDSv1 and IMDSv2 are allowed to access the instance metadata. This means that requests to the instance metadata can be made without a session token (IMDSv1 style) or with a session token (IMDSv2 style). This setting provides compatibility with older applications that might not support IMDSv2's session-based model but potentially at the expense of reduced security compared to enforcing IMDSv2 exclusively.
required: When HttpTokens is set to required, it enforces the use of IMDSv2 by requiring a session token for any instance metadata request. This setting enhances the security posture by mitigating certain types of attacks that rely on unauthorized metadata access, such as SSRF (Server-Side Request Forgery) attacks. All metadata requests must use the PUT method to create a session and then use the session token in subsequent GET requests to retrieve the metadata.
Choosing between optional and required allows AWS users to balance application compatibility and security needs. Enforcing IMDSv2 (required) is recommended for enhanced security, whereas optional might be used temporarily during migration periods or with legacy applications that have not yet been updated to support IMDSv2.
Understanding the MetadataNoToken and the MetadataNoTokenRejected metrics
MetadataNoToken: This metric counts the number of requests to the IMDS that do not include a session token. IMDSv2 requires a session token for each request as part of its enhanced security measures. Therefore, the MetadataNoToken
metric counts the number of requests made using IMDSv1.
MetadataNoTokenRejected: This metric counts the number of IMDSv1 requests that are denied because the instance is configured to require IMDSv2. A nonzero value for the MetadataNoTokenRejected
metric indicates that there are attempts to access metadata using IMDSv1, but the instance's configuration requires the more secure IMDSv2, hence rejecting these requests.
Together, these metrics help you monitor and decide when and how to enforce the use of IMDSv2. They can identify instances receiving IMDSv1 requests, which might be legitimate but outdated or potential security risks. They also identify instances where IMDSv2 is required, but IMDSv1 requests are still used.
The Output of the script
The output of the script will look like this:
Checking region: ap-south-1
Checking region: eu-north-1
Checking region: eu-west-3
Checking region: eu-south-1
Checking region: eu-west-2
Checking region: eu-south-2
Checking region: eu-west-1
No attempt to use IMDSv1 was detected from Instance i-0a9bXXXX468249005 in eu-west-1 in the past 15 months - IMDSv2 is required.
No attempt to use IMDSv1 was detected from Instance i-0627XXXXf51f389fa in eu-west-1 in the past 15 months - IMDSv2 is required.
No attempt to use IMDSv1 was detected from Instance i-0f46XXXX9facc75c9 in eu-west-1 in the past 15 months - IMDSv2 is required.
No attempt to use IMDSv1 was detected from Instance i-0df9XXXX7d9eb1257 in eu-west-1 in the past 15 months - IMDSv2 is required.
No attempt to use IMDSv1 was detected from Instance i-0708XXXX486194574 in eu-west-1 in the past 15 months - IMDSv2 is required.
WARNING: Instance i-0fc0XXXXed483a117 in eu-west-1 has 1 occurrence(s) of the MetadataNoToken metric and 11 occurrence(s) of the MetadataNoTokenRejected metric in the past 15 months - IMDSv2 is required.
WARNING: Instance i-0cfeXXXX51351d734 in eu-west-1 has 1 occurrence(s) of the MetadataNoTokenRejected metric in the past 15 months - IMDSv2 is required.
No attempt to use IMDSv1 was detected from Instance i-088fXXXX6a1794e50 in eu-west-1 in the past 15 months - IMDSv2 is required.
No attempt to use IMDSv1 was detected from Instance i-0b9fXXXX202119cb0 in eu-west-1 in the past 15 months - IMDSv2 is required.
WARNING: Instance i-01f9XXXX8f01dfb94 in eu-west-1 has 2 occurrence(s) of the MetadataNoToken metric in the past 15 months - IMDSv2 is optional.
Checking region: ap-northeast-3
Checking region: ap-northeast-2
Checking region: ap-northeast-1
Checking region: ca-central-1
Checking region: sa-east-1
Checking region: ap-southeast-1
Checking region: ap-southeast-2
Checking region: eu-central-1
Checking region: eu-central-2
Checking region: us-east-1
WARNING: Instance i-00f7XXXX8b3b13161 in us-east-1 has 3 occurrence(s) of the MetadataNoToken metric in the past 15 months - IMDSv2 is optional.
No attempt to use IMDSv1 was detected from Instance i-0ad7XXXX48c73d493 in us-east-1 in the past 15 months - IMDSv2 is required.
Checking region: us-east-2
Checking region: us-west-1
Checking region: us-west-2
Most of the instances have the IMDSv2 attribute set to required (only v2 requests will be accepted) and most of the requests do not use IMDSv1:
No attempt to use IMDSv1 was detected from Instance i-0a9bXXXX468249005 in eu-west-1 in the past 15 months - IMDSv2 is required.
Some of the instances show occurrences of the MetadataNoToken
metric, which means one or more processes inside the instance have used the IMDSv1 protocol (typically because the instance's configuration has the IMDSv2 attribute set to optional). You can use these occurrences to identify instances that can be candidates for upgrade to IMDSv2 and enforce its usage.
WARNING: Instance i-01f9XXXX8f01dfb94 in eu-west-1 has 2 occurrence(s) of the MetadataNoToken metric in the past 15 months - IMDSv2 is optional.
When an instance's IMDSv2 attribute is set to required, but there are still processes using the IMDSv1 protocol, there will be occurrences of the MetadataNoTokenRejected
metric. This means that some applications cannot retrieve the requested metadata and might fail. You should focus on analyzing why this is happening:
WARNING: Instance i-0cfeXXXX51351d734 in eu-west-1 has 1 occurrence(s) of the MetadataNoTokenRejected metric in the past 15 months - IMDSv2 is required.
Finally, an instance can show a non-zero count of the MetadataNoToken
and MetadataNoTokenRejected
metrics during the analyzed period. This can happen if an instance's IMDSv2 attribute has changed between optional and required and requests without the session token have been recorded:
WARNING: Instance i-0fc0XXXXed483a117 in eu-west-1 has 1 occurrence(s) of the MetadataNoToken metric and 11 occurrence(s) of the MetadataNoTokenRejected metric in the past 15 months - IMDSv2 is required.
In other words, first look for occurrences of the MetadataNoTokenRejected
metric, as there might be impaired processes running on them in instances where IMDSv2 is already enforced. Then, look for occurrences of the MetadataNoToken
metric for instance candidates to enforce IMDSv2 after updating the processes to stop using IMDSv1. An excellent explanation of the analysis process to migrate to IMDSv2 is described in the "Transition to using Instance Metadata Service Version 2" documentation.