Skip to main content

Minimize The Admission Of Containers With

More Info:

allowPrivilegeEscalation lets a process gain more privileges than its parent. Blocking it limits in-container privilege escalation.

Risk Level

High

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. List 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)
    | "\($pod.metadata.namespace) \($pod.metadata.name) \(.name)"' \
    | sort -u

    Save this list; you will need it to update the workloads.

  2. Review each affected workload and identify its owner (any machine with kubectl access)
    For each <namespace> <pod> <container> from step 1, get the owning controller (Deployment/DaemonSet/StatefulSet/Job/etc.):

    NAMESPACE="<namespace>"
    POD="<pod_name>"

    kubectl get pod "$POD" -n "$NAMESPACE" -o jsonpath='{.metadata.ownerReferences}' | jq

    Use kind/name from .kind and .name to determine which resource/manifest you must change. If there is no ownerReference, the Pod is standalone and must be edited or recreated directly.

  3. Decide policy: namespace-level restriction vs. per-workload hardening (any machine with kubectl access)

    • If your cluster uses Pod Security Admission, consider labeling user namespaces with pod-security.kubernetes.io/enforce=restricted (or equivalent) only after confirming workloads can run without privilege escalation.
    • Otherwise, design or select an admission policy mechanism (e.g., Kyverno, OPA/Gatekeeper, custom admission webhook) that rejects Pods where any container has securityContext.allowPrivilegeEscalation: true or omits securityContext entirely.
      This benchmark control is MANUAL: you must choose the appropriate tooling and exceptions model for your environment; there is no single mandatory configuration.
  4. Harden workload manifests by setting allowPrivilegeEscalation: false (any machine with kubectl access)
    For each affected controller (Deployment, DaemonSet, etc.), edit its manifest (preferably in your Git/IaC repo) to set this on every container and initContainer:

    spec:
    template:
    spec:
    containers:
    - name: <container-name>
    securityContext:
    allowPrivilegeEscalation: false
    # keep or add any other required securityContext fields
    initContainers:
    - name: <init-container-name>
    securityContext:
    allowPrivilegeEscalation: false

    Apply the updated manifest:

    kubectl apply -f <path-to-updated-manifest>.yaml

    For standalone Pods generated without a controller, recreate them from an updated manifest containing the same change.

  5. (Optional) Introduce a rejecting admission policy for future Pods (any machine with kubectl access)
    After confirming critical workloads function with allowPrivilegeEscalation: false, configure your chosen admission controller to:

    • Deny any Pod in user namespaces where any container or initContainer has .securityContext.allowPrivilegeEscalation: true.
    • Optionally also deny if the field is missing, to force explicit false.
      Implement the policy using your platform’s recommended mechanism (e.g., Kyverno, Gatekeeper, PSA labels), then test in a non-production namespace before rolling out cluster-wide.
  6. Verify remediation (any machine with kubectl access)
    Re-run the audit and confirm no containers report 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

    Investigate and adjust any remaining is_compliant: false workloads or explicitly document them as approved exceptions.

Using kubectl

On any machine with kubectl access:

  1. Create a baseline restricted admission policy for each user-workload namespace
    (example for namespace prod-apps; repeat per namespace with user workloads):
kubectl label namespace prod-apps pod-security.kubernetes.io/enforce=restricted --overwrite
kubectl label namespace prod-apps pod-security.kubernetes.io/audit=restricted --overwrite
kubectl label namespace prod-apps pod-security.kubernetes.io/warn=restricted --overwrite

This uses the built-in “restricted” Pod Security Admission level, which disallows privilege escalation by default.

  1. For clusters/namespaces where you cannot (or do not want to) use PSA labels, create an explicit policy object.
    a) If your cluster still supports PodSecurityPolicy (legacy), apply:
# psp-deny-allowprivilegeescalation.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: deny-allow-priv-escalation
spec:
privileged: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
runAsUser:
rule: MustRunAsNonRoot
seLinux:
rule: RunAsAny
fsGroup:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'

Apply it:

kubectl apply -f psp-deny-allowprivilegeescalation.yaml

Then bind it to your workloads’ service accounts (example for namespace prod-apps using the default service account; adjust as needed):

# rb-psp-deny-allowprivilegeescalation.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: use-deny-allow-priv-escalation-psp
namespace: prod-apps
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['deny-allow-priv-escalation']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: use-deny-allow-priv-escalation-psp
namespace: prod-apps
subjects:
- kind: ServiceAccount
name: default
namespace: prod-apps
roleRef:
kind: Role
name: use-deny-allow-priv-escalation-psp
apiGroup: rbac.authorization.k8s.io

Apply it:

kubectl apply -f rb-psp-deny-allowprivilegeescalation.yaml

