Skip to content

Configure Varroa RBAC

Varroa RBAC controls the dashboard, REST API, CLI, and MCP. Configure Jenkins permissions separately with Jenkins RBAC.

RoleIntended access
adminAll Varroa resources and actions
operatorController lifecycle, configuration, approvals, and update-center upload
developerController and configuration work within the binding scope
viewerRead-only access

The assistant resource has two verbs. use opens the assistant and sends it messages: admin, operator, developer, and viewer hold it. admin changes the kill switch, the defaults, and tenant levels, and reads every session: admin only.

Built-in roles are reconciled. Create a custom role when their rules do not fit.

Bind a user or group:

apiVersion: varroa.dev/v1alpha1
kind: VarroaRoleBinding
metadata:
name: platform-operators
spec:
roleRef: operator
subjects:
- kind: Group
name: platform-team
- kind: User
name: release-user

Subject names match authenticated claims exactly and case-sensitively. A local account beside OIDC is local:<name>. A group binding also matches the Varroa Group of that name. That is the break-glass path.

apiVersion: varroa.dev/v1alpha1
kind: VarroaRoleBinding
metadata:
name: payments-developers
spec:
roleRef: developer
subjects:
- kind: Group
name: payments-team
scope:
namespaces:
- teams-payments
controllerSelector:
matchLabels:
environment: nonproduction

When both scope fields are set, a controller must match both. Namespace scope does not narrow cluster-scoped resources.

A caller reads a CatalogItem, CatalogSource, or ComposedBundle in a namespace where its binding grants read. A caller with read in any scope also reads the operator namespace, because an unqualified itemRef resolves there. A cluster-wide binding reads every namespace. A list leaves out the objects that the caller cannot read. A single read outside that set answers 403. MCP and search apply the same rule. A binding scoped only by controllerSelector reads the operator namespace only.

Each cluster uses its own operator namespace. A member cluster reports that namespace in its heartbeat. The BFF applies the value of the target cluster. A member that reports no namespace grants no operator-namespace read.

The pipelines resource controls Jenkins job and run access through Varroa. It has three verbs.

VerbGrants
readRead a tenant’s jobs, runs, stages, logs, artifacts, tests, and changes
writeStart, cancel, and rerun a tenant’s builds
adminWrite a tenant’s Team.spec.pipelines.exports

A verb does not imply another verb. write does not grant read. List every verb a role needs.

Built-in roles carry this pipelines matrix:

Rolepipelines verbs
adminAll verbs, through the wildcard rule
operatorread, write, admin
developerread, write
viewerread

cellpools carries two verbs. read lists and gets a cell pool; update scales one. There is no create verb and no delete verb: an operator creates and deletes a pool with kubectl or through GitOps.

Rolecellpools verbs
adminBoth verbs, through the wildcard rule
operatorread, update
developerNone
viewerNone

A pool’s status.tenants lists every tenant on the pool, and the Teams API is admin-only. A read verb for a developer or a viewer would therefore disclose the tenant list to a caller who may not list Teams. CellPool is cluster-scoped, so a namespace-scoped binding satisfies neither verb.

fleetlocks carries two verbs. read lists every fleet lock on the /locks page and through GET /locks. release force-releases a releasing lock through POST /locks/release. See Fleet locks.

Rolefleetlocks verbs
adminBoth, through the wildcard rule
operatorread, release
developerNone
viewerNone

A fleet-wide lock list names every tenant’s lock names and job paths at once. Both verbs therefore stay with the operator role, as cellpools does. A tenant sees its own locks on the team home with no verb.

break-glass is a controllers verb. It lets a caller request a temporary admin session on a headless controller.

Only the built-in admin role holds break-glass, through its wildcard rule. No other built-in role holds it. Grant it to a custom role the same way as any other verb:

apiVersion: varroa.dev/v1alpha1
kind: VarroaRole
metadata:
name: incident-responder
spec:
apiRules:
- resources: [controllers]
verbs: [break-glass]

Holding break-glass grants a token. It does not grant Jenkins access on its own. The caller also needs the Jenkins permission hudson.model.Hudson.Administer on the target controller. Otherwise Jenkins denies the session. See Headless controllers for the full runbook.

A pipelines tenant is a Team. Scope a VarroaRoleBinding to one or more tenants:

apiVersion: varroa.dev/v1alpha1
kind: VarroaRoleBinding
metadata:
name: checkout-developers
spec:
roleRef: developer
subjects:
- kind: Group
name: checkout-team
scope:
tenants:
- name: checkout-team
jobs:
- "release/*"

tenants[].name names a Team. tenants[].jobs is an optional list of folder-relative glob patterns. An absent jobs list covers every job of the tenant. A pattern uses * for one path segment, ** to cross a /, and ? for one character.

A scope names one dimension. The API server rejects a scope that sets tenants together with namespaces or controllerSelector. A controllerSelector-scoped binding grants no pipelines access. Scope the binding to tenants to grant pipelines access.

Open Access, then Role Bindings, then the binding. Expand Scope and use the Pipelines Tenants editor.

  1. Select + Add tenant and name the Team. The list offers the teams you can read. A team that does not exist yet is accepted.
  2. Select + Narrow to job patterns to add a jobs list. Leave the list off to cover every job of the tenant.
  3. Select + Add pattern and enter a folder-relative pattern. The editor shows the grammar beside the list, and refuses an empty pattern, a pattern starting with /, and an empty list.

The editor shows a live count in the form matches 3 of 10 jobs you can see. The count reads the team’s job list with your grants, not the subject’s, so a narrower view of your own produces a smaller count. When the job list cannot be read the editor states so, and still accepts every pattern.

The editor disables the Namespaces and Controller Selector Labels controls while a tenant entry exists, and disables the tenant controls while either of those holds a value. Each disabled control states the reason.

A jobs glob is a Varroa rule. It narrows what Varroa shows and what Varroa starts. It changes nothing inside Jenkins. Jenkins keeps one item role per tenant, which covers the tenant’s whole folder. Varroa defines no folder-level Jenkins role.

A Team member reaches the team’s pipelines tenant through the Team. The member needs no separate VarroaRoleBinding for that tenant.

Jenkins role bindings carry no tenant scope

Section titled “Jenkins role bindings carry no tenant scope”

A JenkinsRoleBinding accepts no tenant scope. Its controllerScope field rejects a tenants list. A pipelines verb produces no Jenkins role-strategy entry.

apiVersion: varroa.dev/v1alpha1
kind: VarroaRole
metadata:
name: release-manager
spec:
apiRules:
- resources: [controllers]
verbs: [read, approve-restart, manage]
- resources: [composedbundles]
verbs: [read, update]
jenkinsRoleRef: varroa-operator

apiRules contain resource and verb lists. Use the exact resource and verb names exposed by the API. jenkinsRoleRef may name only a Global Jenkins role.

For one controller and a built-in role, use Controller.spec.rbacSpec:

spec:
rbacSpec:
groups:
- name: payments-team
role: developer

The shorthand accepts admin, operator, developer, and viewer. Use role bindings for custom or cross-controller scope.

Terminal window
curl -sf https://app.example.com/api/v1/me/permissions \
-H "Authorization: Bearer $VARROA_API_KEY"
SymptomCheck
API returns 403Effective permissions, subject spelling, and scope
Group grant is absentResolved groups from /api/v1/me
Built-in edits disappearCreate a custom role
Varroa succeeds but Jenkins deniesAdd a Jenkins role assignment