Safeguard
Container Security

Kubernetes securityContext settings guide

A field-by-field guide to Kubernetes securityContext: which settings stop container breakouts, how to enforce them cluster-wide, and how to audit gaps.

Priya Mehta
DevSecOps Engineer
Updated 6 min read

Every Kubernetes pod that omits or misconfigures securityContext inherits root-equivalent defaults: a container running as UID 0, with Linux capabilities like CAP_SYS_ADMIN intact, a writable root filesystem, and no restriction on privilege escalation. That combination is exactly what turned CVE-2019-5736 (a runc container breakout affecting Docker and Kubernetes runtimes) and CVE-2024-21626 (a leaked file descriptor bug in runc patched in January 2024) into full host-compromise vectors rather than contained incidents. The Kubernetes securityContext is the pod- and container-level API object that closes those gaps — it sets the UID/GID a process runs as, drops Linux capabilities, forces read-only filesystems, and applies seccomp/AppArmor profiles. Since Kubernetes 1.25 (August 2022), Pod Security Admission replaced the deprecated PodSecurityPolicy and enforces three baked-in profiles — privileged, baseline, and restricted — built almost entirely on securityContext fields. This guide breaks down which fields matter, how to enforce them cluster-wide, and how to audit workloads that predate your policy.

What Does Kubernetes securityContext Actually Control?

securityContext controls the Linux security attributes a pod or container runs with, set at either the pod spec level (spec.securityContext) or the container level (spec.containers[].securityContext), with container-level settings overriding pod-level ones. It maps directly to kernel and container-runtime primitives: runAsUser/runAsGroup set the process UID/GID instead of the image's baked-in default (often root, UID 0), capabilities.drop/add manipulate the 40+ Linux capabilities a container is granted beyond a normal process, readOnlyRootFilesystem mounts the container's root filesystem as read-only, and seccompProfile restricts which of the roughly 300+ Linux syscalls a container can invoke. Two fields get confused constantly: privileged: true grants a container essentially all host capabilities and device access (used for things like CNI plugins or kubelet-adjacent tooling), while allowPrivilegeEscalation: true merely permits a process to gain more privileges than its parent via setuid binaries or no_new_privs-disabled execs — Kubernetes sets the latter to true by default unless you explicitly override it.

Which securityContext Fields Should Every Production Pod Set?

Six fields cover the vast majority of real-world exploitation paths, and the Kubernetes "restricted" Pod Security Standard mandates all of them. Set runAsNonRoot: true and runAsUser to a UID above 0 (commonly 1000+) so a container escape doesn't hand the attacker root inside the container as a starting point. Set allowPrivilegeEscalation: false to block setuid/sudo-style escalation inside the container. Set capabilities: {drop: ["ALL"]} and only add back the 1-2 capabilities a workload genuinely needs (e.g., NET_BIND_SERVICE for a process binding to port 80/443) — Docker's default capability set alone includes 14 capabilities most apps never touch, like CAP_MKNOD and CAP_SETFCAP. Set readOnlyRootFilesystem: true and mount an emptyDir for any directory the app writes to, which neutralizes a large class of malware that persists by writing to disk. Set seccompProfile.type: RuntimeDefault to inherit the container runtime's default syscall filter instead of running unconfined. Never set privileged: true unless the workload is a known infrastructure component (CNI, CSI driver, node-level agent) that has been explicitly reviewed.

How Do You Enforce securityContext Settings Across a Cluster?

You enforce them with an admission controller, because per-developer YAML discipline fails at scale within weeks of onboarding a second team. Kubernetes' built-in Pod Security Admission (stable since 1.25) lets you label a namespace with pod-security.kubernetes.io/enforce: restricted and the API server rejects any pod that violates the restricted profile's ~10 checks at creation time — no separate controller to install. For finer-grained rules (e.g., allowing NET_BIND_SERVICE only on specific namespaces, or requiring specific seccompProfile values PSA doesn't check), teams layer on OPA Gatekeeper or Kyverno; Kyverno alone ships more than 30 pre-built policies specifically for securityContext fields in its default policy library. The practical rollout sequence that avoids breaking existing workloads: set new namespaces to enforce: restricted from day one, set existing namespaces to audit: restricted and warn: restricted first, review the violations Kubernetes surfaces in audit logs and kubectl warnings for 2-4 weeks, remediate, then flip to enforce.

What Actually Happens When securityContext Is Missing or Wrong?

A missing or permissive securityContext turns a routine container compromise into a node or cluster compromise. The 2019 runc vulnerability (CVE-2019-5736, CVSS 8.6) let a malicious or compromised container overwrite the host's runc binary and execute code as root on the host — but it required the container to be run with elevated privileges or as root, exactly the default state of a pod with no securityContext set. CVE-2024-21626, patched in runc 1.1.12 (January 2024, CVSS 8.6), allowed a leaked file descriptor to give a newly-spawned container process access to the host filesystem; Kubernetes' own security advisory for this CVE explicitly called out that pods without readOnlyRootFilesystem and with default capability sets were more exploitable. Beyond named CVEs, the pattern repeats in real incidents: the 2021 Azurescape research demonstrated cross-tenant container escapes in Azure Container Instances that relied on containers running with capabilities beyond what their workload needed, and Tesla's 2018 Kubernetes dashboard compromise (an exposed dashboard with no authentication, compounded by containers with broad IAM and filesystem access) turned a single misconfiguration into cryptomining on cloud compute at scale.

How Do You Audit Existing Workloads for securityContext Gaps?

You audit existing workloads by running admission controllers in dry-run/audit mode against live cluster state before enforcing anything, because most clusters older than a few months already run pods that would fail a restricted policy. kubectl alone gets you a manual starting point: kubectl get pods -A -o json | jq filtered for .spec.containers[].securityContext.privileged == true or a missing runAsNonRoot surfaces the highest-risk pods in minutes. For ongoing coverage, static analysis of manifests and Helm charts before deploy (via Kyverno's apply --policy-report, Checkov, or kubesec.io) catches misconfigurations in CI rather than in a running cluster, and tools that score against the CIS Kubernetes Benchmark (currently v1.9, covering securityContext controls under sections 5.2.x and 5.7.x) give a standardized baseline to track drift over time. The gap most teams miss: base container images that run as root by default (many official Docker Hub images, including older node and python tags, default to UID 0) will silently defeat a runAsNonRoot: true setting at the pod level unless the image itself also defines a non-root USER — the fix requires both the manifest control and an image rebuild, not one or the other.

How Safeguard Helps

Safeguard maps securityContext misconfigurations to actual exploitability instead of flat severity scores, using reachability analysis to determine whether a pod's missing capability drop or writable root filesystem is paired with a package or code path an attacker can actually reach — separating the small number of pods that need same-day fixes from the hundreds that don't. Griffin AI, Safeguard's detection engine, continuously scans live cluster manifests and CI-pipeline YAML for drift against the restricted Pod Security Standard and flags newly introduced privileged: true or dropped runAsNonRoot settings before they merge. Safeguard generates and ingests SBOMs across your container images so base-image root defaults (the gap manual audits routinely miss) are tracked alongside manifest-level controls. When a gap is confirmed, Safeguard opens an auto-fix pull request with the corrected securityContext block already scoped to the workload's actual runtime needs, so remediation doesn't require a security engineer to hand-write YAML for every namespace.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.