Files
obsidian-vault/wiki/tech/wireguard-vpn.md
T

3.7 KiB

title, created, updated, type, namespace, tags, sources, confidence
title created updated type namespace tags sources confidence
WireGuard VPN — Eagle to Kraken 2026-05-23 2026-05-23 tech personal
infra
kraken
family/how-to/wireguard-vpn.md
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

# 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