2026-09-03

Kubernetes RBAC Least Privilege: Roles and Bindings

Learn concrete Kubernetes RBAC least-privilege patterns, how to reduce overbroad permissions, and which checks catch risky role bindings before incidents.

RBAC is one of the most effective controls in Kubernetes. Done well, it prevents small mistakes from becoming cluster-wide incidents. Done poorly, it becomes “just give it cluster-admin” and nobody learns anything.

This tip focuses on concrete least privilege: start with good defaults, make escalation intentional, and keep the system operable.

Mental model (the 3 building blocks)

  • Subject: who is making the request (user, group, ServiceAccount).
  • Role / ClusterRole: what actions are allowed (verbs on resources).
  • RoleBinding / ClusterRoleBinding: attaches a role to a subject.

If you remember only one thing:

A Role is just rules. A Binding is what makes it real.

Namespaced vs cluster-wide

  • A Role describes permissions inside one namespace.
  • A ClusterRole can describe namespaced resources, cluster-scoped resources, and non-resource URLs; the Binding determines the effective scope.
  • A RoleBinding grants access only in its own namespace and may reference a reusable ClusterRole.
  • A ClusterRoleBinding grants the referenced ClusterRole across the cluster, including every namespace.

Best practice:

  • Prefer Role + RoleBinding for applications.
  • Use ClusterRole sparingly, mostly for cluster-level controllers or platform operators.

ServiceAccounts: don’t use the default one

Every Pod runs as a ServiceAccount. If you don’t specify one, it uses the namespace’s default ServiceAccount.

Production guidance:

  1. Create a dedicated ServiceAccount per workload (or per app).
  2. Turn off token mounting unless the Pod actually needs to call the Kubernetes API.

Example:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: api
  namespace: app
automountServiceAccountToken: false

Then in your Deployment:

spec:
  template:
    spec:
      serviceAccountName: api
      automountServiceAccountToken: false

If the app needs to call the Kubernetes API (for leader election, watching resources, etc.), set automountServiceAccountToken: true explicitly and review RBAC carefully.

Roles: write the smallest useful rules

RBAC rules are a list of:

  • apiGroups (e.g. "", "apps", "batch")
  • resources (e.g. pods, configmaps, deployments)
  • verbs (e.g. get, list, watch, create, update, patch, delete)

Start with read-only “support” access

For troubleshooting, many teams grant engineers read-only access to common resources in a namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ns-observer
  namespace: app
rules:
  - apiGroups: [""]
    resources: ["pods", "services", "configmaps", "events"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs: ["get"]
  - apiGroups: ["discovery.k8s.io"]
    resources: ["endpointslices"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets"]
    verbs: ["get", "list", "watch"]

Note the subresource pods/log: if you can read Pods but not logs, you’ll still feel “blocked” during incidents.

Understand subresources (common RBAC gotcha)

Some actions require permission on a subresource, not the parent:

  • logs: pods/log
  • exec: pods/exec
  • port-forward: pods/portforward
  • status updates: deployments/status, pods/status

pods/exec and pods/portforward normally use the create verb, not get. If a request is forbidden even though you granted access to pods, check the exact subresource and verb.

Bindings: keep human access separate from workload access

Workloads (ServiceAccounts) and humans (users/groups) should not share bindings casually.

Patterns that work well:

  • Workload SA: only the API permissions the code actually needs.
  • Human operators: read-only by default; separate escalation role for write operations.

This makes audits meaningful: “the app can only do X” is very different from “Alice can do X”.

RBAC is additive and has no explicit deny. Adding a narrower RoleBinding cannot cancel broad access inherited through another Binding, group membership, or ClusterRoleBinding. Review the subject’s complete set of authorization sources.

Debugging RBAC with kubectl auth can-i

This command is your best friend:

kubectl auth can-i get pods -n app
kubectl auth can-i get pods/log -n app
kubectl auth can-i create pods/exec -n app

You can also impersonate:

kubectl auth can-i get secrets -n app --as system:serviceaccount:app:api
kubectl auth can-i --list -n app --as system:serviceaccount:app:api

If you use groups:

kubectl auth can-i get pods -n app --as alice --as-group devs

Using --as or --as-group requires the caller to hold the corresponding impersonation permission; ordinary users cannot impersonate arbitrary identities. This is how you validate rules before shipping them.

Common least-privilege recipes

1) App that only reads ConfigMaps

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: cm-reader
  namespace: app
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: cm-reader
  namespace: app
subjects:
  - kind: ServiceAccount
    name: api
    namespace: app
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: cm-reader

2) App that needs leader election (leases)

