🤖 Generated by the Daily AI Engineer
Evidence
The user-namespace rollout under #1807 is delivered as a mutate-only control. add-security-context.yaml's add-user-namespaces rule injects hostUsers: false into pods in namespaces carrying pod-security.devantler.tech/user-namespaces: enabled, using a conditional anchor:
mutate:
patchStrategicMerge:
spec:
+(hostUsers): false
+(key) means set only if absent. A workload that explicitly sets hostUsers: true therefore keeps it — silently, with no admission error, no policy report, and no signal anywhere. The namespace still carries the opt-in label and still looks hardened.
A repo-wide search confirms hostUsers appears in exactly two places, both inside that one mutate rule — there is no validate rule anywhere that constrains it:
k8s/bases/infrastructure/cluster-policies/best-practices/add-security-context.yaml:10 (comment)
k8s/bases/infrastructure/cluster-policies/best-practices/add-security-context.yaml:93 (+(hostUsers): false)
This is the "enforce ratchet" already named as the remaining step in the 2026-07-12 per-app audit on #1807 ("Remaining after #2597: the stateful set … then the enforce ratchet").
Live production state (read-only, 2026-07-18) confirms the control is currently effective but unguarded — 5 namespaces opted in, every non-CNPG pod unshared, every CNPG-labelled pod correctly left on host userns by the rule's exclude:
| namespace |
non-CNPG pods hostUsers=false |
CNPG pods (excluded, host userns) |
| backstage |
1/1 |
2 |
| crossview |
2/2 |
0 |
| homepage |
2/2 |
0 |
| umami |
4/4 |
3 |
| whoami |
1/1 |
0 |
No pod in production currently sets hostUsers: true, so the ratchet can be applied at Enforce without blocking any existing workload.
Impact
Defence-in-depth that silently degrades is worse than none — it reports as present while being absent. Today a chart upgrade, a values change, or a new workload can opt itself out of user namespaces in a namespace explicitly designated hardened, and nothing surfaces it. This is the same regression class the platform already guards for hostNetwork / hostPID / hostIPC in validate-host-restrictions.yaml; hostUsers is the one host-namespace dimension left unguarded.
Expected behaviour
In a namespace opted into user namespaces, a pod that explicitly sets hostUsers: true is rejected at admission, with the same idiom the platform already uses for the other host namespaces (=(hostUsers): false — validate-if-present, so an absent value still passes and is filled in by the existing mutate rule).
CNPG-managed pods keep the existing cnpg.io/cluster exclude, mirroring the mutate rule exactly, so the validate rule can never block a workload the mutate rule deliberately skipped.
Acceptance criteria
Part of #1807.
Evidence
The user-namespace rollout under #1807 is delivered as a mutate-only control.
add-security-context.yaml'sadd-user-namespacesrule injectshostUsers: falseinto pods in namespaces carryingpod-security.devantler.tech/user-namespaces: enabled, using a conditional anchor:+(key)means set only if absent. A workload that explicitly setshostUsers: truetherefore keeps it — silently, with no admission error, no policy report, and no signal anywhere. The namespace still carries the opt-in label and still looks hardened.A repo-wide search confirms
hostUsersappears in exactly two places, both inside that one mutate rule — there is no validate rule anywhere that constrains it:This is the "enforce ratchet" already named as the remaining step in the 2026-07-12 per-app audit on #1807 ("Remaining after #2597: the stateful set … then the enforce ratchet").
Live production state (read-only, 2026-07-18) confirms the control is currently effective but unguarded — 5 namespaces opted in, every non-CNPG pod unshared, every CNPG-labelled pod correctly left on host userns by the rule's exclude:
hostUsers=falseNo pod in production currently sets
hostUsers: true, so the ratchet can be applied atEnforcewithout blocking any existing workload.Impact
Defence-in-depth that silently degrades is worse than none — it reports as present while being absent. Today a chart upgrade, a values change, or a new workload can opt itself out of user namespaces in a namespace explicitly designated hardened, and nothing surfaces it. This is the same regression class the platform already guards for
hostNetwork/hostPID/hostIPCinvalidate-host-restrictions.yaml;hostUsersis the one host-namespace dimension left unguarded.Expected behaviour
In a namespace opted into user namespaces, a pod that explicitly sets
hostUsers: trueis rejected at admission, with the same idiom the platform already uses for the other host namespaces (=(hostUsers): false— validate-if-present, so an absent value still passes and is filled in by the existing mutate rule).CNPG-managed pods keep the existing
cnpg.io/clusterexclude, mirroring the mutate rule exactly, so the validate rule can never block a workload the mutate rule deliberately skipped.Acceptance criteria
disallow-host-usersrule is added tovalidate-host-restrictions.yaml, scoped by thepod-security.devantler.tech/user-namespaces: enablednamespace selector and excludingcnpg.io/clusterpods.=(hostUsers): falseso an absent value passes and an explicittruefails.kyverno testfixture undertests/covers fail (explicithostUsers: truein an opted-in namespace), pass (hostUsers: false, and absent), and skip (non-opted-in namespace; CNPG-labelled pod) — RED/GREEN proven.Part of #1807.