From 98346b370e199654bc8df3581ba1bc336f5c18d3 Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Mon, 13 Jul 2026 18:46:35 +0200 Subject: [PATCH] feat(secrets): ClusterSecretStore + declarative audit (SOPS migration phase 2) OpenBao was initialized and configured today (kv-v2 at homelab/, k8s auth, eso-read policy + role bound to the external-secrets SA); the store can therefore turn Ready and no longer endangers the post-crds wait gate. Audit device moves into the server HCL because OpenBao 2.5 rejects API-managed audit devices; log_raw stays off so Loki only ever sees HMAC-redacted values. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CRgBFn9m8a2ncFMLusikH6 --- kubernetes/MIGRATION-sops-openbao.md | 10 ++++-- kubernetes/infrastructure/kustomization.yaml | 6 ++-- .../infrastructure/openbao/helmrelease.yaml | 32 ++++++++++++++++--- .../post-crds/kustomization.yaml | 4 +++ .../post-crds/openbao-clustersecretstore.yaml | 17 ++++++++++ 5 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 kubernetes/infrastructure/post-crds/openbao-clustersecretstore.yaml diff --git a/kubernetes/MIGRATION-sops-openbao.md b/kubernetes/MIGRATION-sops-openbao.md index e7a55e2a..2c6ddf6d 100644 --- a/kubernetes/MIGRATION-sops-openbao.md +++ b/kubernetes/MIGRATION-sops-openbao.md @@ -41,7 +41,10 @@ Post-merge, one-time init (single admin → 1 share / threshold 1): ```sh kubectl -n openbao exec -it openbao-0 -- bao operator init \ -key-shares=1 -key-threshold=1 -# Store BOTH outputs in the existing GPG pass store, nowhere else: +# Store BOTH outputs in the GPG pass store, nowhere else: +# (bootstrapped 2026-07-13: key 6B8FED8020C1478F892EA83816866E146A8252A4, +# no passphrase yet — set one via `gpg --edit-key ... passwd`, and BACK THE +# KEY UP off-machine: without it, pass and thus unseal key are gone.) pass insert homelab/openbao/unseal-key pass insert homelab/openbao/root-token kubectl -n openbao exec -it openbao-0 -- bao operator unseal # paste key @@ -61,7 +64,10 @@ Manual, with the root token (then stop using the root token day-to-day): export BAO_ADDR=https://openbao.k.renner.uno bao login # root token, from pass bao secrets enable -path=homelab -version=2 kv -bao audit enable file file_path=stdout # audit into pod logs → Loki +# NOTE: `bao audit enable` is disabled in OpenBao 2.5 — audit devices are +# declarative-only, configured in the server HCL (see the audit stanza in +# infrastructure/openbao/helmrelease.yaml; keep log_raw OFF or plaintext +# secrets land in Loki). bao auth enable kubernetes bao write auth/kubernetes/config \ kubernetes_host=https://kubernetes.default.svc.cluster.local:443 diff --git a/kubernetes/infrastructure/kustomization.yaml b/kubernetes/infrastructure/kustomization.yaml index 33c4b014..e4c5a04c 100644 --- a/kubernetes/infrastructure/kustomization.yaml +++ b/kubernetes/infrastructure/kustomization.yaml @@ -25,10 +25,8 @@ resources: - spegel/ - openbao/ - external-secrets/ -# ClusterSecretStore for OpenBao intentionally NOT committed yet: it can only -# turn Ready after `bao operator init` + auth setup (manual, see -# kubernetes/MIGRATION-sops-openbao.md phase 2) — committing it earlier would -# block the wait/health gate of the infrastructure-crds Kustomization. +# The OpenBao ClusterSecretStore lives in post-crds/ (needs ESO's CRDs and +# an initialized OpenBao; see kubernetes/MIGRATION-sops-openbao.md). # cnpg-barman-plugin/ lives in its own Flux Kustomization # (`infrastructure-cnpg-barman`) so it can dependsOn infrastructure-crds # where the cert-manager Certificates create the TLS Secrets its diff --git a/kubernetes/infrastructure/openbao/helmrelease.yaml b/kubernetes/infrastructure/openbao/helmrelease.yaml index 542f61f8..2e7d1d72 100644 --- a/kubernetes/infrastructure/openbao/helmrelease.yaml +++ b/kubernetes/infrastructure/openbao/helmrelease.yaml @@ -43,16 +43,40 @@ spec: enabled: false server: # Single-node raft (not standalone/file) so we get `bao operator raft - # snapshot` as a consistent backup primitive. The chart's default raft - # config already matches (ui on, tls_disable listener on 8200, raft - # storage at /openbao/data, kubernetes service_registration). TLS - # terminates at the Traefik ingress; in-cluster traffic is plain HTTP. + # snapshot` as a consistent backup primitive. TLS terminates at the + # Traefik ingress; in-cluster traffic is plain HTTP. ha: enabled: true replicas: 1 raft: enabled: true setNodeId: true + # Chart default config (ui, tls_disable listener, raft storage, + # kubernetes service_registration) + the audit device: OpenBao 2.5 + # only supports declarative audit config, `bao audit enable` via + # API is disabled. NO log_raw — raw mode would ship plaintext + # secret values via stdout into Loki; default HMAC redaction stays. + config: | + ui = true + + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + + storage "raft" { + path = "/openbao/data" + } + + service_registration "kubernetes" {} + + audit "file" "stdout" { + description = "audit trail to pod stdout -> Loki" + options { + file_path = "/dev/stdout" + } + } dataStorage: enabled: true size: 5Gi diff --git a/kubernetes/infrastructure/post-crds/kustomization.yaml b/kubernetes/infrastructure/post-crds/kustomization.yaml index e72198fa..f6190d19 100644 --- a/kubernetes/infrastructure/post-crds/kustomization.yaml +++ b/kubernetes/infrastructure/post-crds/kustomization.yaml @@ -6,4 +6,8 @@ resources: - cilium-bgp.yaml - cnpg-barman-certs.yaml - velero-schedules.yaml + # Only safe here because OpenBao is already initialized + k8s-auth is + # configured (2026-07-13): a store that can't turn Ready would block this + # KS's wait gate and freeze the dependsOn chain. + - openbao-clustersecretstore.yaml - ../network-policies/ diff --git a/kubernetes/infrastructure/post-crds/openbao-clustersecretstore.yaml b/kubernetes/infrastructure/post-crds/openbao-clustersecretstore.yaml new file mode 100644 index 00000000..679b745f --- /dev/null +++ b/kubernetes/infrastructure/post-crds/openbao-clustersecretstore.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ClusterSecretStore +metadata: + name: openbao +spec: + provider: + vault: # ESO's provider name; speaks OpenBao fine + server: http://openbao.openbao.svc.cluster.local:8200 + path: homelab + version: v2 + auth: + kubernetes: + mountPath: kubernetes + role: eso + serviceAccountRef: + name: external-secrets + namespace: external-secrets -- 2.54.0