Skip to content

0050. Fixing False Positives and Hardening Security in Kyverno Policies

  • Status: accepted
  • Date: 2026-07-21
  • Deciders: lusoris
  • Related: PR #2824

Context

The argocd-appproject-require-blacklist ClusterPolicy denied on {{ request.object.spec.clusterResourceBlacklist || 'unset' }}. JMESPath treats an empty list [] as falsy, so the || fallback fired even when the field was present, and the sanctioned explicit empty-list opt-out (clusterResourceBlacklist: []) was flagged on all 14 AppProjects (backlog reliability item 9). The policy had no kyverno-test.yaml fixture — exactly the gap that let the falsy-[] bug ship (hygiene item 20). Two container-hardening leftovers rode along: the video-runner render container (hygiene 18) and the etcd-snapshot CronJob pod (hygiene 19).

Decision

  • Rewrite the deny condition to a key-presence check: {{ keys(request.object.spec) | contains(@, 'clusterResourceBlacklist') }} Equals false — deny only when the field is truly absent, so an explicit [] opt-out passes.
  • Add cluster/kyverno/test/argocd-appproject-require-blacklist/ with a fixture that proves both sides: good-project carrying clusterResourceBlacklist: [] passes, bad-project without the field fails. kyverno test cluster/kyverno runs 94 passed, 0 failed, and the pre-commit kyverno test fixture hook now gates edits to this policy.
  • video-runner render container: runAsNonRoot: true, runAsUser: 65532, allowPrivilegeEscalation: false, capabilities.drop: [ALL], seccompProfile: RuntimeDefault. Declare-only — the image already runs non-root and northlight is paused, so there is zero live impact.
  • etcd-snapshot CronJob: pod-level seccompProfile: RuntimeDefault; both the snapshot initContainer and the upload container get allowPrivilegeEscalation: false + capabilities.drop: [ALL]. runAsNonRoot is intentionally unset — the pod must read root-owned etcd PKI via hostPath.

Alternatives considered

  • Keep the || 'unset' expression and remove the empty-list opt-out from the 14 AppProjects instead: rejected — [] is the sanctioned explicit opt-out; the policy logic was wrong, not the AppProjects (documented retroactively).
  • Ship the policy fix without a test fixture: rejected — the missing fixture is precisely what allowed the falsy-[] bug to ship undetected.
  • runAsNonRoot for the etcd-snapshot containers: rejected — they must read root-owned etcd PKI from the hostPath mount.

Consequences

  • The policy no longer false-positives on explicit []; it still denies AppProjects that omit clusterResourceBlacklist entirely.
  • The kyverno test suite covers this policy (94 passed, 0 failed at merge), so future edits to the deny logic are regression-gated locally via pre-commit.
  • The video-runner and etcd-snapshot containers carry PSS-restricted-shaped settings; etcd-snapshot deliberately keeps root, and any future uid flip there requires solving PKI file ownership first.

References

  • PR #2824 — fix: batch 2 of small-fixes backlog (kyverno [] false-positive + test, 2 hardening)