Set runAsUser and Add a Capability with securityContext
In namespace default, Deployment secure-app exists without any security context.
Live lab access
This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.
Create an account to continue →
On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.
The task
Task
In namespace default, Deployment secure-app exists without any security context.
Requirements
- Set Pod-level
runAsUser: 1000 - Add container-level capability
NET_ADMINto the container namedapp
Note: Capabilities are set at the container level, not the Pod level.
Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster.
Pod-level and container-level securityContext, and how to prove each one took effect
A Pod has two places for security settings. spec.securityContext sets defaults that every container inherits, such as which user and group the processes run as. Each container's own securityContext can override those defaults and also holds settings that exist only per container: Linux capabilities, allowPrivilegeEscalation, readOnlyRootFilesystem and privileged. Exam tasks often hinge on putting a field at the right level, and then on showing it worked from inside the container.
spec.securityContext runAsUser, runAsGroup, fsGroup, runAsNonRoot, seccompProfile ...
│ inherited, unless overridden
▼
containers[].securityContext runAsUser, runAsGroup ... (wins on overlap)
capabilities, allowPrivilegeEscalation,
readOnlyRootFilesystem, privileged (container only)
1. Apply a Pod that uses both levels
Save this as ckad-secctx-pod.yaml. The worker container inherits the Pod identity; binder overrides the user and trims its capabilities.
apiVersion: v1
kind: Pod
metadata:
name: ctx-lab
namespace: prepium-tutorial
spec:
securityContext: # Pod level: inherited by every container
runAsUser: 2500
runAsGroup: 2600
fsGroup: 2700
volumes:
- name: scratch
emptyDir: {}
containers:
- name: worker
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: scratch
mountPath: /scratch
securityContext: # container level: only for this container
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
- name: binder
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
securityContext:
runAsUser: 0 # overrides the Pod-level 2500 for this container only
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
kubectl create namespace prepium-tutorial
kubectl apply -f ckad-secctx-pod.yaml
kubectl -n prepium-tutorial wait --for=condition=Ready pod/ctx-lab --timeout=90s
2. Check identity with id and /proc
kubectl -n prepium-tutorial exec ctx-lab -c worker -- id
kubectl -n prepium-tutorial exec ctx-lab -c worker -- grep -E 'Uid|Gid|NoNewPrivs' /proc/1/status
kubectl -n prepium-tutorial exec ctx-lab -c binder -- id
In worker, id reports uid 2500 and gid 2600, and the group list includes 2700 from fsGroup, which is added as a supplementary group to every container. The Uid line of /proc/1/status repeats the same number for the real, effective, saved and filesystem IDs, and NoNewPrivs is 1: setting allowPrivilegeEscalation: false is what turns on the kernel's no_new_privs flag. In binder, id shows uid 0, because the container value beats the Pod value, while the gid is still 2600 since that field was not overridden.
3. Read the capability bitmap
kubectl -n prepium-tutorial exec ctx-lab -c binder -- grep Cap /proc/1/status
CapEff is a hexadecimal mask with one bit per capability. After dropping everything and adding back one, only bit 10 should be set (0000000000000400), and bit 10 is CAP_NET_BIND_SERVICE in the kernel's capability header. Note the spelling in YAML: you list NET_BIND_SERVICE without the CAP_ prefix. To see what a runtime grants by default, run the same command in a container with no capabilities block and compare the masks.
4. Test the read-only root filesystem
kubectl -n prepium-tutorial exec ctx-lab -c worker -- touch /probe
kubectl -n prepium-tutorial exec ctx-lab -c worker -- touch /scratch/probe
kubectl -n prepium-tutorial exec ctx-lab -c worker -- ls -ln /scratch
The first write fails with a read-only file system error; the second succeeds because the emptyDir volume is a separate, writable mount. The numeric listing shows the new file owned by uid 2500, with group 2700 coming from fsGroup.
Common mistakes
- Putting
capabilitiesunderspec.securityContext. The field does not exist there, sokubectl apply(strict validation by default) rejects it as an unknown field. - Writing
CAP_SYS_TIMEinstead ofSYS_TIME. Kubernetes expects the name without the prefix. - Combining
allowPrivilegeEscalation: falsewithprivileged: trueor an addedSYS_ADMIN. The documentation calls these inconsistent; escalation is effectively always allowed for such containers. - Editing a Deployment's live Pod. Security context changes belong in the Pod template; the rollout then creates Pods with the new settings.
| Example check | Evidence |
|---|---|
| Pod-level identity applied | id in an inheriting container shows the Pod's uid, gid and fsGroup |
| Container override wins | Overriding container reports its own uid |
| Capabilities trimmed or added | CapEff mask in /proc/1/status has exactly the expected bits |
| Escalation blocked | NoNewPrivs: 1 in /proc/1/status |
Cleanup: kubectl delete namespace prepium-tutorial. References: Configure a Security Context for a Pod or Container, Pod API reference (PodSecurityContext and SecurityContext), proc_pid_status(5). Next, try immutable containers or the restricted Pod Security Standard.
What this tests
Wire up configmaps, secrets, service accounts, RBAC, securityContext, and resource limits. On the CKAD exam, Application Environment, Configuration and Security tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.
Practice it for real
The public description lets you study the task before signing up. Full access includes this live lab; the selected Task of the Week is free with an account. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.