RBAC (Role-Based Access Control) is the authorization layer that answers: "Can this user/SA do this action on this resource?" It's the most important security mechanism in Kubernetes — every API request passes through RBAC after authentication. Mastering it is essential for CKA, CKS, and production cluster security.
1. The RBAC Model — Four Resources
| Resource | Scope | Purpose |
|---|---|---|
| Role | Namespaced | Defines permissions within a specific namespace |
| ClusterRole | Cluster-wide | Defines permissions cluster-wide OR reusable across namespaces |
| RoleBinding | Namespaced | Grants a Role (or ClusterRole) to subjects within a namespace |
| ClusterRoleBinding | Cluster-wide | Grants a ClusterRole to subjects across the entire cluster |
Combination Rules
| Role Type | Binding Type | Result |
|---|---|---|
| Role | RoleBinding | Permissions in one namespace ✓ |
| ClusterRole | RoleBinding | ClusterRole's permissions scoped to one namespace ✓ |
| ClusterRole | ClusterRoleBinding | Permissions across all namespaces ✓ |
| Role | ClusterRoleBinding | ❌ Invalid (can't bind a namespaced Role cluster-wide) |
2. Roles — Defining Permissions
Role (Namespaced)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""] # Core group (Pods, Services, etc.)
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"] # Subresource
verbs: ["get"]
ClusterRole (Cluster-Wide)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader # No namespace (cluster-scoped)
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list"]
The Verb Matrix
| Verb | HTTP Method | Meaning |
|---|---|---|
get | GET (single) | Read one resource by name |
list | GET (collection) | List all resources (filtered) |
watch | GET (streaming) | Watch for changes (long-poll) |
create | POST | Create a new resource |
update | PUT | Replace an existing resource |
patch | PATCH | Partially modify a resource |
delete | DELETE (single) | Delete one resource |
deletecollection | DELETE (collection) | Delete all resources matching criteria |
Rule Building Blocks
# Multiple resources in one rule (AND between apiGroups+resources, OR between verbs):
rules:
- apiGroups: ["", "apps"] # Core AND apps groups
resources: ["pods", "deployments"]
verbs: ["get", "list", "create", "delete"]
# Restrict to specific resource names:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-creds", "api-key"] # Only these specific secrets
verbs: ["get"]
# Subresources:
- apiGroups: [""]
resources: ["pods/exec"] # Pod exec
verbs: ["create"] # exec requires "create" verb
- apiGroups: ["apps"]
resources: ["deployments/scale"] # Scale subresource
verbs: ["get", "update"]
# Non-resource URLs (health endpoints, metrics):
- nonResourceURLs: ["/healthz", "/metrics"]
verbs: ["get"]
kubectl create role pod-reader --verb=get,list,watch --resource=pods -n production --dry-run=client -o yamlkubectl create clusterrole node-reader --verb=get,list --resource=nodes --dry-run=client -o yaml
3. Bindings — Connecting WHO to WHAT
RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: production
subjects: # WHO gets the permissions
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount
name: ci-sa
namespace: ci-cd # SA namespace (can be different!)
roleRef: # WHAT permissions (immutable after creation!)
kind: Role # or ClusterRole
name: pod-reader
apiGroup: rbac.authorization.k8s.io
ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-admin-binding # No namespace (cluster-scoped)
subjects:
- kind: User
name: admin@company.com
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin # Built-in superuser role
apiGroup: rbac.authorization.k8s.io
Subject Types
| Kind | Name Format | Example |
|---|---|---|
User | Username from authentication (cert CN, OIDC claim) | jane, admin@company.com |
Group | Group from authentication (cert O, OIDC groups) | developers, system:masters |
ServiceAccount | SA name + namespace | ci-sa in namespace ci-cd |
roleRef is IMMUTABLE. Once a binding is created, you cannot change which Role it references. To change the role, you must delete and recreate the binding. This prevents accidental privilege escalation through binding modification.
# Imperative commands (fast for exams): kubectl create rolebinding read-pods \ --role=pod-reader \ --user=jane \ --serviceaccount=ci-cd:ci-sa \ -n production kubectl create clusterrolebinding admin-binding \ --clusterrole=cluster-admin \ --user=admin@company.com
kubectl create rolebinding and kubectl create clusterrolebinding support --user, --group, and --serviceaccount=ns:name flags. Use imperative creation, then verify with kubectl auth can-i.
4. Testing RBAC — kubectl auth can-i
# Check your own permissions: kubectl auth can-i create deployments -n production # yes kubectl auth can-i delete nodes # no # Check as another user (requires admin): kubectl auth can-i get secrets -n production --as=jane # no kubectl auth can-i get pods -n production --as=system:serviceaccount:ci-cd:ci-sa # yes # List ALL permissions for a user: kubectl auth can-i --list --as=jane -n production # Resources Non-Resource URLs Resource Names Verbs # pods [] [] [get list watch] # pods/log [] [] [get]
kubectl auth can-i is your verification tool. After creating a Role + Binding, always test: kubectl auth can-i <verb> <resource> --as=<user> -n <namespace>. This proves the RBAC is working correctly before moving to the next question.
5. Built-in ClusterRoles
| ClusterRole | Permissions | Use Case |
|---|---|---|
cluster-admin | Everything (wildcard *) | Cluster operators (danger!) |
admin | Full access within a namespace (no quota/RBAC modification) | Namespace owner |
edit | Read/write most resources in a namespace (no Roles/Bindings) | Developers |
view | Read-only access to most resources (no Secrets) | Monitoring, auditors |
# Give a developer full edit access in their namespace: kubectl create rolebinding dev-edit \ --clusterrole=edit \ --user=jane \ -n team-alpha # ClusterRole "edit" used with RoleBinding → scoped to namespace team-alpha # Jane can create/delete Pods, Deployments, Services in team-alpha only
view, edit, and admin ClusterRoles are designed to be bound per-namespace via RoleBinding. You don't need to create your own Roles for common use cases — just bind the built-in ones. Only create custom Roles for specific fine-grained needs.
6. Common Patterns
# Pattern: CI/CD pipeline — create/update Deployments only
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "create", "update"]
# NO: delete, pods/exec, secrets — principle of least privilege
# Pattern: Monitoring — read everything, write nothing
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-reader
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["*"]
verbs: ["get", "list", "watch"] # Read only, across all namespaces
- apiGroups: [""]
resources: ["secrets"]
verbs: [] # Explicitly NO access to Secrets
Summary
| Concept | Key Point |
|---|---|
| Role | Namespaced permissions (verbs + apiGroups + resources) |
| ClusterRole | Cluster-wide permissions (or reusable template for namespaces) |
| RoleBinding | Grants Role or ClusterRole to subjects in one namespace |
| ClusterRoleBinding | Grants ClusterRole to subjects across all namespaces |
| Additive only | No deny rules. No binding = no permission. |
| roleRef immutable | Can't change which Role a Binding references (delete + recreate) |
| Verbs | get, list, watch, create, update, patch, delete, deletecollection |
| resourceNames | Restrict to specific named resources (fine-grained) |
| Subresources | pods/exec, deployments/scale, pods/log — separate permissions |
| Testing | kubectl auth can-i — verify before moving on |
📝 Quiz: RBAC Fundamentals
Q1: You create a Role in namespace "dev" and a ClusterRoleBinding referencing it. Does this work?
Q2: A user has a RoleBinding in namespace "dev" granting "get pods" AND a ClusterRoleBinding granting "get nodes." Can they get pods in namespace "prod"?
Q3: You need a CI/CD ServiceAccount to deploy apps but NOT read Secrets. How do you configure this?
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get","list","create","update","patch"]
- apiGroups: [""]
resources: ["services","configmaps"]
verbs: ["get","list","create","update"]Don't include secrets in any rule. RBAC is additive — if Secrets aren't granted, they're denied by default. Bind this Role to the CI SA via RoleBinding.Q4: What's the difference between using ClusterRole "edit" with a RoleBinding vs a ClusterRoleBinding?
ClusterRoleBinding: Grants "edit" permissions in ALL namespaces. User can edit resources everywhere in the cluster. Always prefer RoleBinding for least privilege — only use ClusterRoleBinding when cluster-wide access is truly needed.
Q5: A user can get pods but cannot kubectl exec into a Pod. What's missing?
pods/exec subresource with the create verb:- apiGroups: [""] resources: ["pods/exec"] verbs: ["create"]
kubectl exec creates a new exec session (POST to /api/v1/.../pods/name/exec), which requires create on the pods/exec subresource — separate from get pods.Q6: How do you verify that ServiceAccount "deployer" in namespace "ci" can create Deployments in namespace "production"?
kubectl auth can-i create deployments -n production --as=system:serviceaccount:ci:deployerThe
--as flag impersonates the SA. The full format for SAs is system:serviceaccount:<namespace>:<name>. If it returns "yes," the RBAC is correct.