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') }}Equalsfalse— 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-projectcarryingclusterResourceBlacklist: []passes,bad-projectwithout the field fails.kyverno test cluster/kyvernoruns 94 passed, 0 failed, and the pre-commitkyverno testfixture 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 thesnapshotinitContainer and theuploadcontainer getallowPrivilegeEscalation: false+capabilities.drop: [ALL].runAsNonRootis 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. runAsNonRootfor 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 omitclusterResourceBlacklistentirely. - 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)