b) If your cluster uses another admission controller (e.g., Kyverno or OPA/Gatekeeper), define an equivalent rule that denies pods where .spec.containers[*].securityContext.allowPrivilegeEscalation == true. (This part is policy‑engine specific and must be created with its own CRDs; there is no generic kubectl-only object beyond what is shown above.)

  1. Update existing offending pods’ manifests so containers explicitly set allowPrivilegeEscalation: false.
    Example pod spec snippet you should ensure for each container:
spec:
containers:
- name: my-container
image: my-image:tag
securityContext:
allowPrivilegeEscalation: false

Apply updated workload manifests using your normal deployment process, or:

kubectl apply -f <your-updated-workload-manifest>.yaml
  1. Verification (run on any machine with kubectl):
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 the admission of containers with allowPrivilegeEscalation=true
# Scope: Any machine with kubectl access to the cluster
#
# Strategy:
# - For every namespace that is intended for user workloads, create/update a
# Pod Security admission label to enforce the "restricted" policy at
# enforce level. The restricted profile blocks allowPrivilegeEscalation by default.
# - This script is idempotent and safe to re-run.
#
# IMPORTANT:
# - You must adjust the NAMESPACE_FILTER below to match your environment.
# By default, this script targets all namespaces EXCEPT a small set of
# common system namespaces, which is often what "user workloads" means.
# - The benchmark is MANUAL: you must review which namespaces should be
# protected before running this.

set -euo pipefail

# ------------- CONFIGURATION (EDIT TO SUIT YOUR CLUSTER) --------------------

# Regex pattern of namespaces to EXCLUDE from restricted policy enforcement.
# These are assumed to be system/control-plane namespaces.
EXCLUDE_NS_REGEX='^(kube-system|kube-public|kube-node-lease|kube-*|default|istio-system|linkerd|cilium-system|tigera-operator|calico-system|monitoring|logging)$'

# Pod Security Admission label key/value for restricted policy at enforce level.
PSA_ENFORCE_KEY="pod-security.kubernetes.io/enforce"
PSA_ENFORCE_VALUE="restricted"

# ---------------------------------------------------------------------------

# Ensure kubectl is available and we can reach the cluster.
if ! command -v kubectl >/dev/null 2>&1; then
echo "ERROR: kubectl not found in PATH. Install kubectl and ensure it can reach the cluster." >&2
exit 1
fi

if ! kubectl version --request-timeout=5s >/dev/null 2>&1; then
echo "ERROR: Unable to talk to the Kubernetes API with kubectl." >&2
exit 1
fi

echo "Discovering namespaces intended for user workloads..."
# List all namespaces, then filter out excluded/system namespaces.
USER_NS_LIST=$(
kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' \
| grep -Ev "${EXCLUDE_NS_REGEX}" || true
)

if [ -z "${USER_NS_LIST}" ]; then
echo "No user-workload namespaces matched. Nothing to do."
exit 0
fi

echo "Namespaces selected for restricted Pod Security enforcement:"
echo "${USER_NS_LIST}" | sed 's/^/ - /'

# Apply/Update Pod Security Admission labels on each user namespace
for ns in ${USER_NS_LIST}; do
echo "Applying PSA 'restricted' policy to namespace: ${ns}"

# Check existing label value (if any)
current_value=$(kubectl get ns "${ns}" -o jsonpath="{.metadata.labels.${PSA_ENFORCE_KEY}}" 2>/dev/null || true)

if [ "${current_value}" = "${PSA_ENFORCE_VALUE}" ]; then
echo " Namespace ${ns} already has ${PSA_ENFORCE_KEY}=${PSA_ENFORCE_VALUE} (no change)."
continue
fi

if [ -n "${current_value}" ]; then
echo " Updating ${PSA_ENFORCE_KEY} from '${current_value}' to '${PSA_ENFORCE_VALUE}' on namespace ${ns}."
else
echo " Setting ${PSA_ENFORCE_KEY}=${PSA_ENFORCE_VALUE} on namespace ${ns}."
fi

kubectl label ns "${ns}" "${PSA_ENFORCE_KEY}=${PSA_ENFORCE_VALUE}" --overwrite
done

echo
echo "Verification: Pod Security Admission labels in user-workload namespaces"
kubectl get ns ${USER_NS_LIST} -o custom-columns=NAME:.metadata.name,ENFORCE:.metadata.labels.pod-security\\.kubernetes\\.io/enforce

echo
echo "Additional verification of running Pods' allowPrivilegeEscalation values:"
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

echo
echo "NOTE:"
echo "- New Pods in the labeled namespaces must comply with the 'restricted' profile,"
echo " which disallows privilege escalation unless explicitly configured otherwise."
echo "- Existing non-compliant Pods may continue running until they are recreated."