Skip to content

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

1
2
3
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)

1
2
3
4
5
6
7
8
9
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)

1
2
3
4
5
6
7
8
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:

1
2
3
4
5
6
7
8
9
# 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:

1
2
3
4
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)
1
2
3
4
5
# 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:

1
2
3
4
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:

1
2
3
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

1
2
3
4
5
6
7
8
9
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:

  1. Projected tokens — automatically mounted into pods, short-lived, audience-bound
  2. 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/:

1
2
3
4
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
1
2
3
4
5
# 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)

1
2
3
4
5
6
7
8
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

6.2 Impersonation Format

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:

1
2
3
4
5
6
7
8
9
# 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:

1
2
3
4
5
6
7
8
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