Files
home-network-k3s-impl/platform/vpn-egress/README.md
T
jakman85andClaude Opus 4.8 1a98429434 feat(phase3): author shared VPN egress gateway (Gluetun/PIA/WireGuard) — author-first
Per ADR-0013: platform/vpn-egress/ = a single shared Gluetun gateway (PIA over WireGuard,
built-in kill-switch FIREWALL=on, native port forwarding), namespace (non-app, NET_ADMIN +
/dev/net/tun), control-API Service, and a pre-wired (commented) pia-credentials SOPS line.
Defined the PIA secret location (platform/vpn-egress, keys username/password → OPENVPN_USER/
OPENVPN_PASSWORD) and updated the SOPS template accordingly.

NOT wired into platform/kustomization.yaml (Phase 3, author-first) — deploy needs PIA creds +
validation of the egress-steering mechanism (Cilium Egress Gateway → the Gluetun node's wg0;
documented in README with options + a fail-closed rationale, deliberately NOT committed as an
untested CiliumEgressGatewayPolicy). WireGuard chosen (UDP, protocol != TCP); PIA supports it on
all regions and the secret stays username/password.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-21 22:19:22 -05:00

51 lines
3.5 KiB
Markdown

# platform/vpn-egress — shared VPN egress gateway (Phase 3, ADR-0013)
Single shared **Gluetun** gateway: **PIA over WireGuard**, built-in kill-switch, native port
forwarding. `vpn`-class services (sonarr, radarr, jackett, qbittorrent) egress **only** through this
tunnel; `internet`-class services go out the normal WAN. Serves **R-NET-05/06/07**.
> **STATUS: authored, NOT deployed.** Not wired into `platform/kustomization.yaml` yet. Deploy needs
> (a) the `pia-credentials` SOPS secret, and (b) validation of the egress-steering mechanism below,
> which cannot be tested without a live PIA tunnel.
## Pieces
- `gluetun-gateway.yaml` — the Gluetun Deployment (`replicas: 1`, `Recreate`) + the `gluetun-control`
Service (control API :8000 for forwarded-port lookup + tunnel-state health). Kill-switch = `FIREWALL=on`.
- `pia-credentials` (SOPS) — PIA username/password only; Gluetun derives the WireGuard config at runtime.
## Operator hand-off (when deploying Phase 3)
1. Provision `pia-credentials` (see `bootstrap/sops/secret-templates/pia-credentials.yaml.example`,
namespace `vpn-egress`, keys `username`/`password`) → `sops -e -i` → uncomment it in `kustomization.yaml`.
2. Set cluster-vars: `PIA_REGION` (a **port-forwarding-capable** PIA region — e.g. `Netherlands`,
`CA Toronto`, `Switzerland`; NOT a US region, which lack PF) and `CLUSTER_POD_CIDR` (k3s default
`10.42.0.0/16`).
3. Enable `- vpn-egress` in `platform/kustomization.yaml` (Phase 3).
## Egress steering — the DESIGN DECISION (needs live validation)
The hard part: routing `vpn`-class pods through this ONE shared tunnel. Cilium Egress Gateway
(`CiliumEgressGatewayPolicy`) SNATs matched pods out of a **gateway node's interface** — it does not
natively route through a *pod's* tunnel. Options considered:
1. **Cilium Egress Gateway → the Gluetun node's tunnel interface (recommended).** Pin Gluetun to a
node; a `CiliumEgressGatewayPolicy` selects pods labeled `homelab.keithjackson.dev/egress-class=vpn`
and egresses them via that node's WireGuard interface (`wg0`). If the tunnel drops, `wg0` is gone →
traffic has no egress path → **fail-closed** (the kill-switch behavior R-NET-06 needs). This is the
most Cilium-native fit but requires Gluetun's tunnel interface to be visible to the node datapath
(hostNetwork Gluetun, or a shared netns) — the detail to VALIDATE.
2. **Gateway-pod routing:** client pods default-route to the Gluetun pod IP (init-container +
NET_ADMIN, or a Multus secondary interface); Gluetun forwards+NATs via the tunnel. Works without
Cilium egress gateway but is more moving parts and pod-IP-fragile.
3. **Per-pod Gluetun sidecar** — explicitly REJECTED by ADR-0013 (single shared gateway preferred).
**Plan:** validate option 1 against a live PIA tunnel (confirm `wg0` on the Gluetun node + a
`CiliumEgressGatewayPolicy` steering `egress-class=vpn` pods, then run the `egress-class` (R-NET-05/07)
and `vpn-killswitch` (R-NET-06) conformance tests). Fall back to option 2 if the interface isn't
addressable. The `CiliumEgressGatewayPolicy` is intentionally NOT committed until validated, to avoid
shipping an untested datapath policy.
## Port forwarding (qBittorrent) — follow-up
PIA assigns a forwarded port that can change on reconnect. A small **port-sync helper**
(`qbittorrent-natmap`) reads the current port from the `gluetun-control` API (:8000
`/v1/openvpn/portforwarded`) and writes it into qBittorrent, keeping it hands-off (R-UPD). Authored
alongside the qBittorrent app (Phase 6 apps / or here when qBittorrent lands).