Kubernetes · OpenShift · RBAC · access review
Who can delete secrets in production — and can you prove it?
Kubernetes RBAC is easy to write and nearly impossible to read back. rbacspy answers the question you actually have — who can do this, and how — by tracing every binding, role and group membership that grants it, and printing the exact path for each subject.
Why
A subject's access is the sum of every RoleBinding and ClusterRoleBinding that names it, each pointing at a Role or ClusterRole, each widened by wildcards, and each reaching people through Group membership synced from your identity provider.
To answer one simple question — who can actually do this? — you have to trace all of that by hand. kubectl auth can-i --list answers it for one subject you already suspect; it will not enumerate every subject that can, and it will not tell you how. For an access review, the how is the whole point: you cannot remove access you cannot locate.
The query
| Kind | Subject | Scope | Via |
|---|---|---|---|
| Group | platform-admins | cluster-wide | ClusterRoleBinding/admins → ClusterRole/cluster-admin |
| User | alice | cluster-wide | ↳ via Group/platform-admins → cluster-admin |
| User | bob | cluster-wide | ↳ via Group/platform-admins → cluster-admin |
| ServiceAccount | argocd/…-controller | cluster-wide | ClusterRoleBinding → cluster-admin |
| User | contractor-jones | payments | RoleBinding/payments/contractor-edit → ClusterRole/edit |
The last column is the point. Knowing that someone has access isn't enough for an audit — you need to show how, so you can remove exactly the right binding.
The hop nobody sees
alice was never named in a binding. She can delete secrets in every namespace because she is a member of a group that a ClusterRoleBinding grants cluster-admin — a fact spread across three objects and an identity provider. rbacspy resolves OpenShift Group membership, so the person who only ever gets access through a group still appears, with via Group/… in the path.
And it was asked, who among them holds the keys? And the answer was not written on any one door, but scattered: a name in a group, a group in a binding, a binding upon the master key; and only he who followed the whole chain could say, with proof, who might enter in.
Two modes
No opinions, just the facts of the RBAC graph. Good for answering questions and for producing evidence an auditor will accept.
rbacspy can delete secrets
rbacspy can delete secrets -n payments
rbacspy can '*' secrets # anyone,
# any verb
rbacspy subjects # full
# inventory
Off by default. Turn it on and rbacspy flags deviations from a least-privilege, GitOps-run posture: humans with write access, individual-user bindings, wildcard roles, and every cluster-admin grant.
rbacspy audit --policy \ --allow-writers \ ServiceAccount:argocd/app-controller
The lint, when you ask for it
| Severity | Rule | Subject / object | Issue |
|---|---|---|---|
| CRITICAL | cluster-admin-grant | Group:platform-admins | A group of people is bound to cluster-admin |
| WARNING | human-write-access | User:contractor-jones | A human can modify resources directly, not via CD |
| WARNING | wildcard-role | ClusterRole:cluster-admin | Grants all verbs on all resources |
| INFO | user-direct-binding | User:contractor-jones | Bind a synced Group, not the individual |
The defaults encode a common regulated posture — humans read-only, changes through a CD service account, groups over users. If yours differ, the neutral mode still gives you everything; the policy layer is opt-in and its allow-list is yours.
Why the path is evidence
Least-privilege and separation-of-duties controls — Solvency II, ISO 27001, SOC 2 — don't just ask whether access is limited. They ask you to show it, subject by subject, with the reason each person has what they have.
rbacspy can <verb> <resource> -o json produces exactly that: a grant list with the binding chain for each subject. It is the artifact the review wants, and the artifact kubectl won't assemble across every subject at once.
How it behaves
There is no mutating command in the tool at all. It reads RBAC objects — Roles, ClusterRoles, bindings, and OpenShift Groups — through your existing oc or kubectl, and reasons about them locally. No operator, no client-go skew, nothing to deploy.
--from runs the same queries and the same lint against a directory of JSON dumps — no credentials, no live connection. Hand a reviewer a bundle; they answer "who can do X" without touching the cluster.
mkdir dump
for r in roles clusterroles rolebindings \
clusterrolebindings groups; do
oc get $r -o json > dump/$r.json
done
rbacspy can delete secrets --from dump
rbacspy audit --policy --from dump