#!/bin/bash set -euo pipefail HOST_LAN_IP="${HOST_LAN_IP:-192.168.178.58}" TAILSCALE_IP="${TAILSCALE_IP:-100.80.98.33}" FRITZBOX_TR064_URL="${FRITZBOX_TR064_URL:-http://192.168.178.1:49000/tr64desc.xml}" REPORT_ROOT="${REPORT_ROOT:-/mnt/user/backups/restore-reports}" STAMP="$(date +%F-%H%M%S)" REPORT_FILE="$REPORT_ROOT/guest-iot-preflight-$STAMP.md" mkdir -p "$REPORT_ROOT" tcp_check() { local host="$1" local port="$2" timeout 2 bash -c "cat < /dev/null > /dev/tcp/$host/$port" >/dev/null 2>&1 } result_row() { local name="$1" local target="$2" local expectation="$3" local status="$4" printf '| %s | `%s` | %s | %s |\n' "$name" "$target" "$status" "$expectation" } { echo "# Guest/IoT Preflight" echo echo "Timestamp: $(date '+%F %T')" echo "Scope: host-side read-only checks before enabling FRITZ!Box guest/IoT network" echo echo "## FRITZ!Box TR-064" echo if curl -fsS --max-time 5 "$FRITZBOX_TR064_URL" >/tmp/guest-iot-fritzbox-tr064.xml 2>/dev/null; then model="$(grep -o '[^<]*' /tmp/guest-iot-fritzbox-tr064.xml | head -n1 | sed 's///')" echo "- TR-064 descriptor reachable: yes" echo "- Model: ${model:-unknown}" else echo "- TR-064 descriptor reachable: no" fi rm -f /tmp/guest-iot-fritzbox-tr064.xml echo echo "## Host listeners" echo echo '```text' ss -ltnp | sort -k4 | grep -E ':(53|80|443|222|8082|8181)[[:space:]]' || true echo '```' echo echo "## Port reachability from host namespace" echo echo "| Check | Target | Status | Expectation |" echo "|---|---|---|---|" for port in 80 443 222 53; do if tcp_check "$HOST_LAN_IP" "$port"; then result_row "LAN service" "$HOST_LAN_IP:$port" "may be reachable from normal LAN; must be blocked from guest Wi-Fi" "reachable" else result_row "LAN service" "$HOST_LAN_IP:$port" "not reachable from host namespace or UDP-only" "blocked" fi done if tcp_check "$HOST_LAN_IP" 8082; then result_row "AdGuard Admin via LAN IP" "$HOST_LAN_IP:8082" "should be blocked" "reachable" else result_row "AdGuard Admin via LAN IP" "$HOST_LAN_IP:8082" "should be blocked" "blocked" fi if tcp_check "$TAILSCALE_IP" 8082; then result_row "AdGuard Admin via Tailscale IP" "$TAILSCALE_IP:8082" "operator path should work" "reachable" else result_row "AdGuard Admin via Tailscale IP" "$TAILSCALE_IP:8082" "operator path should work" "blocked" fi if tcp_check "$HOST_LAN_IP" 8181; then result_row "InfluxDB via LAN IP" "$HOST_LAN_IP:8181" "should be blocked unless HA LAN writer is reintroduced" "reachable" else result_row "InfluxDB via LAN IP" "$HOST_LAN_IP:8181" "should be blocked unless HA LAN writer is reintroduced" "blocked" fi echo echo "## Next operator step" echo echo "Enable FRITZ!Box guest Wi-Fi only after confirming LAN isolation is active. Then connect a phone/laptop to guest Wi-Fi and run:" echo echo '```powershell' echo 'G:\Gitea_Clone\homelab-infra\ops\maintenance\check-guest-iot-isolation.ps1' echo '```' } | tee "$REPORT_FILE" echo "Guest/IoT preflight report: $REPORT_FILE"