--- # Flux Kustomization: WireGuard remote-access VPN — reconciles ./platform/vpn-ingress. # # POST-PLATFORM TIER (dependsOn platform-config), NON-GATING (wait:false). Not a line in # platform/kustomization.yaml, for the standard reason plus one specific to this component: # # * STANDARD: a leaf must never sit in the gating `platform` tier. While `platform` is not-Ready # NOTHING downstream reconciles — the incident behind the headlamp/oauth2-proxy/homepage moves # (DECISIONS-LOG: "Headlamp gating stall"). # * SPECIFIC, AND THE REASON THIS IS NOT A JUDGEMENT CALL: this DaemonSet cannot start until the # operator replaces the `REPLACE-ME` placeholder in platform/vpn-ingress/wg-server-key.sops.yaml # with a real key (runbooks/vpn-remote-access.md step 2). That is the correct fail-closed state — an unenrolled VPN must not come up — but in # the gating tier it would have been a self-inflicted platform-wide outage ON MERGE, with the # cause (a missing manual step) invisible from any Flux condition. # # WHY dependsOn platform-config AND NOT cilium. This component needs no Gateway, no TLS cert, no # Keycloak and no LB-IPAM pool — it has no HTTPRoute and no LoadBalancer Service, and hostNetwork # means it does not even need a Cilium endpoint. It could legally reconcile much earlier. It is # pinned behind platform-config anyway so that the ONE thing remote clients actually consume # through it — split-horizon DNS and the shared Gateway — exists by the time the tunnel does. # A tunnel that comes up before the things on the far side of it is a support call, not a feature. # # wait:false: nothing in the graph dependsOn this, and its pods are legitimately not-Ready whenever # the server key is absent or the `wireguard` kernel module has not been loaded on a node. Neither # condition should be able to gate anything else, even if a future resource is mistakenly pointed # at it. apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: vpn-ingress namespace: flux-system spec: interval: 10m retryInterval: 2m timeout: 5m prune: true wait: false dependsOn: - name: platform-config sourceRef: kind: GitRepository name: flux-system path: ./platform/vpn-ingress # REQUIRED, not boilerplate: platform/vpn-ingress/daemonset.yaml renders ${WG_CLIENT_CIDR} into # the config-assembly script. Without this block that token becomes the EMPTY STRING and the # Kustomization still reports Ready (Flux's substitution fails OPEN — ci: # flux-postbuild-substitution), leaving an initContainer that derives a server address of ".1/" # and a MASQUERADE rule with no source. Both fail loudly at pod start, but the Kustomization # would look healthy. postBuild: substituteFrom: - kind: ConfigMap name: cluster-vars # SOPS decryption is layered per-environment by the overlay (clusters/dev and clusters/prod each # patch THIS Kustomization by name), identical to headlamp/crafty/velero — this path carries # platform/vpn-ingress/wg-server-key.sops.yaml, the WireGuard server private key. The `sops-age` # Secret is created out of band and referenced by NAME only. Without the overlay patch the Secret # applies cleanly full of ENC[...] and only the tunnel is broken, so the patch is as load-bearing # as the encrypted file itself.