From 95f4e79ff228785fc5229b4f8675fee3845c693c Mon Sep 17 00:00:00 2001 From: Lemon-miaow Date: Sun, 27 Sep 2026 16:42:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(bootstrap):=20=E6=B2=A1=E6=9C=89=20IPv4=20?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E8=B7=AF=E7=94=B1=E6=97=B6=E4=BB=8E=20hostna?= =?UTF-8?q?me=20-I=20=E5=8F=96=E7=AC=AC=E4=B8=80=E4=B8=AA=20IPv4=20?= =?UTF-8?q?=E5=9C=B0=E5=9D=80=EF=BC=8C=E8=B7=B3=E8=BF=87=20IPv6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/bootstrap.sh | 10 +++++++++- deploy/bootstrap_test.sh | 15 +++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/deploy/bootstrap.sh b/deploy/bootstrap.sh index eb87ac8..ff7b0b1 100644 --- a/deploy/bootstrap.sh +++ b/deploy/bootstrap.sh @@ -1116,7 +1116,15 @@ persisted_root_domain() { detect_node_ip() { NODE_IP="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}')" - [ -n "${NODE_IP:-}" ] || NODE_IP="$(hostname -I 2>/dev/null | awk '{print $1}')" + if [ -z "${NODE_IP:-}" ]; then + # No IPv4 default route. hostname -I lists the IPv6 addresses too, in interface + # order, and an IPv6 one would end up in the nip.io name, the /32 policies and the + # certificate's IP entry: take the first IPv4 one. + local a + for a in $(hostname -I 2>/dev/null); do + if ipv4_to_int "$a" >/dev/null; then NODE_IP="$a"; break; fi + done + fi [ -n "${NODE_IP:-}" ] || die "could not determine this host's primary IPv4 address" # Precedence: an explicit FELIS_ROOT_DOMAIN, then whatever the last run persisted, then diff --git a/deploy/bootstrap_test.sh b/deploy/bootstrap_test.sh index 443017f..7ab3d9c 100644 --- a/deploy/bootstrap_test.sh +++ b/deploy/bootstrap_test.sh @@ -2038,13 +2038,14 @@ rm -rf "$ldir2" dnblock="$(awk '/^detect_node_ip\(\) \{/,/^}/' "$BS")" prdblock="$(awk '/^persisted_root_domain\(\) \{/,/^}/' "$BS")" { [ -n "$dnblock" ] && [ -n "$prdblock" ]; } || { echo "FAIL: detect_node_ip / persisted_root_domain not found in $BS"; exit 1; } -printf '%s\n' "$prdblock" "$dnblock" > "$fnfile" -run_detect() { # state-dir [FELIS_ROOT_DOMAIN] - STATE_DIR="$1" RD="${2:-}" FNFILE="$fnfile" bash -c ' +printf '%s\n' "$prdblock" "$dnblock" "$(awk '/^ipv4_to_int\(\) \{/,/^}/' "$BS")" > "$fnfile" +run_detect() { # state-dir [FELIS_ROOT_DOMAIN]; IPOUT and HOSTOUT stand in for ip route get and hostname -I + STATE_DIR="$1" RD="${2:-}" FNFILE="$fnfile" IPOUT="${IPOUT-1.1.1.1 via 10.0.0.1 dev eth0 src 10.0.0.5 uid 0}" HOSTOUT="${HOSTOUT:-}" bash -c ' die() { printf "DIE: %s\n" "$*"; exit 1; } log() { printf "LOG: %s\n" "$*"; } warn() { printf "WARN: %s\n" "$*"; } - ip() { echo "1.1.1.1 via 10.0.0.1 dev eth0 src 10.0.0.5 uid 0"; } + ip() { [ -z "$IPOUT" ] || printf "%s\n" "$IPOUT"; } + hostname() { [ "$1" = -I ] && printf "%s \n" "$HOSTOUT"; } . "$FNFILE" if [ -n "$RD" ]; then FELIS_ROOT_DOMAIN="$RD"; fi detect_node_ip @@ -2062,6 +2063,12 @@ expect "the installed domain is reused when unset" "ROOT=r.example.com" "$(run_d rm -f "$adir/felis.host.toml" expect "a first install takes FELIS_ROOT_DOMAIN" "ROOT=new.example.net" "$(run_detect "$adir" new.example.net)" expect "a first install defaults to nip.io" "ROOT=10.0.0.5.nip.io" "$(run_detect "$adir")" +# Without an IPv4 default route, hostname -I is the fallback; it lists IPv6 addresses +# too, and the first IPv4 one is taken. +expect "the fallback skips IPv6 addresses" "ROOT=192.168.1.20.nip.io" \ + "$(IPOUT='' HOSTOUT='fd00::5 2001:db8::20 192.168.1.20 10.42.0.1' run_detect "$adir")" +expect "a host with IPv6 addresses alone is refused" "DIE: could not determine this host's primary IPv4 address" \ + "$(IPOUT='' HOSTOUT='fd00::5 2001:db8::20' run_detect "$adir")" rm -rf "$adir" rm -f "$fnfile"