New specs from /kiro-discovery (2026-09-28/29), added to roadmap.md: - job-status-detail-display: show pending reasons and reserved-vs-used resources in REST and xcecli. Requirements generated (7 requirements). - scheduler-alter-pend-reasons: keep a pending reason for every alternative resreq (per-alternative JobPendReason) and fix the obj->reason leak. - controller-job-cleanup: primary-only sweep that removes finished jobs past a configurable retention, via a replicated WAL removal fact. - application-profile: admin-defined application profiles (execution mode, launch type, env, resource defaults) chosen per job, with app:use/app:view RBAC. Queues and profiles stay orthogonal. - job-sandbox-execution / job-sandbox-multinode: run jobs in a systemd DynamicUser sandbox behind a pluggable execution backend. - rbac-persistence: make RBAC changes made through the API survive an apiserver restart (closes the security.md gap). roadmap.md also records the scheduler-sim-diagnostics info job update and a security item: remote-task units appear to run as root (found by code reading). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
5.2 KiB
5.2 KiB
Brief: rbac-persistence
Problem
Cluster admins manage roles and permissions through the apiserver's REST API:
- create and delete roles;
- add permissions to a role;
- assign roles to users and remove them.
Those changes live only in apiserver memory. When the apiserver restarts, every
change made through the API since startup silently reverts to whatever
config/rbac.yaml says. There is no error, warning or audit trail. An admin who
granted a user access (or, worse, revoked it) finds the change gone after a
routine restart or upgrade. A revoked permission silently coming back is a
security problem.
This matters more now:
application-profileaddsapp:use/app:viewgrants, which admins will manage per profile.- The job-sandbox initiative relies on RBAC to decide who may run which applications, including sandbox versus direct execution.
Current State
Documented in .kiro/steering/security.md as a known, deferred gap (flagged
2026-08-27 during the scheduler-snapshot-job-allocation persistence survey).
- Loading.
store.Rolesandstore.UserRoles(apiserver/store/stores.go) are loaded fromconfig/rbac.yamlat startup (LoadRBACConfig(),controllers/rbac_check.go). - Changes made at runtime with no write-back:
RoleController.CreateRole/AddPermissionsForRole/DeleteRoleAndPermissions(controllers/roles.go:499,:540,:594);store.AddRoleForUser/DeleteRoleForUser, called from the automatic role assignment inAuthController.Logonand from the user-role endpoints incontrollers/users.go.
- Model. Permission strings are
action:scopewith domain typesall,self,self_ug,queue(with values) andhostgroup(with values).cluster_adminbypasses checks through one early return. - Failure class. The same save/restore field-completeness failure that
persistence.mddescribes, but in the apiserver's in-memory RBAC store. - HA. The apiserver is a separate process fed by controller push. Nothing covers how several apiserver instances would share RBAC state; the design must settle this.
Desired Outcome
- Every RBAC change accepted by the API is durable. It survives an apiserver restart, or it is rejected; never silently lost.
- Revocations are durable exactly like grants.
- Every RBAC change is audited: who changed what, when, and old versus new value.
- On startup, the effective RBAC state equals the last successfully persisted
state. Conflicts between the shipped
rbac.yamldefaults and API changes are resolved by a documented, predictable rule. - If there are several apiserver instances, they converge on the same RBAC state (the design decides how).
Approach
Design decides. Candidates noted at discovery:
- (a) Write-through to
rbac.yaml. Atomic write (temp file plus rename), preserving the builtin roles. Simple, but a single node's file, and it merges badly with shipped defaults. - (b) Store RBAC state in the controller's replicated state. A new cluster entity persisted through the WAL and snapshots. HA-consistent and shared by every apiserver, but a much larger HA/persistence change.
- (c) A small embedded store (for example SQLite or bbolt) in the apiserver. Durable, but local to the node.
- (d) Declare API changes runtime-only and reject them. Make
rbac.yaml(or xceadmin-managed config) the only source, and turn the mutating endpoints into errors or read-only. The smallest change, but it removes a capability.
Scope
- In:
- Durability of every mutating RBAC API: roles, permissions, user-role assignments, and automatic role assignment at login.
- The audit trail.
- The rule that reconciles shipped defaults with API changes at startup.
- Multi-instance consistency (whatever the design chooses).
- Tests, including restart round-trips of grants and revocations.
- Out:
- New permission types (
application-profileownsapp:*). - Auth backends and JWT.
- Changing the middleware chain or the
cluster_adminbypass.
- New permission types (
Boundary Candidates
- RBAC state storage and load (the persistence mechanism).
- Mutating-API write path (durability, error on failure).
- Audit log.
- Reconciling defaults with runtime changes.
Out of Boundary
- The permission model's semantics (actions, domain types).
- Profile, queue and other resource-specific permission checks.
Upstream / Downstream
- Upstream: the existing RBAC store and APIs. If approach (b) is chosen, the controller WAL/snapshot machinery.
- Downstream:
application-profile(durableapp:usegrants) and any future RBAC-governed feature.
Existing Spec Touchpoints
- Extends: none (closes a gap recorded in
security.md). - Adjacent:
application-profile,admin-config-tool(if RBAC moves into xceadmin-managed config).
Constraints
- Middleware chain unchanged: WhitelistURL → WhitelistIP → RateLimit → JWT → CORS → RBAC.
- Fail closed. If a change cannot be persisted, the API returns an error and the in-memory state is left unchanged.
- Never log secrets (
security.md). - If approach (b): HA/persistence rules, and the full 3-phase workflow.
- When done, update
security.mdto remove the "Known gap" note.