Skip to content

feat(security): enforce the privileged RBAC policy with exact-identity exclusions - #3791

Merged
devantler merged 2 commits into
mainfrom
claude/privileged-rbac-enforce-3486
Sep 14, 2026
Merged

devantler merged 2 commits into
mainfrom
claude/privileged-rbac-enforce-3486

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

Why

Nothing stopped an installed Helm chart from granting itself cluster-admin-equivalent permissions. The privileged-RBAC policy only reported such grants, and CI cannot see what a chart renders in-cluster. Now that reporting coverage is complete and every current holder has been reviewed, the policy can block new grants.

What

  • The policy refuses any Role or ClusterRole that grants wildcard administration, RBAC bind/escalate or impersonation, at admission.
  • The objects that legitimately need those grants stay allowed, each named exactly: Kubernetes' own aggregated roles, and the Crossplane, KRO, KSail operator, Longhorn and Velero controllers.
  • CI pins that list, so widening it takes a reviewed change.

⚠️ Merge note: merging deploys this to production. Only the reviewed objects keep their grants. A future chart version that adds a new privileged role will fail to reconcile until someone reviews it.

Part of #3486

🤖 Generated with Claude Code

…y exclusions

Switch audit-privileged-rbac to Enforce on its rule, so a Role or ClusterRole
granting wildcard administration, bind/escalate or impersonation is refused at
admission, including grants a chart renders in-cluster. The reviewed objects that
need such grants are excluded by exact identity: ClusterRoles by name, Roles by
namespace and name. Drop the scored=false annotation so rejections report as fail.

The gate pins the exclusion list and forbids selectors or userInfo exemptions, and
new fixtures prove an excluded name does not exempt a lookalike name, another
namespace, or another kind.

Part of #3486.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

What was exercised at 86cd3d89235288512f081ca882ff1bf57f41f20b, and how the exclusions were chosen

  • Live classification (read-only prod read, 2026-09-14 ~03:14Z): the Audit reports hold exactly 14 privileged findings, 12 ClusterRoles and 2 Roles, matching the complete-coverage census on No control stops an installed chart granting itself cluster-admin-equivalent RBAC #3486. Each one belongs to Kubernetes' own aggregated roles or to the Crossplane, KRO, KSail operator, Longhorn or Velero controllers. Those are exactly the 14 exclusions, so no running object is refused after deploy. The object-level evidence stays private.
  • Why warn became fail: Kyverno converts fail to warn when policies.kyverno.io/scored is "false", whatever the policy mode. This was checked against Kyverno's report-result documentation and a live control: a non-CEL Audit policy without the annotation reports fail. Keeping the annotation would have hidden rejections as warnings.
  • Enforce and exclusions, observed with Kyverno CLI 1.19.1 (CI and prod pin 1.19.0):
    • kyverno test tests/audit-privileged-rbac: 43/43 pass.
    • Excluded identities report as Excluded.
    • These still fail: a lookalike name, the excluded Role name in another namespace, and a ClusterRole with the excluded Role's name.
  • Gate: scripts/tests/test-audit-privileged-rbac.sh passes with the exact census pass: 15, fail: 24, warn: 0, and shellcheck is clean. Each of four ablations makes it exit non-zero:
    • a wildcard exclusion name
    • a userInfo subject exemption
    • an extra namespace on a Role exclusion
    • the rule switched back to Audit (checked by exit code only, since that assertion prints nothing)
  • Overlays: kubectl kustomize k8s/clusters/local/ and k8s/clusters/prod/ both build.
  • Not observable before merge: the admission refusal itself. After deploy, check that the policy is Ready, that the 14 excluded objects keep reconciling, and that the policy reports show no fail result.

The gate only rejected the exclusion keys it named, so single-field edits
weakened Enforce and still passed: an exclusion with empty names (any Role in a
namespace), failureActionOverrides back to Audit, admission disabled, CREATE-only
matching, or a label precondition. Check the exact key set of the spec, rule,
match, validate and CEL levels instead, and require literal names and exactly one
namespace per Role exclusion.

Also document where the excluded names come from and what a refused chart role
does to its Flux Kustomization, list kube-node-lease among the filtered
namespaces, and note that the apply census, not the skip rows, proves exclusion.

Part of #3486.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

@devantler: I will review pull request #3791.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 10 minutes.

Check out review usage here.

View limit details

Limit 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.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 4daffee9-ad41-482c-a67c-55aeff208651

📥 Commits

Reviewing files that changed from the base of the PR and between b43c092 and 8caffe2.

📒 Files selected for processing (5)
  • docs/rbac-audit.md
  • k8s/bases/infrastructure/cluster-policies/best-practices/audit-privileged-rbac.yaml
  • scripts/tests/test-audit-privileged-rbac.sh
  • tests/audit-privileged-rbac/kyverno-test.yaml
  • tests/audit-privileged-rbac/resources.yaml

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

CodeRabbit refused this round at 8caffe2d (review rate limited, reply 5658732916), so the request moves to the next review lane.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-14T03:58:47.647903Z 8caffe2 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 8caffe2db2

ℹ️ 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".

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Readiness at 8caffe2db2c830120c562547739e836f345023d0: promoting from draft.

  1. Programmatically tested.
    • Every required check is green at this head. There are no review threads and no conflict with main.
    • kyverno test tests/audit-privileged-rbac: 43/43 pass.
    • scripts/tests/test-audit-privileged-rbac.sh passes with the exact census pass: 15, fail: 24, warn: 0. shellcheck is clean, and both overlays build.
    • The gate pins the policy's whole enforcement shape. On a copy of the files, the unmodified policy passes. Each of these 11 single-field weakenings then fails with the shape/exclusion message:
      • an empty-names Role exclusion
      • failureActionOverrides, at rule and at spec level
      • admission: false
      • CREATE-only matching
      • a label precondition
      • a scored: "false" annotation
      • a wildcard name
      • a userInfo subject
      • an extra Role namespace
      • Audit mode
  2. Reviewed.
    • A pre-review self-review found that the gate was a blocklist, and its fixes are in this head.
    • Codex's clean pass names 8caffe2db2 (comment 5658772914).
    • CodeRabbit refused this head with a rate limit (reply 5658732916), recorded as no-gate.
  3. Tried and evaluated as a user. The user is an operator deploying charts, so I checked what they will hit.
    • The live prod reports hold exactly the 14 privileged findings these exclusions name, so no running object is refused on deploy.
    • Excluded identities are reported Excluded. A lookalike name, the same Role name in another namespace, and a ClusterRole carrying an excluded Role's name are still rejected.
    • The admission refusal itself only becomes observable after deploy. I will then check three things: the policy is Ready, the 14 excluded objects keep reconciling, and PolicyReports show no fail for this policy.
    • Chart-rendered RBAC is not evaluated in CI yet; that is tracked in security(rbac): evaluate chart-rendered RBAC against the privileged-RBAC policy in CI #3792.

@devantler
devantler marked this pull request as ready for review September 14, 2026 04:00
@devantler
devantler added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit 1800740 Sep 14, 2026
30 checks passed
@devantler
devantler deleted the claude/privileged-rbac-enforce-3486 branch September 14, 2026 04:20
@github-project-automation github-project-automation Bot moved this from 🫴 Ready to ✅ Done in 🌊 Project Board Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

1 participant