Containers Should Disallow Privilege Escalation
More Info:​
Verifies allowPrivilegeEscalation is false on every container. It defaults to true, letting a process gain more privileges than its parent.
Risk Level​
High
Address​
Security
Compliance Standards​
- Cloudanix Best Practice
Triage and Remediation​
- Remediation
Remediation​
Manual Steps
-
Identify all noncompliant Pods
- On any machine with kubectl access:
kubectl get pods --all-namespaces -o json | jq -r '[ .items[]| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)| .metadata as $m| (.spec.nodeName // "") as $node| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own| ((.spec.containers // []) + (.spec.initContainers // []))[]| (.securityContext.allowPrivilegeEscalation == false) as $ok| select($ok | not)| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)+ (if $node == "" then "" else " node=\($node)" end)+ (if $labels == "" then "" else " labels=\($labels)" end)+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)+ " container=\(.name) image=\(.image)"+ " allowPrivilegeEscalation=\(if .securityContext.allowPrivilegeEscalation == null then "unset" else .securityContext.allowPrivilegeEscalation end)"][]'
- On any machine with kubectl access:
-
For Pods managed by higher-level controllers, patch the controller manifests
- For each noncompliant Pod line with
owner=Deployment/…,ReplicaSet/…,StatefulSet/…, orDaemonSet/…, note theownerkind, namespace, and name. - On any machine with kubectl access, edit the owning object (example for a Deployment; adapt
kind,namespace, andnameas needed):kubectl -n <namespace> edit deployment <name> - In each
spec.template.spec.containers[]andspec.template.spec.initContainers[]entry, ensure:(addsecurityContext:allowPrivilegeEscalation: falsesecurityContextif missing; if present, add or setallowPrivilegeEscalation: false).
- For each noncompliant Pod line with
-
For Pods created directly (no controller owner), edit and recreate them
- For each noncompliant Pod line with no
owner=field:kubectl -n <namespace> get pod <pod-name> -o yaml > /tmp/pod-<namespace>-<pod-name>.yaml - Edit the file locally:
sed -i '/^\s*resourceVersion:/d;/^\s*uid:/d;/^\s*selfLink:/d;/^\s*managedFields:/d;/^\s*status:/d' /tmp/pod-<namespace>-<pod-name>.yaml
- Open the file and, under
spec.containers[]andspec.initContainers[]as needed, set:securityContext:allowPrivilegeEscalation: false - Delete and recreate the Pod:
kubectl -n <namespace> delete pod <pod-name>kubectl -n <namespace> apply -f /tmp/pod-<namespace>-<pod-name>.yaml
- For each noncompliant Pod line with no
-
If manifests are managed via Git/IaC, update source files
- Locate the YAML/Helm/Kustomize definitions for the noncompliant workloads.
- In each container and initContainer spec, set:
securityContext:allowPrivilegeEscalation: false
- Commit and apply/sync through your normal pipeline so the change persists.
-
Consider namespace-wide policy to prevent regression (optional but recommended)
- On any machine with kubectl access, create or update a
PodSecurityadmission label on namespaces where you want to enforce restricted settings, for example:kubectl label namespace <namespace> pod-security.kubernetes.io/enforce=restricted --overwrite - Review workload compatibility before enforcing to avoid inadvertent disruptions.
- On any machine with kubectl access, create or update a
-
Verify remediation
- After workloads are updated and reconciled, re-run the audit on any machine with kubectl access:
kubectl get pods --all-namespaces -o json | jq -r '[ .items[]| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)| .metadata as $m| (.spec.nodeName // "") as $node| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own| ((.spec.containers // []) + (.spec.initContainers // []))[]| (.securityContext.allowPrivilegeEscalation == false) as $ok| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)+ (if $node == "" then "" else " node=\($node)" end)+ (if $labels == "" then "" else " labels=\($labels)" end)+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)+ " container=\(.name) image=\(.image)"+ " allowPrivilegeEscalation=\(if .securityContext.allowPrivilegeEscalation == null then "unset" else .securityContext.allowPrivilegeEscalation end)"+ " is_compliant=\(if $ok then "true" else "false" end)"] as $rows| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
- Confirm the output is
is_compliant=trueor that all listed containers showallowPrivilegeEscalation=false is_compliant=true.
- After workloads are updated and reconciled, re-run the audit on any machine with kubectl access:
Using kubectl
On any machine with kubectl access:
- Identify non‑compliant Pods and their owners (if any):
kubectl get pods --all-namespaces -o wide
If the Pod is controlled by a higher‑level object (Deployment, DaemonSet, Job, etc.), edit that controller; otherwise edit the Pod directly.
- Patch a standalone Pod to disallow privilege escalation (all containers and initContainers):
kubectl patch pod POD_NAME \
-n POD_NAMESPACE \
--type='json' \
-p='[
{"op": "add", "path": "/spec/securityContext", "value": {}},
{"op": "add", "path": "/spec/containers/0/securityContext", "value": {}}
]' 2>/dev/null || true
kubectl get pod POD_NAME -n POD_NAMESPACE -o json \
| jq '
.spec.containers |= map(
.securityContext.allowPrivilegeEscalation = false
)
| .spec.initContainers |= (// []) | . |= map(
.securityContext.allowPrivilegeEscalation = false
)' \
| kubectl apply -f -
- Patch a Deployment so all its containers disallow privilege escalation:
kubectl get deployment DEPLOYMENT_NAME -n DEPLOYMENT_NAMESPACE -o json \
| jq '
.spec.template.spec.containers |= map(
.securityContext.allowPrivilegeEscalation = false
)
| .spec.template.spec.initContainers |= (// []) | . |= map(
.securityContext.allowPrivilegeEscalation = false
)' \
| kubectl apply -f -
Repeat similarly for other controllers, replacing deployment with daemonset, statefulset, job, or cronjob as appropriate.
- Verification (runs on any machine with kubectl):
kubectl get pods --all-namespaces -o json | jq -r '
[ .items[]
| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| (.spec.nodeName // "") as $node
| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
| ((.spec.containers // []) + (.spec.initContainers // []))[]
| (.securityContext.allowPrivilegeEscalation == false) as $ok
| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ (if $node == "" then "" else " node=\($node)" end)
+ (if $labels == "" then "" else " labels=\($labels)" end)
+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
+ " container=\(.name) image=\(.image)"
+ " allowPrivilegeEscalation=\(if .securityContext.allowPrivilegeEscalation == null then "unset" else .securityContext.allowPrivilegeEscalation end)"
+ " is_compliant=\(if $ok then "true" else "false" end)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
Automation
#!/usr/bin/env bash
#
# Remediation: Ensure all containers disallow privilege escalation
# Benchmark: CBP C1.4
#
# Requirements:
# - kubectl configured with access to the cluster
# - jq installed
#
# Behavior:
# - Scans all non-system namespaces for Pods whose containers/initContainers
# do NOT have securityContext.allowPrivilegeEscalation=false
# - Patches their owning workload (Deployment/StatefulSet/DaemonSet/Job/CronJob/Pod)
# to set allowPrivilegeEscalation=false on every container and initContainer
# - Safe to re-run (idempotent)
# - Recreates Pods via normal controller behavior
#
# Run location:
# - Any machine with kubectl access to the cluster
set -euo pipefail
# -------- Config --------
# Namespaces to ignore (system namespaces)
IGNORE_NAMESPACES_REGEX='^(kube-system|kube-public|kube-node-lease)$'
# -------- Helper functions --------
log() { printf '[%s] %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*"; }
require_bin() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "ERROR: '$1' not found in PATH" >&2
exit 1
fi
}
# jq filter used to set allowPrivilegeEscalation=false on all containers
jq_patch_filter='
.spec.template.spec as $spec
| if $spec == null then . else
.spec.template.spec =
(
$spec
| if (.containers // null) != null then
.containers |= map(
.securityContext =
((.securityContext // {}) + {allowPrivilegeEscalation:false})
)
else . end
| if (.initContainers // null) != null then
.initContainers |= map(
.securityContext =
((.securityContext // {}) + {allowPrivilegeEscalation:false})
)
else . end
)
end
'
jq_pod_patch_filter='
.spec as $spec
| if $spec == null then . else
.spec =
(
$spec
| if (.containers // null) != null then
.containers |= map(
.securityContext =
((.securityContext // {}) + {allowPrivilegeEscalation:false})
)
else . end
| if (.initContainers // null) != null then
.initContainers |= map(
.securityContext =
((.securityContext // {}) + {allowPrivilegeEscalation:false})
)
else . end
)
end
'
# -------- Pre-flight checks --------
require_bin kubectl
require_bin jq
# Verify cluster access
kubectl version --short >/dev/null 2>&1 || {
echo "ERROR: kubectl cannot reach the cluster" >&2
exit 1
}
# -------- Discover non-compliant pods and their owners --------
log "Discovering non-compliant Pods and their owners (excluding kube-system, kube-public, kube-node-lease)..."
mapfile -t NONCOMPLIANT_LINES < <(
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| select(.metadata.namespace | test("'"$IGNORE_NAMESPACES_REGEX"'") | not)
| . as $pod
| (.spec.containers // [] + .spec.initContainers // []) as $cs
| [ $cs[]
| (.securityContext.allowPrivilegeEscalation == false) as $ok
| select($ok | not)
] as $bad
| select(($bad | length) > 0)
| .metadata as $m
| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
| "ns=\($m.namespace) kind=\(if $own==null then "Pod" else $own.kind end) name=\(if $own==null then $m.name else $own.name end)"
' \
| sort -u
)
if [ "${#NONCOMPLIANT_LINES[@]}" -eq 0 ]; then
log "No non-compliant Pods found; cluster is already compliant."
else
log "Found ${#NONCOMPLIANT_LINES[@]} owning resources with non-compliant Pods."
fi
# -------- Patch owners --------
for line in "${NONCOMPLIANT_LINES[@]}"; do
ns=$(sed -E 's/^ns=([^ ]+).*/\1/' <<< "$line")
kind=$(sed -E 's/.* kind=([^ ]+).*/\1/' <<< "$line")
name=$(sed -E 's/.* name=([^ ]+).*/\1/' <<< "$line")
# Normalize common controller kinds to kubectl resource types
case "$kind" in
Deployment) res="deployment" ;;
StatefulSet) res="statefulset" ;;
DaemonSet) res="daemonset" ;;
Job) res="job" ;;
CronJob) res="cronjob" ;;
Pod) res="pod" ;;
*) res=$(tr '[:upper:]' '[:lower:]' <<< "$kind") ;;
esac
log "Processing $res/$ns/$name ..."
# Fetch object
if ! kubectl get "$res" "$name" -n "$ns" -o json >/tmp/aescalate-obj.json 2>/dev/null; then
log " WARN: Unable to fetch $res/$ns/$name; skipping."
continue
fi
# Apply appropriate jq patch
if [ "$res" = "pod" ]; then
jq "$jq_pod_patch_filter" /tmp/aescalate-obj.json > /tmp/aescalate-obj-patched.json
else
jq "$jq_patch_filter" /tmp/aescalate-obj.json > /tmp/aescalate-obj-patched.json
fi
# If no change, skip
if diff -q /tmp/aescalate-obj.json /tmp/aescalate-obj-patched.json >/dev/null 2>&1; then
log " No changes needed; already configured."
continue
fi
# Apply patch with server-side apply to be idempotent and schema-safe
if ! kubectl apply -f /tmp/aescalate-obj-patched.json >/dev/null 2>&1; then
log " ERROR: Failed to apply patch to $res/$ns/$name"
continue
fi
log " Patched $res/$ns/$name to set securityContext.allowPrivilegeEscalation=false on all containers."
done
rm -f /tmp/aescalate-obj.json /tmp/aescalate-obj-patched.json 2>/dev/null || true
# -------- Verification --------
log "Waiting briefly for controllers to reconcile pods..."
sleep 10
log "Verifying compliance with benchmark audit command..."
audit_output=$(
kubectl get pods --all-namespaces -o json | jq -r '
[ .items[]
| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| (.spec.nodeName // "") as $node
| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
| ((.spec.containers // []) + (.spec.initContainers // []))[]
| (.securityContext.allowPrivilegeEscalation == false) as $ok
| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ (if $node == "" then "" else " node=\($node)" end)
+ (if $labels == "" then "" else " labels=\($labels)" end)
+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
+ " container=\(.name) image=\(.image)"
+ " allowPrivilegeEscalation=\(if .securityContext.allowPrivilegeEscalation == null then "unset" else .securityContext.allowPrivilegeEscalation end)"
+ " is_compliant=\(if $ok then "true" else "false" end)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
)
if grep -q '^is_compliant=true$' <<< "$audit_output"; then
log "Verification succeeded: all checked Pods are compliant (allowPrivilegeEscalation=false)."
else
log "Verification found remaining non-compliant containers:"
printf '%s\n' "$audit_output"
exit 2
fi
exit 0