Skip to content

Commit 1354678

Browse files
authored
Bug/check imds version 57 (#60)
* Adde check for IMDS version * move metadata below endpoint declaration Moved check for IMDS version to below the metadata_endpoint declaration to be able to take advantage of it. * updated default to IMDSv2 updated the comments to reflect IMDSv2 as the default. * Add IMDS version 2 to README.md Changing IMDSv2 to be default. Updating the README to reflect this change. * dealing with IMDS being blocked Taking in to account if a firewall is configured to block IMDS calls. And setting default to version 2. * check IMDSv2 Token endpoint Checks the IMDSv2 Token endpoint to verify if IMDSv2 is active and available. If not, checks to verify if IMDSv1 endpoint is active and available. * wrap curl in variable curl command failure cause script to abort. Wrapping inside a variable should allow the script to continue. * adding sudo to curl curl to IMDS endpoint fails as a normal user in some cases (including testing with docker-compose). * identify version failure in output Identify in the output if it was IMDSv1 or IMDSv2 that failed. * remove version lock from Dockerfile cnf-lint and pyrsistent unlocking version * unlock cnf-lint and pyrsistent versions Unlocking versions for cnf-lint and pyrsistent in Docker file for Ubuntu 20.04 * adding check for user override to IMDS version Adding if statement to check if the IMDS version has been requested.
1 parent 59cbcbc commit 1354678

File tree

4 files changed

+55
-22
lines changed

4 files changed

+55
-22
lines changed

Dockerfile.ubuntu18.04.bats

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ RUN pip3 install awscli --upgrade --user
1818

1919
# Install moto: https://github.com/spulec/moto
2020
# Lock cfn-lint and pysistent to last known working versions
21-
RUN pip3 install flask moto moto[server] cfn-lint==0.35.1 pyrsistent==0.16.0
21+
RUN pip3 install flask moto moto[server] cfn-lint pyrsistent
2222

2323
# Install tools we'll need to create a mock EC2 metadata server
2424
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y net-tools iptables

Dockerfile.ubuntu20.04.bats

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ RUN pip3 install awscli --upgrade --user
1818

1919
# Install moto: https://github.com/spulec/moto
2020
# Lock cfn-lint and pysistent to last known working versions
21-
RUN pip3 install flask moto moto[server] cfn-lint==0.35.1 pyrsistent==0.16.0
21+
RUN pip3 install flask moto moto[server] cfn-lint pyrsistent
2222

2323
# Install tools we'll need to create a mock EC2 metadata server
2424
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y net-tools iptables

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,13 @@ sudo chown -R "my-os-username:my-os-group" /opt/gruntwork/bash-commons
7373

7474
## Instance Metadata Service versions
7575

76-
`bash-commons` supports both Instance Metadata Service (IMDS) version 1 and 2. Gruntwork and AWS both recommend using version 2 of the Instance Metadata Service whenever possible. Although version 1 is still supported and considered fully secure by AWS, version 2 has been specially hardened against specific threat vectors and is therefore preferable.
76+
`bash-commons` supports both Instance Metadata Service (IMDS) version 1 and 2. Gruntwork and AWS both recommend using version 2 of the Instance Metadata Service whenever possible. Although version 1 is still supported and considered fully secure by AWS, version 2 has been specially hardened against specific threat vectors and is therefore preferable. Version 2 is now the default since all new instances support it by default.
7777

7878
To understand more about Instance Metadata Service version 2 and its features, read [the official AWS documentation on IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html).
7979

80+
If you need help detecting what is still using IMDSv1 AWS has a PacketAnalyzer:
81+
https://github.com/aws/aws-imds-packet-analyzer
82+
8083
There are two ways to specify the version of the Instance Metadata Service that `bash-commons` should use:
8184

8285
1. Set the environment variable `GRUNTWORK_BASH_COMMONS_IMDS_VERSION` to the version of IMDS that you wish to use. Valid values are either `1` or `2`.
@@ -132,7 +135,7 @@ Here's an overview of the modules available in `bash-commons`:
132135
Instance Metadata](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html). These thin
133136
wrappers give you a shorthand way to fetch certain information (e.g., information about an EC2 Instance, such as its
134137
private IP, public IP, Instance ID, and region). Moreover, you can swap out `aws.sh` with a version that returns mock
135-
data to make it easy to run your code locally (e.g., in Docker) and to run unit tests.
138+
data to make it easy to run your code locally (e.g., in Docker) and to run unit tests. This requires IMDS to be enabled.
136139
137140
* `aws-wrapper.sh`: A collection of "high level" wrappers for the [AWS CLI](https://aws.amazon.com/cli/) and [EC2
138141
Instance Metadata](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) to simplify common

