Backstage RBAC
Role-based access control (RBAC) in kubriX Backstage is built on the Backstage RBAC backend plugin. It uses a layered permission model combining a CSV policy file with optional conditional policies.
Layered Permission Model
Permissions are organized in three layers. Each layer adds capabilities on top of the previous one.
Layer 1: role:default/authenticated → all logged-in users
Layer 2: role:default/kubrixdemo → viewers group (reserved for future viewer-specific permissions)
Layer 3: role:default/kubrixdev → editors + kubrix groups (advanced developer features)
Superusers (configured in permission.rbac.admin.superUsers) bypass all policies and always get ALLOW.
Layer 1 — Authenticated (all users)
Every authenticated user gets the following permissions:
| Permission | Action |
|---|---|
catalog-entity | read |
catalog.entity.read | read |
catalog.location.read | read |
catalog.entity.create | create |
catalog.entity.refresh | update |
scaffolder-template | read |
scaffolder-action | use |
scaffolder.task.read | read |
scaffolder.task.create | create |
scaffolder.task.cancel | use |
Layer 3 — kubrixdev (editors / kubrix)
Members of group:default/editors or group:default/kubrix additionally get:
| Permission | Action |
|---|---|
catalog-entity | update |
catalog.location.create | create |
catalog.location.delete | delete |
scaffolder.template.management | use |
kubernetes.clusters.read | read |
kubernetes.resources.read | read |
kubernetes.proxy | use |
bulk-import | use |
kyverno.* | read |
argocd.view.read | read |
policy-entity.* | read / create / update / delete |
announcement.entity.* | create / update / delete |
mssv.view.read | read |
Group → Role Mappings
Group memberships are mapped to roles in the CSV policy file (rbac-policy.csv). All groups also inherit Layer 1 via an explicit mapping to role:default/authenticated:
# All groups get Layer 1 baseline
g, group:default/viewers, role:default/authenticated
g, group:default/editors, role:default/authenticated
g, group:default/kubrix, role:default/authenticated
g, group:default/admins, role:default/authenticated
# Layer 2 (viewers)
g, group:default/viewers, role:default/kubrixdemo
# Layer 3 (editors / kubrix / admins)
g, group:default/editors, role:default/kubrixdev
g, group:default/kubrix, role:default/kubrixdev
g, group:default/admins, role:default/kubrixdev
The Backstage RBAC plugin only applies CSV p lines to roles that have a source: csv-file origin. Adding explicit g lines for every group ensures the role is created with the correct source. Using defaultRole in config creates the role with source: configuration, which conflicts with CSV policies and prevents them from being applied.
Template Visibility
By default, all authenticated users can read all catalog entities including Templates (Layer 1). When conditional policies are enabled, Template visibility is filtered per user:
- Non-Template entities — always readable by all authenticated users
- Template entities — readable only if the user is an owner or the template has the
kubrix.io/visibility: sharedannotation
Making a Template Visible to All Users
Add the annotation to your template's catalog-info.yaml or template.yaml:
metadata:
annotations:
kubrix.io/visibility: shared
Restricting a Template to a Specific Group
Set the template's owner to the group that should have access:
spec:
owner: group:default/editors
Only members of that group will see and be able to execute the template. Superusers always see all templates.
Access Matrix Example
| Template | Owner | Shared | viewers | editors | admins |
|---|---|---|---|---|---|
docs-template | group:default/kubrix | yes | ALLOW | ALLOW | ALLOW |
docs-template2 | group:default/editors | no | DENY | ALLOW | ALLOW |
docs-template3 | group:default/viewers | no | ALLOW | DENY | ALLOW (superuser) |
docs-template4 | group:default/admins | no | DENY | DENY | ALLOW |
Configuration
Enabling Conditional Policies
In your *values*.yaml, set the path to the conditional policies file:
appConfig:
permission:
rbac:
conditionalPoliciesFile: /opt/app-root/src/rbac/conditional-policies.yaml
The file is automatically mounted from the conditional-policies ConfigMap via a projected volume. The ConfigMap is generated from rbac.conditionalPolicies in the Helm values.
Helm Values Structure
rbac:
# If true: Helm render fails when conditionalPolicies is empty.
# If false: silently skips the ConfigMap (no conditional policies applied).
enforceConditionalPolicies: false
conditionalPolicies:
# Key = logical name for Helm map merging. Not visible to Backstage.
# Multiple entries are rendered as YAML multi-document (separated by ---).
authenticated-catalog-read:
result: CONDITIONAL
roleEntityRef: role:default/authenticated
pluginId: catalog
resourceType: catalog-entity
permissionMapping:
- read
conditions:
anyOf:
- not:
rule: IS_ENTITY_KIND
resourceType: catalog-entity
params:
kinds:
- Template
- rule: IS_ENTITY_OWNER
resourceType: catalog-entity
params:
claims:
- $ownerRefs
- rule: HAS_ANNOTATION
resourceType: catalog-entity
params:
annotation: kubrix.io/visibility
value: shared
Adding Custom Conditional Policies
Because rbac.conditionalPolicies is a Helm map (not a list), customer values files are deep-merged automatically. Add new entries in your customer values without repeating the base policies:
# values-customer.yaml
rbac:
conditionalPolicies:
my-custom-policy:
result: CONDITIONAL
roleEntityRef: role:default/kubrixdev
pluginId: catalog
resourceType: catalog-entity
permissionMapping:
- read
conditions:
rule: HAS_LABEL
resourceType: catalog-entity
params:
label: my-team/restricted
Helm merges this with the base authenticated-catalog-read entry. Both policies are written into the single conditional-policies.yaml file separated by ---.
Volume Mount
The CSV policy file and conditional policies file are both mounted via a projected volume that combines two ConfigMaps into a single directory:
extraVolumes:
- name: rbac-policy
projected:
defaultMode: 420
sources:
- configMap:
name: rbac-policy
- configMap:
name: conditional-policies
optional: true # skipped gracefully if enforceConditionalPolicies=false
extraVolumeMounts:
- name: rbac-policy
mountPath: /opt/app-root/src/rbac
optional: true on the conditional-policies ConfigMap source ensures the pod starts without error even when no conditional policies are configured.
Disabling Conditional Policies
To run without conditional policies (all users see all templates):
rbac:
enforceConditionalPolicies: false
conditionalPolicies: {}
And comment out conditionalPoliciesFile in appConfig.permission.rbac.
Simply commenting out conditionalPoliciesFile while the database still holds previously written conditional policies will not remove them. The plugin only clears DB entries when it reads the file on startup and finds them removed. To fully clear, either delete and recreate the Backstage database or use the RBAC REST API to delete conditions manually.
File Reload
With policyFileReload: true, the RBAC plugin watches both the CSV and conditional policies files for changes and reloads without a pod restart:
appConfig:
permission:
rbac:
policyFileReload: true
This applies to both rbac-policy.csv and conditional-policies.yaml.