Listen to this Post

Introduction:
In the race to deploy cloud-native applications, the path of least resistance often involves running containers with root privileges. This convenience, however, creates a catastrophic blind spot in your security posture. If a container running as root is compromised, the attacker inherits those elevated privileges, turning a simple application vulnerability into a direct line to the host kernel and potentially your entire Kubernetes cluster.
Learning Objectives:
- Understand the attack surface created by privileged and root containers.
- Learn to implement a defense-in-depth strategy using Pod Security Standards.
- Gain hands-on skills to audit and harden Kubernetes pod specifications with Linux capabilities, seccomp, and read-only filesystems.
You Should Know:
1. The Anatomy of a Container Escape
A container is not a virtual machine; it is a collection of Linux processes isolated by namespaces and constrained by cgroups. When you run a container as the root user (UID 0) and grant it privileges, you are essentially telling the kernel that this process should have near-equal rights to the host itself.
If an attacker exploits a vulnerable application inside that pod (e.g., a remote code execution flaw), they are now root inside the container. From there, they can look for misconfigurations like mounted docker sockets or kernel exploits to break out.
Step‑by‑step guide: Simulating an Over-Privileged Container
Let’s see what access looks like from inside a default insecure pod.
1. Run a privileged container: This command gives the container access to all devices and disables namespace restrictions.
docker run --privileged -it ubuntu bash Inside the container whoami Output: root
2. Check host device visibility: In a privileged container, you can see and interact with host disks.
ls -la /dev/ | grep sda You might see sda, sda1, sda2 (the host's actual disks)
3. Attempt host namespace interaction: You can often interact with host processes.
nsenter -t 1 -m -u -n -i sh If successful, you are now in the host's namespace.
2. Enforcing a Non-Root User
The first and most critical step is to prevent the container from running as root. Most base images (like ubuntu:latest) run as root by default. You must explicitly define a user.
Step‑by‑step guide: Implementing `runAsNonRoot`
1. Create a non-root user in your Dockerfile:
FROM ubuntu:22.04 RUN useradd -u 1000 -m appuser USER appuser COPY --chown=appuser:appuser app /app CMD ["/app/start.sh"]
2. Enforce it in the Kubernetes Pod Spec: This Pod will be rejected if the image tries to run as root.
apiVersion: v1 kind: Pod metadata: name: secure-pod spec: securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 3000 containers: - name: my-app image: my-secure-image:latest
3. Verification: If an image attempts to run as root with runAsNonRoot: true, Kubernetes will prevent the pod from starting, throwing a “container has runAsNonRoot and image will run as root” error.
3. Locking Down the Filesystem
If an attacker gets in, their first move is often to download malware or modify binaries to persist. Making the root filesystem read-only eliminates this option.
Step‑by‑step guide: Implementing `readOnlyRootFilesystem`
- Identify Write Paths: Your application likely needs to write to specific volumes (e.g., logs, temp files, uploads). You must identify these and mount writable volumes for them.
2. Apply the Pod Spec:
apiVersion: v1
kind: Pod
metadata:
name: read-only-pod
spec:
containers:
- name: app
image: nginx:latest
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp-volume
mountPath: /tmp
- name: cache-volume
mountPath: /var/cache/nginx
volumes:
- name: tmp-volume
emptyDir: {}
- name: cache-volume
emptyDir: {}
3. Testing: Attempt to create a file outside of the mounted volumes.
kubectl exec -it read-only-pod -- touch /test.txt Expected output: touch: cannot touch '/test.txt': Read-only file system
4. Stripping Linux Capabilities
Linux capabilities break the “all or nothing” nature of root access. Even if your app runs as root, you can drop all capabilities except the absolute bare minimum it needs to function.
Step‑by‑step guide: Dropping All Capabilities
- The Principle of Least Privilege: Identify what syscalls your app needs. A typical web app needs networking (
NET_BIND_SERVICEif binding to a port under 1024) and little else. - Implementing
drop: ["ALL"]: This removes all 40+ capabilities. You then only add back the strictly necessary ones.apiVersion: v1 kind: Pod metadata: name: capability-hardened-pod spec: containers:</li> </ol> - name: app image: nginx:latest securityContext: capabilities: drop: - ALL add: - NET_BIND_SERVICE Required for binding to port 80
3. Verification with `capsh`:
Exec into the pod kubectl exec -it capability-hardened-pod -- bash Install capsh if needed: apt update && apt install -y libcap2-bin capsh --print The Current: list should be very short, likely only "cap_net_bind_service"
5. Applying a Seccomp Profile
Seccomp (secure computing mode) filters system calls. While dropping capabilities removes coarse privileges, seccomp blocks the actual kernel functions an attacker might try to use for an escape.
Step‑by‑step guide: Enabling the Runtime Default Profile
For most workloads, the default seccomp profile provided by the container runtime (like containerd) blocks roughly 300+ dangerous syscalls while allowing normal application operations.
1. Enable the Profile:
apiVersion: v1 kind: Pod metadata: name: seccomp-pod spec: securityContext: seccompProfile: type: RuntimeDefault containers: - name: app image: nginx:latest
2. Why this matters: Syscalls like
unshare,mount, and `ptrace` are often blocked by the default profile, effectively neutralizing many container escape exploits (e.g., CVE-2016-5195) that rely on them, even if the container is running as root.- Putting It All Together: The Hardened Pod Spec
A secure pod combines all these elements. Here is a production-ready example that aligns with the “Restricted” Pod Security Standard.
apiVersion: v1 kind: Pod metadata: name: fully-hardened-pod spec: securityContext: runAsNonRoot: true runAsUser: 1001 runAsGroup: 2001 fsGroup: 2001 seccompProfile: type: RuntimeDefault containers: - name: app image: my-app:latest securityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] add: ["NET_BIND_SERVICE"] Only if necessary volumeMounts: - name: tmp-volume mountPath: /tmp volumes: - name: tmp-volume emptyDir: {}7. Auditing Your Cluster for Insecure Pods
You need to identify existing risks. Use `kubectl` to audit your running workloads.
Linux Command to find privileged pods:
kubectl get pods --all-namespaces -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged==true) | .metadata.namespace + "/" + .metadata.name'
Using `kubectl` with `wide` output to check user IDs:
This requires a tool like 'kubectl-get-all' or iterating, but you can check describe kubectl describe pod <pod-name> | grep "User" Or check the pod's security context directly kubectl get pod <pod-name> -o yaml | grep -A 5 "securityContext"
What Undercode Say:
- Key Takeaway 1: Default is Danger. The default security context in Kubernetes is optimized for compatibility, not security. Assuming a default pod is safe is a critical error in risk assessment.
- Key Takeaway 2: Defense in Depth is Mandatory. Relying on a single control like `runAsNonRoot` is insufficient. Combining user restrictions, a read-only filesystem, dropped capabilities, and a seccomp profile creates multiple barriers that an attacker must bypass, exponentially increasing the difficulty of a successful cluster compromise.
The security of your containerized infrastructure is written in YAML. Treat your pod specs as the first line of defense in a zero-trust architecture. By shifting left and enforcing these policies at the admission controller level (using OPA/Gatekeeper or Kyverno), you can prevent these misconfigurations from ever reaching production, ensuring that even if an application is breached, the cluster itself remains intact.
Prediction:
As cloud-native adoption matures, we will see a shift from “shift-left” solely focusing on code vulnerabilities to “shift-left” focusing on infrastructure configuration. The next generation of Kubernetes attacks will not target the kernel, but the misconfigurations in pod security contexts. Consequently, automated policy-as-code engines will become as critical to the software development lifecycle as CI/CD pipelines themselves, with organizations treating a violation of `privileged: true` with the same severity as a critical library vulnerability.
▶️ Related Video (82% Match):
🎯Let’s Practice For Free:
IT/Security Reporter URL:
Reported By: Codeanddecode Kubernetes – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]
📢 Follow UndercodeTesting & Stay Tuned:
- Putting It All Together: The Hardened Pod Spec


