myxce/.kiro/specs/rbac-persistence/brief.md
Rongsong Shen 7fe9b22381
docs(spec): discovery briefs for job status detail, job cleanup, sandbox execution and RBAC persistence
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>
2026-09-29 08:57:31 +00:00

111 lines
5.2 KiB
Markdown

# 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-profile` adds `app:use`/`app:view` grants, 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.Roles` and `store.UserRoles` (`apiserver/store/stores.go`)
are loaded from `config/rbac.yaml` at 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 in `AuthController.Logon` and from the user-role endpoints in
`controllers/users.go`.
- **Model.** Permission strings are `action:scope` with domain types `all`,
`self`, `self_ug`, `queue` (with values) and `hostgroup` (with values).
`cluster_admin` bypasses checks through one early return.
- **Failure class.** The same save/restore field-completeness failure that
`persistence.md` describes, 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.yaml` defaults 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-profile` owns `app:*`).
- Auth backends and JWT.
- Changing the middleware chain or the `cluster_admin` bypass.
## 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` (durable `app:use` grants) 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.md` to remove the "Known gap" note.