wiki-vault/Sources/Homelab/wireguard-postgres-access.md

105 lines
5.0 KiB
Markdown

---
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 <user> 10.99.0.0/24 scram-sha-256`
- [ ] Reload Postgres
- [ ] Test connection from tower: `psql -h db.wg.herbylab.dev -U <user> -d <db>`
- [ ] 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)