diff --git a/clusters/base/cluster-vars.yaml b/clusters/base/cluster-vars.yaml index 998f5b3..5508c31 100644 --- a/clusters/base/cluster-vars.yaml +++ b/clusters/base/cluster-vars.yaml @@ -37,17 +37,18 @@ data: KYVERNO_FORCE_FAILURE_POLICY_IGNORE: "false" # dev: "true" (chart forceFailurePolicyIgnore = fail-open) KYVERNO_ADMISSION_REPLICAS: "3" # dev: 1 (no HA needed to iterate) - # --- egress / VPN (Phase 3, ADR-0013) — consumed by platform/vpn-egress/gluetun-gateway.yaml --- - PIA_REGION: "SE Stockholm" # operator's choice (Sweden). PIA's OpenVPN region name is - # "SE Stockholm" — PF-capable (NOT "SE Streaming Optimized", - # which lacks PF; NOT a US region, which also lack PF) - CLUSTER_POD_CIDR: "10.0.0.0/16" # Gluetun FIREWALL_OUTBOUND_SUBNETS (bypass for steered client pods). - # NARROWED from the /8 cluster-pool: /8 overlaps PIA's tunnel gateway - # 10.31.128.1, so gluetun routed PIA's own PF gateway AROUND the tunnel - # → port forwarding timed out. /16 covers actual pod allocations - # (Cilium assigns /24 per node from 10.0.0.0 up) and excludes 10.31.x. - # NOTE: Phase-3 VPN uses PIA's default OpenVPN UDP port + gluetun's server-list UPDATER (the bundled list - # was stale, dialing retired IPs). Port/region are not cluster-vars (see gluetun-gateway.yaml). See DECISIONS-LOG. + # --- egress / VPN (Phase 3, ADR-0015) — node-level host OpenVPN tunnel steered by Cilium Egress + # Gateway (platform/vpn-egress/egress-steering.yaml). No in-cluster gateway pod. --- + PIA_REGION: "SE Stockholm" # PF-capable PIA region (Sweden). Under ADR-0015 the tunnel is + # host-side (Ansible vault / the node's .ovpn per docs/07), so this + # is now a reference default only — retained, not substituted into + # any manifest. ("SE Stockholm" is PF-capable; NOT "SE Streaming + # Optimized" / a US region, which lack PF.) + CLUSTER_POD_CIDR: "10.0.0.0/16" # Documents the Cilium cluster-pool pod CIDR that the egress-steering + # CiliumEgressGatewayPolicy carves out of the tunnel via excludedCIDRs + # (kept on the LOCAL datapath, never SNAT'd out tun0). The policy + # currently excludes the whole /8 RFC1918 range; this /16 records the + # actual pod allocations (Cilium assigns /24 per node from 10.0.0.0 up). # --- identity / auth (ADR-0014) --- KEYCLOAK_REPLICAS: "1" # dev 1 | prod 2 (HA) diff --git a/platform/kustomization.yaml b/platform/kustomization.yaml index d7fb3b4..c9db73f 100644 --- a/platform/kustomization.yaml +++ b/platform/kustomization.yaml @@ -17,11 +17,10 @@ resources: - cert-manager # Let's Encrypt DNS-01 wildcard — needs SOPS `cloudflare-api-token` - externaldns # auto DNS records — needs SOPS `cloudflare-api-token` - gateway # Gateway API listeners (ingress; replaces Traefik) - # ---- Phase 3 — Egress / VPN (PIA OpenVPN-UDP, ADR-0013 amendment). Real root cause of the earlier - # failures: gluetun's bundled PIA server list was STALE (dialing retired 46.246.x IPs → timeouts); - # fix = UPDATER_PERIOD (fetch current servers) + OPENVPN_MTU/MSSFIX 1350 for the Cilium-VXLAN 1450 - # pod MTU. See DECISIONS-LOG "Phase 3 VPN". ---- - - vpn-egress # shared Gluetun PIA/OpenVPN-UDP egress gateway + kill-switch (ADR-0013) + # ---- Phase 3 — Egress / VPN (ADR-0015): node-level host OpenVPN split-tunnel (Ansible role + # vpn_egress_gateway) steered by a Cilium Egress Gateway policy, with a host nftables kill-switch. + # No in-cluster gateway pod (Gluetun retired). See ADR-0015 + DECISIONS-LOG "Phase 3 VPN". ---- + - vpn-egress # CiliumEgressGatewayPolicy steering egress-class=vpn pods to the node tun0 (ADR-0015) # ---- Phase 4 — Observability (creds provisioned via SOPS: grafana-admin, alertmanager-smtp) ---- - monitoring # kube-prometheus-stack + Loki + Alloy + Alertmanager # ---- Later phases: enabled when their phase opens ---- diff --git a/platform/kyverno/policies/require-egress-class-label.yaml b/platform/kyverno/policies/require-egress-class-label.yaml index 3e4d813..647e352 100644 --- a/platform/kyverno/policies/require-egress-class-label.yaml +++ b/platform/kyverno/policies/require-egress-class-label.yaml @@ -2,7 +2,8 @@ # Spec: docs/03 "Egress is a declared property"; docs/13 `egress: internet | vpn`. # # The egress class is enforced OUTSIDE the service (Cilium Egress Gateway selects -# pods by label -> steers `vpn` pods through the shared PIA/Gluetun gateway, ADR-0013). +# pods by label -> steers `vpn` pods out the egress-gateway node's host tun0 VPN +# tunnel, ADR-0015). # For that steering to be possible the label MUST exist on the pod and be one of the # two valid classes. A missing/typo'd class would silently fall back to the plain # internet path -- a fail-OPEN egress leak -- so we require it explicitly (fail-closed diff --git a/platform/vpn-egress/README.md b/platform/vpn-egress/README.md index 936cd1b..5857fb7 100644 --- a/platform/vpn-egress/README.md +++ b/platform/vpn-egress/README.md @@ -1,70 +1,55 @@ -# platform/vpn-egress — shared VPN egress gateway (Phase 3, ADR-0013) +# platform/vpn-egress — VPN egress steering (Phase 3, ADR-0015) -Single shared **Gluetun** gateway: **PIA over OpenVPN-UDP** (ADR-0013 amendment — Gluetun does not -support 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**. +`vpn`-class services (sonarr, radarr, jackett, qbittorrent) egress **only** through a PIA VPN +tunnel; `internet`-class services go out the normal WAN. Serves **R-NET-05/06/07**. -> **STATUS: enabled in `platform/kustomization.yaml` (Phase 3).** `pia-credentials` SOPS secret is -> provisioned. Remaining work: validate the tunnel comes up + correct egress IP, then the -> egress-steering mechanism below (the hard part) and the kill-switch conformance test. +Egress is realized per **[ADR-0015](../../../home-network-k3s/decisions/adr-0015-vpn-egress-steering-node-tunnel-cilium.md)** +(spec `docs/03`): a **node-level host OpenVPN split-tunnel** on a labeled egress-gateway node, steered +by a **Cilium Egress Gateway** policy, with a **host nftables kill-switch**. There is **no in-cluster +gateway pod** — the tunnel is host infrastructure, not a Flux workload. -## 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 ships PIA's OpenVPN configs. +> **STATUS: live + conformance-green.** The node tunnel was validated end-to-end; the `egress-class` +> (R-NET-05/07) and `vpn-killswitch` (R-NET-06) conformance tests pass against the live cluster. -## 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. cluster-vars (already defined in `clusters/base/cluster-vars.yaml`; a ConfigMap — values are plain - strings under `data:`, injected into the `${...}` tokens by Flux `postBuild.substituteFrom`). Confirm - or override: - - `PIA_REGION` — a **port-forwarding-capable** PIA region (default `Sweden`; e.g. `Netherlands`, - `CA Toronto`, `Switzerland`; NOT a US region — those lack PF). Gluetun `SERVER_REGIONS`. - - `CLUSTER_POD_CIDR` — the **Cilium cluster-pool** pod CIDR (default `10.0.0.0/8`; pods get `10.x`, - confirmed live — NOT the k3s `10.42.0.0/16`). Gluetun `FIREWALL_OUTBOUND_SUBNETS`. - Per-env overrides go in `clusters//kustomization.yaml` as a cluster-vars JSON patch - (`- op: replace, path: /data/PIA_REGION, value: "…"`). With the self-managing root, a `git push` - auto-applies (no manual `kubectl apply -k`). -3. Enable `- vpn-egress` in `platform/kustomization.yaml` (Phase 3). +## How it works (ADR-0015) +Three layers, two owners: -## 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: +- **Tunnel — host OpenVPN (Ansible).** A labeled egress-gateway node runs a **host OpenVPN systemd + service** (PIA over OpenVPN-UDP — provider/protocol still per ADR-0013). It is **split-tunnel** + (`route-noexec` + policy routing): it creates `tun0` but never becomes the node default route, so the + node's own traffic (control plane, image pulls, other pods) stays on the WAN. Managed by the Ansible + role **`infra/ansible/roles/vpn_egress_gateway/`**, like k3s itself — not a cluster workload. +- **Steering — Cilium Egress Gateway (GitOps).** `egress-steering.yaml` holds a + `CiliumEgressGatewayPolicy` that selects pods labeled `homelab.keithjackson.dev/egress-class=vpn` + (Kyverno-enforced) and SNATs them out the gateway node's `tun0`. Native Cilium eBPF — no client-pod + changes, no sidecar, no overlay. `excludedCIDRs` keeps RFC1918 / cluster / LAN traffic on the local + datapath; only public destinations leave via the tunnel. +- **Kill-switch — host nftables (Ansible).** A host nftables rule on the gateway node DROPS the steered + `vpn`-class traffic whenever `tun0` is down (fail-closed, R-NET-06), scoped to vpn-class only so + `internet`-class and node traffic are unaffected. -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 tunnel interface (`tun0`, OpenVPN). If the tunnel drops, `tun0` - 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). +## Secrets — host-side now +PIA credentials + the `.ovpn` live **on the gateway node via Ansible vault** (see spec `docs/07`), **not** +a cluster SOPS Secret. SOPS still governs in-cluster secrets; VPN egress simply no longer needs one. -### FINDING (2026-08-23): option 1 is NOT viable with Gluetun — needs an architect decision -With the tunnel now live (custom-mode Gluetun, PIA Stockholm egress), option 1 fails on inspection: -`CiliumEgressGatewayPolicy interface: tun0` requires `tun0` on the **node**, which means running Gluetun -`hostNetwork: true`. But Gluetun *owns its netns routing* — on connect its `[routing]` logic adds -`0.0.0.0/0` via the tunnel. In hostNetwork that would **redirect the NODE's own default route through the -VPN**, breaking k3s/etcd/kubelet on that node. Gluetun has no supported "create tun0 but don't touch the -default route" mode, so option 1 can't expose `tun0` on the node safely. (The external-router-offload -pattern some use — MikroTik/RouterOS policy routing per pod-subnet — is out of scope: ADR-0013 mandates an -*in-cluster* gateway on a dumb SOHO edge.) +The `pia-credentials.sops.yaml` and `pia-custom-ovpn.sops.yaml` secrets in this directory are **now +UNUSED** — leftovers from the retired Gluetun pod, **retained pending a separate cleanup change**. -**→ Escalated.** The remaining viable path is **option 2 (gateway-pod routing)**: `egress-class=vpn` pods -default-route to the Gluetun pod via an init-container (+NET_ADMIN), and Gluetun forwards+NATs out `tun0`. -More moving parts, and it requires Gluetun to forward for *foreign* pods (not its native same-netns model), -so it needs validation. Kill-switch (R-NET-06) then relies on Gluetun's `FIREWALL=on` dropping non-tunnel -egress. Fixtures for the `egress-class`/`vpn-killswitch` conformance tests are authored; the -`CiliumEgressGatewayPolicy` / steering datapath is intentionally NOT committed until the mechanism is chosen -+ validated, to avoid shipping an untested (and potentially node-breaking) datapath. +## Pieces in this directory +- `namespace.yaml` — the `vpn-egress` namespace (now holds only the Cilium policy + the retained, + unused secrets; no privileged gateway pod under ADR-0015). +- `egress-steering.yaml` — the `CiliumEgressGatewayPolicy` (the steering datapath). +- `pia-credentials.sops.yaml`, `pia-custom-ovpn.sops.yaml` — **unused**, retained pending cleanup. + +## History (brief) +ADR-0013 originally placed the VPN in a single shared **Gluetun pod** and deferred the steering +mechanism. Implementation proved that a Gluetun-in-a-pod tunnel **cannot be a Cilium Egress Gateway +target**: the policy SNATs out a *node* interface, but the pod's `tun0` lives in the pod netns, and +running Gluetun `hostNetwork` to expose `tun0` on the node hijacks the node's default route (breaking +k3s/etcd/kubelet). ADR-0015 resolved this by moving the tunnel to the **node** (host OpenVPN +split-tunnel) and steering with Cilium's native egress gateway. See ADR-0015 for the full rationale. ## 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). +PIA assigns a forwarded port that can change on reconnect. Under ADR-0015 this is re-solved **host-side** +(a small pf/natpmp helper on the gateway node that publishes the assigned port to the app) — Phase 6, +alongside the qBittorrent app. diff --git a/platform/vpn-egress/namespace.yaml b/platform/vpn-egress/namespace.yaml index 3e4ef14..1e229a3 100644 --- a/platform/vpn-egress/namespace.yaml +++ b/platform/vpn-egress/namespace.yaml @@ -1,8 +1,11 @@ --- -# vpn-egress — the single shared VPN egress gateway namespace (ADR-0013, docs/03). -# Platform INFRA, not an application namespace: the Gluetun gateway needs NET_ADMIN + /dev/net/tun, -# so this ns is deliberately NOT labeled `app-namespace=true` (Kyverno restrict-host-namespaces / -# require-*-label policies scope to app namespaces only — see platform/kyverno). +# vpn-egress — the VPN egress namespace (ADR-0015, docs/03). +# Under ADR-0015 there is NO privileged in-cluster gateway pod: the tunnel is a node-level host +# OpenVPN service (Ansible role vpn_egress_gateway). This namespace now holds only the +# CiliumEgressGatewayPolicy (egress-steering.yaml) plus the retained, now-unused pia-* secrets. +# Platform INFRA, not an application namespace, so it is deliberately NOT labeled `app-namespace=true` +# (Kyverno restrict-host-namespaces / require-*-label policies scope to app namespaces only — see +# platform/kyverno). apiVersion: v1 kind: Namespace metadata: diff --git a/platform/vpn-egress/pia-credentials.sops.yaml b/platform/vpn-egress/pia-credentials.sops.yaml index 900669c..f717f1c 100644 --- a/platform/vpn-egress/pia-credentials.sops.yaml +++ b/platform/vpn-egress/pia-credentials.sops.yaml @@ -1,19 +1,12 @@ # ============================================================================= -# TEMPLATE — NOT APPLIED, NOT ENCRYPTED (.yaml.example is ignored by kustomize -# and never auto-encrypted by SOPS). Reference scaffold only. +# UNUSED after Gluetun retirement (ADR-0015). This Secret was consumed by the +# in-cluster Gluetun gateway, which no longer exists: VPN egress is now a +# node-level host OpenVPN tunnel steered by Cilium Egress Gateway, and PIA +# credentials live HOST-SIDE in the Ansible vault (see spec docs/07), not here. # -# (a) Copy to: platform/vpn-egress/pia-credentials.sops.yaml -# (b) kustomization: uncomment `- pia-credentials.sops.yaml` in platform/vpn-egress/kustomization.yaml -# (c) Encrypt: sops -e -i platform/vpn-egress/pia-credentials.sops.yaml -# (d) Consumed by: the Phase-3 Gluetun VPN egress gateway (platform/vpn-egress/gluetun-gateway.yaml), -# which maps username->OPENVPN_USER, password->OPENVPN_PASSWORD. [Phase 3] -# -# VALUE NOTES: -# - username: PIA (Private Internet Access) user, form pNNNNNNN. -# - password: PIA password. -# - Protocol is WireGuard (VPN_TYPE=wireguard, UDP — satisfies "not TCP"). PIA on WireGuard STILL -# authenticates with the username/password below (Gluetun derives the WireGuard config from PIA's -# API at runtime), so these two keys are correct as-is — no extra WireGuard key material needed. +# RETAINED temporarily, pending a separate cleanup change that will remove this +# file and drop it from platform/vpn-egress/kustomization.yaml. Do NOT wire it +# into any workload. The encrypted payload below is left intact. # ============================================================================= apiVersion: v1 kind: Secret diff --git a/platform/vpn-egress/pia-custom-ovpn.sops.yaml b/platform/vpn-egress/pia-custom-ovpn.sops.yaml index 20a0614..218bf5a 100644 --- a/platform/vpn-egress/pia-custom-ovpn.sops.yaml +++ b/platform/vpn-egress/pia-custom-ovpn.sops.yaml @@ -1,3 +1,13 @@ +# ============================================================================= +# UNUSED after Gluetun retirement (ADR-0015). This Secret held the PIA .ovpn +# for the in-cluster Gluetun gateway, which no longer exists: VPN egress is now +# a node-level host OpenVPN tunnel steered by Cilium Egress Gateway, and the +# .ovpn now lives HOST-SIDE in the Ansible vault (see spec docs/07), not here. +# +# RETAINED temporarily, pending a separate cleanup change that will remove this +# file and drop it from platform/vpn-egress/kustomization.yaml. Do NOT wire it +# into any workload. The encrypted payload below is left intact. +# ============================================================================= apiVersion: v1 data: sweden-aes-128-cbc-udp-ip.ovpn: ENC[AES256_GCM,data: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,iv:F6dC6trQxJzYxWScQWgeH3WcD/o3paf3HE4Joa+2Tfg=,tag:ZDUfSj+pu+iZSFYMOHmvjQ==,type:str] diff --git a/schemas/servicedefinition.schema.json b/schemas/servicedefinition.schema.json index f78f643..122e033 100644 --- a/schemas/servicedefinition.schema.json +++ b/schemas/servicedefinition.schema.json @@ -46,7 +46,7 @@ }, "egress": { "type": "string", - "description": "Egress class (docs/03; R-NET-05). internet = default WAN route; vpn = steered through the shared in-cluster Gluetun/PIA gateway with a mandatory kill-switch (R-NET-06).", + "description": "Egress class (docs/03; R-NET-05). internet = default WAN route; vpn = steered by Cilium Egress Gateway out the egress-gateway node's host PIA VPN tunnel, with a mandatory host kill-switch (ADR-0015; R-NET-06).", "enum": ["internet", "vpn"], "default": "internet" },