Skip to main content

Minimize The Admission Of Containers With Capabilities

More Info:

Added Linux capabilities expand a containers privileges beyond the default set. Drop all capabilities where applications do not need them.

Risk Level

High

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. Identify pods using capabilities (any machine with kubectl access)

    kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.containers[*].securityContext.capabilities}{"\n"}{end}' \
    | grep -v '{}'

    Also include init containers:

    kubectl get pods -A -o json | \
    jq -r '.items[] |
    .metadata.namespace as $ns |
    .metadata.name as $pod |
    (.spec.containers[]?,
    .spec.initContainers[]?) as $c |
    select($c.securityContext.capabilities != null) |
    [$ns, $pod, $c.name, ($c.securityContext.capabilities // {})] | @tsv'
  2. Map capabilities to owning teams and justify need
    For each pod/container from step 1, capture its manifest and share with the owning team/application owner:

    kubectl get pod <pod-name> -n <namespace> -o yaml > /tmp/<namespace>-<pod-name>.yaml

    Ask them to confirm which listed capabilities are strictly required and whether the container can run with ALL dropped.

  3. Decide namespace policy: which namespaces can/should forbid capabilities
    Using the information from step 2, classify namespaces:

    • Namespaces where no containers need extra capabilities → candidates to enforce “drop all capabilities”.
    • Namespaces with a few exceptional workloads → decide whether to:
      • Move those workloads to a separate namespace, or
      • Exempt the namespace for now and document the risk.
  4. Design the admission policy to enforce dropping capabilities
    Decide on your enforcement mechanism (example options to prepare for implementation):

    • ValidatingAdmissionPolicy / ValidatingAdmissionWebhook that rejects pods/containers unless:
      • securityContext.capabilities.drop contains "ALL", and
      • securityContext.capabilities.add is unset/empty.
    • Limit the policy’s namespaceSelector or similar scoping so it only applies to namespaces identified in step 3.
  5. Update application manifests to be compliant before enforcing
    For each workload in namespaces you plan to protect, adjust the manifests (local Git/IaC repo, not directly in-cluster) so containers explicitly drop all capabilities, for example:

    securityContext:
    runAsNonRoot: true
    capabilities:
    drop:
    - ALL

    Where teams have justified specific capabilities as required, either:

    • Keep those workloads out of the strict namespaces, or
    • Document and prepare explicit policy exceptions in the admission policy design.
  6. Verify resulting posture after policy deployment
    After implementing your chosen admission policy (outside the scope of this manual review), confirm:

    • New pods can only be created in protected namespaces if they drop all capabilities and do not add any.
    • Periodically re-run:
      kubectl get pods -A -o json | \
      jq -r '.items[] |
      .metadata.namespace as $ns |
      .metadata.name as $pod |
      (.spec.containers[]?,
      .spec.initContainers[]?) as $c |
      select($c.securityContext.capabilities.add != null and
      ($c.securityContext.capabilities.add | length) > 0) |
      [$ns, $pod, $c.name, ($c.securityContext.capabilities.add // [])] | @tsv'
      Review any remaining capabilities and update namespace classification and policies as needed.
Using kubectl
# 1) List all pods with any explicit capabilities set (all namespaces)
# Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o jsonpath='{range .items[?(@.spec.containers[*].securityContext.capabilities)]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{end}'

# Same for initContainers
kubectl get pods --all-namespaces -o jsonpath='{range .items[?(@.spec.initContainers[*].securityContext.capabilities)]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{end}'

Problem indication: Any pod listed here has at least one container or initContainer with capabilities defined; these need review.


# 2) For a specific namespace, show pods and whether any container sets capabilities
NAMESPACE=default
kubectl get pods -n "$NAMESPACE" -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,CAPS:.spec.containers[*].securityContext.capabilities

Problem indication: Any non-empty CAPS field (e.g. map[add:[NET_ADMIN] drop:[ALL]]) means the pod is explicitly manipulating capabilities and must be reviewed.


# 3) Inspect full capability settings for a specific pod
NAMESPACE=default
POD=my-pod
kubectl get pod "$POD" -n "$NAMESPACE" -o yaml

What to look for as potential problems in the YAML:

  • securityContext.capabilities.add present and non-empty, for example:
    • add: ["NET_ADMIN"]
    • add: ["SYS_ADMIN", "NET_RAW"]
  • securityContext.capabilities.drop missing, empty, or does not include ALL.
  • Containers with no drop: ["ALL"] in namespaces where applications do not require Linux capabilities.

These locations should be checked:

  • .spec.containers[*].securityContext.capabilities
  • .spec.initContainers[*].securityContext.capabilities
  • .spec.ephemeralContainers[*].securityContext.capabilities (if used)

# 4) Summarize all added capabilities cluster-wide
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.name}{": "}{.securityContext.capabilities.add}{"; "}{end}{"\n"}{end}' \
| grep -v '\[\]' # filter out lines with empty add lists

Problem indication: Any line showing a non-empty add list (e.g. [NET_ADMIN], [SYS_ADMIN NET_RAW]) is a potential risk and should be justified or removed.


# 5) Verify after manual remediation (spot-check)
# Re-run capability listing for a namespace after you update manifests
NAMESPACE=default
kubectl get pods -n "$NAMESPACE" -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,CAPS:.spec.containers[*].securityContext.capabilities

Verification indication: For namespaces where you decided all applications can run without Linux capabilities, the CAPS column should be empty or show only drop:[ALL] and no add entries.

Automation
#!/usr/bin/env bash
#
# Report pods whose containers add Linux capabilities or fail to drop all capabilities.
# Run on: any machine with kubectl access and current context set.
#
# Requires: kubectl, jq

set -euo pipefail

echo "Scanning all namespaces for capability usage..."

# 1) Pods where any container explicitly *adds* capabilities
echo
echo "=== Pods with explicitly ADDED capabilities (potentially high risk) ==="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
containers: (
[
(.spec.initContainers // [] )[],
(.spec.containers // [] )[]
]
)
}
| .containers[]
| {
ns: .ns,
pod: .pod,
cname: .name,
adds: (.securityContext.capabilities.add // []),
drops: (.securityContext.capabilities.drop // [])
}
| select((.adds | length) > 0)
| "\(.ns)\t\(.pod)\t\(.cname)\tadd=\(.adds | join(\";\"))\tdrop=\(.drops | join(\";\"))"
' | column -t || echo "None found or jq error."

# 2) Pods where containers define capabilities but do NOT drop ALL
echo
echo "=== Pods with capabilities defined but NOT dropping ALL (review) ==="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
containers: (
[
(.spec.initContainers // [] )[],
(.spec.containers // [] )[]
]
)
}
| .containers[]
| {
ns: .ns,
pod: .pod,
cname: .name,
adds: (.securityContext.capabilities.add // []),
drops: (.securityContext.capabilities.drop // [])
}
# Container has a capabilities section at all
| select((.adds | length) > 0 or (.drops | length) > 0)
# but does not drop ALL
| select((.drops | map(ascii_upcase) | index("ALL")) | not)
| "\(.ns)\t\(.pod)\t\(.cname)\tadd=\(.adds | join(\";\"))\tdrop=\(.drops | join(\";\"))"
' | column -t || echo "None found or jq error."

# 3) Summary counts
echo
echo "=== Summary ==="
echo "- Pods with any container that ADDS capabilities:"
kubectl get pods --all-namespaces -o json \
| jq '
.items[]
| [
(.spec.initContainers // [] )[],
(.spec.containers // [] )[]
]
| map(.securityContext.capabilities.add // [])
| map(length)
| add
' | awk '{ if ($1=="") print 0; else print $1; }'

echo "- Containers defining capabilities but NOT dropping ALL:"
kubectl get pods --all-namespaces -o json \
| jq '
[
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
containers: [
(.spec.initContainers // [] )[],
(.spec.containers // [] )[]
]
}
| .containers[]
| {
adds: (.securityContext.capabilities.add // []),
drops: (.securityContext.capabilities.drop // [])
}
| select((.adds | length) > 0 or (.drops | length) > 0)
| select((.drops | map(ascii_upcase) | index("ALL")) | not)
] | length
'

echo
echo "Review guidance:"
echo "- Any line in the first section (ADDED capabilities) indicates a pod that expands beyond default capabilities."
echo "- Any line in the second section indicates a container that defines capabilities but does not drop ALL."
echo "- For namespaces where applications do not require capabilities, ensure containers drop ALL and avoid add= entries."

How to interpret problematic output

  • Any row in Pods with explicitly ADDED capabilities (potentially high risk) indicates a container that is explicitly requesting extra Linux capabilities. These should be reviewed and justified; in namespaces that should not use capabilities, they are a problem.
  • Any row in Pods with capabilities defined but NOT dropping ALL (review) shows containers that configure capabilities but do not include drop: ["ALL"]. In namespaces that should drop all capabilities, these are non-compliant and should be redesigned or restricted by admission policy.