modules/bash-commons/src/aws.sh

+48-18
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,39 @@ readonly metadata_endpoint="http://169.254.169.254/latest"
1212
readonly metadata_dynamic_endpoint="http://169.254.169.254/latest/dynamic"
1313
# The AWS EC2 Instance document endpoint
1414
readonly instance_identity_endpoint="http://169.254.169.254/latest/dynamic/instance-identity/document"
15+
# The AWS EC2 Instance IMDSv2 Token endpoint
16+
readonly imdsv2_token_endpoint="http://169.254.169.254/latest/api/token"
1517
# A convenience variable representing 3 hours, for use in requesting a token from the IMDSv2 endpoint
1618
readonly three_hours_in_s=10800
1719
# A convenience variable representing 6 hours, which is the maximum configurable session duration when requesting
1820
# a token from IMDSv2
1921
readonly six_hours_in_s=21600
20-
# By default, we use Instance Metadata service version 1. Although version 2 is preferred, version 1 is "fully secure" according to Amazon. We'll continue defaulting to version 1 as long as we're updating our dependent modules to take advantage of this new functionality in bash-commons. Once we've completed our migration, we will begin defaulting to version 2. Users can always specify the version of the Instance Metadata Service they want bash-commons to consult by setting the environment variable GRUNTWORK_BASH_COMMONS_IMDS_VERSION
21-
default_instance_metadata_version="1"
22+
23+
# Detect if the instance is using IMDSv2 or if it is using IMDSv1 still.
24+
# Users can always specify the version of the Instance Metadata Service they want bash-commons
25+
# to consult by setting the environment variable GRUNTWORK_BASH_COMMONS_IMDS_VERSION
26+
# Defaults to IMDSv2 since that is now enabled by default on instances (IMDS only has two options,
27+
# "optional" = both v1 and v2, or "required" = v2 only). All new instances support v2 now.
28+
if [[ -z "$GRUNTWORK_BASH_COMMONS_IMDS_VERSION" ]]; then
29+
to_token_or_not_to_token=$(sudo curl -s -o /dev/null -X PUT ${imdsv2_token_endpoint} -H "X-aws-ec2-metadata-token-ttl-seconds: 10"; echo $?)
30+
if [ ${to_token_or_not_to_token} -eq 0 ]; then
31+
default_instance_metadata_version="2"
32+
elif [ ${to_token_or_not_to_token} -eq 7 ]; then
33+
echo "Check for IMDSv2 failed. IMDS endpoint connection refused."
34+
default_instance_metadata_version="0"
35+
else
36+
finish_code=$(sudo curl -s -o /dev/null $metadata_endpoint; echo $?)
37+
if [ ${finish_code} -eq 0 ]; then
38+
default_instance_metadata_version="1"
39+
elif [ ${finish_code} -eq 7 ]; then
40+
echo "Check for IMDSv1 and v2 failed. IMDS endpoint connection refused."
41+
default_instance_metadata_version="0"
42+
else
43+
echo "IMDS endpoint connection failed for an unknown reason with error code: ${finish_code}"
44+
default_instance_metadata_version="0"
45+
fi
46+
fi
47+
fi
2248

2349
# shellcheck source=./modules/bash-commons/src/assert.sh
2450
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/assert.sh"
@@ -30,8 +56,10 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/log.sh"
3056
# specific threat vectors. Read more at:
3157
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
3258
#
33-
# If you prefer to use Instance Metadata service version 2, you can do so by setting the environment variable:
34-
# export GRUNTWORK_BASH_COMMONS_IMDS_VERSION="2"
59+
# The default is now version 2, but if you prefer to use Instance Metadata service version 1, you can do
60+
# so by setting the environment variable:
61+
# export GRUNTWORK_BASH_COMMONS_IMDS_VERSION="1"
62+
# This function will override the default based on the contents of that variable if it is set.
3563
function aws_get_instance_metadata_version_in_use {
3664
using=${GRUNTWORK_BASH_COMMONS_IMDS_VERSION:-$default_instance_metadata_version}
3765
assert_value_in_list "Instance Metadata service version in use" "$using" "1" "2"
@@ -49,8 +77,8 @@ function aws_get_instance_metadata_version_in_use {
4977
# This is due to the fact that we will need to operate in a split-brain mode while all our dependent
5078
# modules are being updated to use IMDSv2.
5179
#
52-
# Version 1 is the default, but can be overridden by setting:
53-
# env var GRUNTWORK_BASH_COMMONS_IMDS_VERSION=2
80+
# Version 2 is the default, but can be overridden by setting:
81+
# env var GRUNTWORK_BASH_COMMONS_IMDS_VERSION=1
5482
function aws_lookup_path_in_instance_metadata {
5583
local -r path="$1"
5684
version_in_use=$(aws_get_instance_metadata_version_in_use)
@@ -65,8 +93,8 @@ function aws_lookup_path_in_instance_metadata {
6593
# This is due to the fact that we will need to operate in a split-brain mode while all our dependent
6694
# modules are being updated to use IMDSv2.
6795
#
68-
# Version 1 is the default, but can be overridden by setting:
69-
# env var GRUNTWORK_BASH_COMMONS_IMDS_VERSION=2
96+
# Version 2 is the default, but can be overridden by setting:
97+
# env var GRUNTWORK_BASH_COMMONS_IMDS_VERSION=1
7098
function aws_lookup_path_in_instance_dynamic_data {
7199
local -r path="$1"
72100
version_in_use=$(aws_get_instance_metadata_version_in_use)
@@ -84,8 +112,8 @@ function aws_lookup_path_in_instance_dynamic_data {
84112
# meaning that they do not retrieve or present the tokens returned and expected by
85113
# IMDSv2
86114

87-
# This function uses Instance Metadata service version 1. It requests the supplied path from the endpoint, but
88-
# does not use the token-based authorization scheme.
115+
# This function uses Instance Metadata service version 1. It requests the supplied
116+
# path from the endpoint, but does not use the token-based authorization scheme.
89117
function aws_lookup_path_in_instance_metadata_v1 {
90118
local -r path="$1"
91119
curl --silent --show-error --location "http://169.254.169.254/latest/meta-data/$path/"
@@ -103,9 +131,10 @@ function aws_lookup_path_in_instance_dynamic_data_v1 {
103131
# The following functions use IMDSv2, meaning they request and present IMDSv2
104132
# tokens when making requests to IMDS.
105133

106-
# This function calls the Instance Metadata Service endpoint version 2 (IMDSv2) which is hardened against certain attack vectors.
107-
# The endpoint returns a token that must be supplied on subsequent requests. This implementation fetches a new token
108-
# for each transaction. See:
134+
# This function calls the Instance Metadata Service endpoint version 2 (IMDSv2)
135+
# which is hardened against certain attack vectors. The endpoint returns a token
136+
# that must be supplied on subsequent requests. This implementation fetches a new
137+
# token for each transaction. See:
109138
# https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/
110139
# for more information
111140
function ec2_metadata_http_get {
@@ -119,8 +148,8 @@ function ec2_metadata_http_get {
119148
--silent --location --fail --show-error
120149
}
121150

122-
# This function uses Instance Metadata service version 2. It requests the supplied path from the
123-
# dynamic endpoint.
151+
# This function uses Instance Metadata service version 2. It requests the supplied
152+
# path from the dynamic endpoint.
124153
function ec2_metadata_dynamic_http_get {
125154
assert_not_empty "path" "$1"
126155
local -r path="$1"
@@ -174,14 +203,15 @@ function configure_imdsv2_ttl {
174203
echo "$ttl"
175204
}
176205

177-
# This function uses Instance Metadata version 2.It requests the supplied path from the endpoint, leveraging
178-
# the token-based authorization scheme.
206+
# This function uses Instance Metadata version 2.It requests the supplied path from
207+
# the endpoint, leveraging the token-based authorization scheme.
179208
function aws_lookup_path_in_instance_metadata_v2 {
180209
assert_not_empty "path" "$path" "Must specify a metadata path to request"
181210
ec2_metadata_http_get "$path"
182211
}
183212

184-
# This function uses Instance Metadata version 2. It requests the specified path from the IMDS dynamic endpont
213+
# This function uses Instance Metadata version 2. It requests the specified path from
214+
# the IMDS dynamic endpont
185215
function aws_lookup_path_in_instance_dynamic_data_v2 {
186216
local -r path="$1"
187217
assert_not_empty "path" "$path" "Must specify a metadata dynamic path to request"

0 commit comments

Comments
 (0)