diff --git a/README.md b/README.md index 115b578..fe033ba 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,6 @@ override the default IMDS endpoint by setting `IMDS_ENDPOINT` in `/etc/tiny-cloud.conf`: ```sh -# For Tinkerbell or other custom metadata services IMDS_ENDPOINT=192.0.2.1:50061 ``` @@ -131,6 +130,16 @@ The default endpoint is `169.254.169.254` for most cloud providers. This setting allows you to specify a custom IP address and optional port for the metadata service. +### Metadata API Version + +Each provider's API has a built-in default version. You can override the +version specifying a value for IMDS_API_VERSION in `/etc/tiny-cloud.conf`: + +```sh +# e.g. use AWS IMDSv1 (2009-04-04) +IMDS_API_VERSION=2009-04-04 +``` + ## Operation The first time an instance boots -- either freshly instantiated from an image, diff --git a/bin/imds b/bin/imds index 575ac53..5d3c657 100755 --- a/bin/imds +++ b/bin/imds @@ -41,7 +41,6 @@ fi unset \ IMDS_HEADER \ - IMDS_URI \ IMDS_QUERY unset -f \ _imds_token \ diff --git a/docs/tiny-cloud.conf.5.scd b/docs/tiny-cloud.conf.5.scd index 0bc6e3e..d01cedf 100644 --- a/docs/tiny-cloud.conf.5.scd +++ b/docs/tiny-cloud.conf.5.scd @@ -33,6 +33,10 @@ Blank lines and shell comments are ignored. Default user account for instance SSH keys and default-user setup. The default is *alpine*. +*IMDS_API_VERSION*= + Provider's API version to use. Providers that have versioned APIs have + built-in default values. + *IMDS_ENDPOINT*= Provider endpoint IP address to use. Defaults to 169.254.169.254 for many providers. diff --git a/lib/tiny-cloud/cloud/aws/imds b/lib/tiny-cloud/cloud/aws/imds index f7b877d..613b635 100644 --- a/lib/tiny-cloud/cloud/aws/imds +++ b/lib/tiny-cloud/cloud/aws/imds @@ -5,14 +5,23 @@ IMDS_HEADER="X-aws-ec2-metadata-token" IMDS_TOKEN_TTL_HEADER="X-aws-ec2-metadata-token-ttl-seconds" : "${IMDS_TOKEN_TTL:=5}" -IMDS_URI="latest" +: "${IMDS_API_VERSION:=latest}" +IMDS_URI="$IMDS_API_VERSION" _imds_token() { + # Only try to get token if using IMDSv2 + # IMDSv1: API versions 2009-04-04 and earlier (no token support) + # IMDSv2: API versions 2009-04-05 and later, or 'latest' (requires token) + expr "$IMDS_API_VERSION" "<=" "2009-04-04" > /dev/null && return + # IMDSv2 - request token printf "PUT /latest/api/token HTTP/1.0\r\n%s: %s\r\n\r\n" \ "$IMDS_TOKEN_TTL_HEADER" "$IMDS_TOKEN_TTL" \ | nc -w 1 "$IMDS_ENDPOINT" 80 | tail -n 1 } _imds_header() { - echo "$IMDS_HEADER: $(_imds_token)" + local token="$(_imds_token)" + if [ -n "$token" ]; then + echo "$IMDS_HEADER: $token" + fi } diff --git a/lib/tiny-cloud/cloud/azure/imds b/lib/tiny-cloud/cloud/azure/imds index d17d474..87425d5 100644 --- a/lib/tiny-cloud/cloud/azure/imds +++ b/lib/tiny-cloud/cloud/azure/imds @@ -2,8 +2,9 @@ # vim:set filetype=sh: # shellcheck shell=sh +: "${IMDS_API_VERSION:=2021-05-01}" IMDS_HEADER="Metadata" -IMDS_QUERY="?format=text&api-version=2021-05-01" +IMDS_QUERY="?format=text&api-version=$IMDS_API_VERSION" IMDS_URI="metadata/instance" IMDS_HOSTNAME="compute/name" diff --git a/lib/tiny-cloud/cloud/digitalocean/imds b/lib/tiny-cloud/cloud/digitalocean/imds index de29879..6e855ea 100644 --- a/lib/tiny-cloud/cloud/digitalocean/imds +++ b/lib/tiny-cloud/cloud/digitalocean/imds @@ -2,7 +2,8 @@ # vim: set filetype=sh: # shellcheck shell=sh -IMDS_URI="metadata/v1" +: "${IMDS_API_VERSION:=v1}" +IMDS_URI="metadata/$IMDS_API_VERSION" IMDS_HOSTNAME="hostname" IMDS_LOCAL_HOSTNAME="$IMDS_HOSTNAME" IMDS_SSH_KEYS="public-keys" diff --git a/lib/tiny-cloud/cloud/gcp/imds b/lib/tiny-cloud/cloud/gcp/imds index 66a06fc..f79d6db 100644 --- a/lib/tiny-cloud/cloud/gcp/imds +++ b/lib/tiny-cloud/cloud/gcp/imds @@ -2,8 +2,9 @@ # vim:set filetype=sh: # shellcheck shell=sh +: "${IMDS_API_VERSION:=v1}" IMDS_HEADER="Metadata-Flavor" -IMDS_URI="computeMetadata/v1" +IMDS_URI="computeMetadata/$IMDS_API_VERSION" IMDS_HOSTNAME="instance/hostname" IMDS_LOCAL_HOSTNAME="$IMDS_HOSTNAME" diff --git a/lib/tiny-cloud/cloud/hetzner/imds b/lib/tiny-cloud/cloud/hetzner/imds index bfb716c..27baea8 100644 --- a/lib/tiny-cloud/cloud/hetzner/imds +++ b/lib/tiny-cloud/cloud/hetzner/imds @@ -2,7 +2,8 @@ # vim:set filetype=sh: # shellcheck shell=sh -IMDS_BASE_URI="hetzner/v1" +: "${IMDS_API_VERSION:=v1}" +IMDS_BASE_URI="hetzner/$IMDS_API_VERSION" IMDS_URI="$IMDS_BASE_URI/metadata" IMDS_HOSTNAME="hostname" diff --git a/lib/tiny-cloud/cloud/incus/imds b/lib/tiny-cloud/cloud/incus/imds index 9754566..78da7db 100644 --- a/lib/tiny-cloud/cloud/incus/imds +++ b/lib/tiny-cloud/cloud/incus/imds @@ -4,7 +4,8 @@ # https://linuxcontainers.org/incus/docs/main/dev-incus/ -IMDS_URI=/1.0/meta-data +: "${IMDS_API_VERSION:=1.0}" +IMDS_URI="/$IMDS_API_VERSION/meta-data" IMDS_LOCAL_HOSTNAME="local-hostname" IMDS_HOSTNAME="local-hostname" IMDS_ENDPOINT=local:/dev/incus/sock diff --git a/lib/tiny-cloud/cloud/oci/imds b/lib/tiny-cloud/cloud/oci/imds index 4c458ef..68a0d64 100644 --- a/lib/tiny-cloud/cloud/oci/imds +++ b/lib/tiny-cloud/cloud/oci/imds @@ -2,8 +2,9 @@ # vim:set filetype=sh: # shellcheck shell=sh +: "${IMDS_API_VERSION:=v2}" IMDS_HEADER="Authorization" -IMDS_URI="opc/v2" +IMDS_URI="opc/$IMDS_API_VERSION" IMDS_HOSTNAME="instance/hostname" IMDS_LOCAL_HOSTNAME="$IMDS_HOSTNAME" diff --git a/lib/tiny-cloud/tiny-cloud.conf b/lib/tiny-cloud/tiny-cloud.conf index 7100c49..f2cb82c 100644 --- a/lib/tiny-cloud/tiny-cloud.conf +++ b/lib/tiny-cloud/tiny-cloud.conf @@ -13,7 +13,11 @@ # Useful for custom metadata services #IMDS_ENDPOINT=169.254.169.254 -# IMDS token validity, in seconds (AWS only) +# IMDS API version +# Most providers have a default version, overrideable here if necessary +#IMDS_API_VERSION="" + +# IMDS token validity, in seconds (AWS only, IMDSv2) #IMDS_TOKEN_TTL=5 # Location of var directory diff --git a/tests/imds.test b/tests/imds.test index a4cfd56..b7b9c94 100755 --- a/tests/imds.test +++ b/tests/imds.test @@ -41,6 +41,10 @@ init_tests \ imds_ssh_keys_oci \ imds_ssh_keys_scaleway \ \ + imds_aws_api_version_imdsv1 \ + imds_aws_api_version_imdsv2_explicit \ + imds_aws_api_version_imdsv2_latest \ + \ imds_nocloud_cmdline_local_hostname \ imds_nocloud_smbios_local_hostname \ \ @@ -198,6 +202,55 @@ EOF CLOUD="scaleway" atf_check -o match:"$key" imds @ssh-keys } +imds_aws_api_version_imdsv1_body() { + # IMDSv1 (2009-04-04) should not attempt to get a token + # Set IMDS_API_VERSION before fake_metadata so WGET_STRIP_PREFIX is correct + IMDS_API_VERSION=2009-04-04 CLOUD=aws fake_metadata aws <<-EOF + hostname: test-imdsv1 + EOF + # For IMDSv1, nc should not be called at all + # If it is called, the test will fail because we're not mocking it + IMDS_API_VERSION=2009-04-04 CLOUD=aws atf_check \ + -o match:"test-imdsv1" \ + imds @hostname +} + +imds_aws_api_version_imdsv2_explicit_body() { + # IMDSv2 with explicit version (2009-04-05 or later) + # Verify that metadata can be retrieved with explicit API version + # Set IMDS_API_VERSION before fake_metadata so WGET_STRIP_PREFIX is correct + IMDS_API_VERSION=2009-04-05 CLOUD=aws fake_metadata aws <<-EOF + hostname: test-imdsv2-explicit + EOF + # Mock nc to provide a token (for IMDSv2 token request) + fake_bin nc <<-'NCEOF' + #!/bin/sh + cat > /dev/null + printf "HTTP/1.0 200 OK\r\n\r\nmock-token" + NCEOF + IMDS_API_VERSION=2009-04-05 CLOUD=aws atf_check \ + -o match:"test-imdsv2-explicit" \ + imds @hostname +} + +imds_aws_api_version_imdsv2_latest_body() { + # IMDSv2 with 'latest' (default behavior) + # Verify that metadata can be retrieved with latest API version + # Set IMDS_API_VERSION before fake_metadata so WGET_STRIP_PREFIX is correct + IMDS_API_VERSION=latest CLOUD=aws fake_metadata aws <<-EOF + hostname: test-imdsv2-latest + EOF + # Mock nc to provide a token (for IMDSv2 token request) + fake_bin nc <<-'NCEOF' + #!/bin/sh + cat > /dev/null + printf "HTTP/1.0 200 OK\r\n\r\nmock-token" + NCEOF + IMDS_API_VERSION=latest CLOUD=aws atf_check \ + -o match:"test-imdsv2-latest" \ + imds @hostname +} + imds_nocloud_cmdline_local_hostname_body() { atf_require_prog yx mkdir proc diff --git a/tests/test_env.sh b/tests/test_env.sh index d807959..f96dc26 100644 --- a/tests/test_env.sh +++ b/tests/test_env.sh @@ -66,7 +66,7 @@ fake_userdata_nocloud() { fake_metadata_aws() { cat > "169.254.169.254.yaml" - export WGET_STRIP_PREFIX="/latest/meta-data" + export WGET_STRIP_PREFIX="/${IMDS_API_VERSION:-latest}/meta-data" } fake_metadata_azure() {