correction: credential-forward + unified graph; activate SOPS (author-first)

Operator directive: don't diverge architecture to avoid asking for credentials;
provision them upfront via SOPS and keep the existing (unified) graph.

Roadmap reconciliation: Phase 2 = Network/Storage/TLS/DNS/Ingress, so
cert-manager/externaldns/gateway (the credential-gated pieces) ARE Phase 2;
monitoring is Phase 4. Earlier A2/rollout scope was inverted by credential-avoidance.

Reverts (credential-driven): remove clusters/dev/rollout/ + the $patch:delete's;
restore unified base graph (dev = ../base + cluster-vars + SOPS decryption,
R-ENV-02 intact); restore monitoring files (Phase 4). Kept non-credential
improvements: tofu for_each/rejoin, Kyverno parametrization + webhook excludes.

platform/kustomization.yaml now phased (single shared list): Phase-2 set =
kyverno, cilium, longhorn, cert-manager, externaldns, gateway; later components
commented with phase markers.

SOPS activated: .sops.yaml at repo root; bootstrap/sops/secret-templates/*.yaml.example
for all secrets (cloudflare x2, grafana, alertmanager-smtp, keycloak, oauth2-proxy, pia);
bootstrap/sops/CREDENTIALS.md checklist; pre-wired commented cloudflare-api-token lines
in cert-manager + externaldns; filled non-secret ACME email. Agents never touch real
secret values (operator encrypts).

Validated: kubectl kustomize dev(unified)/prod/platform(Phase-2 only), templates inert,
make -C ci gate PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
jakman85andClaude Opus 4.8 committed 2026-08-16 22:09:20 -05:00
1 parent e1c738fa63
commit a108d5ab98
23 files changed
+448 -198

No files matched your search

+18 -20
View File
@@ -2,38 +2,39 @@
# clusters/dev — the ACTIVE build target overlay.
#
# `flux bootstrap ... --path=clusters/dev` points the dev cluster's Flux here.
# TRANSITIONAL (Phase-2 bootstrap): this overlay reuses the base source + cluster-vars, but
# REPLACES the monolithic base `platform`/`apps` Kustomizations with a per-component NO-SECRET
# rollout (./rollout) — so dev brings up cilium/longhorn/monitoring/kyverno in order WITHOUT
# dragging in the secret-blocked components. It patches cluster-vars with dev values (R-ENV-02)
# and softens Kyverno for bootstrap. CONVERGENCE: delete ./rollout + the two $patch:delete
# below to return to the unified base graph once all operator hand-offs land. See DECISIONS-LOG.
# This overlay adds NO resources of its own. Its ONLY job is to patch the shared cluster-vars
# ConfigMap with dev-scoped values (R-ENV-02). The base reconciliation graph is reused verbatim.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
- rollout
patches:
# --- Drop the monolithic base platform/apps Kustomizations. They reconcile ALL 11 components
# as one unit (incl. secret-blocked cert-manager/externaldns/velero/keycloak/oauth2-proxy/
# image-automation/gateway/kubevirt); ./rollout replaces them with the per-component
# no-secret set for dev bootstrap. Remove these two deletes to re-adopt the unified graph.
# (No decryption patch is needed here: the rollout components carry no SOPS content, so
# the `sops-age` Secret is not required for the dev bootstrap tier.) ---
# --- SOPS decryption + bootstrap source (PM-inlined from bootstrap/flux/patches/, 2026-08-16) ---
# Inlined (not cross-dir file refs) to stay within kustomize's default load restrictions.
# Realises the commented decryption TODOs in clusters/base and sets the real source URL.
# The `sops-age` Secret is created out-of-band (bootstrap/sops/install-age-key.sh) — name only.
- patch: |-
$patch: delete
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: platform
namespace: flux-system
spec:
decryption:
provider: sops
secretRef:
name: sops-age
- patch: |-
$patch: delete
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
decryption:
provider: sops
secretRef:
name: sops-age
- patch: |-
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
@@ -84,6 +85,8 @@ patches:
path: /data/VELERO_RETENTION
value: "72h"
# dev-softened Kyverno for cold-start (base defaults Enforce/Fail/3 = prod posture).
# Not credential-driven — bootstrap-safety knobs so a cold cluster comes up before
# admission is proven/HA; flip dev to the strict posture once green.
- op: replace
path: /data/KYVERNO_VALIDATION_ACTION
value: "Audit"
@@ -93,8 +96,3 @@ patches:
- op: replace
path: /data/KYVERNO_ADMISSION_REPLICAS
value: "1"
# dev: no Grafana admin Secret yet → empty makes the chart use its default admin password
# (dev-only, NAT-isolated). Prod keeps the by-name SOPS secret from the base default.
- op: replace
path: /data/GRAFANA_ADMIN_SECRET
value: ""
-25
View File
@@ -1,25 +0,0 @@
---
# Cilium — ADOPT the running Ansible-installed CNI. Parity between platform/cilium and the
# live config was verified (k8sServiceHost/port via pod env, kube-proxy-replacement, gateway-api,
# egress+bpf masquerade, hubble, l2, cgroup, v1.16.5) → this reconcile is a NO-OP.
# prune: false — Cilium was bootstrapped out-of-band; adopt its config, never own-delete the CNI.
# No decryption (no SOPS content). No dependsOn (it is the datapath everything else rides on).
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: cilium
namespace: flux-system
spec:
interval: 30m
retryInterval: 2m
timeout: 10m
prune: false
wait: true
sourceRef:
kind: GitRepository
name: flux-system
path: ./platform/cilium
postBuild:
substituteFrom:
- kind: ConfigMap
name: cluster-vars
-20
View File
@@ -1,20 +0,0 @@
---
# Phase-2 NO-SECRET incremental rollout — DEV bootstrap scaffold.
#
# Per-component Flux Kustomizations for the components that need NO operator secret:
# kyverno (admission, dev-softened) · cilium (adopt running CNI) · longhorn (storage)
# · monitoring (dependsOn longhorn).
# Secret-blocked components (cert-manager, externaldns, gateway, velero, keycloak,
# oauth2-proxy, image-automation, kubevirt) are intentionally NOT created until their
# operator hand-offs arrive (DECISIONS-LOG A2 / hand-off table).
#
# TRANSITIONAL: this is the dev bootstrap graph. Once every hand-off lands, dev drops
# ./rollout and re-adopts the unified base monolith (clusters/base/platform.yaml) so dev
# and prod share one reconciliation graph again (R-ENV-02 convergence). See DECISIONS-LOG.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- kyverno.yaml # first: admission present (Audit/Ignore in dev — non-blocking) before workloads
- cilium.yaml # adopt the running CNI (parity verified — no-op reconcile)
- longhorn.yaml # storage; monitoring depends on its StorageClass
- monitoring.yaml # dependsOn longhorn
-25
View File
@@ -1,25 +0,0 @@
---
# Kyverno — in-cluster admission enforcer. DEV-SOFTENED via cluster-vars:
# KYVERNO_VALIDATION_ACTION=Audit, KYVERNO_WEBHOOK_FAILURE_POLICY=Ignore,
# KYVERNO_ADMISSION_REPLICAS=1 (base defaults Enforce/Fail/3 for prod).
# Non-blocking on today's cluster (policies match only `app-namespace=true` namespaces,
# none exist yet). No SOPS content. Listed first so admission is present before workloads.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: kyverno
namespace: flux-system
spec:
interval: 1h
retryInterval: 2m
timeout: 10m
prune: true
wait: true
sourceRef:
kind: GitRepository
name: flux-system
path: ./platform/kyverno
postBuild:
substituteFrom:
- kind: ConfigMap
name: cluster-vars
-25
View File
@@ -1,25 +0,0 @@
---
# Longhorn — block storage / CSI (default `longhorn` StorageClass). No SOPS content.
# No dependsOn. The bulk-NFS StorageClass in this component carries REPLACE-ME (OQ-2) but is
# an isolated, unreferenced object — it does NOT block Longhorn's HelmRelease reconcile.
# PRE-DEPLOY CHECK (runtime, out of git): confirm `iscsid` active + /var/lib/longhorn mounted
# on all 3 nodes (cloud-init provides both; verify after the T-1.5 rebuild).
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: longhorn
namespace: flux-system
spec:
interval: 10m
retryInterval: 2m
timeout: 10m
prune: true
wait: true
sourceRef:
kind: GitRepository
name: flux-system
path: ./platform/longhorn
postBuild:
substituteFrom:
- kind: ConfigMap
name: cluster-vars
-26
View File
@@ -1,26 +0,0 @@
---
# Monitoring — kube-prometheus-stack + Loki + Alloy. Prometheus/Loki PVCs bind the
# `longhorn` StorageClass, so this dependsOn longhorn (Flux waits for it Ready first).
# Grafana admin password (chart default in dev) and Alertmanager SMTP (deferred to the
# Phase-4 hand-off) are handled WITHOUT an operator secret in dev → NO decryption block.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: monitoring
namespace: flux-system
spec:
interval: 10m
retryInterval: 2m
timeout: 15m
prune: true
wait: true
dependsOn:
- name: longhorn
sourceRef:
kind: GitRepository
name: flux-system
path: ./platform/monitoring
postBuild:
substituteFrom:
- kind: ConfigMap
name: cluster-vars