wiki-vault/Sources/Homelab/travel-pi-router.md

4.7 KiB

created path project tags type
2026-06-26 Sources/Homelab travel-pi-router
dns
tailscale
project-plan

Goal

Stop hand-running resolvectl DNS overrides every time a captive portal won't load on the road. Build a travelmate hook that drops Pi-hole out of the DNS path on a fresh uplink association so the portal's DNS interception can fire, lets the portal auth complete, then restores Pi-hole for normal filtered browsing.

Scope this trip realistically: the association-side drop is the reliable half and the primary deliverable. The swap-back trigger (knowing when auth completed) is portal-dependent and flaky across the EU hospitality gateway mix — acceptable to leave that half manual for now.

Problem

Stack is OpenWRT + travelmate + Pi-hole on a Raspberry Pi travel router. LAN clients point at the Pi (Pi-hole) for DNS, which forwards upstream.

Captive portals deadlock: the portal needs to hijack the client's first DNS queries to serve its login page, but Pi-hole answers authoritatively, so the hijack never lands and the portal page never loads ("you're offline"). Confirmed root cause this trip — pinning a client's DNS directly at the upstream WiFi resolver (resolvectl dns <iface> <upstream>) let the portal render and auth succeeded immediately.

US vs EU difference: US portals lean on standard captive-detection probes (captive.apple.com, connectivitycheck.gstatic.com) that tolerate Pi-hole in the path. EU hospitality gateways more often rely on transparent DNS interception of whatever you query first, which Pi-hole breaks. RFC 8910 (DHCP-delivered portal URL) would sidestep all of this but adoption is uneven, with more legacy gateways in the EU mix.

A hardcoded 1.1.1.1 secondary resolver makes this worse — queries that escape to Cloudflare can't be hijacked, so the portal never triggers. During the portal phase DNS must point only at the upstream-provided resolver.

Locked Decisions

  • Primary deliverable is the association-side Pi-hole drop; swap-back can stay manual for now.
  • OpenWRT config backup is mandatory before any travelmate edits. Failure mode of a buggy DNS-rewrite hook is "no DNS at all," which on a captive network means you can't reach the portal to recover — i.e. locked out over SSH in a hotel room. Backup is the rollback.
  • No 1.1.1.1 (or any fixed public) secondary resolver in the portal-phase config — it lets queries escape the hijack.
  • resolvectl link overrides are runtime-only (don't survive reconnect/reboot); fine as the manual stopgap, not a persistent fix.

Open Items

  • Confirm travelmate version and exact hook filename/mechanism (opkg list-installed | grep travelmate) — hook name changed across releases.
  • Decide swap-back trigger: manual for this trip vs. a detection probe against a known-good upstream domain once stable.
  • Validate the swap-back logic before trusting it (don't ship a hook that can strand DNS).
  • Decide whether to build on the road (stable connection + snapshot) or defer to home where rollback is safe.

Phases

Phase 0 — Backup (do first, non-negotiable)

  • sysupgrade -b /tmp/backup-$(date +%Y%m%d).tar.gz on the router
  • Copy the backup tarball off the router to the laptop
  • Confirm the tarball is on the laptop before any config change

Phase 1 — Association-side drop (reliable half)

  • Identify the travelmate login/captive hook for the installed version
  • Hook rewrites OpenWRT dnsmasq to upstream-only DNS on new uplink association (Pi-hole out of path)
  • Test: associate to a portal network, confirm the login page renders and auth completes

Phase 2 — Swap-back (portal-dependent, optional)

  • Define auth-complete detection (probe a known domain through the upstream)
  • On detection, restore Pi-hole as LAN resolver
  • Validate swap-back end to end; fall back to manual restore if detection is unreliable

Notes

  • Manual stopgap that works right now: sudo resolvectl dns <iface> <upstream-ip> + sudo resolvectl flush-caches, load portal, auth, then sudo resolvectl revert <iface>. Auth survives the revert because the portal tracks session by MAC, not DNS.
  • Tailscale split-DNS is a separate concern surfaced same session: herbylab.dev failing to resolve while public domains resolve = MagicDNS not routing the private zone to the home resolver. Fix lives in Tailscale admin → DNS (split-DNS / restricted-nameserver entry mapping herbylab.dev → home resolver tailnet IP), not in the travel-router config. Track separately if it persists once portals are out of the picture.
  • Reaching home services remotely requires Tailscale up with an approved subnet route; split-DNS resolving a private LAN IP with no route still won't connect.