Pod Security & Workload Protection
Container security in Kubernetes goes far beyond just using secure base images. The runtime environment where your containers execute can be just as important as the code they’re running. I’ve seen perfectly secure applications become vulnerable simply because their pod security configuration allowed privilege escalation or host access that attackers could exploit.
Pod security in Kubernetes operates through multiple layers: security contexts that define how containers run, pod security standards that enforce cluster-wide policies, and admission controllers that validate configurations before pods are created. Understanding how these layers work together is crucial for building truly secure workloads.
Security Contexts and Container Isolation
Security contexts are your first line of defense against container breakouts and privilege escalation attacks. They control the security settings for pods and containers, including user IDs, group IDs, filesystem permissions, and Linux capabilities. The key principle here is running containers with the least privilege necessary to function.
Most containers don’t need to run as root, yet many do simply because that’s the default. Here’s how to properly configure a security context for a typical web application:
apiVersion: v1
kind: Pod
metadata:
name: secure-web-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: web-server
image: nginx:alpine
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
This configuration ensures the container runs as a non-root user, prevents privilege escalation, makes the root filesystem read-only, and drops all Linux capabilities except the one needed to bind to privileged ports. The read-only root filesystem is particularly effective—it prevents attackers from modifying system files even if they compromise the container.
When you make the root filesystem read-only, you’ll need to provide writable volumes for directories where the application needs to write data:
spec:
containers:
- name: web-server
volumeMounts:
- name: tmp-volume
mountPath: /tmp
- name: cache-volume
mountPath: /var/cache/nginx
volumes:
- name: tmp-volume
emptyDir: {}
- name: cache-volume
emptyDir: {}
Pod Security Standards Implementation
Pod Security Standards replace the deprecated Pod Security Policies with a simpler, more maintainable approach. There are three standard levels: Privileged (unrestricted), Baseline (minimally restrictive), and Restricted (heavily restricted). I recommend starting with Baseline for most workloads and moving to Restricted for security-sensitive applications.
The beauty of Pod Security Standards is that they’re implemented through admission controllers, so they’re enforced automatically without requiring custom policies. Here’s how to configure namespace-level pod security:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
The Restricted standard is quite strict—it requires non-root users, read-only root filesystems, and drops all capabilities by default. This might seem excessive, but it’s actually achievable for most applications with proper configuration. The key is understanding what your applications actually need versus what they’re configured to use by default.
For applications that need some additional privileges, you can use the Baseline standard and add specific security controls:
apiVersion: apps/v1
kind: Deployment
metadata:
name: monitoring-agent
namespace: system-monitoring
spec:
template:
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: agent
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
add:
- SYS_PTRACE
Runtime Security and Threat Detection
Runtime security goes beyond static configuration to monitor and protect running containers. This includes detecting unusual process execution, file system modifications, and network connections that might indicate a compromise. Tools like Falco can provide real-time threat detection based on system call monitoring.
AppArmor and SELinux provide additional layers of runtime protection by enforcing mandatory access controls. While these require more setup, they can prevent entire classes of attacks by restricting what processes can do at the kernel level:
apiVersion: v1
kind: Pod
metadata:
name: confined-app
annotations:
container.apparmor.security.beta.kubernetes.io/web-server: runtime/default
spec:
containers:
- name: web-server
image: nginx:alpine
Seccomp profiles offer another powerful runtime protection mechanism by filtering system calls. The default seccomp profile blocks many dangerous system calls while allowing normal application operations:
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
Image Security and Supply Chain Protection
Container image security is fundamental to workload protection. This starts with using minimal base images, regularly updating dependencies, and scanning images for vulnerabilities. But it goes deeper—you need to ensure that only trusted images run in your cluster.
Image signing and verification help establish trust in your supply chain. Tools like Cosign can sign container images, and admission controllers can verify these signatures before allowing pods to run:
apiVersion: v1
kind: Pod
metadata:
name: verified-app
spec:
containers:
- name: app
image: myregistry.com/myapp:v1.2.3@sha256:abc123...
Using image digests instead of tags ensures that you’re running exactly the image you expect, preventing tag-based attacks where malicious images are pushed with the same tag as legitimate ones.
Resource Limits and Quality of Service
Resource limits aren’t just about preventing resource exhaustion—they’re also a security control. Without proper limits, a compromised container could consume all available CPU or memory, creating a denial-of-service condition that affects other workloads on the same node.
spec:
containers:
- name: web-app
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
Quality of Service classes determine how Kubernetes handles resource contention. Guaranteed QoS (where requests equal limits) provides the most predictable behavior and protection against resource-based attacks.
Workload Identity and Service Mesh Integration
Modern workload protection increasingly involves service mesh technologies that provide identity, encryption, and policy enforcement at the network level. While service mesh adds complexity, it also provides powerful security capabilities like mutual TLS, fine-grained authorization policies, and traffic encryption.
The combination of pod security standards, proper security contexts, and service mesh policies creates multiple layers of protection that work together. Even if an attacker compromises a container, they face additional barriers from network policies, identity verification, and runtime monitoring.
These workload protection mechanisms form the foundation for secure container operations. In the next part, we’ll explore secrets and configuration security, focusing on how to securely handle sensitive data and credentials that your protected workloads need to function.