Setting Kubernetes securityContext capabilities to drop: ["ALL"] and adding back only the specific capabilities your workload cannot live without is the single highest-value, lowest-effort container hardening step available, because it enforces least privilege at the Linux kernel level. Most containers run with a default set of capabilities they never use, and those unused privileges are exactly what an attacker reaches for after compromising a process inside the pod. This guide explains what capabilities are and how to lock them down.
What Linux capabilities are
Traditionally, a Unix process was either root (able to do anything) or not. Linux capabilities break root's power into roughly forty discrete privileges that can be granted independently, such as CAP_NET_BIND_SERVICE (bind to ports below 1024), CAP_SYS_ADMIN (a broad and dangerous grab-bag), CAP_CHOWN (change file ownership), and CAP_NET_RAW (use raw sockets). A container runtime grants a default subset to every container unless you say otherwise.
The problem is that the default set includes privileges the vast majority of application containers never exercise. A web service that listens on port 8080 and reads a config file does not need to change system time, mount filesystems, or load kernel modules. Yet the capabilities to attempt those things may be sitting right there for a compromised process to abuse.
The core best practice: drop ALL, add back specifically
The recommended pattern is to drop every capability and then, only if the application genuinely requires one, add it back explicitly. This is least privilege made concrete:
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: my-web-app:1.4.2
securityContext:
capabilities:
drop:
- ALL
For the common case of a modern application that binds to an unprivileged port (8080, 3000, 9090), this is the whole configuration. It needs nothing added back. If the container is compromised, the attacker inherits a process with essentially no special kernel privileges: it cannot change system time, mount filesystems, or load kernel modules, so the blast radius is dramatically smaller.
When you actually need NET_BIND_SERVICE
The most common capability teams think they need is NET_BIND_SERVICE, which permits binding to ports below 1024. But you only need it if your process actually binds to a privileged port such as 80 or 443. If you bind to 8080 and let a Service or Ingress map 80 to it, you do not need the capability at all.
If you genuinely must bind low ports inside the container, add back exactly that one:
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
Now the container can bind to 80 or 443 and do nothing else privileged. Resist the temptation to add more "just in case." Every added capability is a rung on the ladder an attacker uses to escalate.
Pair capabilities with the rest of the securityContext
Dropping capabilities is most effective alongside the other securityContext controls that together make a container hard to escape:
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
runAsNonRootand a non-zerorunAsUserkeep the process off UID 0.allowPrivilegeEscalation: falsestops a process gaining more privileges than its parent (it blocks setuid-binary escalation).readOnlyRootFilesystemprevents an attacker from writing tools or modifying binaries in the image.seccompProfile: RuntimeDefaultfilters dangerous syscalls.
Capabilities and these settings reinforce each other. Dropping ALL removes the privileges; allowPrivilegeEscalation: false stops the process from clawing them back.
Enforce it cluster-wide, do not rely on discipline
Configuring one pod correctly does not help if the next team ships a privileged one. Enforce the policy at admission:
- Pod Security Admission at the
restrictedlevel requiresdrop: ["ALL"](allowing onlyNET_BIND_SERVICEto be added) and blocks noncompliant pods from being created. - Policy engines like Kyverno or OPA Gatekeeper can enforce the same rules with custom messaging and audit modes so you see violations before you enforce them.
Roll out in audit or warn mode first, fix the workloads the reports surface, then flip to enforce. Turning on restricted enforcement without a survey period tends to break deployments and erode trust in the control.
Finding what your workload really needs
If a container fails after dropping capabilities, resist adding SYS_ADMIN to make the error go away. Instead, find the minimum set:
- Run the workload and watch for permission errors that name a capability.
- Use
straceorauditdto observe which syscalls the process makes and map them to the capability that gates them. - Add back one capability at a time, testing after each, and document why each survivor is required.
That documentation matters for reviews: a capability with a written justification survives audits, and an undocumented one gets challenged. Container image scanning and Kubernetes posture checks in tools like Safeguard can flag pods that run without a dropped-capabilities policy, so the gap is visible before it reaches production. Our container security track covers the full pod-hardening checklist.
FAQ
What does capabilities drop ALL do in a Kubernetes securityContext?
It removes every Linux capability from the container, including the default set the runtime would otherwise grant. The process runs with no special kernel privileges, so a compromised container cannot change system time, mount filesystems, or load kernel modules. You then add back only the specific capabilities the app truly needs.
Do I need NET_BIND_SERVICE for my container?
Only if the process binds to a port below 1024, such as 80 or 443, from inside the container. If it binds to an unprivileged port like 8080 and a Service or Ingress maps the low port externally, you do not need the capability at all.
How is dropping capabilities different from runAsNonRoot?
They address different privileges. runAsNonRoot keeps the process off UID 0, while dropping capabilities removes specific kernel privileges that even a non-root process might hold. Use both together, along with allowPrivilegeEscalation: false, for defense in depth.
How do I enforce a drop-ALL policy across the whole cluster?
Use Pod Security Admission at the restricted level, which requires dropping all capabilities, or a policy engine such as Kyverno or OPA Gatekeeper. Roll out in audit or warn mode first to find noncompliant workloads, then switch to enforce.