# Serves: R-SEC-01 (in-cluster admission variant) # # WHY THIS EXISTS (and diverges from the CI policy): # The CI policy `disallow-plaintext-secrets` requires a top-level `sops` block on # the Secret manifest. That works in CI (it scans the encrypted git manifest) but # is IMPOSSIBLE to satisfy at admission: Flux decrypts SOPS *before* the Secret # reaches the API server, so an admitted Secret never has a `sops` block. Applying # the CI policy in-cluster would reject every real Secret. # # So the in-cluster invariant is PROVENANCE, not ciphertext: a Secret in an # application namespace must have been produced by the SOPS/Flux path (it carries # Flux's kustomize labels) OR be an exempt controller-generated Secret. A Secret # hand-applied with `kubectl` (the plaintext-into-the-cluster path) has neither and # is rejected. This is the enforceable reading of docs/07's "reject a plaintext # Secret at admission" -- flagged as an escalation in the T-0.5 report. apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: secret-provenance annotations: policies.kyverno.io/title: Require managed provenance on app-namespace Secrets policies.kyverno.io/category: Secrets policies.kyverno.io/severity: critical homelab.keithjackson.dev/requirements: R-SEC-01 homelab.keithjackson.dev/scope: admission-only spec: validationFailureAction: ${KYVERNO_VALIDATION_ACTION} background: false rules: - name: secrets-must-be-flux-managed match: any: - resources: kinds: - Secret namespaceSelector: matchLabels: homelab.keithjackson.dev/app-namespace: "true" # Controller-generated Secrets (SA tokens, cert-manager, Helm) are exempt. exclude: any: - resources: selector: matchLabels: homelab.keithjackson.dev/secret-managed-by: controller - resources: selector: matchExpressions: - key: kustomize.toolkit.fluxcd.io/name operator: Exists validate: message: >- Secrets in application namespaces must be delivered by Flux from SOPS-encrypted git (they carry a kustomize.toolkit.fluxcd.io/name label) or be exempt controller-generated Secrets. Hand-applied (plaintext) Secrets are rejected (R-SEC-01, docs/07). deny: {}