CKA — RBAC & Security Lab
Lab Overview
Time to put everything together. In this lab, you’ll create Roles, RoleBindings, ServiceAccounts, and Security Contexts from scratch — just like on the CKA exam.
Prerequisites
- A running Kubernetes cluster (minikube, kind, or kubeadm)
kubectlconfigured and connected- Cluster admin access (to create Roles and Bindings)
What You’ll Practice
- Creating namespaces, Roles, and RoleBindings
- Binding Roles to users and ServiceAccounts
- Testing permissions with
kubectl auth can-i - Deploying Pods with ServiceAccounts
- Applying Security Contexts to Pods
- Creating ClusterRoles and ClusterRoleBindings
Let’s go.
Task 1: Create a Namespace and Role
Step 1: Create the namespace
kubectl create namespace secure-ns
Expected output:
namespace/secure-ns created
Step 2: Verify the namespace
kubectl get namespace secure-ns
Expected output:
NAME STATUS AGE
secure-ns Active 5s
Step 3: Create a Role that allows reading Pods
kubectl create role pod-reader \
--verb=get,list,watch \
--resource=pods \
-n secure-ns
Expected output:
role.rbac.authorization.k8s.io/pod-reader created
Step 4: Verify the Role
kubectl describe role pod-reader -n secure-ns
Expected output:
Name: pod-reader
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [get list watch]
You now have a Role that grants read-only access to Pods in the secure-ns namespace.
Task 2: Create a RoleBinding
Step 1: Bind the Role to user “dev-user” (imperative)
kubectl create rolebinding dev-read \
--role=pod-reader \
--user=dev-user \
-n secure-ns
Expected output:
rolebinding.rbac.authorization.k8s.io/dev-read created
Step 2: Verify the RoleBinding
kubectl describe rolebinding dev-read -n secure-ns
Expected output:
Name: dev-read
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: pod-reader
Subjects:
Kind Name Namespace
---- ---- ---------
User dev-user
Step 3: Create the same binding declaratively (for practice)
Save this to a file and apply it — or just review the structure:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-read-declarative
namespace: secure-ns
subjects:
- kind: User
name: dev-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
kubectl apply -f rolebinding.yaml
Task 3: Test Permissions
Step 1: Test if dev-user can list Pods
kubectl auth can-i list pods --as dev-user -n secure-ns
Expected output:
yes
Step 2: Test if dev-user can create Pods (should fail)
kubectl auth can-i create pods --as dev-user -n secure-ns
Expected output:
no
Step 3: Test if dev-user can list Pods in default namespace (should fail)
kubectl auth can-i list pods --as dev-user -n default
Expected output:
no
The RoleBinding only grants access in secure-ns — not in other namespaces.
Step 4: List all permissions for dev-user in secure-ns
kubectl auth can-i --list --as dev-user -n secure-ns
Expected output should include:
Resources Non-Resource URLs Resource Names Verbs
pods [] [] [get list watch]
...
Task 4: ServiceAccount RBAC
This task walks through the complete SA → Role → RoleBinding → Pod pattern.
Step 1: Create a ServiceAccount
kubectl create serviceaccount app-sa -n secure-ns
Expected output:
serviceaccount/app-sa created
Step 2: Create a Role for the ServiceAccount
kubectl create role configmap-reader \
--verb=get,list \
--resource=configmaps \
-n secure-ns
Expected output:
role.rbac.authorization.k8s.io/configmap-reader created
Step 3: Bind the Role to the ServiceAccount
kubectl create rolebinding app-sa-binding \
--role=configmap-reader \
--serviceaccount=secure-ns:app-sa \
-n secure-ns
Expected output:
rolebinding.rbac.authorization.k8s.io/app-sa-binding created
Step 4: Test the ServiceAccount’s permissions
# Can it list ConfigMaps?
kubectl auth can-i list configmaps --as system:serviceaccount:secure-ns:app-sa -n secure-ns
Expected output:
yes
# Can it list Pods? (should be no)
kubectl auth can-i list pods --as system:serviceaccount:secure-ns:app-sa -n secure-ns
Expected output:
no
Step 5: Create a Pod using the ServiceAccount
apiVersion: v1
kind: Pod
metadata:
name: app-pod
namespace: secure-ns
spec:
serviceAccountName: app-sa
containers:
- name: app
image: bitnami/kubectl
command: ["sleep", "3600"]
kubectl apply -f app-pod.yaml
Expected output:
pod/app-pod created
Step 6: Verify the Pod is using the correct ServiceAccount
kubectl get pod app-pod -n secure-ns -o jsonpath='{.spec.serviceAccountName}'
Expected output:
app-sa
Step 7: Test from inside the Pod
# Create a ConfigMap to test against
kubectl create configmap test-config --from-literal=key=value -n secure-ns
# Exec into the Pod and try listing ConfigMaps
kubectl exec app-pod -n secure-ns -- kubectl get configmaps -n secure-ns
Expected output (should succeed):
NAME DATA AGE
kube-root-ca.crt 1 5m
test-config 1 10s
# Try listing Pods (should fail)
kubectl exec app-pod -n secure-ns -- kubectl get pods -n secure-ns
Expected output:
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:secure-ns:app-sa" cannot list resource "pods"...
This confirms RBAC is working correctly.
Task 5: Security Contexts
Step 1: Deploy a Pod with Security Contexts
apiVersion: v1
kind: Pod
metadata:
name: secure-context-pod
namespace: secure-ns
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: app
image: busybox
command: ["sleep", "3600"]
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
volumeMounts:
- name: writable-tmp
mountPath: /tmp
volumes:
- name: writable-tmp
emptyDir: {}
kubectl apply -f secure-context-pod.yaml
Expected output:
pod/secure-context-pod created
Step 2: Verify the user ID
kubectl exec secure-context-pod -n secure-ns -- id
Expected output:
uid=1000 gid=3000 groups=2000
The process is running as UID 1000, not root.
Step 3: Verify read-only root filesystem
kubectl exec secure-context-pod -n secure-ns -- touch /test-file
Expected output:
touch: /test-file: Read-only file system
command terminated with exit code 1
Step 4: Verify writable tmp directory
kubectl exec secure-context-pod -n secure-ns -- touch /tmp/test-file
kubectl exec secure-context-pod -n secure-ns -- ls -la /tmp/test-file
Expected output:
-rw-r--r-- 1 1000 2000 0 Feb 9 06:00 /tmp/test-file
The emptyDir volume is writable, and the file is owned by UID 1000 and GID 2000 (fsGroup).
Step 5: Verify no privilege escalation
kubectl get pod secure-context-pod -n secure-ns -o jsonpath='{.spec.containers[0].securityContext.allowPrivilegeEscalation}'
Expected output:
false
Task 6: ClusterRole and ClusterRoleBinding
Step 1: Create a ClusterRole for node access
kubectl create clusterrole node-viewer \
--verb=get,list,watch \
--resource=nodes
Expected output:
clusterrole.rbac.authorization.k8s.io/node-viewer created
Step 2: Create a ClusterRoleBinding
kubectl create clusterrolebinding ops-node-viewer \
--clusterrole=node-viewer \
--user=ops-user
Expected output:
clusterrolebinding.rbac.authorization.k8s.io/ops-node-viewer created
Step 3: Test the permissions
# Can ops-user list nodes?
kubectl auth can-i list nodes --as ops-user
Expected output:
yes
# Can ops-user delete nodes? (should be no)
kubectl auth can-i delete nodes --as ops-user
Expected output:
no
# Can ops-user list pods? (should be no — nodes only)
kubectl auth can-i list pods --as ops-user
Expected output:
no
Step 4: Verify ops-user can actually list nodes
kubectl get nodes --as ops-user
Expected output:
NAME STATUS ROLES AGE VERSION
control-plane Ready control-plane 30d v1.28.0
Cleanup
When you’re done, clean up all resources:
# Delete the namespace (this removes all namespaced resources within it)
kubectl delete namespace secure-ns
Expected output:
namespace "secure-ns" deleted
# Delete cluster-scoped resources
kubectl delete clusterrole node-viewer
kubectl delete clusterrolebinding ops-node-viewer
Expected output:
clusterrole.rbac.authorization.k8s.io "node-viewer" deleted
clusterrolebinding.rbac.authorization.k8s.io "ops-node-viewer" deleted
Note: Deleting the namespace automatically removes all Roles, RoleBindings, ServiceAccounts, and Pods within it. Cluster-scoped resources (ClusterRoles, ClusterRoleBindings) must be deleted separately.
Lab Summary
In this lab you practiced:
| Task | What You Did |
|---|---|
| Task 1 | Created a namespace and a Role with read-only Pod access |
| Task 2 | Created a RoleBinding to grant permissions to a user |
| Task 3 | Used kubectl auth can-i to verify permissions work correctly |
| Task 4 | Full ServiceAccount RBAC workflow: SA → Role → RoleBinding → Pod |
| Task 5 | Applied Security Contexts: runAsUser, readOnlyRootFilesystem, fsGroup |
| Task 6 | Created a ClusterRole and ClusterRoleBinding for node access |
These patterns come up frequently on the CKA exam. Practice them until you can do them from memory — speed matters on exam day.