Kubernetes RBAC Without the Headaches: Roles, Bindings, and Least Privilege
RBAC is where most clusters quietly grant cluster-admin to everything. Here's how to design least-privilege access that's auditable, with AI to reason about permission scope.
- #kubernetes
- #rbac
- #security
- #access-control
- #ai
- #least-privilege
The dirty secret of Kubernetes RBAC is that most clusters don’t really use it. Someone hit a permission error during setup, bound cluster-admin to a ServiceAccount to make it go away, and never looked back. Now half the workloads can delete every secret in the cluster and nobody knows.
RBAC isn’t hard. It’s just four object types and one core idea: a subject gets a verb on a resource. Get those straight and the rest follows.
The four objects, and how they fit
- Role — a set of permissions (verbs on resources) scoped to one namespace.
- ClusterRole — the same, but cluster-wide, or for cluster-scoped resources like nodes.
- RoleBinding — grants a Role (or ClusterRole) to subjects within a namespace.
- ClusterRoleBinding — grants a ClusterRole to subjects across the whole cluster.
The combination that trips everyone up: a RoleBinding can reference a ClusterRole. That’s how you reuse a generic “read-only” ClusterRole but scope it to one namespace. Use it constantly — it’s the cleanest pattern in RBAC.
Start from the verb, not the role
When granting access, ask exactly what the subject needs to do:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: payments
name: deploy-manager
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
Note pods/log is a separate sub-resource. People grant pods: get and then can’t figure out why kubectl logs is denied. The log endpoint is its own resource.
Note also: no delete, no create on secrets, no wildcards. Grant the verbs the job needs and nothing more.
Never use wildcards in production
verbs: ["*"] and resources: ["*"] are how cluster-admin happens by accident. A wildcard role auto-inherits every new resource type you install later — including CRDs you’ve never heard of. Spell out the verbs. It’s more typing and far fewer surprises.
The one exception is intentionally-admin roles, and even those should be bound to humans through groups, not baked into ServiceAccounts.
ServiceAccounts are the real attack surface
Human access matters, but the permissions that get exploited belong to ServiceAccounts, because a compromised pod uses its mounted token. Two rules:
- Every workload gets its own ServiceAccount with a minimal Role. Don’t let pods run as
default, which by convention many people over-grant. - Disable token automounting unless the pod actually calls the API:
spec:
automountServiceAccountToken: false
That one line removes the API token from a pod that never needed it — closing the most common lateral-movement path after a container compromise.
Audit what you’ve actually granted
You can’t reason about RBAC you can’t see. Two commands I run regularly:
# Can this subject do this thing?
kubectl auth can-i delete secrets \
--as=system:serviceaccount:payments:deploy-manager -n payments
# What can a subject do? (with the rbac-lookup or who-can plugins)
kubectl who-can delete secrets -n payments
kubectl auth can-i --as=... is the most underused command in Kubernetes. It answers the exact question that matters — “does this thing have this power?” — without guessing from YAML.
Where AI helps with RBAC
RBAC’s failure mode is human: the YAML is correct but grants more than intended, and nobody notices because reasoning about transitive permission scope is tedious. Paste a Role and ask:
“Here’s a Role and RoleBinding. List every concrete action this grants, flag anything broader than ‘read deployments and logs in this namespace,’ and explain the blast radius if the bound ServiceAccount’s token leaked.”
That’s a question AI answers well — it’s pattern-matching and enumeration, not judgment. Keep a few of these Kubernetes RBAC prompts ready. You still decide what’s acceptable; AI just makes the implications visible.
Use groups for humans, ServiceAccounts for machines
Bind humans through groups your identity provider already manages (oidc:platform-team), never by individual username. When someone leaves the team, they leave the group, and their cluster access evaporates with no RBAC change required. Binding individuals means RBAC objects rot every time someone moves teams.
A least-privilege rollout that won’t page you
Tightening RBAC on a live cluster is risky — yank a permission a controller needs and things stop reconciling silently. Do it safely:
- Audit first. Enumerate current bindings; find the
cluster-admingrants. - Build the replacement Role scoped to observed usage.
- Test with
auth can-ibefore you switch the binding. - Switch, then watch controller logs for
forbiddenerrors for a full reconcile cycle. - Keep the old binding one command away so rollback is instant.
Before any RBAC change merges, I run it through the Code Review tool — it’s good at catching the wildcard that crept in or the ClusterRoleBinding that should have been namespace-scoped.
RBAC done right is invisible: every workload can do its job and nothing more, and you can prove it with one command. The work is enumeration and discipline, not cleverness — which is exactly where a careful operator plus AI beats a tired one alone.
AI permission analysis is assistive. Always verify with kubectl auth can-i against your cluster before trusting a grant.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.