Write Your Own Keycard – Custom RBAC in Action
Duration: ~20 minutes
Overview
By the end you’ll be able to design and apply a narrowly scoped Role that grants only the handful of permissions an application maintainer truly needs, verify what works, prove what is denied, and explain how that shrinks incident blast radius.
Create a minimal custom Role and RoleBinding, prove allowed versus denied verbs, and state the least-privilege intent in plain language.
Why it matters
Imagine an office where every employee’s badge opens every room—including finance, HR, and the server closet. Nobody intends abuse, but one lost badge means a long night of worrying what was accessed. Built‑in broad roles (edit, admin) are those master badges. Custom RBAC is like cutting a key that only opens the supply cabinet your role requires. When (not if) an account is phished or a token is leaked, tightly scoped permissions turn a potential headline into a routine ticket. Auditors also smile when intent is obvious: “This identity may only adjust configuration.”
What does it solve
Most real security incidents aren’t cinematic hacks—they’re ordinary credentials used in extraordinary places. Over-broad roles quietly enable: * Unapproved pod deployments (persistence footholds) * Reading secrets far outside an app’s scope (data leakage) * Creating new RoleBindings to escalate (privilege stacking) * Difficult audit narratives (“Why did this CI user delete a Secret?”)
Crafting a custom Role removes silent “just in case” powers. It converts vague responsibility (“Dev team rights”) into precise intent (“May adjust non-sensitive app configuration values and rotate one secret”). That precision speeds investigations and compliance reviews.
Your Mission
Click each step only if you need a hint.
Create a lab namespace
oc new-project 201-02-c-rbac-lab
Create a minimal Role (include patch so modern tooling works)
oc create role config-secret-updater \
--verb=get --verb=list --verb=update --verb=patch \
--resource=configmaps,secrets
Bind the Role to a (demo) user
oc create rolebinding updater-binding \
--role=config-secret-updater --user=devuser
Seed sample objects
oc create configmap app-config --from-literal=flag=on
oc create secret generic db-creds --from-literal=password=Initial123!
Show allowed operations
oc auth can-i list configmaps --as devuser
oc auth can-i update secret/db-creds --as devuser
oc patch configmap app-config -p '{"data":{"flag":"off"}}' --type=merge --as devuser
oc patch secret db-creds -p '{"stringData":{"password":"Rotated456!"}}' --type=merge --as devuser
Show explicit denials (proof of least privilege)
oc auth can-i create pods --as devuser
oc run test --image=registry.access.redhat.com/ubi9/ubi --as devuser || echo DENIED
oc auth can-i create rolebinding --as devuser
oc create rolebinding bogus --clusterrole=view --user=devuser --as devuser || echo DENIED
oc auth can-i delete secret/db-creds --as devuser
oc delete secret db-creds --as devuser || echo DENIED
Service Account variant (typical for automation)
oc create sa app-sa
oc create rolebinding sa-updater \
--role=config-secret-updater \
--serviceaccount=201-02-c-rbac-lab:app-sa
oc auth can-i patch configmap/app-config \
--as system:serviceaccount:201-02-c-rbac-lab:app-sa
Disable default token automount to shrink passive risk
oc patch sa app-sa -p '{"automountServiceAccountToken":false}'
Debrief
Custom Roles shrink blast radius: losing a closet key hurts less than losing a master key—precise verbs beat broad edit/admin for sensitive paths.
What breaks without this:
-
Broad namespaced roles → pod create persistence, Secret sprawl, RBAC self-escalation
-
ClusterRole for namespace objects → accidental cluster reach
Controls that matter: minimal Roles, separated ServiceAccounts, disable unused automount, oc auth can-i matrices, and RHACS/wildcard reviews.
Quick facts: include patch when updates need it; avoid “just in case” delete; prefer Role over ClusterRole for namespace-scoped work.
