4.7 KiB
| created | path | project | tags | type | ||
|---|---|---|---|---|---|---|
| 2026-06-26 | Sources/Homelab | travel-pi-router |
|
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. resolvectllink 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.gzon 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, thensudo 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.devfailing 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 mappingherbylab.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.