You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
security(kubescape): C-0211's residual population behind the pod-security exception is unsized and unowned #3217
pod-security-mutations suppresses C-0211 alongside C-0013/C-0016/C-0055, cluster-wide. The
other three have been sized: #3064 measured their residual population against prod and closed
COMPLETED once the three kubescape Deployments were hardened in-spec.
C-0211 was explicitly excluded from that sizing. The exception's own annotation says so:
C-0211 is deliberately NOT sized here. It has no other exception, its field set differs from the
three above (fsGroupChangePolicy, runAsUser and runAsGroup are supplied by the mutation), and the
name-scoped kubescape-privileged.yaml does not cover it — so node-agent and storage remain
inside its residual population rather than excepted out of it.
Sizing it needs its own per-workload pass; #3064 carries that.
That last pointer no longer holds.#3064 is CLOSED/COMPLETED, and it never carried the general
per-workload pass — its only C-0211 acceptance criterion was narrow: "C-0211 coverage for node-agent is decided explicitly". So the pass the annotation defers to does not exist and is
owned by nothing.
Impact
C-0211 is the one control in this exception with no compensating coverage anywhere: it has no
other exception, and kubescape-privileged.yaml does not name it, so node-agent and storage sit
inside its residual population rather than being excepted out of it. Suppression is silent and
indistinguishable from compliance, so nothing here would show a regression — the failure mode #2447
exists to prevent.
This also blocks the narrowing in #2824: the scope is now expressible (NotIn, #3083), but the set
that would surface for C-0211 is unknown, so a narrowing would be sized by guess.
Expected
The residual population for C-0211 is enumerated per workload, and each entry is either hardened
in-spec or carries a narrow, workload-scoped exception whose reason is true of it.
Acceptance criteria
Every workload in the twelve namespaces add-security-context excludes is checked against
C-0211's field set (fsGroupChangePolicy, runAsUser, runAsGroup), and the result recorded.
node-agent and storage each get an explicit decision — hardened, or named in a
workload-scoped exception stating why.
The measurement is re-run rather than inherited: the 2026-08-10 snapshot in the annotation is
prod-only and the local overlay opts in to a different namespace set.
The result is written where the next reader will find it, so this does not become a third
stale pointer.
Size
Small-to-medium, and it is measurement-first — no manifest change is justified until the population
is known.
The row-level per-workload table is in the private operator notes rather than here — a complete
per-workload hardening inventory of a live cluster is more than the sanitized minimum this repo
publishes.
Three premises in this issue did not survive measurement
C-0211 is a twelve-rule control, not a three-field check. Its rules are rule-privilege-escalation, immutable-container-filesystem, non-root-containers, drop-capability-netraw, set-seLinuxOptions, set-seccomp-profile, set-procmount-default, set-fsgroup-value, set-fsgroupchangepolicy-value, set-sysctls-params, set-supplementalgroups-values, rule-allow-privilege-escalation. The stated
field set (fsGroupChangePolicy, runAsUser, runAsGroup) omits fsGroup and seLinuxOptions,
both of which fail widely — so sizing against it would have under-counted.
node-agent and storage are not in the residual population. Both live in kubescape, whose
workloads are excluded from scanning: neither has a workloadconfigurationscan object at all, so
there is no verdict to un-suppress. Unscanned is not the same as passing, but it does mean narrowing
cannot change their result. ⚠️ Do not read the 384 scan objects in that namespace as evidence it
is scanned — they are cluster-scoped results (ClusterRoleBindings, ServiceAccounts, node and
control-plane info) that Kubescape stores there, and none is a workload.
The name is shared, the workload is not. There are two node-agent DaemonSets — kubescape/node-agent (not scanned) and velero/node-agent (in the population). An unqualified
"node-agent" resolves to the wrong one.
Decision — C-0211 stays cluster-wide
Narrowing it today would surface all 33 with no fix behind any of them, which lowers the score to
absorb a gap instead of closing it. The sizing and this decision now live in pod-security-mutations-unscoped.yaml itself, replacing the pointer that deferred to #3064.
Method caveat: a LIST of workloadconfigurationscans returns .spec.controls as null on
every object; only a single-object GET populates it. A LIST-based sweep reports "no C-0211
anywhere" and is wrong.
Counts are prod-only; the local overlay opts into a different namespace set.
I did not re-implement all twelve rules. The four field-level rules were validated against
Kubescape's own verdicts (24/24 agreement, with an inverted negative control collapsing to 0/24);
the three residual disagreements were each confirmed as genuine hardening since the scan.
Independently of that issue, this pass measured all six flux-system controllers
(flux-operator, helm-controller, kustomize-controller, notification-controller, source-controller, tofu-controller) as missing runAsGroup today — so #3223's premise still holds
on current state and has not been overtaken.
Closing — every acceptance criterion is met, re-verified independently against live prod today
I selected this as the oldest actionable Security issue and measured it from scratch against admin@prod before reading the existing record, so the confirmation below is independent rather than inherited.
The four criteria
Criterion
State
Every workload in the twelve excluded namespaces checked against C-0211's field set, result recorded
met — the per-workload measurement is the 2026-08-19 comment above
node-agent and storage each get an explicit decision
met — both are resolved as outside the population, with the reason stated
The measurement is re-run rather than inherited
met — 2026-08-19, after this issue was filed, and again today
The result is written where the next reader will find it
met — pod-security-mutations-unscoped.yaml now carries the sizing inline; the stale #3064 pointer this issue was filed about is gone
Independent reconciliation
I enumerated pod-level securityContext across all twelve excluded namespaces on admin@prod and rolled pods up to their owning workload. Restricting to the four scanned namespaces (flux-system, longhorn-system, observability, velero) and excluding ephemeral Jobs gives exactly 33 workloads — matching the recorded 17 Deployments + 7 DaemonSets + 6 CronJobs + 3 StatefulSets. My live-pod view resolves some owners differently (a CronJob's pods carry a Job owner; Longhorn's carry InstanceManager/Cluster), which is why the kind split differs while the total does not.
The universal gap reproduces: fsGroupChangePolicy is absent on every one of them. Across all six populated namespaces (127 workloads including the unscanned ones) it is absent on 127 of 127, with runAsGroup absent on 55 and runAsUser on 43 — each a strict subset, as recorded.
The node-agent / storage decision, verified rather than assumed
The record says both are outside the population because the kubescape namespace is unscanned, and warns against reading the scan objects sitting there as evidence it is scanned. Checked directly:
1597 workloadconfigurationscans cluster-wide, 440 of them stored in kubescape.
Filtering those for a workload named node-agent or storage returns, in kubescape, only ClusterRoleBinding objects — cluster-scoped RBAC results, not workloads. There is no DaemonSet/node-agent and no Deployment/storage scan object.
The same filter returns velero/node-agent as a real DaemonSet scan object, confirming the name collision the record calls out is exactly that.
So there is no verdict to un-suppress for either, and narrowing cannot change their result. Unscanned is not the same as passing — which is why they are documented rather than silently dropped.
What this does not close
C-0211 stays cluster-wide, and correctly so: narrowing it would surface all 33 with no fix behind them. That remains #2824, gated on the remediation work, and the fsGroupChangePolicy-absent-on-33-of-33 finding is the concrete shape of it. C-0013's own blocker is #3223, unaffected by this.
Closing as completed. Claim retired and self-assignment removed.
Activity
chore: agent-claim #3217 nonce=cfccc5e95815fe2dff7bdedff21c9eb9
devantler commentedon Aug 19, 2026
Measurement — C-0211's residual population is sized
Measured 2026-08-19 against the live prod stored specs (the surface Kubescape reads), not
inherited from the annotation snapshot.
the population is 33 workloads, not two
The twelve namespaces
add-security-contextexcludes split into three groups in prod:flux-system,longhorn-system,observability,velerokube-system,kube-public,kube-node-lease,kubescapelocal-path-storage,kubevirt,cdi,chaos-meshNot one of the 33 passes C-0211 today. Two gaps are universal:
spec.template.spec.securityContext.fsGroupChangePolicysecurityContext.seLinuxOptions(every container)fsGrouprunAsGrouprunAsNonRootThe row-level per-workload table is in the private operator notes rather than here — a complete
per-workload hardening inventory of a live cluster is more than the sanitized minimum this repo
publishes.
Three premises in this issue did not survive measurement
C-0211 is a twelve-rule control, not a three-field check. Its rules are
rule-privilege-escalation,immutable-container-filesystem,non-root-containers,drop-capability-netraw,set-seLinuxOptions,set-seccomp-profile,set-procmount-default,set-fsgroup-value,set-fsgroupchangepolicy-value,set-sysctls-params,set-supplementalgroups-values,rule-allow-privilege-escalation. The statedfield set (
fsGroupChangePolicy,runAsUser,runAsGroup) omitsfsGroupandseLinuxOptions,both of which fail widely — so sizing against it would have under-counted.
node-agentandstorageare not in the residual population. Both live inkubescape, whoseworkloads are excluded from scanning: neither has a
workloadconfigurationscanobject at all, sothere is no verdict to un-suppress. Unscanned is not the same as passing, but it does mean narrowing
cannot change their result.
is scanned — they are cluster-scoped results (ClusterRoleBindings, ServiceAccounts, node and
control-plane info) that Kubescape stores there, and none is a workload.
The name is shared, the workload is not. There are two
node-agentDaemonSets —kubescape/node-agent(not scanned) andvelero/node-agent(in the population). An unqualified"node-agent" resolves to the wrong one.
Decision — C-0211 stays cluster-wide
Narrowing it today would surface all 33 with no fix behind any of them, which lowers the score to
absorb a gap instead of closing it. The sizing and this decision now live in
pod-security-mutations-unscoped.yamlitself, replacing the pointer that deferred to #3064.What I could NOT establish
workloadconfigurationscanis 2026-06-27 — 53 days stale (fix(security): a Kubescape framework-load failure still reports zero findings instead of failing #2933, security(kubescape): a framework-load failure must fail loudly, not read as a zero-finding clean result #3024). The Kubescape verdictsabove therefore come from that stale surface; the field-level population is freshly measured, and
the two are consistent.
LISTofworkloadconfigurationscansreturns.spec.controlsasnullonevery object; only a single-object
GETpopulates it. A LIST-based sweep reports "no C-0211anywhere" and is wrong.
Kubescape's own verdicts (24/24 agreement, with an inverted negative control collapsing to 0/24);
the three residual disagreements were each confirmed as genuine hardening since the scan.
Corroboration for #3223
Independently of that issue, this pass measured all six
flux-systemcontrollers(
flux-operator,helm-controller,kustomize-controller,notification-controller,source-controller,tofu-controller) as missingrunAsGrouptoday — so #3223's premise still holdson current state and has not been overtaken.
docs(security): note the posture surface is not currently producing
docs(security): name which exceptions do not cover C-0211
devantler commentedon Aug 29, 2026
Closing — every acceptance criterion is met, re-verified independently against live prod today
I selected this as the oldest actionable Security issue and measured it from scratch against
admin@prodbefore reading the existing record, so the confirmation below is independent rather than inherited.The four criteria
node-agentandstorageeach get an explicit decisionpod-security-mutations-unscoped.yamlnow carries the sizing inline; the stale#3064pointer this issue was filed about is goneIndependent reconciliation
I enumerated pod-level
securityContextacross all twelve excluded namespaces onadmin@prodand rolled pods up to their owning workload. Restricting to the four scanned namespaces (flux-system,longhorn-system,observability,velero) and excluding ephemeral Jobs gives exactly 33 workloads — matching the recorded 17 Deployments + 7 DaemonSets + 6 CronJobs + 3 StatefulSets. My live-pod view resolves some owners differently (a CronJob's pods carry aJobowner; Longhorn's carryInstanceManager/Cluster), which is why the kind split differs while the total does not.The universal gap reproduces:
fsGroupChangePolicyis absent on every one of them. Across all six populated namespaces (127 workloads including the unscanned ones) it is absent on 127 of 127, withrunAsGroupabsent on 55 andrunAsUseron 43 — each a strict subset, as recorded.The
node-agent/storagedecision, verified rather than assumedThe record says both are outside the population because the
kubescapenamespace is unscanned, and warns against reading the scan objects sitting there as evidence it is scanned. Checked directly:workloadconfigurationscanscluster-wide, 440 of them stored inkubescape.node-agentorstoragereturns, inkubescape, onlyClusterRoleBindingobjects — cluster-scoped RBAC results, not workloads. There is noDaemonSet/node-agentand noDeployment/storagescan object.velero/node-agentas a real DaemonSet scan object, confirming the name collision the record calls out is exactly that.So there is no verdict to un-suppress for either, and narrowing cannot change their result. Unscanned is not the same as passing — which is why they are documented rather than silently dropped.
What this does not close
C-0211 stays cluster-wide, and correctly so: narrowing it would surface all 33 with no fix behind them. That remains #2824, gated on the remediation work, and the
fsGroupChangePolicy-absent-on-33-of-33 finding is the concrete shape of it. C-0013's own blocker is #3223, unaffected by this.Closing as completed. Claim retired and self-assignment removed.
docs(security): repoint the C-0211 issue references from closed #3217 …