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
Remediation
Manual Steps
-
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.
-
Classify namespaces and workloads by business/operational need
- Identify security-sensitive or “least privilege” namespaces (e.g.,
prod,payments,defaultfor 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).
- Identify security-sensitive or “least privilege” namespaces (e.g.,
-
Validate necessity of each added capability with workload owners
- For each
(namespace, pod, container)withcapabilities.addfrom 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?
- For each
-
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.addand, where possible, drop all capabilities:
securityContext:runAsNonRoot: trueallowPrivilegeEscalation: falsecapabilities: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' - For workloads where capabilities are not strictly needed, update their manifests (Deployments/StatefulSets/DaemonSets/Jobs/CronJobs) to remove
-
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
restrictedor an admission controller / policy engine that forbidscapabilities.addand/or enforcesdrop: ["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.addand ensure it is rejected).
- For namespaces whose workloads do not need any Linux capabilities, implement a guarding admission policy (e.g., Pod Security Standards
-
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