Skip to main content
4 min read·699 words

Configuring Pod and Container Security Contexts

Required knowledge for the CKS certification.

A Kubernetes security context defines privilege and access control settings for a Pod or individual container. Correctly configuring security contexts is the foundational control for running microservices with least privilege: it constrains the Linux UIDs, capabilities, and filesystem access available to each workload without requiring external admission policies to catch every case.

Security contexts apply at two scopes. Settings on spec.securityContext — the PodSecurityContext — apply to all containers in the Pod. Settings on spec.containers[*].securityContext — the container-level SecurityContext — apply to a single container and override the pod-level setting for the same field where both are defined.


1. Pod-Level Security Context​

Pod-level settings enforce process identity and group membership uniformly across every container in a Pod.

Issue: Omitting runAsUser and runAsGroup allows containers to run as UID 0 (root), granting broad filesystem and kernel access. If runAsGroup is omitted, the primary GID defaults to 0.
Fix: Set an explicit non-zero UID and GID so every process in the Pod runs as an unprivileged identity.

apiVersion: v1
kind: Pod
metadata:
name: secure-microservice
spec:
securityContext:
runAsUser: 1000 # all container processes run as UID 1000
runAsGroup: 3000 # primary GID; defaults to 0 (root) if omitted
fsGroup: 2000 # volume ownership group; files created in volumes are owned by this GID
supplementalGroups: [4000] # additional group IDs for all processes

runAsNonRoot​

Add runAsNonRoot: true to enforce that no container in the Pod starts as UID 0. If the container image's configured user is root and runAsNonRoot is true, the container fails to start. This is a safety net against images that have not been built with a non-root USER instruction:

spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000

fsGroup and volume ownership​

Setting fsGroup causes Kubernetes to change the ownership of mounted volumes to the specified GID, so that non-root processes can read and write shared storage without requiring root-owned files. All processes in the Pod also become members of the fsGroup supplementary group. With runAsUser: 1000 and fsGroup: 2000, files created in mounted volumes are owned by 1000:2000.


2. Container-Level Security Context​

Container-level settings apply to a single container and override pod-level settings for the same field. This allows per-container tuning within a Pod.

allowPrivilegeEscalation​

Issue: allowPrivilegeEscalation defaults to true when not explicitly set. A process can then acquire more privileges than its parent — for example, by executing a setuid binary — enabling local privilege escalation from within the container.
Fix: Explicitly set allowPrivilegeEscalation: false on every container. This sets the Linux no_new_privs kernel flag, blocking any privilege gain via execve.

containers:
- name: app
image: registry.example.com/myapp:1.0
securityContext:
allowPrivilegeEscalation: false

allowPrivilegeEscalation: false cannot be combined with privileged: true or with the CAP_SYS_ADMIN capability — those combinations are rejected by Kubernetes.

readOnlyRootFilesystem​

Issue: A writable root filesystem allows an attacker who achieves code execution inside the container to modify binaries, write scripts, or plant persistence mechanisms in well-known locations.
Fix: Set readOnlyRootFilesystem: true. Mount writable paths explicitly as volumes where the application requires temporary write access.

containers:
- name: app
image: registry.example.com/myapp:1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}

privileged​

Issue: privileged: true grants the container nearly all host capabilities and bypasses most kernel security restrictions, making container escape straightforward.
Fix: Never set privileged: true for application workloads. The Baseline Pod Security Standard forbids privileged containers.


3. Linux Capabilities​

Capabilities allow fine-grained privilege grants without requiring full root. By default, containers run with a set of capabilities that expands the kernel attack surface, including NET_RAW, SYS_CHROOT, and others.

Issue: Unused default capabilities provide unnecessary paths to privilege escalation or network-level attacks from inside the container.
Fix: Drop all capabilities, then add back only those the specific workload requires.

containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"] # only if this container must bind a port below 1024

The Restricted Pod Security Standard requires capabilities.drop: ["ALL"]. See Dropping Linux Capabilities in Containers for a detailed reference on what each capability grants and when it is needed.


4. Aligning with the Restricted Pod Security Standard​

The Restricted Pod Security Standard defines the most stringent set of security context requirements for production workloads. Every container — including init and ephemeral containers — must satisfy all of the following to pass the Restricted profile:

  • allowPrivilegeEscalation: false
  • runAsNonRoot: true (at pod or container level)
  • securityContext.capabilities.drop: ["ALL"]
  • A seccompProfile of type RuntimeDefault or Localhost (not Unconfined)

A pod-level configuration that satisfies the Restricted profile:

apiVersion: v1
kind: Pod
metadata:
name: restricted-microservice
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: registry.example.com/myapp:1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]

This configuration, applied at namespace level via Pod Security Admission with the enforce: restricted label, blocks pods that do not meet every one of these requirements. See Pod Security Standards for how to enforce the profile cluster-wide.


5. Verification​

After deploying a workload with a security context, confirm the settings are active inside the running container.

Verify UID and group membership:

kubectl exec -n <namespace> <pod-name> -- id
# Expected output with runAsUser: 1000, runAsGroup: 3000, fsGroup: 2000, supplementalGroups: [4000]:
# uid=1000 gid=3000 groups=2000,3000,4000

Verify the root filesystem is read-only:

kubectl exec -n <namespace> <pod-name> -- touch /testfile
# Expected: touch: /testfile: Read-only file system

Inspect the applied security context via the API:

kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.securityContext}' | jq .
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[0].securityContext}' | jq .

Audit all pods in a namespace that do not set runAsNonRoot:

kubectl get pods -n <namespace> -o json | \
jq '.items[] | select(
(.spec.securityContext.runAsNonRoot != true) and
(.spec.containers[].securityContext.runAsNonRoot != true)
) | .metadata.name'

References​

This article is based on information from the following official sources:

  1. Configure a Security Context for a Pod or Container - Kubernetes Documentation
  2. Pod Security Standards - Kubernetes Documentation
  3. Security Checklist - Kubernetes Documentation