Administer
Roles & permissions
Roles decide what a user can do in the Wardengate control plane and admin console. They are separate from policies, which decide what a user can do at the other end of a brokered session. Keeping those two layers distinct is the single most important mental model for this page.
Built-in roles
| Role | Scope | Summary |
|---|---|---|
| SystemAdmin | global | Creates orgs, owns licensing, operates the control plane. |
| OrgAdmin | one org | Full read/write inside the org; cannot cross org lines. |
| Auditor | org or global | Read-only on audit events, recordings, and policy. |
| Operator | org | Onboards targets and accounts; cannot edit policy or IdP. |
| User | org | Opens sessions to targets that policy allows. |
Everyone who signs in gets User implicitly. The other four are additive. A person can hold several roles at once; their effective permissions are the union.
The permission model
A permission is a pair of verb and resource. Verbs are read, list, create, update, delete, connect, approve, and impersonate. Resources are the kinds you see in the YAML: Target, Account, Policy, IdentityProvider, and so on.
# read every policy in the acme org
permission: { verb: read, resource: Policy, scope: org/acme }
# connect to any target tagged env=staging in acme
permission: { verb: connect, resource: Target, scope: org/acme, selector: "env=staging" }
# approve approvals routed to the sre-leads group
permission: { verb: approve, resource: ApprovalRequest, scope: org/acme, selector: "route=sre-leads" }Custom roles
Custom roles let you express the capabilities your team cares about without breaking the permission boundary that built-ins enforce. A custom role can only narrow; it cannot grant a permission the binding subject would not otherwise be able to receive.
apiVersion: wardengate/v1
kind: Role
metadata:
name: db-readonly-operator
organization: acme
spec:
permissions:
- verb: list
resource: Target
selector: "protocol=postgres,env=prod"
- verb: connect
resource: Target
selector: "protocol=postgres,env=prod"
- verb: read
resource: Recording
selector: "initiator=self"Note the last rule — Wardengate supports the initiator=self selector so users can always play back their own sessions without seeing anyone else's.
Binding roles to IdP groups
Role bindings point at subjects. The cheap and correct subject to pick is a group from your IdP — membership then flows naturally from the directory. Binding individual users by email is a code-smell; do it only for service principals and break-glass.
apiVersion: wardengate/v1
kind: RoleBinding
metadata:
name: sre-operators
organization: acme
spec:
role: Operator
subjects:
- kind: Group
name: sre
- kind: Group
name: platform-engScoped role bindings
A binding can be scoped to a subset of resources using selectors. This is how you grant "Operator, but only for billing assets" without inventing a new role for every combination.
apiVersion: wardengate/v1
kind: RoleBinding
metadata:
name: billing-operators
organization: acme
spec:
role: Operator
subjects:
- kind: Group
name: billing-team
scope:
resource: Target
selector: "team=billing"Selectors are label expressions over the same tags you put on assets. Combining a narrow selector with a wide role is the cleanest way to delegate.
Permission boundaries
Three guardrails are enforced unconditionally by the control plane:
- A role binding cannot grant a permission the binder does not themselves hold. An OrgAdmin cannot mint a role that exceeds OrgAdmin.
- Org-scoped principals cannot be granted SystemAdmin. Only the bootstrap account or an existing SystemAdmin can promote.
impersonateis always audited, always time-bounded, and cannot be delegated — even to a SystemAdmin peer.
Least-privilege patterns
Teams that keep roles clean usually follow a small set of habits:
- Bind groups, not users. If a role binding names a specific person, write down why in the metadata.
- Use one role per responsibility, not one role per team. A person can hold two roles; a role should not try to cover two unrelated responsibilities.
- Prefer scoped bindings over custom roles. You get the same narrowing with fewer objects.
- Separate operator from auditor. The same human may hold both, but the roles should be distinct so you can remove one cleanly during an audit event.
Troubleshooting "why can't I see X?"
The single most useful tool when someone reports missing access is the permission explainer:
wgctl whyami --as jane.doe@acme.example --verb connect --target web-01.prod
# direct bindings
# - sre-operators (Operator in org/acme)
# group memberships (from corp-saml)
# - sre
# - wardengate-admins
# evaluated scopes
# - Operator.connect Target org/acme
# selector env=prod OK
# selector team=web OK
# decision
# - ALLOW (via sre-operators)When the answer is DENY, the output walks you back through the missing rule. Common culprits are a typo in a group name coming from the IdP, a scope selector that excludes the target, or a role binding pinned to a different org.
Related
- Identity providers — how group names arrive from your IdP.
- Organizations — the tenancy that roles bind inside.
- Session policies — what happens after a role says yes.