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:
+4
-2
@@ -9,8 +9,10 @@ CLOUD_URL=https://xenia-admin.bonamin.gr
|
|||||||
SECRET_KEY=generate-with-openssl-rand-hex-32
|
SECRET_KEY=generate-with-openssl-rand-hex-32
|
||||||
LICENSE_GRACE_HOURS=24
|
LICENSE_GRACE_HOURS=24
|
||||||
|
|
||||||
# This machine's LAN IP — the address phones open (http://<HOST_IP>) and the
|
# This machine's LAN IP on the PHYSICAL network (Ethernet/WiFi) — the address
|
||||||
# pairing QR code encodes. install.sh detects it; reserve it in the router's DHCP.
|
# phones open (http://<HOST_IP>) and the pairing QR code encodes. Never a VPN
|
||||||
|
# address (WireGuard/Tailscale/ZeroTier). install.sh detects it; reserve it in
|
||||||
|
# the router's DHCP.
|
||||||
HOST_IP=
|
HOST_IP=
|
||||||
|
|
||||||
# Break-glass support account (leave blank to disable)
|
# Break-glass support account (leave blank to disable)
|
||||||
|
|||||||
+27
-7
@@ -7,17 +7,37 @@ set -e
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
# LAN IP of this machine (the address phones use: http://<HOST_IP>).
|
# LAN IP of this machine on the PHYSICAL network (the address phones use:
|
||||||
# `ip route get` picks the interface that actually routes out, which avoids
|
# http://<HOST_IP>). Must never be a VPN address: with a full-tunnel WireGuard /
|
||||||
# docker0 / bridge addresses that `hostname -I` may list first.
|
# 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() {
|
detect_host_ip() {
|
||||||
local ip=""
|
local sys_net="${XENIA_SYS_NET:-/sys/class/net}" ifc ip=""
|
||||||
if command -v ip >/dev/null 2>&1; then
|
command -v ip >/dev/null 2>&1 || { hostname -I 2>/dev/null | awk '{print $1}'; return; }
|
||||||
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}}')
|
|
||||||
|
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
|
fi
|
||||||
|
# 2) Otherwise the first real-hardware interface that has an IPv4 address.
|
||||||
if [ -z "$ip" ]; then
|
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
|
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"
|
echo "$ip"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user