Safeguard
Containers

Kubernetes securityContext Capabilities: Drop ALL, Add Only What You Need

Linux capabilities are the privileges inside a container that attackers reuse after a breakout. Setting Kubernetes securityContext capabilities to drop ALL is the cheapest hardening you will do.

Karan Patel
Platform Engineer
6 min read

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
  • runAsNonRoot and a non-zero runAsUser keep the process off UID 0.
  • allowPrivilegeEscalation: false stops a process gaining more privileges than its parent (it blocks setuid-binary escalation).
  • readOnlyRootFilesystem prevents an attacker from writing tools or modifying binaries in the image.
  • seccompProfile: RuntimeDefault filters 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 restricted level requires drop: ["ALL"] (allowing only NET_BIND_SERVICE to 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 strace or auditd to 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.

Never miss an update

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