The Layers of Pod Security
Summary
- Pod security is not one control. It is a stack of independent layers, each of which catches what the layer above it missed.
- Insight: No single knob makes a pod safe. Security comes from composing image hygiene,
securityContext, admission enforcement, and kernel confinement so a failure in one layer does not become a cluster compromise. - Key Concepts: image provenance,
securityContext, Pod Security Standards (PSS), Pod Security Admission (PSA), seccomp/AppArmor, NetworkPolicy, RBAC. - When to Use: Every production workload. Treat the full stack as the baseline, not an aspiration for "sensitive" pods only.
- Limitations/Trade-offs: Layers add friction. Restricted defaults break images that assume root, and admission enforcement can block deploys, so rollout needs staging.
- Related Topics: Pod Security Standards,
securityContext, seccomp and AppArmor, why containers are not a security boundary.
Foundations
A container is a Linux process with namespaces and cgroups around it. It shares the host kernel with every other pod on the node.
That single fact drives the entire model. If one control fails, the blast radius is the node and potentially the cluster, not just the pod.
Defense in depth answers this by stacking controls that fail independently. An attacker has to defeat all of them in sequence, not one.
Think of it as five concentric rings, outermost first.
Ring one is the image. A minimal, non-root, scanned, digest-pinned base removes most of the tools and CVEs an attacker would use.
Ring two is the securityContext. It constrains the process at admission time: no root, no privilege escalation, dropped capabilities, a read-only root filesystem.
Ring three is admission control. Pod Security Admission (or a policy engine like Kyverno or OPA Gatekeeper) rejects pods that do not meet a namespace baseline before they are ever scheduled.
Ring four is kernel confinement. seccomp filters syscalls and AppArmor or SELinux restricts what the process can touch even if it is running.
Ring five is network and identity. Default-deny NetworkPolicy limits lateral movement, and tight RBAC plus a scoped ServiceAccount token limits what a compromised pod can ask the API server to do.
Mechanics & Interactions
The layers run at different times, which is what makes them complementary.
Image controls act at build time and in the registry. Scanning with Trivy or Grype and signing with cosign happen before anything reaches the cluster.
securityContext and admission act at admission time, when a Pod object hits the API server. The kubelet then applies the fields when it starts the container.
seccomp and AppArmor act at runtime, on every syscall or file access, for the life of the process.
NetworkPolicy and RBAC act continuously, mediating each connection and each API call.
The Pod Security Standards define three profiles that bundle securityContext expectations. Privileged is unrestricted. Baseline blocks known privilege escalations like host namespaces and privileged containers. Restricted additionally requires runAsNonRoot, a seccomp profile, dropped capabilities, and no privilege escalation.
Pod Security Admission is the built-in controller that enforces those profiles per namespace. You label a namespace and PSA does the rest.
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restrictedThe enforce label rejects violating pods. The warn and audit labels surface violations without blocking, which is how you dry-run a tighter profile before turning it on.
Here the layers interact directly. PSA in restricted mode will reject a pod unless its securityContext satisfies the profile. The admission layer forces the securityContext layer to be filled in correctly.
Advanced Considerations & Applications
PSA is coarse. It enforces one of three fixed profiles per namespace and cannot express custom rules like "images must come from our registry" or "must have resource limits".
For those, add a policy engine. Kyverno and OPA Gatekeeper run as validating admission webhooks and evaluate arbitrary rules, and they can mutate pods to inject defaults.
A common production pattern is PSA for the baseline profile plus Kyverno for organization-specific rules. PSA is fast and built in; the webhook handles everything PSA cannot express.
Order your rollout from least to most disruptive. Start with image scanning and signing, which do not touch running pods.
Then set namespaces to warn: restricted and read the warnings in your deploy logs and audit trail. Fix the workloads that trip.
Only then flip enforce: restricted. Doing it in the other order turns a security upgrade into an outage.
Some workloads legitimately need more than restricted allows, such as a CNI agent that needs NET_ADMIN. Isolate those in their own namespace with a looser profile rather than weakening the profile everywhere.
For genuinely hostile or multi-tenant workloads, add a sandboxed runtime. gVisor or Kata Containers give each pod a stronger kernel boundary via a RuntimeClass, at a measurable performance cost.
Common Misconceptions
"Restricted PSS makes my pod secure." It raises the floor, but it says nothing about your image contents, your network exposure, or your RBAC. It is one ring of five.
"Pod Security Policy still works." PodSecurityPolicy was removed in Kubernetes 1.25. Pod Security Admission replaced it, and it is what the current stack uses.
"A read-only root filesystem breaks my app." Most apps only need a writable /tmp or cache path. Mount an emptyDir there and keep the root read-only.
"Admission control is enough on its own." Admission validates the spec, not the running process. A pod that passes admission can still be exploited at runtime, which is why seccomp and NetworkPolicy exist.
"Docker Engine security settings protect my nodes." Docker is a build and dev tool here. Pods on nodes run under containerd via the CRI, and the runtime is configured through Kubernetes objects, not the Docker daemon.
FAQs
Do I need all the layers for every pod? Aim for it. The image, securityContext, admission, and NetworkPolicy layers cost almost nothing per pod and should be universal. Sandboxed runtimes are selective.
PSA or a policy engine? Use both. PSA gives you the standard profiles for free; a policy engine covers custom rules PSA cannot express, like registry allowlists or required labels.
Where do secrets fit? They are their own concern layered alongside these. Keep them out of images and env vars where possible, and scope the ServiceAccount token that fronts the API.
What is the single highest-leverage layer? runAsNonRoot plus dropped capabilities in the securityContext. It removes the easiest path from container escape to node compromise.
Does this apply outside Kubernetes? The image and kernel-confinement ideas apply to any container host. The admission and NetworkPolicy layers are Kubernetes constructs.
Related
- Pod Security Basics
- Why Containers Aren’t a Security Boundary
- securityContext
- seccomp & AppArmor
- Pod Security Best Practices
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).