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
* 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

View File

@ -140,6 +140,10 @@ must use URL-style brackets:
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
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_ENDPOINTS:=$IMDS_ENDPOINT}"
: "${IMDS_ENDPOINT_CACHE:=$TINY_CLOUD_VAR/.imds-endpoint}"
: "${IMDS_ENDPOINT_WAIT_ATTEMPTS:=10}"
# Common to AWS and NoCloud(ish)
IMDS_HOSTNAME="meta-data/hostname"
@ -107,9 +108,37 @@ _imds_host_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() {
local endpoint
for endpoint in $(_imds_endpoints); do
local endpoint endpoints routed attempts=1
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_CURRENT_ENDPOINT="$endpoint"
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
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
*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
*[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>
Metadata token lifetime in seconds for AWS metadata access. This is only used
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
#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
# Most providers have a default version, overrideable here if necessary
#IMDS_API_VERSION=""

View File

@ -13,6 +13,8 @@ init_tests \
imds_endpoint_fallback \
imds_endpoint_cache \
imds_endpoint_ipv6 \
imds_endpoint_route_skip \
imds_endpoint_route_wait \
\
imds_hostname_aws \
imds_hostname_azure \
@ -71,7 +73,8 @@ imds_endpoint_fallback_body() {
IMDS_API_VERSION=2009-04-04 CLOUD=aws fake_metadata aws <<-EOF
hostname: myhostname
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" \
imds @hostname
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
hostname: first-hostname
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" \
IMDS_ENDPOINTS="first.example cached.example" CLOUD=aws atf_check \
-o match:"cached-hostname" \
@ -98,7 +102,8 @@ imds_endpoint_ipv6_body() {
cat > "fd00:ec2::254.yaml" <<-EOF
hostname: ipv6-hostname
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" \
IMDS_ENDPOINTS="[fd00:ec2::254]" CLOUD=aws atf_check \
-o match:"ipv6-hostname" \
@ -106,6 +111,39 @@ imds_endpoint_ipv6_body() {
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() {
fake_metadata "$1" <<-EOF
# 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 ROOT="$PWD"
export IMDS_ENDPOINT_WAIT_ATTEMPTS=0
init_tests() {