--- title: WireGuard VPN — Eagle to Kraken created: '2026-05-23' updated: '2026-05-23' type: tech namespace: personal tags: [infra, kraken] sources: [family/how-to/wireguard-vpn.md] confidence: high --- # WireGuard VPN — Eagle to Kraken Split-tunnel VPN connecting Eagle (Mac M4, home) to Kraken (RPi5, home) via a VPS relay. Enables SSH to Kraken from anywhere and routes Time Machine backups over the tunnel. Two VPS interfaces avoid hairpin forwarding. ## Topology ``` Eagle (10.99.0.2) ←→ wg0 VPS (10.99.0.1) ←→ wg1 VPS (10.99.1.1) ←→ Kraken (10.99.1.2) :51820 :51821 ``` SNAT rewrites source address: Eagle packets arrive at Kraken with src `10.99.1.1` (VPS wg1), not Eagle's `10.99.0.2`. FORWARD runs wg0→wg1 on the VPS with nftables. ## VPN Addresses | Node | VPN IP | |------|--------| | VPS wg0 | 10.99.0.1 | | VPS wg1 | 10.99.1.1 | | Eagle | 10.99.0.2 | | Kraken | 10.99.1.2 | `kraken` hostname resolves via dnsmasq on VPS (`DNS = 10.99.0.1` in Eagle's wg0.conf). DNS is scoped to the `kraken` domain only (see DNS section). ## Persistence | Component | Node | How it survives reboot | |-----------|------|------------------------| | wg-quick@wg0 + wg-quick@wg1 | VPS | systemd enabled | | PostUp FORWARD + SNAT rules | VPS | embedded in `/etc/wireguard/wg0.conf` | | nftables (10.99.0.0/16 forward) | VPS | `/etc/nftables.conf`, systemd enabled | | dnsmasq (`kraken` → 10.99.1.2) | VPS | `/etc/dnsmasq.d/vpn-hosts.conf`, systemd enabled | | wg-quick@wg0 | Kraken | systemd enabled | | wg-auto LaunchDaemon | Eagle | `/Library/LaunchDaemons/top.eagle.wg-auto.plist` | ## Auto-Connect on Eagle `/usr/local/bin/wg-auto.sh` polls every 30 seconds via launchd (`StartInterval`, **not** WatchPaths): - At home (router MAC `f0:79:59:77:9b:70` visible on ARP) → `wg-quick down` - Off home network → `wg-quick up` **Why StartInterval and not WatchPaths:** WatchPaths triggered a race — `wg-quick down` changes DNS via `networksetup`, which writes to `/Library/Preferences/SystemConfiguration/`, which re-fires launchd, tearing the tunnel down immediately after connecting. ## DNS (Scoped Resolver) wg0.conf uses **no global `DNS =`**. PostUp/PostDown manage a macOS scoped resolver for the `kraken` domain only: ``` PostUp = mkdir -p /etc/resolver && \ echo "nameserver 10.99.0.1" > /etc/resolver/kraken && \ killall -HUP mDNSResponder PostDown = rm -f /etc/resolver/kraken && killall -HUP mDNSResponder ``` Result: only `kraken` resolves via VPS dnsmasq. Global DNS and Wi-Fi IP are untouched — prevents address-bar breakage during tunnel transitions. ## Time Machine | Setting | Value | |---------|-------| | Old destination (broken) | `smb://timemachine@kraken._smb._tcp.local./TimeMachine` | | New destination (working) | `smb://timemachine@kraken/TimeMachine` | mDNS (`_smb._tcp.local`) doesn't work off the local LAN. VPN DNS (`kraken` → 10.99.1.2 via dnsmasq) works from anywhere. Container on Kraken: `mbentley/timemachine:smb`, `restart: unless-stopped`. ## Public Keys | Node | Public Key | |------|------------| | VPS wg0 | `6W6hSw0JFLm0BbyeNp1g7wF7dKKTZmGvPRdir68mz0w=` | | VPS wg1 | `ssRV2497NQ3Lfzvx/FLO0jDlcCXdK/RReJQ36i0yuR8=` | | Eagle | `dDgIjBUNguodOHWoz7iFsImvKhteodHcFWM8mMe4UiE=` | | Kraken | `8kIdCKMIoMsW09/aQX6+pU0SS/rN4PysJL59w17eGRg=` | ## Diagnostics ```bash # Eagle — check tunnel state sudo wg show ping kraken # VPS ssh root@91.207.28.205 "wg show all" # Kraken ssh kraken "sudo wg show" # Eagle auto-connect log cat /var/log/wg-auto.log ``` ## Related - [[tech/kraken-network]] — Kraken SSH access, media volume paths - [[personal-os-architecture]] — Eagle hardware, home infra overview