KCSA — Security Contexts
What Are Security Contexts?
A Security Context defines privilege and access control settings for a pod or container. It’s the primary mechanism for configuring how a workload runs from a security perspective — user IDs, capabilities, filesystem access, and system call restrictions.
Security contexts are set directly in the pod spec. They are the building blocks that Pod Security Standards (PSS) evaluate.
Key Point: PSS/PSA defines the policy. Security contexts are the implementation — the actual fields you set on pods and containers.
Pod-Level vs Container-Level Security Context
Security context fields can be set at two levels. When both are set, the container-level setting takes precedence.
| Aspect | Pod Level (spec.securityContext) |
Container Level (spec.containers[].securityContext) |
|---|---|---|
| Scope | Applies to all containers in the pod | Applies to a specific container |
| Precedence | Lower — acts as default | Higher — overrides pod-level |
| Fields available | runAsUser, runAsGroup, runAsNonRoot, fsGroup, seccompProfile, supplementalGroups, sysctls |
All pod-level fields + allowPrivilegeEscalation, privileged, readOnlyRootFilesystem, capabilities, procMount |
| When to use | Set defaults for the entire pod | Override for specific containers |
YAML Structure
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo
spec:
securityContext: # Pod-level security context
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:1.0
securityContext: # Container-level security context
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
Key Security Context Fields
This is the most important reference table for the KCSA exam:
| Field | Level | Description | Recommended Value |
|---|---|---|---|
runAsNonRoot |
Both | Prevents running as root (UID 0) | true |
runAsUser |
Both | Specifies the UID to run as | > 1000 |
runAsGroup |
Both | Specifies the primary GID | > 1000 |
readOnlyRootFilesystem |
Container | Makes the container filesystem read-only | true |
allowPrivilegeEscalation |
Container | Prevents gaining more privileges than parent | false |
privileged |
Container | Gives full host access (all capabilities + devices) | false (never!) |
capabilities |
Container | Fine-grained Linux capabilities | Drop ALL, add only needed |
seccompProfile |
Both | System call filtering profile | RuntimeDefault |
fsGroup |
Pod | GID for all volumes mounted in the pod | Set to app’s GID |
procMount |
Container | Type of proc mount (Default or Unmasked) |
Default |
Field Deep Dives
runAsNonRoot
Ensures the container does not run as the root user (UID 0).
securityContext:
runAsNonRoot: true
- If the container image tries to run as root, the pod will not start
- The container image must have a non-root
USERdefined, or you must setrunAsUser - This is required by the Restricted PSS level
Why it matters: Running as root inside a container means root on the host if there’s a container escape vulnerability.
runAsUser
Specifies the exact UID the container process should run as:
securityContext:
runAsUser: 1000
- Overrides the
USERdirective in the Dockerfile - Use a UID > 1000 to avoid conflicts with system users
- If set at the pod level, applies to all containers (unless overridden)
readOnlyRootFilesystem
Makes the container’s root filesystem read-only:
securityContext:
readOnlyRootFilesystem: true
- Prevents attackers from writing malicious scripts or modifying binaries inside the container
- If the app needs to write files, use
emptyDirvolumes for temporary storage - This is a best practice for the Restricted level
# Complete example with writable temp directory
spec:
containers:
- name: app
image: myapp:1.0
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
allowPrivilegeEscalation
Controls whether a process can gain more privileges than its parent:
securityContext:
allowPrivilegeEscalation: false
- When
true, setuid and setgid binaries can escalate privileges - Must be
falseunder the Restricted PSS level - Set this to
falseunless the container specifically needs setuid/setgid
privileged
Grants the container full host access — equivalent to running on the host:
securityContext:
privileged: true # ⚠️ DANGEROUS — avoid unless absolutely necessary
- The container gets ALL Linux capabilities
- The container can access ALL host devices
- The container can modify the host kernel via
/procand/sys - Only allowed under the Privileged PSS level
- Use cases: CNI plugins, device plugins, node monitoring agents
Never use privileged: true unless the workload absolutely requires host-level access (e.g., system daemons). A compromised privileged container = a compromised host.
Linux Capabilities
Linux capabilities break down root’s monolithic power into smaller units. Instead of giving a container full root access, you can grant specific capabilities.
Common Capabilities
| Capability | What It Allows | Risk Level |
|---|---|---|
NET_BIND_SERVICE |
Bind to ports below 1024 | Low |
SYS_TIME |
Modify the system clock | Medium |
NET_RAW |
Use raw sockets (ping, packet crafting) | Medium |
SYS_ADMIN |
Broad administrative access (mount, namespace ops) | Critical |
SYS_PTRACE |
Debug/trace other processes | High |
NET_ADMIN |
Network configuration (iptables, routing) | High |
DAC_OVERRIDE |
Bypass file permission checks | High |
Best Practice: Drop ALL, Add Only What’s Needed
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
Why drop ALL?
- Docker containers start with a default set of capabilities (about 14)
- Many of these are unnecessary for most applications
- Dropping ALL and adding back only what’s needed follows the principle of least privilege
- This is required by the Restricted PSS level
Default Container Capabilities (Docker/containerd)
These capabilities are granted by default to containers:
AUDIT_WRITE, CHOWN, DAC_OVERRIDE, FOWNER, FSETID, KILL,
MKNOD, NET_BIND_SERVICE, NET_RAW, SETFCAP, SETGID,
SETPCAP, SETUID, SYS_CHROOT
Under the Restricted PSS level, you must drop ALL of these and may only add back NET_BIND_SERVICE.
Seccomp Profiles
Seccomp (Secure Computing Mode) filters the system calls a container can make to the host kernel. It’s a critical defense-in-depth layer.
Profile Types
| Profile | Description | Security Level |
|---|---|---|
Unconfined |
No syscall filtering | ❌ Least secure |
RuntimeDefault |
Default profile from the container runtime | ✅ Recommended |
Localhost |
Custom profile from a file on the node | ✅ Most customizable |
YAML Configuration
securityContext:
seccompProfile:
type: RuntimeDefault
For a custom profile:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profiles/my-seccomp-profile.json
What RuntimeDefault Blocks
The RuntimeDefault profile blocks approximately 40-60 dangerous syscalls including:
reboot— rebooting the hostmount/umount— mounting filesystemsswapon/swapoff— managing swapkexec_load— loading a new kernelbpf— Berkeley Packet Filter operations
Exam Tip: The Restricted PSS level requires either RuntimeDefault or Localhost seccomp profile. Unconfined is not allowed.
AppArmor and SELinux
While the KCSA exam focuses primarily on security contexts, capabilities, and seccomp, you should be aware of these additional Linux security modules:
AppArmor
- Mandatory Access Control (MAC) system for Linux
- Confines programs to a limited set of resources
- Kubernetes supports AppArmor profiles via annotations (GA since Kubernetes 1.30)
- Common on Ubuntu/Debian-based nodes
securityContext:
appArmorProfile:
type: RuntimeDefault
SELinux
- Mandatory Access Control (MAC) system, common on RHEL/CentOS/Fedora
- Uses labels to define access policies
- Configured via
seLinuxOptionsin the security context
securityContext:
seLinuxOptions:
level: "s0:c123,c456"
For the KCSA exam: Know that AppArmor and SELinux exist as additional layers of defense. Focus your study on security context fields, capabilities, and seccomp.
Complete Hardened Pod Example
Here’s a pod spec that follows all security best practices:
apiVersion: v1
kind: Pod
metadata:
name: hardened-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
fsGroup: 10000
seccompProfile:
type: RuntimeDefault
automountServiceAccountToken: false
containers:
- name: app
image: myapp:1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
privileged: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: "64Mi"
This pod:
- ✅ Runs as non-root (UID 10000)
- ✅ Has a read-only root filesystem
- ✅ Cannot escalate privileges
- ✅ Drops ALL capabilities
- ✅ Uses RuntimeDefault seccomp profile
- ✅ Does not mount the service account token
- ✅ Has resource limits set
- ✅ Uses emptyDir for temporary writes
Key Takeaways for the Exam
-
Security contexts are set at two levels: pod-level (default for all containers) and container-level (overrides pod-level).
-
Critical fields to remember:
runAsNonRoot,readOnlyRootFilesystem,allowPrivilegeEscalation,capabilities,seccompProfile. -
Never use
privileged: trueunless the workload requires host-level access (kube-system components). -
Best practice for capabilities: Drop ALL, then add only what’s needed. Under Restricted PSS, only
NET_BIND_SERVICEcan be added. -
Seccomp profiles:
RuntimeDefaultis recommended.Unconfinedmeans no syscall filtering.Localhostuses a custom profile. -
SYS_ADMINis the most dangerous capability — it grants near-root access. -
Container-level security context takes precedence over pod-level when both are set.
Next: Network Policies — controlling pod-to-pod network traffic with firewall rules.