CKA — Security Contexts
CKA Exam Domain
Cluster Architecture, Installation & Configuration — 25%
Security Contexts control privilege and access at the Pod and container level. While RBAC controls who can talk to the API server, Security Contexts control what containers can do on the node — which user they run as, what Linux capabilities they have, and whether they can escalate privileges.
What Are Security Contexts?
A Security Context defines privilege and access control settings for a Pod or container:
- Which user/group the process runs as
- Whether the container can escalate privileges
- Whether the filesystem is read-only
- Which Linux capabilities are added or dropped
Security Contexts apply the principle of least privilege — containers should only have the minimum permissions they need to function.
Pod-Level vs Container-Level
Security Contexts can be set at two levels. Container-level settings override Pod-level settings when both are specified.
| Field | Pod-Level | Container-Level |
|---|---|---|
runAsUser |
✅ | ✅ (overrides Pod) |
runAsGroup |
✅ | ✅ (overrides Pod) |
runAsNonRoot |
✅ | ✅ (overrides Pod) |
fsGroup |
✅ | ❌ |
supplementalGroups |
✅ | ❌ |
readOnlyRootFilesystem |
❌ | ✅ |
allowPrivilegeEscalation |
❌ | ✅ |
capabilities |
❌ | ✅ |
privileged |
❌ | ✅ |
seccompProfile |
✅ | ✅ (overrides Pod) |
Pod-level applies to all containers in the Pod. Container-level is specific to one container.
Common Security Context Fields
runAsUser and runAsGroup
Force the container process to run as a specific UID/GID:
securityContext:
runAsUser: 1000
runAsGroup: 3000
runAsUser: 1000— Process runs as UID 1000 (not root)runAsGroup: 3000— Primary group is GID 3000
fsGroup
Sets the group ownership for all files in mounted volumes:
securityContext:
fsGroup: 2000
All files created in volumes will be owned by GID 2000. This is a Pod-level only setting.
runAsNonRoot
Prevents the container from running as root (UID 0):
securityContext:
runAsNonRoot: true
If the container image tries to run as root, Kubernetes will reject the Pod. This is a safety net — even if the image is configured to run as root, Kubernetes will block it.
readOnlyRootFilesystem
Makes the container’s root filesystem read-only:
securityContext:
readOnlyRootFilesystem: true
The container can still write to mounted volumes (emptyDir, PVC, etc.), but cannot modify anything on the root filesystem. This prevents attackers from modifying binaries or writing malicious files.
allowPrivilegeEscalation
Controls whether a process can gain more privileges than its parent:
securityContext:
allowPrivilegeEscalation: false
Set to false for security hardening. When false, the no_new_privs flag is set on the container process.
capabilities
Add or remove Linux capabilities:
securityContext:
capabilities:
add: ["NET_BIND_SERVICE"]
drop: ["ALL"]
Common capabilities:
NET_BIND_SERVICE— Bind to ports below 1024SYS_TIME— Modify system clockNET_ADMIN— Network configurationALL— Drop all capabilities (recommended starting point)
Best practice: Drop ALL capabilities, then add back only what you need.
Full YAML Example
Here’s a Pod demonstrating both Pod-level and container-level Security Contexts:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: dev
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
runAsNonRoot: true
containers:
- name: app
image: nginx
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
What’s happening here:
- Pod-level: Runs as UID 1000, GID 3000, non-root enforced
- Container-level: Read-only root filesystem, no privilege escalation, only NET_BIND_SERVICE capability
- Volumes: writable directories mounted via emptyDir (since root FS is read-only)
Pod Security Standards
Kubernetes defines three Pod Security Standards that describe different levels of restriction:
| Standard | Description | Use Case |
|---|---|---|
| Privileged | Unrestricted — allows everything | System-level workloads (CNI, storage drivers) |
| Baseline | Minimally restrictive — prevents known privilege escalation | Standard workloads |
| Restricted | Heavily restricted — follows hardening best practices | Security-sensitive workloads |
What Each Standard Restricts
Baseline restricts:
- Privileged containers
- Host namespaces (hostPID, hostIPC, hostNetwork)
- Host ports
- Certain volume types
- Certain capabilities (beyond a safe set)
Restricted adds:
- Must run as non-root
- Must drop ALL capabilities
- Seccomp profile must be RuntimeDefault or Localhost
- No privilege escalation
Pod Security Admission
Pod Security Admission (PSA) enforces the Pod Security Standards at the namespace level using labels.
Modes
| Mode | Behavior |
|---|---|
enforce |
Reject Pods that violate the standard |
audit |
Allow but log violations |
warn |
Allow but show a warning to the user |
Namespace Labels
Apply PSA via namespace labels:
apiVersion: v1
kind: Namespace
metadata:
name: secure-ns
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
You can also set a version:
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.28
Example: Apply baseline enforcement to a namespace:
kubectl label namespace dev pod-security.kubernetes.io/enforce=baseline
Now any Pod in the dev namespace that violates the baseline standard will be rejected.
Testing PSA
# Try creating a privileged Pod in a restricted namespace
kubectl run test --image=nginx --privileged -n secure-ns
# Error: pods "test" is forbidden: violates PodSecurity "restricted:latest"
kubectl Commands
# Check the security context of a running Pod
kubectl get pod secure-pod -o jsonpath='{.spec.securityContext}'
# Check container-level security context
kubectl get pod secure-pod -o jsonpath='{.spec.containers[0].securityContext}'
# Verify which user the container is running as
kubectl exec secure-pod -- id
# uid=1000 gid=3000 groups=2000
# Check if root filesystem is read-only
kubectl exec secure-pod -- touch /test-file
# touch: cannot touch '/test-file': Read-only file system
# Check Pod Security Admission labels on a namespace
kubectl get namespace secure-ns -o yaml | grep pod-security
# Apply Pod Security Standard to a namespace
kubectl label namespace dev pod-security.kubernetes.io/enforce=baseline
# Dry-run to check if a Pod would be admitted
kubectl apply -f pod.yaml --dry-run=server -n secure-ns
Practice Questions
Q1. Create a Pod named restricted-pod that runs as user ID 1000 and group ID 3000 with a read-only root filesystem.
Show Answer
apiVersion: v1
kind: Pod
metadata:
name: restricted-pod
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
containers:
- name: app
image: nginx
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
Note: nginx needs a writable /tmp, so we mount an emptyDir there.
Q2. What is the difference between Pod-level and container-level security contexts?
Show Answer
Pod-level security context (under spec.securityContext) applies to all containers in the Pod. It supports fields like runAsUser, runAsGroup, fsGroup, and runAsNonRoot.
Container-level security context (under spec.containers[].securityContext) applies to a single container and overrides Pod-level settings where both are supported. It has additional fields like readOnlyRootFilesystem, capabilities, allowPrivilegeEscalation, and privileged.
If both set runAsUser, the container-level value wins.
Q3. How do you enforce the “restricted” Pod Security Standard in namespace prod?
Show Answer
kubectl label namespace prod pod-security.kubernetes.io/enforce=restricted
Or declaratively:
apiVersion: v1
kind: Namespace
metadata:
name: prod
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/warn: restricted
Q4. Create a Pod that drops ALL Linux capabilities and only adds NET_BIND_SERVICE.
Show Answer
apiVersion: v1
kind: Pod
metadata:
name: minimal-caps
spec:
containers:
- name: app
image: nginx
securityContext:
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
Q5. How do you verify that a running Pod’s process is not running as root?
Show Answer
kubectl exec <pod-name> -- id
Expected output for a non-root Pod:
uid=1000 gid=3000 groups=2000
If it shows uid=0(root), the container is running as root.
You can also check the security context:
kubectl get pod <pod-name> -o jsonpath='{.spec.securityContext.runAsUser}'