Skip to main content
RunBook Academy

KubernetesLXIII · Linux Security Controls in KubernetesLinux security controls

Security Context — runAsUser, runAsNonRoot, readOnlyRootFilesystem

Advanced⏱ ~13 minkubectl

What you'll learn

  • Configure the securityContext fields correctly (runAsUser, runAsNonRoot, readOnlyRootFilesystem, allowPrivilegeEscalation, fsGroup)
  • Distinguish Pod-level and container-level securityContext
  • Identify the production patterns and the failure modes
  • Recognise the interaction with PSS profiles and capability drops

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

The Pod’s securityContext field is the operational control surface for workload-level Linux security: UID/GID, root filesystem, privilege escalation, and fsGroup. This lesson covers the fields, the Pod-level vs container-level scope, the production patterns, and the failure modes.

The fields

FieldEffect
runAsUserThe UID the container runs as
runAsGroupThe GID the container runs as
runAsNonRootBoolean: must run as non-root (UID != 0)
fsGroupThe GID that owns the volume
readOnlyRootFilesystemBoolean: root filesystem is read-only
allowPrivilegeEscalationBoolean: setuid binaries can escalate
supplementalGroupsAdditional GIDs the process has
seccompProfileThe seccomp profile (RuntimeDefault, Localhost, Unconfined)
seLinuxOptionsThe SELinux labels
capabilitiesThe Linux capabilities (drop, add)
runAsUser (container)Override the Pod-level setting for this container

A Pod’s securityContext applies to all containers; a container’s securityContext overrides the Pod-level setting.

flowchart LR
    A[Pod securityContext] --> B[Container 1]
    A --> C[Container 2]
    B --> D{Container override?}
    D -->|yes| E[Container securityContext]
    D -->|no| F[Use Pod setting]
    C --> D

A hardened securityContext

apiVersion: v1
kind: Pod
metadata:
  name: api
  namespace: prod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: api
    image: myapp:v1.0
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: cache
      mountPath: /var/cache/myapp
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 500m
        memory: 512Mi
  volumes:
  - name: tmp
    emptyDir: {}
  - name: cache
    emptyDir: {}

The Pod meets PSS restricted:

  • runAsNonRoot: true — must run as non-root.
  • runAsUser: 1000, runAsGroup: 1000 — specific UID/GID.
  • fsGroup: 1000 — volumes are owned by GID 1000.
  • seccompProfile.type: RuntimeDefault — constrained syscall surface.
  • allowPrivilegeEscalation: false — setuid binaries cannot escalate.
  • readOnlyRootFilesystem: true — root filesystem is read-only.
  • capabilities.drop: ["ALL"] — no capabilities.
  • No host namespaces, no hostPath, no privileged.

readOnlyRootFilesystem

The readOnlyRootFilesystem: true setting makes the container’s root filesystem read-only. The container can only write to mounted volumes (emptyDir, CSI, hostPath). Workloads that need to write at runtime must mount a volume at the write path.

securityContext:
  readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
  mountPath: /tmp  # writable
- name: cache
  mountPath: /var/cache/myapp  # writable
volumes:
- name: tmp
  emptyDir: {}
- name: cache
  emptyDir: {}

A workload that requires a writable root filesystem (e.g., a legacy binary that writes /etc) is an exception that should be documented.

runAsNonRoot and runAsUser

The runAsNonRoot: true setting enforces that the container’s UID is not 0. The kubelet checks at admission: if the image’s USER directive is 0, the container is rejected.

securityContext:
  runAsNonRoot: true
  runAsUser: 1000

The runAsUser: 1000 setting explicitly sets the UID. If runAsUser is omitted and runAsNonRoot: true, the kubelet picks a non-zero UID (the image’s USER if non-zero, otherwise a random UID).

fsGroup: 1000 sets the GID that owns the volumes mounted by the Pod. The kubelet chowns the volumes on startup.

allowPrivilegeEscalation

The allowPrivilegeEscalation: false setting prevents setuid binaries from escalating privileges. The container cannot execute setuid binaries.

securityContext:
  allowPrivilegeEscalation: false

The setting is required by restricted. It is also implied by runAsNonRoot: true (a non-root user cannot escalate in any case), but the explicit setting is the convention.

Common failure modes

  1. runAsNonRoot: true but the image runs as root. The Pod is rejected at admission with container has runAsNonRoot and image will run as root. The fix is to fix the image’s USER directive or to set runAsUser explicitly.
  2. readOnlyRootFilesystem: true but the workload writes to the root fs. The workload fails with Read-only file system. The fix is to mount emptyDir at the write paths.
  3. allowPrivilegeEscalation: false but the workload needs setuid binaries. The workload fails. The fix is to use a non-setuid binary or to set the flag to true (with documented justification).
  4. fsGroup: 1000 but the volume is owned by another UID. The volume is chowned on startup; the workload may see delays. The fix is to set fsGroup to match the volume’s existing owner.

Production patterns

  1. Pod-level securityContext for common settings. Set runAsNonRoot, seccompProfile, and fsGroup at the Pod level.
  2. Container-level overrides for specific needs. Set readOnlyRootFilesystem, capabilities, and allowPrivilegeEscalation at the container level.
  3. No root anywhere. Every container runs as non-root; the only root usage is in init containers for setup.
  4. Audited securityContext. A container with runAsUser: 0 is rejected outright under restricted. readOnlyRootFilesystem: false is not — Pod Security admission ignores it, so it needs its own policy rule if it is to be a finding.

Cross-course references

  • The Linux course covers UID/GID, fsGroup, and capabilities.
  • The Observability course covers the audit log entries for securityContext violations.

Quiz

Knowledge check · 4 questions

  1. Q1. What is the operational effect of `securityContext.runAsNonRoot: true`?

  2. Q2. A Pod with `securityContext.readOnlyRootFilesystem: true` cannot write to any path inside the container.

  3. Q3. Your workload uses an image that runs as root (`USER root`). You set `securityContext.runAsNonRoot: true`. The Pod is rejected with `container has runAsNonRoot and image will run as root`. Walk the response.

    The image is a third-party application that runs as root by default. The team added `runAsNonRoot: true` to comply with the `restricted` profile, but the image's `USER` directive is `root`.

  4. Q4. Name three fields of `securityContext` that are required by PSS `restricted`, and explain what each one prevents.

Passing score: 75%. Answers are checked in this browser.

Production discipline

The Pod’s securityContext is the operational control surface for workload-level security. A defensible configuration sets runAsNonRoot, readOnlyRootFilesystem, allowPrivilegeEscalation: false, seccompProfile: RuntimeDefault, and capabilities.drop: ["ALL"]. Every container runs as a non-root UID; the root filesystem is read-only; the syscall surface is constrained; no capabilities are granted. A cluster whose containers all meet these requirements has a workload security programme that is auditable.