RBAC
Org roles and scoped role bindings — Wardn's access model
RBAC
Wardn's access model is organizations + scoped role bindings. Org membership assigns a baseline role; role bindings elevate specific users or teams on specific clusters or deployments.
Two Layers
Org roles — the baseline
Assigned by Better Auth at org membership:
| Org role | Capabilities |
|---|---|
Owner | Everything — full org administration |
Admin | Full operational access — can't edit org settings |
Contributor | Read + sync + most creates — can't delete clusters or manage stores |
Viewer | Read-only — deployments, clusters, logs |
Scoped bindings — the elevation
Rows in role_bindings grant a higher ceiling on a narrower scope:
| Subject | Role | Scope | Example scope_id |
|---|---|---|---|
user:<id> or team:<id> | admin | sync | read | org | "" |
cluster | "prod-cluster" | ||
deployment | "prod-cluster/default" |
A deployment is the cluster/namespace destination pairing — not a stored resource, but every App targeting it falls under the binding. Give a Viewer sync on deployment:prod-cluster/default and they can sync every app deploying into that namespace on that cluster — that's the intended granularity (a namespace is the deployment unit). cluster scope covers all deployments on that cluster.
A binding is additive — it can grant sync on one deployment to a Viewer, never remove an Owner's rights.
Permission Matrix
The full permission set checked by rbacService.Require:
| Permission | Owner | Admin | Contributor | Viewer |
|---|---|---|---|---|
org:read / org:manage | ✅ / ✅ | ✅ / ❌ | ✅ / ❌ | ✅ / ❌ |
cluster:read / register / update / remove / shell | all ✅ | all ✅ | read ✅ / rest ❌ | read ✅ / rest ❌ |
deployment:read / create / update / delete / sync / restart / scale / rollback | all ✅ | all ✅ | read+sync+restart+scale+rollback ✅, create/update/delete ❌ | read ✅, rest ❌ |
team:read / team:manage | ✅ / ✅ | ✅ / ✅ | ✅ / ❌ | ✅ / ❌ |
audit:read | ✅ | ✅ | ✅ | ❌ |
secretstore:read / manage | ✅ / ✅ | ✅ / ✅ | ✅ / ❌ | ✅ / ❌ |
registry:read / manage | ✅ / ✅ | ✅ / ✅ | ✅ / ❌ | ✅ / ❌ |
webhook:read / manage | ✅ / ✅ | ✅ / ✅ | ✅ / ❌ | ✅ / ❌ |
remediation:approve | ✅ | ✅ | ❌ | ❌ |
mcp:read | ✅ | ✅ | ✅ | ✅ |
deployment:manage is the umbrella perm for create/update/delete; deployment:sync covers sync/deploy/rollback/restart/approve. A scoped binding can grant any of these on a narrower scope.
How It Works
Request ──▶ auth middleware ──▶ Caller{user_id, org_id, org_role, api_key?}
│
▼
rbacService.Require(perm, ctx)
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
org role grants scoped binding api key ceiling
it directly? grants it on scope? clamps the user?
│ │ │
└──────────── any true → allow ─── else → 403 ──────┘- Baseline — the caller's org role grants the perm
- Scoped — a
role_bindingsrow wheresubjectmatches the caller (or one of their teams),rolecovers the perm, andscope_idmatches the cluster/deployment - Key ceiling — API-key callers can never exceed the key's
role+scope, even when the owning user could
can-i — the frontend contract
POST /api/v1/auth/can-i lets the UI hide actions the caller can't take:
curl -X POST http://localhost:8080/api/v1/auth/can-i -b cookies.txt \
-H "Content-Type: application/json" \
-d '{"permission":"deployment:sync","scope":"deployment","scope_id":"prod-cluster/default"}'
# {"allowed": true}Same rules as the can_i MCP tool.
Sessions vs API Keys
| Caller | Scope |
|---|---|
| Session cookie | Full user capability — org role + bindings |
wrd_ API key | User capability ∩ key's role/scope ceiling |
| Session-only endpoints | /auth/sessions, secret-store/registry writes — unreachable with a key |
Kubernetes RBAC vs Wardn RBAC
Separate systems:
| System | Controls | Ceiling |
|---|---|---|
| Kubernetes RBAC | What the operator/registered kubeconfig can apply on clusters | Cluster-level |
| Wardn RBAC | What users/keys can trigger through the API | Operator's kubeconfig is the ceiling |
A Wardn Owner can't deploy anything the operator's SA can't apply.
Best Practices
- Most users
Viewer— grantsyncscoped bindings per deployment or cluster instead of broadContributor - CI =
wrd_key withsyncdeployment-scope — the key can't create clusters or stores even if leaked Contributorfor day-to-day — can sync and restart but not delete clusters or manage secret stores- Audit
team.create/apikey.*— role elevation events are in the audit log
Related
- Teams API — subjects for bindings
- Authentication — sessions + API keys
- Audit API — tracking access changes