Minimize Admission Containers AllowPrivilegeEscalation
More Info:
Do not generally permit containers to be run with the allowPrivilegeEscalation flag set to true. Allowing this right can lead to a process running a container getting more rights than it started with.
Risk Level
High
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List all pods with
allowPrivilegeEscalation=true(any machine with kubectl access)kubectl get pods --all-namespaces -o json \| jq -r '.items[]| . as $pod| .spec.containers[]| select(.securityContext.allowPrivilegeEscalation == true)| "NAMESPACE=\($pod.metadata.namespace) POD=\($pod.metadata.name) CONTAINER=\(.name)"' -
Decide which workloads are allowed an exception (manual review)
For each listed pod/container:- Determine if it is a user workload vs. a system/infra component.
- For user workloads, strongly prefer no privilege escalation.
- Only allow escalation if there is a documented, reviewed need (e.g., a process that must gain extra Linux capabilities after start).
- For system/infra workloads, prefer to run them in dedicated namespaces with a clearly documented exception policy.
-
Create or update a baseline policy in each user-workload namespace (any machine with kubectl access)
Example using aPodSecurityPolicy-style Kyverno policy (adjust to your chosen admission controller/tooling):cat << 'EOF' | kubectl apply -f -apiVersion: kyverno.io/v1kind: ClusterPolicymetadata:name: disallow-privilege-escalationspec:validationFailureAction: Enforcebackground: truerules:- name: disallow-privilege-escalationmatch:any:- resources:kinds:- Podnamespaces:- "user-namespace-1"- "user-namespace-2"validate:message: "Containers must not enable allowPrivilegeEscalation."pattern:spec:containers:- securityContext:allowPrivilegeEscalation: "false"EOFReplace
user-namespace-1,user-namespace-2with the actual namespaces that host user workloads. If you use PSA/PSP, configure the equivalent “restricted” behavior that forbids privilege escalation. -
Update existing workload manifests to disable privilege escalation (any machine with kubectl access)
For each non-exempt Deployment/StatefulSet/DaemonSet/Job in user namespaces, patch containers to setallowPrivilegeEscalation: false:# example for a single Deploymentkubectl -n user-namespace-1 patch deployment my-app \--type='json' \-p='[{"op": "add","path": "/spec/template/spec/containers/0/securityContext","value": {"allowPrivilegeEscalation": false}}]'Adjust
namespace,deployment, container index, and, if needed, merge with any existingsecurityContextinstead of overwriting it. -
Handle justified exceptions explicitly (any machine with kubectl access)
- For workloads you decided must keep
allowPrivilegeEscalation: true, place them in dedicated namespaces and/or label them, and update your admission policy to match on those namespaces/labels and allow the setting. - Document each exception: purpose, owner, review date, and risk acceptance.
- For workloads you decided must keep
-
Verify no pods are running with
allowPrivilegeEscalation=true(except approved exceptions) (any machine with kubectl access)kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespacedokubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json \| jq -c '.spec.containers[]' | while read -r containerdocontainer_name=$(echo ${container} | jq -r '.name')container_allowprivesc=$(echo ${container} | jq -r '.securityContext.allowPrivilegeEscalation' | sed -e 's/null/notset/g')if [ "${container_allowprivesc}" = "false" ] || [ "${container_allowprivesc}" = "notset" ]; thenecho "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: true"elseecho "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: false"fidonedoneInvestigate any
is_compliant: falseentries and ensure they are either corrected or explicitly approved exceptions.
Using kubectl
# 1) Create a baseline Pod Security restricted policy in each user-workload namespace
# (run on any machine with kubectl access)
# Example: apply to one namespace "my-app-namespace"
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: my-app-namespace
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
EOF
# For existing namespaces, patch them to enforce the restricted profile
kubectl label namespace my-app-namespace \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/warn-version=latest \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/audit-version=latest \
--overwrite
# 2) Optionally, add an explicit ValidatingAdmissionPolicy to reject allowPrivilegeEscalation=true
# (Kubernetes 1.26+ with validaton admission policies enabled)
# Create the ValidatingAdmissionPolicy
cat << 'EOF' | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: deny-allow-privilege-escalation
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "!(has(object.spec.containers) && object.spec.containers.exists(c, has(c.securityContext) && c.securityContext.allowPrivilegeEscalation == true))"
message: "Containers must not set securityContext.allowPrivilegeEscalation to true."
- expression: "!(has(object.spec.initContainers) && object.spec.initContainers.exists(c, has(c.securityContext) && c.securityContext.allowPrivilegeEscalation == true))"
message: "Init containers must not set securityContext.allowPrivilegeEscalation to true."
EOF
# Bind the policy only to user-workload namespaces (example: my-app-namespace)
cat << 'EOF' | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: deny-allow-privilege-escalation-binding
spec:
policyName: deny-allow-privilege-escalation
validationActions: ["Deny"]
matchResources:
namespaceSelector:
matchLabels:
security.kubernetes.io/user-workload: "true"
EOF
# Label user-workload namespaces so the binding applies to them
kubectl label namespace my-app-namespace security.kubernetes.io/user-workload=true --overwrite
# 3) Verification (run on any machine with kubectl access)
# Re-run the original audit; no lines should end with "is_compliant: false"
kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespace
do
kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container
do
container_name=$(echo ${container} | jq -r '.name')
container_allowprivesc=$(echo ${container} | jq -r '.securityContext.allowPrivilegeEscalation' | sed -e 's/null/notset/g')
if [ "${container_allowprivesc}" = "false" ] || [ "${container_allowprivesc}" = "notset" ]; then
echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: true"
else
echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: false"
fi
done
done
Automation
#!/usr/bin/env bash
#
# Enforce: Minimize Admission Containers allowPrivilegeEscalation
# Scope: Any machine with kubectl access to the cluster
#
# Approach:
# - Create/patch a restrictive policy per namespace (where user workloads run)
# - Policy denies containers with securityContext.allowPrivilegeEscalation=true
# - Policy allows pods where allowPrivilegeEscalation is false or not set
#
# NOTE:
# - This script assumes:
# * You are using Kubernetes >= 1.25 with Pod Security Admission (PSA), OR
# * You can enforce admission via a ValidatingWebhookConfiguration/Policy engine.
# - Because this benchmark control is MANUAL, there is no single mandatory mechanism.
# This script uses built‑in Pod Security Admission labels with the "restricted" profile,
# which explicitly disallows privilege escalation by default.
#
# Operational impact:
# - Applying the "restricted" PSA level/versions on namespaces may cause admission
# failures for future pod creations in those namespaces if they violate restricted policy.
#
# Idempotency:
# - Namespace labels are applied with "kubectl label --overwrite", so re-running is safe.
#
# REQUIREMENTS:
# - kubectl installed and configured (KUBECONFIG or in-cluster config)
# - jq installed
#
# USAGE:
# ./enforce_no_priv_escalation.sh # target all namespaces EXCEPT system ones
# ./enforce_no_priv_escalation.sh ns1 ns2 ... # target specific namespaces only
set -euo pipefail
# -------- Configuration --------
# System / control namespaces to skip by default
SYSTEM_NAMESPACES=(
kube-system
kube-public
kube-node-lease
local-path-storage
ingress-nginx
cert-manager
tigera-operator
)
# PSA level/profile and version to enforce
PSA_LEVEL="restricted"
PSA_VERSION="latest"
# -------- Helper functions --------
is_system_namespace() {
local ns="$1"
for s in "${SYSTEM_NAMESPACES[@]}"; do
if [[ "$ns" == "$s" ]]; then
return 0
fi
done
return 1
}
log() {
printf '%s\n' "$*" >&2
}
# -------- Namespace selection --------
if [[ "$#" -gt 0 ]]; then
TARGET_NAMESPACES=("$@")
else
# All namespaces except the system ones
mapfile -t TARGET_NAMESPACES < <(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
fi
if [[ "${#TARGET_NAMESPACES[@]}" -eq 0 ]]; then
log "No target namespaces found."
exit 0
fi
# -------- Apply Pod Security Admission labels --------
for ns in "${TARGET_NAMESPACES[@]}"; do
if is_system_namespace "$ns"; then
log "Skipping system namespace: $ns"
continue
fi
log "Enforcing PSA '${PSA_LEVEL}' on namespace: $ns"
# Apply pod-security.kubernetes.io/enforce and version labels.
kubectl label namespace "$ns" \
"pod-security.kubernetes.io/enforce=${PSA_LEVEL}" \
"pod-security.kubernetes.io/enforce-version=${PSA_VERSION}" \
--overwrite >/dev/null
# Optional: also set 'warn' and 'audit' labels for easier detection/visibility
kubectl label namespace "$ns" \
"pod-security.kubernetes.io/warn=${PSA_LEVEL}" \
"pod-security.kubernetes.io/warn-version=${PSA_VERSION}" \
"pod-security.kubernetes.io/audit=${PSA_LEVEL}" \
"pod-security.kubernetes.io/audit-version=${PSA_VERSION}" \
--overwrite >/dev/null
done
# -------- Verification --------
# Re-run the benchmark audit and show only NON-COMPLIANT pods (if any).
# This uses the exact logic from the finding.
log ""
log "Verification: scanning for containers with allowPrivilegeEscalation=true ..."
log "Only non-compliant containers (is_compliant: false) will be shown."
kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | \
while read -r pod_name pod_namespace; do
kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | \
jq -c '.spec.containers[]' | while read -r container; do
container_name=$(echo "${container}" | jq -r '.name')
container_allowprivesc=$(echo "${container}" | jq -r '.securityContext.allowPrivilegeEscalation' | sed -e 's/null/notset/g')
if [ "${container_allowprivesc}" = "false" ] || [ "${container_allowprivesc}" = "notset" ]; then
:
else
echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_allowprivesc: ${container_allowprivesc} is_compliant: false"
fi
done
done
log ""
log "If no lines with 'is_compliant: false' are printed above, the cluster is compliant with respect to existing Pods."
log "New Pods in labeled namespaces will be denied if they attempt to set allowPrivilegeEscalation=true under the restricted profile."