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
Remediation
Manual Steps
-
List namespaces that host user workloads (to decide where to apply restriction)
- Run on: any machine with kubectl access
kubectl get namespacesIdentify all non-system namespaces (exclude those starting with kube- and the default system ones used by your provider) where you deploy applications.
-
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 -
If Pod Security Admission is not available in your cluster, create a restrictive policy object (e.g., Kyverno or Gatekeeper) that denies
allowPrivilegeEscalation: truein 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/v1kind: ClusterPolicymetadata:name: deny-allow-privilege-escalationspec:validationFailureAction: enforcebackground: truerules:- name: disallow-allow-privilege-escalationmatch:resources:kinds:- Podnamespaces:- prod-apps# add more namespaces here as neededvalidate: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 -
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 -
Proactively block future changes that reintroduce
allowPrivilegeEscalation: trueby 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 -
Verification (no pods with
allowPrivilegeEscalation: trueremain)- 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'