[security][medium] OpenBao hardening gaps (single replica, plaintext listener, flat path, unversioned policy) #202

Closed
opened 2026-07-14 09:48:22 +00:00 by claude-bot · 1 comment
Member

Finding (bundle)

  • helmrelease.yaml: server.ha + raft enabled but replicas: 1 — single pod holds the whole secret estate (documented/accepted, compensated by snapshots).
  • Listener tls_disable = 1 — ESO<->OpenBao and CronJob<->OpenBao traffic is plaintext HTTP in-cluster (auth JWTs + secret values unencrypted on the wire).
  • All secrets under one flat homelab/* KV path with a single 'eso' read role; no per-app/per-namespace segregation.
  • The eso-read policy exists only as a shell snippet in a migration doc + live OpenBao config — not version-controlled, no drift detection.
  • ClusterSecretStore has no namespaceSelector/conditions — any namespace's ESO-authorized workload can read any app's secret under homelab/*.
  • infrastructure/openbao/snapshot-pvc.yaml (sole restore artifact) is unprotected from Flux prune (no prune: disabled annotation), unlike sibling PVCs.

Verified

Cited in helmrelease.yaml, openbao-clustersecretstore.yaml, snapshot-pvc.yaml.

Fix

Consider mTLS/enable listener TLS; segregate KV paths per app with scoped policies; commit the policy as .hcl; add namespaceSelector; add prune: disabled to the snapshot PVC.


Automated security review 2026-07-14. Verified; several are documented/accepted tradeoffs.

## Finding (bundle) - helmrelease.yaml: server.ha + raft enabled but replicas: 1 — single pod holds the whole secret estate (documented/accepted, compensated by snapshots). - Listener tls_disable = 1 — ESO<->OpenBao and CronJob<->OpenBao traffic is plaintext HTTP in-cluster (auth JWTs + secret values unencrypted on the wire). - All secrets under one flat homelab/* KV path with a single 'eso' read role; no per-app/per-namespace segregation. - The eso-read policy exists only as a shell snippet in a migration doc + live OpenBao config — not version-controlled, no drift detection. - ClusterSecretStore has no namespaceSelector/conditions — any namespace's ESO-authorized workload can read any app's secret under homelab/*. - infrastructure/openbao/snapshot-pvc.yaml (sole restore artifact) is unprotected from Flux prune (no prune: disabled annotation), unlike sibling PVCs. ## Verified Cited in helmrelease.yaml, openbao-clustersecretstore.yaml, snapshot-pvc.yaml. ## Fix Consider mTLS/enable listener TLS; segregate KV paths per app with scoped policies; commit the policy as .hcl; add namespaceSelector; add prune: disabled to the snapshot PVC. --- _Automated security review 2026-07-14. Verified; several are documented/accepted tradeoffs._
Author
Member

PR #231 opened — the safely-codeable subset of this bundle:

  1. prune-guard the snapshot PVC (prune: disabled, matching sibling data PVCs).
  2. version-control the eso-read policy as infrastructure/openbao/policies/eso-read.hcl (reference-only; OpenBao policies aren't Flux-reconciled — apply/verify commands inline).
  3. scope the ClusterSecretStore via spec.conditions to the exact 12 current consumer namespaces (verified against every secretStoreRef name=openbao; no ClusterExternalSecret/PushSecret exist) — closes the any-namespace-reads-all-of-homelab/* gap. Tradeoff documented: a new consuming namespace must be added to the list or its ExternalSecret won't sync.
  4. egress NetworkPolicy on the openbao ns (default-deny egress; allows intra-ns, kube-dns, kube-apiserver/host, world, ICMP — authentik-ns house pattern) to block lateral in-cluster egress from the secrets backend.

All four kubectl kustomize-build clean. Items 3 & 4 are live-sensitive — flagged post-merge verification in the PR (ESO all SecretSynced, snapshot CronJob login, apiserver-auth reachability).

Single-replica raft left untouched (documented/accepted, snapshot-compensated).

Two disruptive items need an owner decision — can't be done non-disruptively, so out of this PR:

  • Enable listener TLS/mTLS (tls_disable = 1 → https): needs certs issued/mounted, store server: + CronJob BAO_ADDR switched to https, and auth/kubernetes/config re-pointed. Requires a maintenance window — OK to schedule, or defer?
  • Per-app KV path re-segregation: split homelab/* into per-app paths + scoped policies/roles (data + policy migration). Item 3 is the non-disruptive partial mitigation. Preferred approach — per-app SecretStores with distinct roles, or keep the single store and rely on the namespace scoping?

Will proceed on either once you confirm approach/window.

PR #231 opened — the safely-codeable subset of this bundle: 1. **prune-guard** the snapshot PVC (`prune: disabled`, matching sibling data PVCs). 2. **version-control** the `eso-read` policy as `infrastructure/openbao/policies/eso-read.hcl` (reference-only; OpenBao policies aren't Flux-reconciled — apply/verify commands inline). 3. **scope the ClusterSecretStore** via `spec.conditions` to the exact 12 current consumer namespaces (verified against every `secretStoreRef name=openbao`; no ClusterExternalSecret/PushSecret exist) — closes the any-namespace-reads-all-of-`homelab/*` gap. Tradeoff documented: a new consuming namespace must be added to the list or its ExternalSecret won't sync. 4. **egress NetworkPolicy** on the openbao ns (default-deny egress; allows intra-ns, kube-dns, kube-apiserver/host, world, ICMP — `authentik-ns` house pattern) to block lateral in-cluster egress from the secrets backend. All four `kubectl kustomize`-build clean. Items 3 & 4 are live-sensitive — flagged post-merge verification in the PR (ESO all `SecretSynced`, snapshot CronJob login, apiserver-auth reachability). Single-replica raft left untouched (documented/accepted, snapshot-compensated). **Two disruptive items need an owner decision — can't be done non-disruptively, so out of this PR:** - **Enable listener TLS/mTLS** (`tls_disable = 1` → https): needs certs issued/mounted, store `server:` + CronJob `BAO_ADDR` switched to https, and `auth/kubernetes/config` re-pointed. Requires a maintenance window — OK to schedule, or defer? - **Per-app KV path re-segregation**: split `homelab/*` into per-app paths + scoped policies/roles (data + policy migration). Item 3 is the non-disruptive partial mitigation. Preferred approach — per-app SecretStores with distinct roles, or keep the single store and rely on the namespace scoping? Will proceed on either once you confirm approach/window.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: konairius/homelab#202