Skip to main content

Minimize Admission Containers With Allow Privilege

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

  • 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)
  • 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

Manual Steps
  1. List namespaces that host user workloads (to decide where to apply restriction)

    • Run on: any machine with kubectl access
    kubectl get namespaces

    Identify all non-system namespaces (exclude those starting with kube- and the default system ones used by your provider) where you deploy applications.

  2. For each selected namespace, create a baseline Pod Security admission label set (if not already present) that disallows privilege escalation (Pod Security “restricted” level)

    • Run on: any machine with kubectl access
    • Example for one namespace named prod-apps (repeat for each target namespace, adjusting the name):
    kubectl label namespace prod-apps \
    pod-security.kubernetes.io/enforce=restricted \
    pod-security.kubernetes.io/enforce-version=latest \
    pod-security.kubernetes.io/audit=restricted \
    pod-security.kubernetes.io/audit-version=latest \
    pod-security.kubernetes.io/warn=restricted \
    pod-security.kubernetes.io/warn-version=latest \
    --overwrite
  3. If Pod Security Admission is not available in your cluster, create a restrictive policy object (e.g., Kyverno or Gatekeeper) that denies allowPrivilegeEscalation: true in target namespaces

    • Run on: any machine with kubectl access
    • Example: Kyverno ClusterPolicy (save as /tmp/deny-allow-priv-escalation.yaml, then apply):
    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
    name: deny-allow-privilege-escalation
    spec:
    validationFailureAction: enforce
    background: true
    rules:
    - name: disallow-allow-privilege-escalation
    match:
    resources:
    kinds:
    - Pod
    namespaces:
    - prod-apps
    # add more namespaces here as needed
    validate:
    message: "Containers must not set securityContext.allowPrivilegeEscalation to true."
    pattern:
    spec:
    containers:
    - =(securityContext):
    =(allowPrivilegeEscalation): "false"

    Apply it:

    kubectl apply -f /tmp/deny-allow-priv-escalation.yaml
  4. Update existing workload manifests in target namespaces so that containers cannot request privilege escalation

    • Run on: any machine with kubectl access
    • Export and edit a workload (example Deployment in prod-apps):
    kubectl -n prod-apps get deploy myapp -o yaml > /tmp/myapp-deploy.yaml
    • In /tmp/myapp-deploy.yaml, under each container, ensure:
    securityContext:
    allowPrivilegeEscalation: false
    • Apply the updated manifest:
    kubectl apply -f /tmp/myapp-deploy.yaml
  5. Proactively block future changes that reintroduce allowPrivilegeEscalation: true by placing the namespace-level restriction under version control and reviewing any exception requests

    • Run on: any machine with kubectl access
    • Export namespace config (for Git/IaC management):
    kubectl get namespace prod-apps -o yaml > prod-apps-namespace.yaml
  6. Verification (no pods with allowPrivilegeEscalation: true remain)

    • Run on: any machine with kubectl access
    kubectl get pods --all-namespaces -o json | \
    jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.allowPrivilegeEscalation == true) then "ALLOWPRIVILEGEESCALTION_FOUND" else "NO_ALLOWPRIVILEGEESCALATION" end'
Using kubectl
# 1) Create a baseline Pod Security restricted policy (applies to all namespaces)
# Run on: any machine with kubectl access
cat << 'EOF' | kubectl apply -f -
apiVersion: policy/v1
kind: PodSecurityPolicy
metadata:
name: restricted-no-priv-esc
labels:
app.kubernetes.io/part-of: cis-hardening
pod-security.kubernetes.io/enforce: "restricted"
spec:
privileged: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
fsGroup:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
readOnlyRootFilesystem: false
EOF

If your cluster does not support PodSecurityPolicy (PSP is removed in newer Kubernetes/EKS versions), use a ValidatingAdmissionPolicy or a policy engine like Gatekeeper/Kyverno instead. Below is an example with ValidatingAdmissionPolicy (Kubernetes ≥ 1.30 or EKS with this feature enabled).

# 2) Create a ValidatingAdmissionPolicy to deny allowPrivilegeEscalation=true
cat << 'EOF' | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: deny-privilege-escalation
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE","UPDATE"]
resources: ["pods"]
validations:
- expression: "!(has(object.spec) && object.spec != null && object.spec.containers.exists(c, has(c.securityContext) && has(c.securityContext.allowPrivilegeEscalation) && c.securityContext.allowPrivilegeEscalation == true))"
message: "Containers must not set securityContext.allowPrivilegeEscalation=true"
EOF