Many controllers use coordination.k8s.io Leases:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: leader-election
  namespace: app
rules:
  - apiGroups: ["coordination.k8s.io"]
    resources: ["leases"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]

3) Job runner that creates Pods (high risk)

If an app can create Pods, it may choose a more privileged ServiceAccount in the namespace, mount accessible Secrets or PVCs, reach internal services, run arbitrary images, or consume resources. Treat create pods as a high-privilege action and gate it heavily.

If you must allow it:

  • scope to a namespace
  • restrict Pod Security and admission policies
  • consider using a dedicated controller with strict templates rather than arbitrary pod creation

Red flags (things to avoid)

  • cluster-admin for application ServiceAccounts
  • broad wildcard roles such as resources: ["*"] and verbs: ["*"]; future APIs, resources, or subresources may enter the permission set automatically
  • granting secrets read access to many workloads (secrets are the keys to the kingdom)
  • mixing “break-glass” access into day-to-day bindings

Make escalation intentional (break-glass)

In real operations, you will sometimes need elevated access. The key is to make it:

  • time-bound (temporary)
  • audited (ticket/incident link)
  • scoped (namespace, specific action)

Many teams implement:

  • a dedicated “break-glass” group
  • approvals and session recording

Even simple process changes (and logs) are a big improvement over permanent cluster-admin.

Kubernetes RBAC has no native Binding TTL. Time-bound access needs an identity platform, approval system, or controller that removes the Binding on schedule, followed by a fresh kubectl auth can-i check to prove revocation.

Advanced knobs that help in mature clusters

Restrict with resourceNames (surgical permissions)

RBAC can restrict some rules to specific objects:

rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["api-config"]
    verbs: ["get"]

This is useful when an app needs access to one ConfigMap but not every ConfigMap in the namespace.

Treat pods/exec and pods/portforward as privileged

Allowing exec or port-forward is effectively “shell access”. In many environments it should be:

  • restricted to a small operator group
  • audited
  • disabled for general developer roles

If your organization needs “debug access”, separate it from general read-only access instead of bundling everything into one role.

Prefer built-in roles for human access (when appropriate)

Kubernetes includes standard ClusterRoles such as view, edit, and admin. They can be a starting point for human access, but read the full rules first. The built-in edit role can read Secrets and run Pods as any ServiceAccount in the namespace, which can indirectly expose stronger API permissions.

For application ServiceAccounts, define explicit Roles so the code’s permissions remain visible.

Make least privilege operational

RBAC is most effective when it becomes a habit:

  • every new workload starts with a minimal ServiceAccount
  • permissions are reviewed like code
  • escalation is normal but controlled

That’s how you keep clusters safe without slowing teams down.

Pre-rollout checklist

  • Each workload uses a dedicated ServiceAccount.
  • automountServiceAccountToken: false unless the app needs the API.
  • RoleBindings are preferred over cluster-wide grants.
  • Required subresources and verbs are listed explicitly.
  • kubectl auth can-i validates the real ServiceAccount or user groups.
  • Break-glass access expires, is audited, and is verified as revoked.

Three access-review checks

Q: Role vs ClusterRole? A: A Role describes namespaced permissions. A ClusterRole can describe broader resources, but a RoleBinding still limits its grant to that Binding’s namespace.

Q: Why does a Pod have more permissions than expected? A: It may be using the default ServiceAccount or a broad ClusterRoleBinding. Verify the SA and bindings.

Q: How do I test permissions? A: Use kubectl auth can-i with the same ServiceAccount.

References