1
0
mirror of https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git synced 2026-06-21 00:07:16 +03:00

add IMDS endpoint route checking

This commit is contained in:
Jake Buchholz Göktürk 2026-06-14 20:50:57 -07:00
parent 75871161ba
commit b9e1857a59
8 changed files with 94 additions and 6 deletions

View File

@ -2,7 +2,11 @@
## 2026-06-XX - Tiny Cloud v3.3.3 ## 2026-06-XX - Tiny Cloud v3.3.3
* Support IPv6 and multiple endpoints [#68](https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud/-/work_items/68) * Support IPv6 and multiple endpoints
[#68](https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud/-/work_items/68)
* Check for IP routes to IMDS endpoints before trying them; retry if none are
routable. Fixes race condition between `dhcpcd` starting and attempting to
reach IMDS before routes are resolved.
## 2026-06-08 - Tiny Cloud v3.3.2 ## 2026-06-08 - Tiny Cloud v3.3.2

View File

@ -140,6 +140,10 @@ must use URL-style brackets:
IMDS_ENDPOINTS="169.254.169.254 [fd00:ec2::254]" IMDS_ENDPOINTS="169.254.169.254 [fd00:ec2::254]"
``` ```
Tiny Cloud checks for routes to configured IMDS endpoints before trying metadata
requests. `IMDS_ENDPOINT_WAIT_ATTEMPTS` controls how many route checks are made
before metadata requests are tried anyway.
### Metadata API Version ### Metadata API Version
Each provider's API has a built-in default version. You can override the Each provider's API has a built-in default version. You can override the

View File

@ -55,6 +55,7 @@ unset -f \
: "${IMDS_ENDPOINT:=169.254.169.254}" : "${IMDS_ENDPOINT:=169.254.169.254}"
: "${IMDS_ENDPOINTS:=$IMDS_ENDPOINT}" : "${IMDS_ENDPOINTS:=$IMDS_ENDPOINT}"
: "${IMDS_ENDPOINT_CACHE:=$TINY_CLOUD_VAR/.imds-endpoint}" : "${IMDS_ENDPOINT_CACHE:=$TINY_CLOUD_VAR/.imds-endpoint}"
: "${IMDS_ENDPOINT_WAIT_ATTEMPTS:=10}"
# Common to AWS and NoCloud(ish) # Common to AWS and NoCloud(ish)
IMDS_HOSTNAME="meta-data/hostname" IMDS_HOSTNAME="meta-data/hostname"
@ -107,9 +108,37 @@ _imds_host_port() {
echo "$port" echo "$port"
} }
_imds_has_route() {
local host
set -- $(_imds_host_port "$1")
host="$1"
case "$host" in
*:*) ip -6 route get "$host" >/dev/null 2>&1 ;;
[0-9]*.[0-9]*.[0-9]*.[0-9]*) ip route get "$host" >/dev/null 2>&1 ;;
*) return 0 ;;
esac
}
_imds() { _imds() {
local endpoint local endpoint endpoints routed attempts=1
for endpoint in $(_imds_endpoints); do while :; do
endpoints=
routed=
for endpoint in $(_imds_endpoints); do
if _imds_has_route "$endpoint"; then
endpoints="$endpoints $endpoint"
routed=1
fi
done
[ -n "$routed" ] && break
[ "$attempts" -ge "$IMDS_ENDPOINT_WAIT_ATTEMPTS" ] && {
endpoints="$(_imds_endpoints)"
break
}
sleep 1
attempts=$((attempts + 1))
done
for endpoint in $endpoints; do
IMDS_ENDPOINT="$endpoint" IMDS_ENDPOINT="$endpoint"
IMDS_CURRENT_ENDPOINT="$endpoint" IMDS_CURRENT_ENDPOINT="$endpoint"
wget --quiet --timeout 1 --output-document - \ wget --quiet --timeout 1 --output-document - \

View File

@ -106,6 +106,10 @@ inside *@nic:* queries.
Whitespace-separated provider metadata endpoint list. IPv6 endpoints must use Whitespace-separated provider metadata endpoint list. IPv6 endpoints must use
brackets, for example *[fd00:ec2::254]*. brackets, for example *[fd00:ec2::254]*.
*IMDS_ENDPOINT_WAIT_ATTEMPTS*
Number of times to check for routes to any IMDS endpoint before trying
metadata requests anyway. The default is *10*.
# EXIT STATUS # EXIT STATUS
*0* *0*

View File

@ -47,6 +47,10 @@ Blank lines and shell comments are ignored.
first on later queries. IPv6 endpoints must use brackets, for example first on later queries. IPv6 endpoints must use brackets, for example
*[fd00:ec2::254]* or *[fd00:ec2::254]:80*. *[fd00:ec2::254]* or *[fd00:ec2::254]:80*.
*IMDS_ENDPOINT_WAIT_ATTEMPTS*=<number>
Number of times to check for routes to any IMDS endpoint before trying
metadata requests anyway. The default is *10*.
*IMDS_TOKEN_TTL*=<seconds> *IMDS_TOKEN_TTL*=<seconds>
Metadata token lifetime in seconds for AWS metadata access. This is only used Metadata token lifetime in seconds for AWS metadata access. This is only used
by the AWS provider. The default is *5*. by the AWS provider. The default is *5*.

View File

@ -17,6 +17,10 @@
# IPv6 endpoints must use brackets: [fd00:ec2::254] or [fd00:ec2::254]:80 # IPv6 endpoints must use brackets: [fd00:ec2::254] or [fd00:ec2::254]:80
#IMDS_ENDPOINTS="169.254.169.254 [fd00:ec2::254]" #IMDS_ENDPOINTS="169.254.169.254 [fd00:ec2::254]"
# Number of times to check for routes to any IMDS endpoint before trying
# metadata requests anyway.
#IMDS_ENDPOINT_WAIT_ATTEMPTS=10
# IMDS API version # IMDS API version
# Most providers have a default version, overrideable here if necessary # Most providers have a default version, overrideable here if necessary
#IMDS_API_VERSION="" #IMDS_API_VERSION=""

View File

@ -13,6 +13,8 @@ init_tests \
imds_endpoint_fallback \ imds_endpoint_fallback \
imds_endpoint_cache \ imds_endpoint_cache \
imds_endpoint_ipv6 \ imds_endpoint_ipv6 \
imds_endpoint_route_skip \
imds_endpoint_route_wait \
\ \
imds_hostname_aws \ imds_hostname_aws \
imds_hostname_azure \ imds_hostname_azure \
@ -71,7 +73,8 @@ imds_endpoint_fallback_body() {
IMDS_API_VERSION=2009-04-04 CLOUD=aws fake_metadata aws <<-EOF IMDS_API_VERSION=2009-04-04 CLOUD=aws fake_metadata aws <<-EOF
hostname: myhostname hostname: myhostname
EOF EOF
IMDS_API_VERSION=2009-04-04 IMDS_ENDPOINTS="fail 169.254.169.254" CLOUD=aws atf_check \ IMDS_API_VERSION=2009-04-04 IMDS_ENDPOINT_WAIT_ATTEMPTS=0 \
IMDS_ENDPOINTS="fail 169.254.169.254" CLOUD=aws atf_check \
-o match:"myhostname" \ -o match:"myhostname" \
imds @hostname imds @hostname
atf_check -o match:"^169.254.169.254$" cat var/lib/cloud/.imds-endpoint atf_check -o match:"^169.254.169.254$" cat var/lib/cloud/.imds-endpoint
@ -86,7 +89,8 @@ imds_endpoint_cache_body() {
cat > first.example.yaml <<-EOF cat > first.example.yaml <<-EOF
hostname: first-hostname hostname: first-hostname
EOF EOF
IMDS_API_VERSION=2009-04-04 WGET_STRIP_PREFIX="/2009-04-04/meta-data" \ IMDS_API_VERSION=2009-04-04 IMDS_ENDPOINT_WAIT_ATTEMPTS=0 \
WGET_STRIP_PREFIX="/2009-04-04/meta-data" \
WGET_HOST_LOG="$PWD/hosts.log" \ WGET_HOST_LOG="$PWD/hosts.log" \
IMDS_ENDPOINTS="first.example cached.example" CLOUD=aws atf_check \ IMDS_ENDPOINTS="first.example cached.example" CLOUD=aws atf_check \
-o match:"cached-hostname" \ -o match:"cached-hostname" \
@ -98,7 +102,8 @@ imds_endpoint_ipv6_body() {
cat > "fd00:ec2::254.yaml" <<-EOF cat > "fd00:ec2::254.yaml" <<-EOF
hostname: ipv6-hostname hostname: ipv6-hostname
EOF EOF
IMDS_API_VERSION=2009-04-04 WGET_STRIP_PREFIX="/2009-04-04/meta-data" \ IMDS_API_VERSION=2009-04-04 IMDS_ENDPOINT_WAIT_ATTEMPTS=0 \
WGET_STRIP_PREFIX="/2009-04-04/meta-data" \
WGET_HOST_LOG="$PWD/hosts.log" \ WGET_HOST_LOG="$PWD/hosts.log" \
IMDS_ENDPOINTS="[fd00:ec2::254]" CLOUD=aws atf_check \ IMDS_ENDPOINTS="[fd00:ec2::254]" CLOUD=aws atf_check \
-o match:"ipv6-hostname" \ -o match:"ipv6-hostname" \
@ -106,6 +111,39 @@ imds_endpoint_ipv6_body() {
atf_check -o match:"^\\[fd00:ec2::254\\]$" cat hosts.log atf_check -o match:"^\\[fd00:ec2::254\\]$" cat hosts.log
} }
imds_endpoint_route_skip_body() {
IMDS_API_VERSION=2009-04-04 CLOUD=aws fake_metadata aws <<-EOF
hostname: myhostname
EOF
fake_bin ip <<-'EOF'
#!/bin/sh
[ "$3" = 169.254.169.254 ]
EOF
IMDS_API_VERSION=2009-04-04 \
IMDS_ENDPOINTS="192.0.2.1 169.254.169.254" CLOUD=aws atf_check \
-o match:"myhostname" \
imds @hostname
atf_check -o match:"^169.254.169.254$" cat var/lib/cloud/.imds-endpoint
}
imds_endpoint_route_wait_body() {
IMDS_API_VERSION=2009-04-04 CLOUD=aws fake_metadata aws <<-EOF
hostname: myhostname
EOF
fake_bin ip <<-'EOF'
#!/bin/sh
mkdir -p tmp
count=$(cat tmp/route-count 2>/dev/null || echo 0)
count=$((count + 1))
echo "$count" > tmp/route-count
[ "$count" -gt 1 ]
EOF
IMDS_API_VERSION=2009-04-04 IMDS_ENDPOINT_WAIT_ATTEMPTS=5 CLOUD=aws atf_check \
-o match:"myhostname" \
imds @hostname
atf_check -o match:"^2$" cat tmp/route-count
}
check_hostname() { check_hostname() {
fake_metadata "$1" <<-EOF fake_metadata "$1" <<-EOF
# aws, digitalocean, hetzner, nocloud # aws, digitalocean, hetzner, nocloud

View File

@ -6,6 +6,7 @@ PATH="$atf_srcdir/bin:$srcdir/bin:$srcdir/sbin:$PATH"
export TINY_CLOUD_BASEDIR="$srcdir" export TINY_CLOUD_BASEDIR="$srcdir"
export ROOT="$PWD" export ROOT="$PWD"
export IMDS_ENDPOINT_WAIT_ATTEMPTS=0
init_tests() { init_tests() {