# 3) Bind this policy to all user namespaces (example: all non-system namespaces)
# Adjust the namespaces: list only namespaces with user workloads
kubectl get ns --no-headers | awk '$1 !~ /^(kube-system|kube-public|kube-node-lease)$/ {print $1}' | while read ns; do
cat << EOF | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: deny-privilege-escalation-${ns}
spec:
policyName: deny-privilege-escalation
validationActions: [ "Deny" ]
matchResources:
namespaceSelector:
matchLabels:
cis/priv-escalation-protected: "true"
EOF
kubectl label namespace "${ns}" cis/priv-escalation-protected=true --overwrite
done
# 4) (Optional but recommended) Fix existing pods that violate the policy
# This will restart Deployments/DaemonSets/StatefulSets with corrected securityContext
# Adjust workloads manually where needed.
# Example for a single deployment in namespace 'app-namespace':
kubectl -n app-namespace patch deployment my-deployment \
--type='json' \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/securityContext","value":{"allowPrivilegeEscalation":false}}]'
# 5) Verification (same as audit, run on any machine with kubectl access)
kubectl get pods --all-namespaces -o json | \
jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.allowPrivilegeEscalation == true) then "ALLOWPRIVILEGEESCALTION_FOUND" else "NO_ALLOWPRIVILEGEESCALATION" end'
Automation
#!/usr/bin/env bash
set -euo pipefail

# Purpose:
# - For every namespace that contains user workloads (i.e., any pod),
# create/patch a namespace-scoped PodSecurityPolicy-style restriction
# using Kyverno to deny allowPrivilegeEscalation=true.
# - Idempotent and safe to re-run.
#
# Requirements:
# - Run on any machine with kubectl access and Kyverno installed in the cluster.
# - kubectl must be configured with cluster-admin privileges.

# 1. Discover namespaces that have user workloads (pods)
echo "Discovering namespaces with pods..."
NS_LIST=$(kubectl get pods --all-namespaces --no-headers 2>/dev/null | awk '{print $1}' | sort -u)

if [[ -z "${NS_LIST}" ]]; then
echo "No namespaces with pods were found. Nothing to do."
exit 0
fi

# 2. Create Kyverno ClusterPolicy (global) to deny allowPrivilegeEscalation=true,
# but scoped by namespace selector based on label:
# security.kubernetes.io/allow-privilege-escalation=false
#
# This policy is created once and reused by all namespaces that opt in
# via the label.
cat <<'EOF' | kubectl apply -f -
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-allow-privilege-escalation
spec:
validationFailureAction: Enforce
background: true
rules:
- name: disallow-allow-privilege-escalation
match:
any:
- resources:
kinds:
- Pod
namespaces:
- "*"
selector:
matchLabels:
security.kubernetes.io/allow-privilege-escalation: "false"
validate:
message: "Containers must not set securityContext.allowPrivilegeEscalation=true."
pattern:
spec:
containers:
- =(securityContext):
=(allowPrivilegeEscalation): "false"
=(initContainers):
- =(securityContext):
=(allowPrivilegeEscalation): "false"
EOF

echo "ClusterPolicy 'disallow-allow-privilege-escalation' ensured."

# 3. Label each namespace with workloads to opt in to the restriction.
for ns in ${NS_LIST}; do
# Skip kube-system and kyverno namespaces by default, as they often run privileged components.
if [[ "${ns}" == "kube-system" ]] || [[ "${ns}" == "kyverno" ]]; then
echo "Skipping system namespace: ${ns}"
continue
fi

echo "Labeling namespace ${ns} with security.kubernetes.io/allow-privilege-escalation=false"
kubectl label namespace "${ns}" \
security.kubernetes.io/allow-privilege-escalation=false \
--overwrite
done

# 4. Verification
# - Run the benchmark audit command.
# - Note: existing pods that already have allowPrivilegeEscalation=true
# will not be changed by admission control; they must be redeployed
# with corrected securityContext.
echo
echo "Verification: checking for pods with allowPrivilegeEscalation=true..."
kubectl get pods --all-namespaces -o json | \
jq -r 'if any(.items[]?.spec.containers[]?; .securityContext?.allowPrivilegeEscalation == true) then "ALLOWPRIVILEGEESCALTION_FOUND" else "NO_ALLOWPRIVILEGEESCALATION" end'

Additional Reading: