Minimize The Admission Privileged Containers
More Info:
Do not generally permit containers to be run with the securityContext.privileged flag set to true.
Risk Level
Critical
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CIS GKE
- 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)
- NIS2 Directive
- 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 all pods using privileged containers (evidence gathering)
- Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o json \| jq -r '.items[]| select((.spec.containers[]? | select(.securityContext.privileged == true))or(.spec.initContainers[]? | select(.securityContext.privileged == true)))| [.metadata.namespace, .metadata.name]| @tsv'Save the output as your initial inventory of privileged pods.
-
Identify how privileged pods are being admitted (exemptions, policies, namespaces)
- Run on: any machine with kubectl access
a) Check Pod Security admission labels on each namespace:
kubectl get ns --show-labelsb) If using Pod Security Policies (legacy) in this cluster, list them and inspect the
privilegedfield and related rules:kubectl get psp -o yamlReview which namespaces/service accounts are allowed to use PSPs that permit
privileged: true(via RBACusebindings). - Run on: any machine with kubectl access
-
Classify privileged usage as required or unnecessary
For each pod from step 1, review its purpose and image, and determine ifsecurityContext.privileged: trueis strictly required (e.g., node-level monitoring, CNI, storage, low‑level debugging) or if finer-grained capabilities / other mechanisms would suffice.- To inspect the full pod spec for a specific pod:
kubectl -n <namespace> get pod <pod-name> -o yamlDecide pod-by-pod whether to (a) keep privileged, (b) remove privileged, or (c) replace with narrower capabilities (e.g.,
capabilities.add). -
Remove or reduce privileged settings where not strictly needed
- Run on: any machine with kubectl access (editing manifests in your Git/IaC system is strongly recommended; use
kubectl editonly for temporary/urgent changes).
For each workload determined unnecessary to be privileged:
a) Locate and edit its Deployment/StatefulSet/DaemonSet/Job manifest.
b) In each relevant container spec, either remove theprivilegedsetting or set it to false:
securityContext:privileged: falsec) Apply the updated manifest:
kubectl apply -f <updated-manifest>.yamlNote: Changing controller specs will recreate pods; plan for any resulting restarts or brief disruption.
- Run on: any machine with kubectl access (editing manifests in your Git/IaC system is strongly recommended; use
-
Tighten admission controls for future pods
- If using Pod Security (recommended on GKE):
- Set stricter labels (e.g.,
pod-security.kubernetes.io/enforce=restricted) on namespaces that should not allow privileged pods:
kubectl label namespace <ns-name> \pod-security.kubernetes.io/enforce=restricted \--overwrite - Set stricter labels (e.g.,
- If still using Pod Security Policies and they’re enabled in this cluster, ensure the PSPs bound to general workloads omit
.spec.privilegedor set it tofalse, and restrictuseof any PSP that allows privileged containers only to specific service accounts/namespaces that require it.
- If using Pod Security (recommended on GKE):
-
Re-verify and document accepted exceptions
- Run on: any machine with kubectl access
Re-run the evidence command from step 1 to confirm only intentionally approved privileged workloads remain:
kubectl get pods --all-namespaces -o json \| jq -r '.items[]| select((.spec.containers[]? | select(.securityContext.privileged == true))or(.spec.initContainers[]? | select(.securityContext.privileged == true)))| [.metadata.namespace, .metadata.name]| @tsv'For any remaining privileged pods, record: namespace, controller (Deployment/DaemonSet/etc.), business owner, and justification, and ensure their namespaces/policies are explicitly configured as controlled exceptions.
- Run on: any machine with kubectl access
Using kubectl
# 1) List all namespaces
# Run on: any machine with kubectl access
kubectl get namespaces
Review the list to understand scope; the following commands should typically focus on non‑system namespaces (exclude kube-system, kube-public, etc., unless you intentionally run workloads there).
# 2) Find pods that request privileged containers (current state)
# Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{" ")
{.metadata.name}{"\t"}{range .spec.containers[*]}{.name}{"="}
{.securityContext.privileged}{"; "}{end}{"\n"}{end}' | grep "=true"
Problem indication: Any line returned here shows a pod with at least one container running with securityContext.privileged=true. Each match requires human review to decide if it is strictly necessary.
# 3) Inspect full pod spec for a specific result from step 2
# Replace <namespace> and <pod-name> from the previous output
kubectl get pod <pod-name> -n <namespace> -o yaml
Review under .spec.containers[].securityContext and .spec.initContainers[].securityContext:
privileged: trueindicates a privileged container. Decide whether the privilege is justified; if not, the workload manifest must be changed and redeployed.
# 4) Find workloads that can create privileged containers via templates
# (Deployments, StatefulSets, DaemonSets, ReplicaSets, Jobs, CronJobs)
kubectl get deploy,sts,ds,rs,job,cronjob --all-namespaces -o json | \
jq -r '
.items[] |
.kind as $k |
.metadata.namespace as $ns |
.metadata.name as $n |
((.spec.template.spec.containers // []) + (.spec.template.spec.initContainers // []))[]? |
select(.securityContext.privileged == true) |
"\($k)\t\($ns)\t\($n)\t" + (.name + "=privileged=true")
'
Problem indication: Any line printed identifies a controller whose pod template includes securityContext.privileged: true. These controllers will continue to create privileged pods until their manifests are changed.
# 5) Check for default PodSecurity admission behavior that might allow privileged pods
# (GKE uses PodSecurity Admission by default in recent versions)
kubectl get ns -o json | \
jq -r '
.items[] |
.metadata.name as $ns |
(.metadata.labels["pod-security.kubernetes.io/enforce"] // "none") as $enforce |
(.metadata.labels["pod-security.kubernetes.io/audit"] // "none") as $audit |
(.metadata.labels["pod-security.kubernetes.io/warn"] // "none") as $warn |
"\($ns)\tenforce=\($enforce)\taudit=\($audit)\twarn=\($warn)"
'
Problem indication:
- Namespaces with
enforce=privileged(or with no enforce label, depending on your cluster defaults) may admit privileged pods. - Namespaces intended to be restricted should typically use
enforce=baselineorrestricted. Any more‑permissive configuration should be reviewed.
# 6) (If PSPs are still enabled in your cluster) list any PodSecurityPolicies
kubectl get podsecuritypolicies.policy -o yaml
Review each PSP:
- A PSP with
.spec.privileged: trueor missing.spec.privilegedbut effectively permitting privileged pods (depending on other fields and RBAC bindings) is a potential concern. - Validate which service accounts / users are bound to such PSPs with RBAC:
kubectl get clusterrole,role -A -o yaml | grep -n "podsecuritypolicies" -n
Problem indication: Any PSP that allows privileged pods and is bound (via Role/ClusterRole + RoleBinding/ClusterRoleBinding) to general workloads or broad principals (e.g., system:serviceaccounts:<namespace>) should be carefully reviewed.
Automation
#!/usr/bin/env bash
# Run on: any machine with kubectl access
# Purpose: Report where privileged pods/containers are allowed or in use
set -euo pipefail
echo "== 1. Namespaces with any privileged pods (current workload state) =="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| . as $pod
| ($pod.spec.containers + ($pod.spec.initContainers // []))[]
| select(.securityContext.privileged == true)
| "\($pod.metadata.namespace)\t\($pod.metadata.name)\t\(.name)\tPRIVILEGED=true"
' 2>/dev/null | sort || echo "jq not installed or no privileged containers found."
echo
echo "Explanation: Any line here shows a running pod with at least one container running privileged."
echo "These should be reviewed; most workloads should not appear here."
echo
echo "== 2. Pods whose spec *requests* privileged containers (even if not running now) =="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| . as $pod
| ($pod.spec.containers + ($pod.spec.initContainers // []))[]
| select(.securityContext.privileged == true)
| "\($pod.metadata.namespace)\t\($pod.metadata.name)\t\(.name)\tPRIVILEGED_REQUESTED=true"
' 2>/dev/null | sort || echo "jq not installed or no pods request privileged containers."
echo
echo "Explanation: Any line indicates a pod spec that explicitly sets securityContext.privileged: true."
echo "These are candidates for redesign or exception documentation."
echo
echo "== 3. Namespaces that allow privileged pods via PodSecurity admission labels (v1.25+) =="
kubectl get ns --show-labels | awk '
NR==1 { print; next }
/pod-security.kubernetes.io/ { print }
'
echo
echo "Explanation:"
echo "- For clusters using Pod Security Admission, look at labels:"
echo " pod-security.kubernetes.io/enforce"
echo " pod-security.kubernetes.io/audit"
echo " pod-security.kubernetes.io/warn"
echo "- Namespaces labeled with \"privileged\" or a custom policy that permits privileged containers"
echo " effectively allow privileged workloads. Those namespaces should be explicitly justified."
echo
echo "== 4. (If present) PodSecurityPolicy objects that allow privileged containers =="
if kubectl api-resources 2>/dev/null | grep -q "^podsecuritypolicies"; then
kubectl get podsecuritypolicies.policy -o json \
| jq -r '
.items[]
| select(.spec.privileged == true)
| "\(.metadata.name)\tPRIVILEGED_ALLOWED=true"
' 2>/dev/null | sort || echo "jq not installed or no PSPs allow privileged=true."
else
echo "PodSecurityPolicy API not present in this cluster (likely removed in v1.25+)."
fi
echo
echo "Explanation: Any PSP listed here has spec.privileged: true and allows privileged pods."
echo "Review which namespaces/service accounts can use these PSPs (RBAC bindings) and justify or tighten."
echo
echo "== How to interpret problematic output =="
echo "- Any non-empty output in sections 1 or 2 indicates pods/containers explicitly running or requesting"
echo " privileged=true. Each should be reviewed; only tightly controlled infrastructure components"
echo " should need this."
echo "- Namespaces in section 3 with pod-security.* labels set to \"privileged\" (or equivalent) allow"
echo " privileged workloads; they should be minimized and well-documented."
echo "- Any PSP listed in section 4 indicates a cluster-wide policy object that permits privileged pods."
echo " Its usage and scope should be carefully reviewed."
Problem indicators:
- Lines in sections 1 or 2: pods/containers with
PRIVILEGED=true/PRIVILEGED_REQUESTED=true. - Namespaces in section 3 with Pod Security labels set to
privileged(or a custom policy allowing privileged). - PSPs in section 4 with
PRIVILEGED_ALLOWED=true.
These findings require human review and policy decisions; they cannot be safely remediated in a fully automated way.