Skip to main content

Minimize The Admission Containers With Added Capabilities

More Info:

Do not generally permit containers with capabilities assigned beyond the default set.

Risk Level

Medium

Address

Security

Compliance Standards

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS Critical Security Controls v8
  • CIS EKS
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • Essential 8
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIS2 Directive
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation

Remediation

Manual Steps
  1. Inventory pods and their capabilities

    • Run on: any machine with kubectl access
    kubectl get pods --all-namespaces -o json \
    | jq -r '.items[]
    | .metadata.namespace as $ns
    | .metadata.name as $pod
    | (.spec.initContainers // [] + .spec.containers // [])
    | .[]
    | {
    ns: $ns,
    pod: $pod,
    container: .name,
    seccomp: (.securityContext.seccompProfile.type // "unset"),
    cap_add: (.securityContext.capabilities.add // []),
    cap_drop: (.securityContext.capabilities.drop // [])
    }
    | select((.cap_add | length) > 0)
    | "\(.ns) \(.pod) \(.container) add=\(.cap_add) drop=\(.cap_drop) seccomp=\(.seccomp)"
    '

    This lists all containers that explicitly add Linux capabilities.

  2. Classify namespaces and workloads by business/operational need

    • Identify security-sensitive or “least privilege” namespaces (e.g., prod, payments, default for shared apps).
    • For each namespace found in step 1, list workloads and owners:
    kubectl get deploy,sts,ds,job,cronjob -A -o wide
    • Map each workload to an application/team and document whether it truly requires added capabilities (e.g., NET_ADMIN, SYS_ADMIN, DAC_OVERRIDE).
  3. Validate necessity of each added capability with workload owners

    • For each (namespace, pod, container) with capabilities.add from step 1:
      • Retrieve the full pod spec and security context:
      kubectl get pod -n <NAMESPACE> <POD_NAME> -o yaml
      • Review with the application owner:
        • What specific feature requires each capability?
        • Can the feature be implemented using standard permissions (RBAC, read-only FS, non-root UID) instead of Linux capabilities?
        • Can the capability be replaced with a smaller set or removed entirely?
  4. Harden pod specs by dropping unnecessary capabilities

    • For workloads where capabilities are not strictly needed, update their manifests (Deployments/StatefulSets/DaemonSets/Jobs/CronJobs) to remove .securityContext.capabilities.add and, where possible, drop all capabilities:
    securityContext:
    runAsNonRoot: true
    allowPrivilegeEscalation: false
    capabilities:
    drop:
    - ALL
    • Apply the updated manifests:
    kubectl apply -f <UPDATED-MANIFEST>.yaml
    • Confirm the running pods no longer have added capabilities:
    kubectl get pod -n <NAMESPACE> <POD_NAME> -o json \
    | jq '.spec.containers[].securityContext.capabilities'
  5. Enforce “no extra capabilities” at the namespace level where appropriate

    • For namespaces whose workloads do not need any Linux capabilities, implement a guarding admission policy (e.g., Pod Security Standards restricted or an admission controller / policy engine that forbids capabilities.add and/or enforces drop: ["ALL"]).
    • Example (if using Pod Security Admission labels):
    kubectl label namespace <NAMESPACE> \
    pod-security.kubernetes.io/enforce=restricted \
    --overwrite
    • Then verify new pods in that namespace cannot be created with added capabilities (attempt a test pod with capabilities.add and ensure it is rejected).
  6. Re-audit and document residual exceptions

    • Re-run the capability inventory to confirm reduction:
    kubectl get pods --all-namespaces -o json | jq '…' # reuse command from step 1
    • For any remaining containers with added capabilities:
      • Ensure there is documented justification, approval, and a review date.
      • Consider adding dedicated “high-privilege” namespaces for them and applying stricter monitoring/alerting on those workloads.
Using kubectl
# 1) List all pods and their containers’ security contexts (cluster-wide)
# Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{range .spec.initContainers[*]}{" initContainer: "}{.name}{"\n"}{" securityContext: "}{@.securityContext}{"\n"}{end}{range .spec.containers[*]}{" container: "}{.name}{"\n"}{" securityContext: "}{@.securityContext}{"\n"}{end}{"\n"}{end}' \
| sed 's/ map\[/ {/g' | sed 's/\]}/}/g'

Problem indication: Containers or initContainers showing capabilities.add or missing capabilities.drop where your policy expects drop: ["ALL"].


# 2) Directly list containers that add capabilities (cluster-wide)
# Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| .metadata as $m
| (.spec.initContainers // [])[]? as $c
| select($c.securityContext.capabilities.add != null)
| "NAMESPACE=\($m.namespace)\tPOD=\($m.name)\tTYPE=initContainer\tCONTAINER=\($c.name)\tADDED_CAPS=\($c.securityContext.capabilities.add)"
,
(.spec.containers // [])[]? as $c
| select($c.securityContext.capabilities.add != null)
| "NAMESPACE=\($m.namespace)\tPOD=\($m.name)\tTYPE=container\tCONTAINER=\($c.name)\tADDED_CAPS=\($c.securityContext.capabilities.add)"
'

Problem indication: Any line returned here is a workload explicitly adding Linux capabilities and must be reviewed for necessity.


# 3) List containers that do NOT drop all capabilities (where securityContext is set)
# Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| .metadata as $m
| (.spec.initContainers // [])[]? as $c
| select($c.securityContext != null)
| select( (.securityContext.capabilities.drop // []) | index("ALL") | not )
| "NAMESPACE=\($m.namespace)\tPOD=\($m.name)\tTYPE=initContainer\tCONTAINER=\($c.name)\tDROP=\($c.securityContext.capabilities.drop // [])"
,
(.spec.containers // [])[]? as $c
| select($c.securityContext != null)
| select( (.securityContext.capabilities.drop // []) | index("ALL") | not )
| "NAMESPACE=\($m.namespace)\tPOD=\($m.name)\tTYPE=container\tCONTAINER=\($c.name)\tDROP=\($c.securityContext.capabilities.drop // [])"
'

Problem indication: In namespaces where you intend workloads to run without capabilities, any container listed here is not configured to drop: ["ALL"] and should be examined.


# 4) Inspect security policies that control capabilities (PSP, PodSecurityPolicy) – if present
# Run on: any machine with kubectl access
kubectl get psp -o yaml

Problem indication: For namespaces where no capabilities should be allowed, corresponding PodSecurityPolicies should:

  • either require drop: ["ALL"] for container capabilities, or
  • otherwise ensure no additional capabilities can be added.

If no such PSP (or equivalent admission policy) exists for those namespaces, admission is not minimized as recommended.


# 5) For a specific namespace you believe should NOT use capabilities, review all pods
# Replace NAMESPACE with the actual namespace name
# Run on: any machine with kubectl access
NAMESPACE=kube-system # example; change as needed
kubectl get pods -n "$NAMESPACE" -o json \
| jq -r '
.items[]
| .metadata.name as $pod
| (.spec.initContainers // [])[]? as $c
| "POD=\($pod)\tTYPE=initContainer\tCONTAINER=\($c.name)\tCAPS=\($c.securityContext.capabilities // {})"
,
(.spec.containers // [])[]? as $c
| "POD=\($pod)\tTYPE=container\tCONTAINER=\($c.name)\tCAPS=\($c.securityContext.capabilities // {})"
'

Problem indication: In that namespace, containers showing capabilities.add or not dropping all capabilities where your policy expects no capabilities are candidates for redesign or exemption.

Automation
#!/usr/bin/env bash
# Report pods and controllers that add Linux capabilities beyond the default/drop-all
# Run on: any machine with kubectl access
# Requirements: kubectl, jq

set -euo pipefail

echo "=== POD-LEVEL CAPABILITIES (all namespaces) ==="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| . as $pod
| ($pod.spec.initContainers[]?, $pod.spec.containers[]?) as $c
| {
ns: $pod.metadata.namespace,
pod: $pod.metadata.name,
container: $c.name,
add: $c.securityContext.capabilities.add // [],
drop: $c.securityContext.capabilities.drop // [],
privileged: $c.securityContext.privileged // false,
allowPrivEsc: $c.securityContext.allowPrivilegeEscalation // "unset",
runAsUser: $c.securityContext.runAsUser // "unset",
runAsNonRoot: $c.securityContext.runAsNonRoot // "unset"
}
| select(
(.add | length > 0)
or
((.drop | length == 0) and (.add | length > 0))
or
(.privileged == true)
)
| "NAMESPACE=\(.ns) POD=\(.pod) CONTAINER=\(.container)\n add_caps=\(.add)\n drop_caps=\(.drop)\n privileged=\(.privileged)\n allowPrivilegeEscalation=\(.allowPrivEsc)\n runAsUser=\(.runAsUser) runAsNonRoot=\(.runAsNonRoot)\n"
'

echo
echo "=== CONTROLLER TEMPLATES (Deployments, DaemonSets, StatefulSets, Jobs, CronJobs) ==="
for kind in deployment daemonset statefulset job cronjob; do
echo "--- $kind(s) ---"
kubectl get "$kind" --all-namespaces -o json 2>/dev/null \
| jq -r '
.items[]
| . as $obj
| ( .spec.template.spec.initContainers[]?, .spec.template.spec.containers[]? ) as $c
| {
apiVersion: $obj.apiVersion,
kind: $obj.kind,
ns: $obj.metadata.namespace,
name: $obj.metadata.name,
container: $c.name,
add: $c.securityContext.capabilities.add // [],
drop: $c.securityContext.capabilities.drop // [],
privileged: $c.securityContext.privileged // false,
allowPrivEsc: $c.securityContext.allowPrivilegeEscalation // "unset",
runAsUser: $c.securityContext.runAsUser // "unset",
runAsNonRoot: $c.securityContext.runAsNonRoot // "unset"
}
| select(
(.add | length > 0)
or
((.drop | length == 0) and (.add | length > 0))
or
(.privileged == true)
)
| "NS=\(.ns) KIND=\(.kind) NAME=\(.name) CONTAINER=\(.container)\n add_caps=\(.add)\n drop_caps=\(.drop)\n privileged=\(.privileged)\n allowPrivilegeEscalation=\(.allowPrivEsc)\n runAsUser=\(.runAsUser) runAsNonRoot=\(.runAsNonRoot)\n"
' || true
done

echo
echo "=== NAMESPACE-LEVEL POD SECURITY (PodSecurity or PodSecurityPolicies, if present) ==="
echo "-- Namespaces with Pod Security Admission labels (v1.25+ recommended) --"
kubectl get ns --show-labels | grep -E 'pod-security.kubernetes.io/(enforce|audit|warn)=' || echo "No Pod Security labels found."

echo
echo "-- PodSecurityPolicies (if any; deprecated but still relevant where enabled) --"
kubectl get podsecuritypolicies.policy 2>/dev/null -o json \
| jq -r '
.items[]
| {
name: .metadata.name,
allowedCaps: .spec.allowedCapabilities // [],
requiredDropCaps: .spec.requiredDropCapabilities // [],
defaultAddCaps: .spec.defaultAddCapabilities // []
}
| "PSP=\(.name)\n allowedCapabilities=\(.allowedCaps)\n requiredDropCapabilities=\(.requiredDropCaps)\n defaultAddCapabilities=\(.defaultAddCaps)\n"
' || echo "No PodSecurityPolicies found."

cat <<'EOF'

INTERPRETING OUTPUT (what indicates a potential problem):

1) POD-LEVEL / CONTROLLER OUTPUT:
- Any line with non-empty "add_caps" is a candidate for review:
add_caps=["NET_ADMIN","SYS_ADMIN"]
Check if each listed capability is strictly required.
- If "drop_caps" is empty or does not include "ALL", and add_caps is non-empty,
the container is not dropping all capabilities before adding back a minimal set.
- Any "privileged=true" container is very high risk and should be scrutinized.

2) NAMESPACE / POLICY OUTPUT:
- Namespaces lacking Pod Security Admission labels likely have weaker default
restrictions; consider adding labels to enforce restricted or baseline policies.
- PodSecurityPolicies (if present) that:
* have broad "allowedCapabilities" (e.g., ["*"] or many powerful caps), or
* do NOT include "ALL" in "requiredDropCapabilities"
may allow admission of overly-privileged pods in those namespaces.

Use this report to:
- Identify workloads that add Linux capabilities or run privileged.
- Confirm whether your pod-security mechanisms actually prevent such pods in
namespaces where they are not required.
- Decide, per application/namespace, whether to tighten manifests or policies.
EOF

Additional Reading: