Skip to content

fix(rbac): scope configmaps write off the controller namespace — protect state-sync trust root (PLT-471)#406

Merged
bdchatham merged 1 commit into
mainfrom
plt471/rbac-trust-root-lockdown
Jun 13, 2026
Merged

fix(rbac): scope configmaps write off the controller namespace — protect state-sync trust root (PLT-471)#406
bdchatham merged 1 commit into
mainfrom
plt471/rbac-trust-root-lockdown

Conversation

@bdchatham

Copy link
Copy Markdown
Collaborator

What

Closes PLT-471 (controller side): the controller can no longer rewrite its own state-sync trust-root ConfigMap sei-controller-config via its ServiceAccount token.

The trust root lives in sei-k8s-controller-system and is read via the mounted file, not the API — yet the controller held two configmaps-write paths into that namespace:

  1. cluster-wide configmaps CRUD (manager-role, from the controller.go marker) → narrowed to get;list;watch (the informer cache needs cluster-wide read; write moves to node namespaces).
  2. a vestigial configmaps CRUD rule in leader-election-roledeleted. Leader election is Lease-based (controller-runtime default; verified live: lease bc1f5b0a.sei.io held, no leader-election ConfigMap), so this grant was dead and was the only remaining configmaps-write path in the controller's namespace.

Regenerated role.yaml / manifests/role.yaml via make manifests.

Why not move the trust root to its own namespace

A pod can only mount a ConfigMap from its own namespace, and the controller reads the trust root via a mount — so it must stay in sei-k8s-controller-system. In-place scoping (this PR) is the viable path.

Companion (required before rollout) — platform per-namespace write Roles

The controller still legitimately writes ConfigMaps in node namespaces (rbac-proxy config, workflow-vars). A follow-up platform-repo PR adds a Role + RoleBinding (subject sei-k8s-controller-manager) granting configmaps write in each chain namespace: prod {pacific-1, arctic-1, atlantic-2}, prod-euw1 {arctic-1}, prod-use2 {arctic-1}, harbor {staging}. Those must reconcile before this controller ref deploys (else node-namespace configmap writes 403 → reconciles wedge) — additive + safe to land first.

Security acceptance criteria (from the threat model)

  • kubectl auth can-i {create,update,patch,delete} configmaps -n sei-k8s-controller-system --as=...:sei-k8s-controller-managerno (both holes closed).
  • No ClusterRoleBinding grants configmaps write; write is namespaced-only.
  • Leader election still acquires the Lease.
  • Node-namespace rbac-proxy/workflow-vars writes still succeed.
  • No new rbac/serviceaccounts/deployments grant; no wildcards.

Verification done

Build clean; verify-generated satisfied (fresh make manifests generate yields no diff); live harbor confirms Lease-based election + SA name + no other configmaps-write SA in the namespace.

🤖 Generated with Claude Code

@cursor

cursor Bot commented Jun 13, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Security-critical RBAC on the state-sync trust root; rollout depends on companion platform Roles for node-namespace ConfigMap writes or reconciles can wedge with 403s.

Overview
Removes cluster-wide ConfigMap write from the controller manager so it can no longer mutate sei-controller-config (state-sync trust root) in sei-k8s-controller-system via its ServiceAccount.

The SeiNode kubebuilder:rbac marker and regenerated manager-role now grant ConfigMaps only get;list;watch (cache/informer reads). Leader-election Role drops the unused ConfigMap CRUD block; election stays on coordination.k8s.io/leases only.

manager-role is also reshuffled so PVCs/services keep full reconcile verbs in a separate rule; ConfigMaps no longer share a rule with mutating verbs.

Legitimate ConfigMap writes in node namespaces (e.g. rbac-proxy, workflow-vars) are expected to come from companion platform per-namespace Roles—those should land before this controller deploy or node-namespace writes will 403.

Reviewed by Cursor Bugbot for commit ca766f0. Bugbot is set up for automated code reviews on this repo. Configure here.

The trust-root ConfigMap sei-controller-config is in the controller's namespace
and read via the mounted file, not the API, yet the controller held two
configmaps-write paths into that namespace:
- the manager-role cluster-wide configmaps CRUD (controller.go marker) ->
  narrowed to get;list;watch; write is node-namespace-scoped via platform Roles.
- a configmaps rule in leader-election-role -> removed (leader election is
  Lease-based; the grant was unused).

Regenerated role.yaml / manifests/role.yaml.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bdchatham bdchatham force-pushed the plt471/rbac-trust-root-lockdown branch from 15dc1a0 to ca766f0 Compare June 13, 2026 17:27
@bdchatham bdchatham merged commit 9dc7d53 into main Jun 13, 2026
5 checks passed
@bdchatham bdchatham deleted the plt471/rbac-trust-root-lockdown branch June 13, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant