docs(security): correct what actually blocks the C-0211 controller-scope opt-in - #3587
docs(security): correct what actually blocks the C-0211 controller-scope opt-in#3587devantler wants to merge 7 commits into
Conversation
…seline context The four namespaces opted into add-baseline-context-optin-* carry only `baseline-context`, which gates three rules that all match `kinds: Pod`. Those mutate at admission, while Kubescape reads the STORED SPEC of the controller — so the opt-in cannot move C-0211 no matter how many namespaces receive it. The controller-scope rules that can move it (add-baseline-context-optin- controllers*, matching Deployment/StatefulSet/DaemonSet on CREATE and UPDATE) gate on a second label, `baseline-context-controllers`, which is applied in no namespace anywhere in this repository. This adds the first one. Measured on prod 2026-09-04, separating the two surfaces: - kubescape: its pods pick the fields up as they recreate (7 of 15, then 8 of 16 eleven minutes later, the same pods carrying both) while 0 of its 6 controllers ever do. The pod figures are volatile by construction and only their direction is the evidence; the controller figures are the stable ones. - longhorn-system: all 11 workloads carry neither, though opted in since 09-02. - velero reads 2/2 only because its HelmRelease supplies the fields at source, so the one namespace that looked healthy could not have revealed the gap. longhorn-system first: its 11 workloads are genuinely scanned (Deployment:6 + DaemonSet:5 workloadconfigurationscan objects), all 11 miss both fields, and none sets fsGroup, so fsGroupChangePolicy is inert at runtime here. kubescape is deliberately not next — the operator's excludeNamespaces drops its workloads, which hold no workload-kind scan objects, so mutating them cannot move C-0211. Exercised with the Kyverno CLI against the real longhorn-ui Deployment in both states. With the label: fsGroupChangePolicy=OnRootMismatch and the container's seLinuxOptions.level=s0 are added while runAsUser/runAsGroup/runAsNonRoot/ seccompProfile survive untouched. Without it the output is byte-identical to the input. Part of #3239 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…in rules The note said "all three of those rules match `kinds: Pod`", which reads as the opt-in mechanism being structurally unable to reach the stored spec. That was true when written and is not now: add-baseline-context-optin-controllers* match Deployment/StatefulSet/DaemonSet on CREATE and UPDATE. What is actually true is narrower and more useful — those controller rules gate on a SECOND label, `baseline-context-controllers`, which was applied in no namespace at all. Left as it stood, the note would tell the next reader the opt-in can never move C-0211, when the accurate statement is that the half that can move it had never been switched on anywhere. Comment-only: rendering k8s/bases/infrastructure/cluster-security-exceptions before and after is byte-identical at 962 lines. Part of #3239 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Exercised and evaluatedBoth states, against a real workload. Took the live
Two controls, both fired: the OFF output is byte-identical to the input (so the flag genuinely gates), and the ON output differs from the input (so the test is not vacuous). Sibling preservation is asserted explicitly because that is the property #3580 was about — a whole-object write would have discarded Validation.
For the second commit, which is comment-only, I rendered What is NOT proven yetThe Kyverno CLI shows the rule produces the right mutation; it does not show helm-controller and Flux driving it onto the live cluster. That is exactly the gap #3577 fell into — every static and GitHub-side signal was green while nothing landed. So after this merges I will re-read the live stored specs and require all 11 longhorn-system workloads to carry both fields, and I will not report it as working before that read. ScopeOne namespace. observability is the natural next one but is deliberately not in this PR — 6 of its 9 workloads set |
…scope baseline context" This reverts commit 6f78243. The controller-scope opt-in is not a label edit. tests/add-baseline-context pins `expected_controllers_optin=""` deliberately, and the procedure beside the rules in add-security-context.yaml requires, per namespace: the label, a template annotation UPDATE per existing Deployment/StatefulSet/DaemonSet/CronJob (the label alone changes nothing already stored — helm-controller reapplies only on a chart or values change and excludes /spec/template from drift correction), a read-back of the stored specs, a watch of every owner's write rate for the reconcile loop the same comment warns about, and only then the recorded evidence the gate expects. That flip restarts Longhorn's manager, CSI and UI — the cluster's storage system — and step 3 needs sustained observation. It is a mutating prod operation and this repository's prod access is read-only, so it is not an unattended change. The gate caught this correctly. The measurement stands and is recorded on #3239; only the premature label is withdrawn. The doc correction in the following commit is independent and holds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t it is absent
Follows the revert. The previous wording implied the second label was an
oversight about to be corrected here. It is not: tests/add-baseline-context pins
the empty inventory on purpose, and a namespace joins only together with the
measured post-rollout read-back its flip procedure produces.
So the accurate statement is that the 27 are waiting on a per-namespace rollout,
not on a mechanism that cannot reach them — which is the opposite of what the
original sentence ("all three of those rules match `kinds: Pod`") told a reader,
and also not the "nobody switched it on" reading my first attempt suggested.
Comment-only: rendering cluster-security-exceptions before and after is
byte-identical.
Part of #3239
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correction — scope reduced, and my earlier comment overstated the findingMy first version of this PR opted 1. The empty inventory is deliberate, not an oversight. 2. My mechanism claim was wrong. I wrote that the controller rules matching 3. Why I am not completing it here. The documented flip is four steps: label → a template-annotation UPDATE per workload → read the stored specs back → watch every owner's write rate for the reconcile loop the same comment warns about (Coroot is named as a known candidate). For What survives, and is verified
One process note for anyone validating this area locally: |
@coderabbitai review |
|
Action performedReview triggered.
|
…truncated The previous commit's rewrite of this comment block ended mid-sentence at "Template-level changes are what moved", dropping both its object (velero and flux-system) and the flux-system breakdown that named tofu-controller and its open removal issue #3480. Restores the dropped text verbatim. Comment-only: the rendered output is byte-identical before and after, verified by rendering the base directory both ways (31314 B each) with a sensitivity control proving the comparison detects a semantic change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Exercised and evaluated at
|
CI is green at @coderabbitai review |
|
|
|
Warning Review limit reachedNext included review available in 3 seconds. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Lane progress: Green at @codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7c6db99f7f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The note identified only add-baseline-context-optin-controllers* as the rules that reach the stored spec, but the policy handles CronJobs through a separate add-baseline-context-optin-cronjobs* family (CronJob on CREATE and UPDATE), and seven of the residual workloads this note sizes are CronJobs. Planning the rollout from the note as written would have left those seven and their read-back checks out. Names both families and says why both matter. Raised by Codex review at 7c6db99 (P2); verified against the policy before accepting. Comment-only: the rendered output is byte-identical. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Pull request metadata =='
gh pr view 3587 --repo devantler-tech/platform --json number,title,baseRefName,headRefName,commits,files \
--jq '{number,title,baseRefName,headRefName,commits:[.commits[]|{oid,messageHeadline}],files:[.files[]|{path,additions,deletions}]}'
echo
echo '== Incremental commit diff =='
git show --format=fuller --find-renames --find-copies adf9609f668e276014078ecb4afef8dee1862225 -- || true
echo
echo '== Changed files on PR range =='
git diff --name-status "$(git merge-base origin/HEAD HEAD)" HEAD || true
echo
echo '== Candidate policy files =='
fd -t f -e yaml -e yml . | rg 'cluster-security-exceptions|pod-security|kyverno' || trueLength of output: 9916 🏁 Script executed: #!/bin/bash
set -euo pipefail
policy='k8s/bases/infrastructure/cluster-security-exceptions/pod-security-mutations-unscoped.yaml'
echo '== Policy outline =='
ast-grep outline "$policy" --items all || true
echo
echo '== Updated comment and policy rule names =='
rg -n -A16 -B8 \
'add-baseline-context-optin-(controllers|cronjobs)|baseline-context-controllers|residual workloads|observability and longhorn-system' \
"$policy"
echo
echo '== Relevant test inventory and assertions =='
rg -n -A12 -B8 \
'expected_controllers_optin|baseline-context-controllers|CronJob|cronjob|residual' \
tests/add-baseline-context scripts/tests 2>/dev/null || true
echo
echo '== PR-wide diff against main =='
git diff --check origin/main...HEAD
git diff --find-renames --unified=30 origin/main...HEAD -- "$policy"Length of output: 108449 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Policy definition locations =='
rg -n -l --glob '*.yaml' --glob '*.yml' \
'^(\s*)name:\s*add-baseline-context-optin-(controllers|cronjobs)' \
k8s tests
echo
echo '== Rule definitions and match/precondition context =='
rg -n -A55 -B8 --glob '*.yaml' --glob '*.yml' \
'name:\s*add-baseline-context-optin-(controllers|cronjobs)' \
k8s
echo
echo '== References that substantiate the residual count =='
rg -n -A4 -B4 \
'(seven|7).{0,80}(CronJob|cronjob)|(CronJob|cronjob).{0,80}(seven|7)|remaining 29|residual workloads' \
README.md k8s tests scripts .github 2>/dev/null || true
echo
echo '== GitHub issue `#3217` and `#3239` summaries =='
gh issue view 3217 --repo devantler-tech/platform --json number,title,state,body,url,labels
gh issue view 3239 --repo devantler-tech/platform --json number,title,state,body,url,labelsLength of output: 1211 🏁 Script executed: #!/bin/bash
set -u -o pipefail
echo '== Candidate add-security-context files =='
fd -t f -e yaml -e yml . k8s tests | rg 'add-security-context|security-context|mutation' || true
echo
echo '== All baseline-context rule references in repository YAML =='
rg -n -A35 -B6 --glob '*.yaml' --glob '*.yml' \
'add-baseline-context|baseline-context-controllers' \
k8s tests || true
echo
echo '== Residual-population references in repository =='
rg -n -A5 -B5 \
'(seven|7).{0,100}(CronJob|cronjob)|(CronJob|cronjob).{0,100}(seven|7)|remaining 29|residual workload' \
k8s tests scripts .github README.md 2>/dev/null || true
echo
echo '== GitHub issue `#3217` =='
gh issue view 3217 --repo devantler-tech/platform --json number,title,state,body,url,labels || true
echo
echo '== GitHub issue `#3239` =='
gh issue view 3239 --repo devantler-tech/platform --json number,title,state,body,url,labels || trueLength of output: 140689
You are interacting with an AI system. |
add-baseline-context-optin-controllers* also matches Job, on CREATE only. The rollout note described the family as Deployment/StatefulSet/DaemonSet, presenting it as narrower than it is. Comment-only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolution record — CodeRabbit P2 (comment-form, no thread): "Include the Verified valid.
The rollout note described only the first, presenting the family as narrower than it is. Fixed in
The "TWO families" count is unchanged, because Verification: one line changed, file line count identical (157), all nine surrounding clauses asserted present after the edit, YAML re-parsed clean. |
@coderabbitai full review |
|
|
Lane state — CodeRabbit refused this round at Recorded as Not advancing to Codex on this PR deliberately. CodeRabbit's quota is per repository, and the free window reopens shortly; Codex is weekly-limited, and this run is spending its Codex budget on Two observations worth recording, since both cost a request today:
|
|
Tip For best results, initiate chat on the files or code changes.
The CodeRabbit review did not run at The ✏️ Learnings added
You are interacting with an AI system. |
@coderabbitai review |
|
🧠 Learnings used
|
Why
A comment in our security-exception notes told the next reader that the mutation we rely on to close two Kubescape checks structurally cannot reach the thing the scanner grades. That was true when written and is no longer true — the capability was added later. Left as it stood, it reads as "this route is a dead end", which would send whoever picks this up next looking for a different solution that isn't needed.
The accurate position is narrower and more useful: the capability exists, it is deliberately switched off everywhere, and each namespace turns it on only alongside evidence that it worked there. Those are very different situations — one says the approach is broken, the other says there is a queue of per-namespace rollouts waiting.
What
Corrects that comment. Comment-only — the rendered output is byte-identical before and after, so nothing about the cluster changes.
Note on scope
This PR originally also opted one namespace into that rollout. A repository guard correctly rejected it: turning it on is a documented procedure — the switch, a restart of every workload in the namespace, a read-back proving the fields landed, and a watch for a specific failure mode — not a one-line change. For the namespace I picked that means restarting the cluster's storage components on production, which is not an unattended change. That commit is reverted here; the measurement behind it is recorded on #3239 so the rollout can be picked up deliberately.
Part of #3239
🤖 Generated with Claude Code