fix(install): detect HOST_IP from physical NICs only, never a VPN tunnel
`ip route get 1.1.1.1` returns the tunnel address on servers running a full-tunnel WireGuard/Tailscale/ZeroTier client, so phones got a QR code and URL pointing at an address they can't reach. Detection now uses the main-table default route's interface if it is real hardware (/sys/class/net/<if>/device), else the first physical NIC with an IPv4, else `hostname -I` as a last resort (the installer shows it for confirmation). .env.example says HOST_IP must be the physical LAN address. Tested with a stubbed `ip` + fake sysfs in Debian: wg-quick full tunnel, tunnel owning the default route, no default route and WiFi-only all pick the physical address; full install.sh scenarios unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+27
-7
@@ -7,17 +7,37 @@ set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
# LAN IP of this machine (the address phones use: http://<HOST_IP>).
|
||||
# `ip route get` picks the interface that actually routes out, which avoids
|
||||
# docker0 / bridge addresses that `hostname -I` may list first.
|
||||
# LAN IP of this machine on the PHYSICAL network (the address phones use:
|
||||
# http://<HOST_IP>). Must never be a VPN address: with a full-tunnel WireGuard /
|
||||
# Tailscale / ZeroTier client, "the interface that reaches the internet" is the
|
||||
# tunnel, which phones on the restaurant WiFi can't reach.
|
||||
# Real NICs (Ethernet, WiFi) have /sys/class/net/<if>/device; VPN tunnels,
|
||||
# Docker bridges, veths and loopback don't.
|
||||
# XENIA_SYS_NET overrides the sysfs path (tests only).
|
||||
detect_host_ip() {
|
||||
local ip=""
|
||||
if command -v ip >/dev/null 2>&1; then
|
||||
ip=$(ip route get 1.1.1.1 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')
|
||||
local sys_net="${XENIA_SYS_NET:-/sys/class/net}" ifc ip=""
|
||||
command -v ip >/dev/null 2>&1 || { hostname -I 2>/dev/null | awk '{print $1}'; return; }
|
||||
|
||||
first_ipv4() { ip -4 -o addr show dev "$1" 2>/dev/null | awk '{split($4, a, "/"); print a[1]; exit}'; }
|
||||
|
||||
# 1) Interface of the main-table default route, if it's real hardware.
|
||||
# (wg-quick full-tunnel routing uses policy rules + its own table, so the
|
||||
# main table's default route still points at the physical uplink.)
|
||||
ifc=$(ip route show default 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="dev") {print $(i+1); exit}}')
|
||||
if [ -n "$ifc" ] && [ -e "$sys_net/$ifc/device" ]; then
|
||||
ip=$(first_ipv4 "$ifc")
|
||||
fi
|
||||
# 2) Otherwise the first real-hardware interface that has an IPv4 address.
|
||||
if [ -z "$ip" ]; then
|
||||
ip=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
for path in "$sys_net"/*; do
|
||||
ifc=$(basename "$path")
|
||||
[ -e "$path/device" ] || continue
|
||||
ip=$(first_ipv4 "$ifc")
|
||||
[ -n "$ip" ] && break
|
||||
done
|
||||
fi
|
||||
# 3) Last resort — the admin confirms it at the prompt anyway.
|
||||
[ -z "$ip" ] && ip=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
echo "$ip"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user