diff --git a/Sources/Homelab/travel-pi-router.md b/Sources/Homelab/travel-pi-router.md new file mode 100644 index 0000000..2154cd4 --- /dev/null +++ b/Sources/Homelab/travel-pi-router.md @@ -0,0 +1,62 @@ +--- +created: '2026-06-26' +path: Sources/Homelab +project: travel-pi-router +tags: +- dns +- tailscale +type: 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 `) 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 ` + `sudo resolvectl flush-caches`, load portal, auth, then `sudo resolvectl revert `. 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. \ No newline at end of file