--- created: '2026-05-25' path: Sources/Homelab project: wireguard-postgres-access tags: - wireguard - postgres - networking - knot - homelab - herbys-dev type: project-plan --- ## Summary Stand up a hand-rolled WireGuard hub-and-spoke overlay terminating on herbys-dev, with Postgres (currently serving 5 MCP services) as the first consumer. Tower (Lab/11) connects over the tunnel to access Postgres on herbys-dev (Private/21) without opening a Lab→Private firewall hole. Knot DNS serves `*.wg.herbylab.dev` for stable hostname access. ## Goals - Tower → herbys-dev Postgres access without VLAN firewall changes - Future-proof shape: additional peers (other hosts, other services) drop in cleanly - Knot DNS authoritative for the overlay subdomain from day one - Manual setup first, Ansible-ize later once the shape is proven ## Architecture Decisions - **Topology**: Hub-and-spoke. herbys-dev is the hub (services live there); all other peers dial in. - **Termination point**: WireGuard interface on the herbys-dev VM host (not inside containers). Services bind to the hub WG IP via Docker port mapping. - **Interface name**: `wg-lan` - **Subnet**: `10.99.0.0/24` (no collision with `10.0.x.x` LAN VLANs) - hub (herbys-dev): `10.99.0.1` - tower: `10.99.0.2` - room for future peers `.3`+ - **Listen port**: UDP/51820 on herbys-dev - **Key management**: per-host generation; private keys never leave the host; public keys registered with hub - **DNS**: Knot serves `wg.herbylab.dev` zone. Names like `db.wg.herbylab.dev` → `10.99.0.1`, `herbys-dev.wg.herbylab.dev` → `10.99.0.1`, etc. - **Postgres exposure**: Compose `ports:` rebinds from localhost to `10.99.0.1:5432`. `pg_hba.conf` allows `10.99.0.0/24`. ## Why not the alternatives - **Direct VLAN routing**: opens Lab→Private firewall hole; any future Lab host can hit Postgres. Tunnel-as-access-control is cleaner. - **Tailscale**: works fine and is peer-to-peer on LAN, but adds a third-party control plane for a two-host LAN-local need. WireGuard is already in-kernel. - **Traefik TCP passthrough**: wrong layer for L4 forwarding of one port. Traefik stays HTTP(S)-focused. - **SSH tunnel**: per-session, dies with the connection. Not viable for always-on MCPs. ## Phases ### Phase 1 — Hub setup on herbys-dev - [ ] Install `wireguard` package (Ubuntu repos) - [ ] Generate hub keypair (`wg genkey | tee privatekey | wg pubkey > publickey`) - [ ] Write `/etc/wireguard/wg-lan.conf` with `[Interface]` block (private key, address `10.99.0.1/24`, listen port 51820) - [ ] Open UDP/51820 on herbys-dev firewall + OpenWrt firewall rule allowing tower VLAN → herbys-dev:51820 - [ ] `systemctl enable --now wg-quick@wg-lan` - [ ] Verify interface up with `wg show` ### Phase 2 — Tower peer - [ ] Install `wireguard` on tower (Endeavour OS) - [ ] Generate tower keypair - [ ] Add tower's `[Peer]` block to hub config; reload hub - [ ] Write tower's `/etc/wireguard/wg-lan.conf` with `[Interface]` (tower key, `10.99.0.2/24`) and `[Peer]` (hub pubkey, endpoint `10.0.21.207:51820`, AllowedIPs `10.99.0.0/24`, PersistentKeepalive 25) - [ ] Bring tunnel up; verify handshake with `wg show` - [ ] Ping `10.99.0.1` from tower ### Phase 3 — Knot DNS records - [ ] Add `wg.herbylab.dev` zone (or records under existing zone) - [ ] `db.wg.herbylab.dev` → `10.99.0.1` - [ ] `herbys-dev.wg.herbylab.dev` → `10.99.0.1` - [ ] Verify resolution from tower via Pi-hole conditional forwarding ### Phase 4 — Postgres rebind - [ ] Update Postgres container compose: `ports: - "10.99.0.1:5432:5432"` - [ ] Verify `listen_addresses = '*'` in `postgresql.conf` - [ ] Add `pg_hba.conf` rule: `host all 10.99.0.0/24 scram-sha-256` - [ ] Reload Postgres - [ ] Test connection from tower: `psql -h db.wg.herbylab.dev -U -d ` - [ ] Verify existing 5 MCP services still connect (they're on the host's loopback or docker network — bind change shouldn't affect them, but verify) ### Phase 5 — Documentation & follow-ups - [ ] Document peer registration steps for future hosts - [ ] Capture key rotation procedure - [ ] Decide Ansible role shape (extend existing in-progress role) ## Open Questions - Does the Postgres rebind to `10.99.0.1:5432` break the 5 existing MCP services? Need to confirm how they currently connect (loopback on host? docker network?). May need to bind to multiple addresses or rethink published port. - Should `wg-lan` come up before Docker, so Postgres can bind to it on boot? `After=wg-quick@wg-lan.service` on the docker unit, or reorder via compose. ## Future-Proofing Notes - Subnet has room for ~250 peers; way more than needed - New peer = one `[Peer]` block on hub + one config on new host - Any future service on herbys-dev can bind to `10.99.0.1` and inherit the same access model - When Ansible role lands, hub config becomes templated from peer registry ## References - Related: `traefik-deployment` (Knot DNS already deployed) - Related: `postgres-consolidation-reflection-layer` (the Postgres instance this exposes) - Related: `herbys-dev-setup` (host this terminates on)