Module 5 — RBAC (Role-Based Access Control)
Overview
RBAC controls who can do what on which resources in the cluster. It's a core CKA topic — you'll need to create roles, bind them to users or service accounts, and troubleshoot permission issues. This module covers all four RBAC objects, ServiceAccounts, token management, and practical policy design.
1. RBAC Concepts
1.1 The Three Questions RBAC Answers
| WHO? → User, Group, or ServiceAccount (the subject)
WHAT? → get, list, create, delete, etc. (the verbs)
WHICH? → pods, services, secrets, etc. (the resources)
|
1.2 The Four RBAC Objects
| Object |
Scope |
Purpose |
| Role |
Namespace |
Defines permissions within a single namespace |
| ClusterRole |
Cluster-wide |
Defines permissions across all namespaces or for cluster-scoped resources |
| RoleBinding |
Namespace |
Grants a Role (or ClusterRole) to a subject within a namespace |
| ClusterRoleBinding |
Cluster-wide |
Grants a ClusterRole to a subject across the entire cluster |
1.3 How They Connect
| ┌────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Subject │◀────────│ RoleBinding │────────▶│ Role │
│ (who) │ │ (connects) │ │ (permissions) │
│ │ │ │ │ │
│ - User │ │ namespace: │ │ namespace: │
│ - Group │ │ default │ │ default │
│ - SA │ │ │ │ │
└────────────┘ └──────────────┘ └──────────────────┘
┌────────────┐ ┌───────────────────┐ ┌──────────────────┐
│ Subject │◀────────│ClusterRoleBinding │───▶│ ClusterRole │
│ (who) │ │ (connects) │ │ (permissions) │
│ │ │ │ │ │
│ - User │ │ cluster-wide │ │ cluster-wide │
│ - Group │ │ │ │ │
│ - SA │ │ │ │ │
└────────────┘ └───────────────────┘ └──────────────────┘
|
1.4 Binding Combinations
| Binding Type |
Role Type |
Effect |
| RoleBinding → Role |
Namespaced → Namespaced |
Permissions in a single namespace |
| RoleBinding → ClusterRole |
Namespaced → Cluster |
ClusterRole permissions scoped to one namespace |
| ClusterRoleBinding → ClusterRole |
Cluster → Cluster |
Permissions across all namespaces |
| ClusterRoleBinding → Role |
❌ Not allowed |
— |
CKA Tip: A RoleBinding can reference a ClusterRole — this is a common pattern to reuse a ClusterRole across multiple namespaces without granting cluster-wide access.
2. Role and ClusterRole
2.1 Role (Namespaced)
| apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: default
rules:
- apiGroups: [""] # "" = core API group
resources: ["pods"]
verbs: ["get", "list", "watch"]
|
2.2 ClusterRole (Cluster-Scoped)
| apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
|
2.3 Understanding the rules Fields
| Field |
Description |
Examples |
apiGroups |
API group the resource belongs to |
"" (core), "apps", "rbac.authorization.k8s.io", "networking.k8s.io" |
resources |
Resource types |
"pods", "deployments", "services", "secrets", "nodes" |
verbs |
Allowed actions |
"get", "list", "watch", "create", "update", "patch", "delete" |
resourceNames |
(Optional) Specific resource names |
["my-pod", "my-secret"] — limits to named resources only |
2.4 Common API Groups
| API Group |
Resources |
"" (core) |
pods, services, secrets, configmaps, namespaces, nodes, persistentvolumes, persistentvolumeclaims, serviceaccounts |
apps |
deployments, replicasets, daemonsets, statefulsets |
batch |
jobs, cronjobs |
networking.k8s.io |
networkpolicies, ingresses |
rbac.authorization.k8s.io |
roles, clusterroles, rolebindings, clusterrolebindings |
storage.k8s.io |
storageclasses |
policy |
poddisruptionbudgets |
How to find the API group for any resource:
| # List all API resources with their groups
kubectl api-resources
# Filter for a specific resource
kubectl api-resources | grep deployment
# NAME SHORTNAMES APIVERSION NAMESPACED KIND
# deployments deploy apps/v1 true Deployment
# ^^^^
# API group = "apps"
|
2.5 Imperative Commands
| # Create a Role
kubectl create role pod-reader \
--verb=get,list,watch \
--resource=pods \
-n default
# Create a ClusterRole
kubectl create clusterrole node-reader \
--verb=get,list,watch \
--resource=nodes
# Create a Role for deployments (apps API group)
kubectl create role deploy-manager \
--verb=get,list,create,delete \
--resource=deployments.apps \
-n default
# Create a ClusterRole for all verbs on pods
kubectl create clusterrole pod-admin \
--verb='*' \
--resource=pods
|
CKA Tip: Use imperative commands on the exam — they're faster than writing YAML. Use --resource=<resource>.<apiGroup> for non-core resources.
3. RoleBinding and ClusterRoleBinding
3.1 RoleBinding (Namespaced)
| apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
|
3.2 ClusterRoleBinding (Cluster-Wide)
| apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-nodes-global
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: node-reader
apiGroup: rbac.authorization.k8s.io
|
3.3 Subject Types
| Kind |
Description |
apiGroup |
User |
External user (certificate CN) |
rbac.authorization.k8s.io |
Group |
External group (certificate O) |
rbac.authorization.k8s.io |
ServiceAccount |
In-cluster identity |
"" (empty string) |
ServiceAccount binding example:
| subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: default # namespace where the SA lives
|
3.4 Imperative Commands
| # Bind a Role to a user
kubectl create rolebinding read-pods \
--role=pod-reader \
--user=jane \
-n default
# Bind a ClusterRole to a user (cluster-wide)
kubectl create clusterrolebinding read-nodes-global \
--clusterrole=node-reader \
--user=jane
# Bind a Role to a ServiceAccount
kubectl create rolebinding app-pod-reader \
--role=pod-reader \
--serviceaccount=default:my-app-sa \
-n default
# Bind a ClusterRole to a group
kubectl create clusterrolebinding admin-group \
--clusterrole=cluster-admin \
--group=system:masters
# Bind a ClusterRole via RoleBinding (scoped to namespace)
kubectl create rolebinding dev-view \
--clusterrole=view \
--user=jane \
-n development
|
CKA Tip: The --serviceaccount flag uses the format namespace:name.
4. Built-in ClusterRoles
Kubernetes ships with default ClusterRoles. Know these:
| ClusterRole |
Permissions |
cluster-admin |
Full access to everything (superuser) |
admin |
Full access within a namespace (no resource quotas or namespace itself) |
edit |
Read/write most resources in a namespace (no roles, rolebindings) |
view |
Read-only access to most resources in a namespace (no secrets) |
| # List all built-in ClusterRoles
kubectl get clusterroles | grep -v system:
# Inspect what "view" can do
kubectl describe clusterrole view
|
Common pattern — give a developer read-only access to a namespace:
| kubectl create rolebinding dev-view \
--clusterrole=view \
--user=developer \
-n production
|
5. ServiceAccounts and Token Management
5.1 What Is a ServiceAccount?
A ServiceAccount (SA) is an in-cluster identity for pods. Every pod runs as a ServiceAccount.
| ┌──────────────┐ authenticates as ┌──────────────┐
│ Pod │ ────────────────────────▶ │ ServiceAccount│
│ │ (auto-mounted token) │ │
│ my-app │ │ my-app-sa │
└──────────────┘ └──────┬───────┘
│
bound via RoleBinding
│
▼
┌──────────────┐
│ Role │
│ pod-reader │
└──────────────┘
|
5.2 Default ServiceAccount
Every namespace has a default ServiceAccount created automatically:
| kubectl get serviceaccounts -n default
# NAME SECRETS AGE
# default 0 30d
|
If a pod doesn't specify a ServiceAccount, it uses default.
5.3 Creating ServiceAccounts
| # Imperative
kubectl create serviceaccount my-app-sa -n default
# Declarative
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: default
EOF
|
5.4 Assigning a ServiceAccount to a Pod
| apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: my-app-sa # ← specify the SA here
containers:
- name: app
image: nginx
|
For Deployments:
| apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
serviceAccountName: my-app-sa # ← in the pod template
containers:
- name: app
image: nginx
|
5.5 Token Management (Kubernetes 1.24+)
Since Kubernetes 1.24, ServiceAccount tokens are no longer auto-created as Secrets. Instead, tokens are:
- Projected tokens — automatically mounted into pods, short-lived, audience-bound
- Manually created tokens — using the TokenRequest API or long-lived Secrets
Projected Token (Automatic)
Every pod gets a projected token mounted at /var/run/secrets/kubernetes.io/serviceaccount/:
| kubectl exec my-app -- ls /var/run/secrets/kubernetes.io/serviceaccount/
# ca.crt ← cluster CA certificate
# namespace ← pod's namespace
# token ← JWT token (short-lived, auto-rotated)
|
| # View the token
kubectl exec my-app -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
|
TokenRequest API (Recommended for External Use)
| # Create a short-lived token (1 hour default)
kubectl create token my-app-sa -n default
# Create a token with custom expiration
kubectl create token my-app-sa -n default --duration=48h
|
Long-Lived Token Secret (Legacy Approach)
| apiVersion: v1
kind: Secret
metadata:
name: my-app-sa-token
namespace: default
annotations:
kubernetes.io/service-account.name: my-app-sa
type: kubernetes.io/service-account-token
|
| # Retrieve the token
kubectl get secret my-app-sa-token -n default -o jsonpath='{.data.token}' | base64 -d
|
CKA Tip: Use kubectl create token for short-lived tokens. Only create Secret-based tokens when you specifically need a non-expiring token.
5.6 Disabling Token Auto-Mount
If a pod doesn't need API access, disable the token mount for security:
| # On the ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: no-api-access
automountServiceAccountToken: false
# Or on the Pod
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
automountServiceAccountToken: false
containers:
- name: app
image: nginx
|
Pod-level setting overrides the ServiceAccount-level setting.
6. Testing and Verifying Permissions
6.1 kubectl auth can-i
The most important RBAC debugging command:
| # Check if YOU can do something
kubectl auth can-i create deployments -n default
# yes
kubectl auth can-i delete nodes
# no
# Check if ANOTHER USER can do something (requires admin)
kubectl auth can-i get pods -n default --as=jane
# no
kubectl auth can-i get pods -n default --as=system:serviceaccount:default:my-app-sa
# yes
# Check ALL permissions for a user
kubectl auth can-i --list --as=jane -n default
# Check ALL permissions for a ServiceAccount
kubectl auth can-i --list --as=system:serviceaccount:default:my-app-sa -n default
|
| Subject Type |
--as Format |
| User |
--as=jane |
| ServiceAccount |
--as=system:serviceaccount:<namespace>:<sa-name> |
| Group |
--as-group=developers |
6.3 Inspecting Existing RBAC
| # List all Roles in a namespace
kubectl get roles -n default
# List all ClusterRoles
kubectl get clusterroles
# List all RoleBindings in a namespace
kubectl get rolebindings -n default
# List all ClusterRoleBindings
kubectl get clusterrolebindings
# Describe a RoleBinding to see who is bound to what
kubectl describe rolebinding read-pods -n default
# See what a ClusterRole allows
kubectl describe clusterrole view
|
7. Practical RBAC Policy Design
7.1 Scenario: Developer Access to a Namespace
Give user dev-jane full read/write access to the development namespace, but no access to secrets:
| # Create a custom Role (edit minus secrets)
kubectl create role dev-role \
--verb=get,list,watch,create,update,patch,delete \
--resource=pods,deployments.apps,services,configmaps,persistentvolumeclaims \
-n development
# Bind it
kubectl create rolebinding dev-jane-binding \
--role=dev-role \
--user=dev-jane \
-n development
# Verify
kubectl auth can-i get pods -n development --as=dev-jane # yes
kubectl auth can-i get secrets -n development --as=dev-jane # no
kubectl auth can-i get pods -n production --as=dev-jane # no
|
7.2 Scenario: CI/CD Pipeline ServiceAccount
A CI/CD pipeline needs to deploy applications in the staging namespace:
| # Create the ServiceAccount
kubectl create serviceaccount cicd-deployer -n staging
# Create a Role for deployment operations
kubectl create role cicd-role \
--verb=get,list,watch,create,update,patch,delete \
--resource=deployments.apps,services,configmaps,secrets \
-n staging
# Bind it
kubectl create rolebinding cicd-binding \
--role=cicd-role \
--serviceaccount=staging:cicd-deployer \
-n staging
# Generate a token for the pipeline
kubectl create token cicd-deployer -n staging --duration=720h
|
7.3 Scenario: Read-Only Cluster Monitoring
A monitoring tool needs read-only access to pods and nodes across all namespaces:
| # Create a ClusterRole
kubectl create clusterrole monitoring-reader \
--verb=get,list,watch \
--resource=pods,nodes,services,endpoints
# Bind cluster-wide
kubectl create clusterrolebinding monitoring-binding \
--clusterrole=monitoring-reader \
--serviceaccount=monitoring:monitor-sa
|
7.4 Scenario: Namespace Admin
Give user team-lead full admin access to the team-a namespace using the built-in admin ClusterRole:
| kubectl create rolebinding team-a-admin \
--clusterrole=admin \
--user=team-lead \
-n team-a
# Verify
kubectl auth can-i '*' '*' -n team-a --as=team-lead # yes
kubectl auth can-i get nodes --as=team-lead # no (cluster-scoped)
|
7.5 Scenario: Restrict to Specific Resources by Name
Allow a user to only read a specific Secret:
| apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-specific-secret
namespace: default
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials"] # ← only this secret
verbs: ["get"]
|
7.6 Aggregated ClusterRoles
ClusterRoles can be composed using aggregation labels:
| apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-extras
labels:
rbac.authorization.k8s.io/aggregate-to-view: "true" # ← auto-added to "view"
rules:
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list", "watch"]
|
The built-in view, edit, and admin ClusterRoles use aggregation — any ClusterRole with the matching label is automatically merged in.
8. RBAC Troubleshooting
8.1 Common Errors
| Error Message |
Cause |
Fix |
forbidden: User "jane" cannot get resource "pods" |
Missing Role or RoleBinding |
Create the appropriate Role and RoleBinding |
forbidden: User "jane" cannot list resource "pods" in API group "" in the namespace "kube-system" |
RoleBinding is in the wrong namespace |
Create a RoleBinding in kube-system |
forbidden: User "system:serviceaccount:default:my-sa" cannot create resource "deployments" |
SA missing permissions |
Check the Role verbs and resources, check the RoleBinding |
8.2 Debugging Checklist
| # 1. What can this user/SA actually do?
kubectl auth can-i --list --as=jane -n default
# 2. What RoleBindings exist in this namespace?
kubectl get rolebindings -n default -o wide
# 3. What ClusterRoleBindings reference this user?
kubectl get clusterrolebindings -o wide | grep jane
# 4. What does the bound Role/ClusterRole allow?
kubectl describe role <role-name> -n default
kubectl describe clusterrole <clusterrole-name>
# 5. Is the API group correct?
kubectl api-resources | grep <resource>
# 6. Is the verb correct?
# Common mistake: using "list" when "get" is needed, or vice versa
# "get" = single resource by name
# "list" = all resources of that type
# "watch" = stream changes
|
8.3 RBAC Does Not Deny
RBAC is additive only — there are no deny rules. If a user has no matching Role/RoleBinding, access is denied by default. You cannot explicitly deny a specific action.
To restrict access, simply don't grant it. If a user has too many permissions from a ClusterRoleBinding, you need to remove that binding and create more specific ones.
9. Practice Exercises
Exercise 1 — Create and Test a Role
| # 1. Create a namespace
kubectl create namespace rbac-test
# 2. Create a Role that allows get, list, watch on pods
kubectl create role pod-reader --verb=get,list,watch --resource=pods -n rbac-test
# 3. Create a RoleBinding for user "alice"
kubectl create rolebinding alice-pod-reader --role=pod-reader --user=alice -n rbac-test
# 4. Test
kubectl auth can-i get pods -n rbac-test --as=alice # yes
kubectl auth can-i create pods -n rbac-test --as=alice # no
kubectl auth can-i get pods -n default --as=alice # no
kubectl auth can-i get deployments -n rbac-test --as=alice # no
# 5. Clean up
kubectl delete namespace rbac-test
|
Exercise 2 — ServiceAccount with RBAC
| # 1. Create a namespace and ServiceAccount
kubectl create namespace app-ns
kubectl create serviceaccount app-sa -n app-ns
# 2. Create a Role for managing deployments and services
kubectl create role app-manager \
--verb=get,list,create,update,delete \
--resource=deployments.apps,services \
-n app-ns
# 3. Bind the Role to the ServiceAccount
kubectl create rolebinding app-sa-binding \
--role=app-manager \
--serviceaccount=app-ns:app-sa \
-n app-ns
# 4. Test
kubectl auth can-i create deployments -n app-ns \
--as=system:serviceaccount:app-ns:app-sa # yes
kubectl auth can-i create secrets -n app-ns \
--as=system:serviceaccount:app-ns:app-sa # no
# 5. Create a pod using this ServiceAccount
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: app-ns
spec:
serviceAccountName: app-sa
containers:
- name: kubectl
image: bitnami/kubectl
command: ["sleep", "3600"]
EOF
# 6. Exec into the pod and test API access
kubectl exec -it test-pod -n app-ns -- kubectl get deployments -n app-ns
kubectl exec -it test-pod -n app-ns -- kubectl get secrets -n app-ns # should fail
# 7. Clean up
kubectl delete namespace app-ns
|
Exercise 3 — ClusterRole with RoleBinding
| # 1. Use the built-in "view" ClusterRole scoped to a namespace
kubectl create namespace team-b
kubectl create rolebinding team-b-view \
--clusterrole=view \
--user=bob \
-n team-b
# 2. Test
kubectl auth can-i get pods -n team-b --as=bob # yes
kubectl auth can-i get pods -n default --as=bob # no (scoped to team-b)
kubectl auth can-i create pods -n team-b --as=bob # no (view is read-only)
# 3. Clean up
kubectl delete namespace team-b
|
Exercise 4 — Troubleshoot Broken RBAC
| # Setup: create a broken RBAC configuration
kubectl create namespace debug-ns
kubectl create serviceaccount debug-sa -n debug-ns
# Create a Role with wrong API group (intentional mistake)
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deploy-manager
namespace: debug-ns
rules:
- apiGroups: [""] # BUG: should be "apps"
resources: ["deployments"]
verbs: ["get", "list", "create"]
EOF
kubectl create rolebinding debug-binding \
--role=deploy-manager \
--serviceaccount=debug-ns:debug-sa \
-n debug-ns
# Task: Figure out why the SA can't create deployments
kubectl auth can-i create deployments.apps -n debug-ns \
--as=system:serviceaccount:debug-ns:debug-sa
# no — because apiGroups is wrong
# Fix: patch the Role
kubectl patch role deploy-manager -n debug-ns --type=json \
-p='[{"op":"replace","path":"/rules/0/apiGroups","value":["apps"]}]'
# Verify
kubectl auth can-i create deployments.apps -n debug-ns \
--as=system:serviceaccount:debug-ns:debug-sa
# yes
# Clean up
kubectl delete namespace debug-ns
|
10. Key Takeaways for the CKA Exam
| Point |
Detail |
| Use imperative commands |
kubectl create role, kubectl create rolebinding — faster than YAML |
| Role = namespaced, ClusterRole = cluster-wide |
But a RoleBinding can reference a ClusterRole to scope it to a namespace |
kubectl auth can-i |
Your primary RBAC debugging tool — use --as for impersonation |
| Know the API groups |
"" for core, "apps" for deployments, "networking.k8s.io" for network policies |
| ServiceAccount format |
--serviceaccount=namespace:name in bindings, system:serviceaccount:namespace:name in --as |
| Tokens changed in 1.24 |
Projected tokens are auto-mounted; use kubectl create token for external use |
| RBAC is additive only |
No deny rules — absence of a grant means denied |
| Built-in ClusterRoles |
cluster-admin, admin, edit, view — reuse them with RoleBindings |
resourceNames for fine-grained control |
Restrict access to specific named resources |
Check api-resources for groups |
kubectl api-resources | grep <resource> to find the correct apiGroup |
Previous: 04-etcd-backup-restore.md — etcd Backup & Restore
Next: 06-kubeconfig-contexts.md — Kubeconfig & Contexts