Files
home-network-k3s-impl/platform/kyverno/policies/disallow-plaintext-secrets.yaml

62 lines
2.8 KiB
YAML

# Serves: R-SEC-01 (no plaintext secrets in git; SOPS-encrypted only)
# Spec: docs/07-security-and-secrets.md "Secrets management (R-SEC-01)"; docs/05
# "Secrets in GitOps"; ADR-0007 (SOPS + age).
#
# SCOPE / IMPORTANT NUANCE (see platform/kyverno/README.md and admission/README.md):
# This policy is the CI / L0 gate over the SECRET MANIFESTS COMMITTED TO GIT.
# A SOPS-encrypted Secret file carries a top-level `sops:` block (with `sops.mac`);
# a plaintext Secret does not. We reject any committed Secret lacking that block.
#
# This policy is CI-ONLY. It MUST NOT be applied unchanged in-cluster: Flux
# decrypts SOPS before the object reaches the API server, so an admitted Secret
# never has a `sops:` block -- applying this at admission would reject EVERY real
# Secret. The in-cluster admission variant enforces PROVENANCE instead
# (admission/secret-provenance.yaml). This CI/admission divergence is an
# escalation item (see report) because docs/07's phrase "reject a plaintext
# Secret at admission" is not literally achievable post-decrypt.
#
# "As far as policy can express" (per task): value-level ciphertext and git-history
# leakage are additionally covered by a secret scanner (gitleaks) per docs/07
# acceptance gate "No plaintext secret exists anywhere in git history (scanned)".
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-plaintext-secrets
annotations:
policies.kyverno.io/title: Disallow plaintext Secrets (require SOPS)
policies.kyverno.io/category: Secrets
policies.kyverno.io/severity: critical
homelab.keithjackson.dev/requirements: R-SEC-01
homelab.keithjackson.dev/scope: ci-only
policies.kyverno.io/description: >-
Secret manifests committed to git must be SOPS-encrypted (top-level `sops`
block present). Plaintext Secrets are rejected. CI/L0 gate only -- see the
provenance-based admission variant for in-cluster enforcement.
spec:
validationFailureAction: Enforce
background: false
rules:
- name: secrets-must-be-sops-encrypted
match:
any:
- resources:
kinds:
- Secret
# Controller-generated Secrets (cert-manager, SA tokens, Helm) are never
# committed to git, so the CI gate never sees them; the label keeps the rule
# safe if ever reused where such Secrets exist.
exclude:
any:
- resources:
selector:
matchLabels:
homelab.keithjackson.dev/secret-managed-by: controller
validate:
message: >-
Secrets committed to git must be SOPS-encrypted: the manifest must carry a
top-level `sops` block (with a `mac`). No plaintext secrets (R-SEC-01,
docs/07, ADR-0007).
pattern:
sops:
mac: "?*"