RBAC, ClusterRoles & ServiceAccounts
RBAC decides whether a subject can perform a verb on a resource at a scope. Subjects are users, groups, or ServiceAccounts. Roles and ClusterRoles contain rules. RoleBindings and ClusterRoleBindings attach subjects to those rules. Use namespace Roles and RoleBindings by default; reserve ClusterRoleBindings and powerful verbs for cases that truly need cluster-wide access.
Mental Model
Every Kubernetes API request is checked by the API server. After authentication proves who the caller is, RBAC asks: does this subject have a binding to a role whose rules allow this verb on this resource in this namespace or cluster scope?
RBAC is additive. There is no explicit deny in standard Kubernetes RBAC. If any bound rule allows the request, the request is allowed. If no rule allows it, the API server returns Forbidden.
Bindings connect identities to permission rules; the API server evaluates the request.
Core Objects
| Object | Scope | Purpose | Example |
|---|---|---|---|
Role | Namespace | Rules for namespaced resources in one namespace. | Read Pods in app. |
RoleBinding | Namespace | Binds users/groups/SAs to a Role or ClusterRole within one namespace. | Give sre-readonly access to app. |
ClusterRole | Cluster | Rules for cluster resources or reusable namespaced rule sets. | Read nodes, namespaces, or reusable app read-only rules. |
ClusterRoleBinding | Cluster | Binds a ClusterRole across the whole cluster. | Controller needing all namespaces. |
ServiceAccount | Namespace | Identity used by Pods and automation. | system:serviceaccount:app:cd-bot. |
Verbs, Resources, And Subresources
RBAC rules grant verbs on resources. Some actions use subresources: reading logs needs pods/log, exec needs pods/exec, scaling a Deployment can involve deployments/scale.
| Need | Resource | Verb | Note |
|---|---|---|---|
| List Pods | pods | list | Often paired with get and watch. |
| Read logs | pods/log | get | Separate from reading Pod specs. |
| Exec into Pod | pods/exec | create | Sensitive; equivalent to app shell access. |
| Patch Deployment | deployments | patch | Common for CI/CD image updates. |
| Scale Deployment | deployments/scale | patch or update | Needed by autoscalers or operators. |
| Read Secrets | secrets | get | Very sensitive; grants credential visibility. |
kubectl auth can-i
kubectl auth can-i is the fastest RBAC debugger. Use it as yourself, or impersonate a ServiceAccount/user when you have permission to impersonate.
kubectl auth can-i get pods -n app
kubectl auth can-i get pods/log -n app
kubectl auth can-i patch deployments.apps -n app
kubectl auth can-i list nodes
# Test as a ServiceAccount.
kubectl auth can-i patch deployments.apps -n app \
--as=system:serviceaccount:app:cd-bot
# Show allowed actions for current identity.
kubectl auth can-i --list -n appFrom-Scratch Read-Only Lab
This creates read-only namespace access for a group. The ClusterRole is reusable, but the RoleBinding limits it to namespace app.
apiVersion: v1
kind: Namespace
metadata:
name: app
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: app-readonly-template
rules:
- apiGroups: ["", "apps", "batch"]
resources:
- pods
- pods/log
- services
- endpoints
- configmaps
- deployments
- replicasets
- jobs
- cronjobs
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-readonly
namespace: app
subjects:
- kind: Group
name: sre-readonly@example.com
apiGroup: rbac.authorization.k8s.io
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: app-readonly-templateServiceAccounts For Workloads
Pods use ServiceAccounts as their Kubernetes API identity. A controller, job, CI/CD runner, or app that talks to the API should use a dedicated ServiceAccount with narrow permissions.
apiVersion: v1
kind: ServiceAccount
metadata:
name: cd-bot
namespace: app
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployment-patcher
namespace: app
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "patch"]
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cd-bot-deployment-patcher
namespace: app
subjects:
- kind: ServiceAccount
name: cd-bot
namespace: app
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: deployment-patcherkubectl get serviceaccount -n app
kubectl get pod <pod> -n app -o jsonpath='{.spec.serviceAccountName}{"\\n"}'
kubectl create token cd-bot -n app --duration=15m
kubectl auth can-i patch deployments.apps -n app \
--as=system:serviceaccount:app:cd-bot
kubectl auth can-i delete secrets -n app \
--as=system:serviceaccount:app:cd-botArgoCD And CI/CD Permissions
Deployment automation needs enough permission to manage the objects it owns, but not unlimited cluster-admin access. In client environments, check whether the deployer owns only one namespace, many app namespaces, or cluster-scoped resources like CRDs.
| Automation | Typical Scope | Notes |
|---|---|---|
| Namespace app deployer | Role + RoleBinding in one namespace. | Good for app teams and CI jobs. |
| ArgoCD app controller | Often broader, sometimes cluster-wide. | Scope by project/destination and Kubernetes RBAC. |
| Helm installer | Depends on chart contents. | Charts installing CRDs or ClusterRoles need elevated permissions. |
| Operator | Usually ClusterRole + ClusterRoleBinding. | Review generated permissions carefully. |
Inspection Commands
NS=<namespace>
kubectl get role,rolebinding,serviceaccount -n "$NS"
kubectl get clusterrole,clusterrolebinding
kubectl describe rolebinding -n "$NS"
kubectl describe clusterrole <clusterrole>
kubectl describe clusterrolebinding <binding>
# Find bindings that mention a subject name.
kubectl get rolebinding,clusterrolebinding -A -o yaml | grep -n "name: <subject-name>"Symptom To Cause
| Symptom | Likely Cause | Check First |
|---|---|---|
Forbidden on Pods | No RoleBinding in namespace, wrong subject, missing verbs. | kubectl auth can-i get pods -n ns --as=... |
| Can list Pods but cannot see logs | Missing pods/log subresource permission. | Role rules for pods/log. |
| Can update Deployment but cannot scale | Missing deployments/scale permission. | Subresource rule. |
| RoleBinding exists but still denied | Binding subject name/namespace/kind is wrong. | Subject block and ServiceAccount namespace. |
| Works in one namespace, denied in another | RoleBinding is namespace-scoped. | Bindings in target namespace. |
| Helm install fails on CRDs or ClusterRoles | Deployer lacks cluster-scoped permissions. | Chart resources and deployer ClusterRole. |
| Controller cannot watch resources | Missing list/watch permissions. | Controller logs and ClusterRole rules. |
Production Patterns
- Prefer least privilege: grant only the verbs and resources the subject needs.
- Avoid cluster-admin: temporary emergency access should be time-bound and audited.
- Separate humans and workloads: use groups for people and ServiceAccounts for Pods/automation.
- Treat Secrets as privileged: read access to Secrets often means access to external systems.
- Protect exec and impersonation:
pods/execandimpersonatecan be very powerful.
Safe Change Pattern
- Write the required action as a sentence: “SA X needs to patch Deployments in namespace Y.”
- Translate that into API group, resource, verb, and scope.
- Use Role/RoleBinding for namespaced access where possible.
- Validate with
kubectl auth can-i --as=...before handing it to automation. - After rollout, check audit logs or controller logs for denied requests and trim unused permissions later.