RBAC: Least Privilege in Practice
Real RBAC hygiene means scoping ServiceAccounts to a namespace, granting only the verbs a workload needs, and never binding cluster-admin to an app.
RBAC is easy to get working (grant everything) and hard to get right (grant exactly enough). The senior habit is to start from zero and add only the verbs a workload actually calls.
The four objects
- Role — a set of permissions within one namespace.
- ClusterRole — the same, but cluster-wide or for cluster-scoped resources.
- RoleBinding — grants a Role (or ClusterRole) to a subject in one namespace.
- ClusterRoleBinding — grants cluster-wide. Use sparingly; this is where over-permissioning hides.
Rules of thumb
- Give every workload its own ServiceAccount — never let Pods use the namespace
defaultSA. - Prefer a namespaced Role + RoleBinding over ClusterRole. If an app only reads ConfigMaps in its own namespace, that is all it should be able to do.
- List explicit
verbs—get,list,watchfor read-only. Avoid the*wildcard on verbs and resources. - Set
automountServiceAccountToken: falseon Pods that never call the Kubernetes API, so a compromised container has no token to steal.
Audit what a subject can do
kubectl auth can-i is your fastest sanity check — it answers yes/no for any verb, resource, and ServiceAccount without trial and error.
Example
apiVersion: v1
kind: ServiceAccount
metadata:
name: config-reader
namespace: prod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role # namespaced, not ClusterRole
metadata:
namespace: prod
name: read-configmaps
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"] # read-only, no wildcards
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: prod
name: config-reader-binding
subjects:
- kind: ServiceAccount
name: config-reader
namespace: prod
roleRef:
kind: Role
name: read-configmaps
apiGroup: rbac.authorization.k8s.ioWhen to use it
- A CI robot ServiceAccount is given a Role that can only update Deployment images in its namespace, preventing it from reading Secrets or creating new resources.
- A team audits its RBAC configuration with kubectl auth can-i --list to identify ServiceAccounts with excessive permissions before a security review.
- A developer creates a separate read-only ClusterRole for external monitoring tools so they can list pods and metrics without write access.
More examples
Minimal CI deploy Role
Creates a minimal Role that allows a CI robot to update Deployments and read pods in production, nothing more.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ci-deployer
namespace: production
rules:
- apiGroups: [apps]
resources: [deployments]
verbs: [get, list, patch, update]
- apiGroups: [""] # Only read pods, not write
resources: [pods]
verbs: [get, list]Audit permissions for a ServiceAccount
Audits all permissions held by the ci-robot ServiceAccount and verifies it cannot perform a sensitive operation like deleting secrets.
kubectl auth can-i --list \
--as=system:serviceaccount:production:ci-robot
# Resources Verbs
# deployments get list patch update
# pods get list
# Check a specific action
kubectl auth can-i delete secrets \
--as=system:serviceaccount:production:ci-robot
# noBind Role to ServiceAccount
Binds the ci-deployer Role to the ci-robot ServiceAccount using a namespaced RoleBinding in the production namespace.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ci-robot-binding
namespace: production
subjects:
- kind: ServiceAccount
name: ci-robot
namespace: production
roleRef:
kind: Role
name: ci-deployer
apiGroup: rbac.authorization.k8s.io
Discussion