Skip to main content

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​

Manual Steps
  1. 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)"'
  2. 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.
  3. Create or update a baseline policy in each user-workload namespace (any machine with kubectl access)
    Example using a PodSecurityPolicy-style Kyverno policy (adjust to your chosen admission controller/tooling):

    cat << 'EOF' | kubectl apply -f -
    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
    name: disallow-privilege-escalation
    spec:
    validationFailureAction: Enforce
    background: true
    rules:
    - name: disallow-privilege-escalation
    match:
    any:
    - resources:
    kinds:
    - Pod
    namespaces:
    - "user-namespace-1"
    - "user-namespace-2"
    validate:
    message: "Containers must not enable allowPrivilegeEscalation."
    pattern:
    spec:
    containers:
    - securityContext:
    allowPrivilegeEscalation: "false"
    EOF

    Replace user-namespace-1, user-namespace-2 with the actual namespaces that host user workloads. If you use PSA/PSP, configure the equivalent “restricted” behavior that forbids privilege escalation.

  4. 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 set allowPrivilegeEscalation: false:

    # example for a single Deployment
    kubectl -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 existing securityContext instead of overwriting it.

  5. 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.
  6. 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_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

    Investigate any is_compliant: false entries 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."

Additional Reading:​