Umbrella issue for turning this operator into a Kubernetes-native VPN gateway: a WireGuard server that terminates client tunnels and forwards their traffic onward through gluetun to a commercial VPN provider, with clients declared explicitly or injected into existing workloads by label.
Full design rationale, architecture and leak analysis: docs/ROADMAP.md.
Phase 0 — Foundations
Blocks everything else. #1 and #2 in particular: until there is one Deployment builder and declarative reconciliation, every new spec field needs a bespoke drift check, and anything implemented in internal/resources has no runtime effect at all.
Phase 1 — Pod extension points
Phase 2 — gluetun
Phase 3 — Client paths and ingress
Phase 4 — Webhook infrastructure
Phase 5 — Implicit clients
Phase 6 — Project
Decisions
Recorded so they don't get relitigated. Rationale in docs/ROADMAP.md.
Single server, not HA. A generic WireGuard client already reconnects on its own after the server pod reschedules, with no client-side logic, given a stable endpoint address and a persisted server key — both of which already hold. WireGuard has no connection to lose. Recovery is ~20–40s for a pod crash and tunable for node failure via tolerationSeconds. HA would have cost multi-peer config generation, a failover sidecar, metric semantics, per-server credential management, and a permanent split between how internal and external clients fail over. The CRD stays forward-compatible so HA remains additive.
Three client paths. ClusterIP for in-cluster pods, LoadBalancer UDP for mobile and stock clients, Gateway plus wstunnel for obfuscated desktop clients. Mobile cannot use the tunnel path — PreUp and friends are wg-quick features and are unsupported on iOS and Android.
LoadBalancer by default for raw UDP, UDPRoute opt-in. Envoy Gateway supports UDPRoute, but it proxies non-transparently and puts every VPN packet through a userspace proxy. Gateway API is used unconditionally for the wstunnel path, where L7 termination is the actual value.
Implicit clients are minted per Pod, not per Deployment. A peer identity shared across replicas misdelivers silently: WireGuard tracks one endpoint per public key, so the server sends pod A's return traffic to whichever pod handshook last.
Opt-in is a label, not an annotation. Webhook objectSelector matches labels only.
Killswitch in two layers. In-pod rules work on any CNI; the CNI policy survives sidecar crash or interface teardown, which is the failure that actually leaks.
Umbrella issue for turning this operator into a Kubernetes-native VPN gateway: a WireGuard server that terminates client tunnels and forwards their traffic onward through gluetun to a commercial VPN provider, with clients declared explicitly or injected into existing workloads by label.
Full design rationale, architecture and leak analysis:
docs/ROADMAP.md.Phase 0 — Foundations
Blocks everything else. #1 and #2 in particular: until there is one Deployment builder and declarative reconciliation, every new spec field needs a bespoke drift check, and anything implemented in
internal/resourceshas no runtime effect at all.PersistentKeepaliveto generated peer configs — independently useful todayPhase 1 — Pod extension points
spec.initContainersandspec.extraContainersspec.volumesand volume mounts for user-supplied containersPhase 2 — gluetun
spec.gluetunAPIsecretRefspec.dnsPhase 3 — Client paths and ingress
HTTPRoutefor the wstunnel pathUDPRouterendering for the direct pathspec.gateway.hostnameand watch Gateway statusPhase 4 — Webhook infrastructure
Phase 5 — Implicit clients
wireguardRefwith bidirectional consentPhase 6 — Project
Decisions
Recorded so they don't get relitigated. Rationale in
docs/ROADMAP.md.Single server, not HA. A generic WireGuard client already reconnects on its own after the server pod reschedules, with no client-side logic, given a stable endpoint address and a persisted server key — both of which already hold. WireGuard has no connection to lose. Recovery is ~20–40s for a pod crash and tunable for node failure via
tolerationSeconds. HA would have cost multi-peer config generation, a failover sidecar, metric semantics, per-server credential management, and a permanent split between how internal and external clients fail over. The CRD stays forward-compatible so HA remains additive.Three client paths. ClusterIP for in-cluster pods, LoadBalancer UDP for mobile and stock clients, Gateway plus wstunnel for obfuscated desktop clients. Mobile cannot use the tunnel path —
PreUpand friends are wg-quick features and are unsupported on iOS and Android.LoadBalancer by default for raw UDP,
UDPRouteopt-in. Envoy Gateway supports UDPRoute, but it proxies non-transparently and puts every VPN packet through a userspace proxy. Gateway API is used unconditionally for the wstunnel path, where L7 termination is the actual value.Implicit clients are minted per Pod, not per Deployment. A peer identity shared across replicas misdelivers silently: WireGuard tracks one endpoint per public key, so the server sends pod A's return traffic to whichever pod handshook last.
Opt-in is a label, not an annotation. Webhook
objectSelectormatches labels only.Killswitch in two layers. In-pod rules work on any CNI; the CNI policy survives sidecar crash or interface teardown, which is the failure that actually leaks.