Containers Should Not Run In Privileged Mode
More Info:​
Verifies no container sets securityContext.privileged=true. A privileged container can compromise the node and every other pod scheduled on it.
Risk Level​
Critical
Address​
Security
Compliance Standards​
- Cloudanix Best Practice
Triage and Remediation​
- Remediation
Remediation​
Manual Steps
-
Identify all privileged containers (any machine with kubectl access):
kubectl get pods --all-namespaces -o json | jq -r '[ .items[]| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)| .metadata as $m| (.spec.nodeName // "") as $node| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own| ((.spec.containers // []) + (.spec.initContainers // []))[]| (.securityContext.privileged // false) as $priv| select($priv == true)| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)+ (if $node == "" then "" else " node=\($node)" end)+ (if $labels == "" then "" else " labels=\($labels)" end)+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)+ " container=\(.name) image=\(.image) privileged=\($priv)"][]' -
For each affected pod, determine the owning controller (any machine with kubectl access). Replace NAMESPACE and POD_NAME:
kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.metadata.ownerReferences}'- If empty: pod is standalone.
- If present: note
.kindand.nameto edit the right controller (e.g., Deployment, DaemonSet, StatefulSet).
-
Edit the owning controller or pod manifest to remove privileged mode (any machine with kubectl access). Examples (run one appropriate command per affected workload):
- Deployment:
kubectl -n NAMESPACE edit deployment DEPLOYMENT_NAME
- DaemonSet:
kubectl -n NAMESPACE edit daemonset DAEMONSET_NAME
- StatefulSet:
kubectl -n NAMESPACE edit statefulset STATEFULSET_NAME
- Standalone Pod:
kubectl -n NAMESPACE edit pod POD_NAME
In the opened manifest, locate each container (including
initContainers) and remove or change:securityContext:privileged: trueto either remove
privilegedentirely or explicitly set:securityContext:privileged: falseIf specific kernel capabilities are required, add only those instead of privileged:
securityContext:capabilities:add:- NET_ADMIN- SYS_TIME - Deployment:
-
If workloads are managed via manifest files or GitOps, update the source manifests (on your config/IaC repo workstation) so changes are not reverted. In each relevant YAML file, apply the same
securityContextedits as in step 3, then re-apply:kubectl apply -f path/to/manifest.yaml -
Allow pods to be recreated with the new spec. For controllers this happens automatically; for standalone pods you may need to delete and recreate them if the spec is immutable (any machine with kubectl access):
kubectl -n NAMESPACE delete pod POD_NAME# Recreate from updated manifest if neededkubectl -n NAMESPACE apply -f updated-pod.yaml -
Verify no containers are running privileged (any machine with kubectl access):
kubectl get pods --all-namespaces -o json | jq -r '[ .items[]| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)| .metadata as $m| (.spec.nodeName // "") as $node| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own| ((.spec.containers // []) + (.spec.initContainers // []))[]| (.securityContext.privileged // false) as $priv| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)+ (if $node == "" then "" else " node=\($node)" end)+ (if $labels == "" then "" else " labels=\($labels)" end)+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)+ " container=\(.name) image=\(.image) privileged=\($priv)"+ " is_compliant=\(if $priv then "false" else "true" end)"] as $rows| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'Ensure the output is either
is_compliant=trueor that every listed line ends withis_compliant=true.
Using kubectl
On any machine with kubectl access:
- Identify the owning controller of the privileged pod
Use the audit output to see owner=.... If you only have the pod name, re-fetch it:
kubectl get pod <POD_NAME> -n <NAMESPACE> -o jsonpath='{.metadata.ownerReferences[*].kind}{" "}{.metadata.ownerReferences[*].name}{" "}{.metadata.ownerReferences[*].uid}{"\n"}'
If this prints a kind such as Deployment/StatefulSet/DaemonSet/Job, you must edit that controller, not the pod.
- Export the current manifest for the owning controller
Example for a Deployment (substitute the correct kind/name/namespace):
kubectl get deployment <DEPLOYMENT_NAME> -n <NAMESPACE> -o yaml > /tmp/<DEPLOYMENT_NAME>.yaml
For other controller types:
kubectl get statefulset <STATEFULSET_NAME> -n <NAMESPACE> -o yaml > /tmp/<STATEFULSET_NAME>.yaml
kubectl get daemonset <DAEMONSET_NAME> -n <NAMESPACE> -o yaml > /tmp/<DAEMONSET_NAME>.yaml
kubectl get job <JOB_NAME> -n <NAMESPACE> -o yaml > /tmp/<JOB_NAME>.yaml
- Edit the manifest to remove privileged mode and (optionally) add specific capabilities
Open the exported file and, for every container (including initContainers) that has securityContext.privileged: true, remove that field or set it to false. If you need specific kernel capabilities, set them explicitly.
Minimal example patch in the pod template:
spec:
template:
spec:
containers:
- name: <CONTAINER_NAME>
image: <IMAGE>
securityContext:
# remove this line if present:
# privileged: true
# optionally add only required capabilities:
capabilities:
add:
- NET_ADMIN
- SYS_TIME
# if any initContainers are privileged, fix them similarly:
initContainers:
- name: <INIT_CONTAINER_NAME>
image: <IMAGE>
securityContext:
# privileged: true # remove this
capabilities:
add:
- NET_ADMIN
Ensure no container has privileged: true under securityContext.
- Apply the updated manifest
kubectl apply -f /tmp/<CONTROLLER_FILE>.yaml
This will cause a rolling update for Deployments/StatefulSets/DaemonSets; pods will be recreated with the new security context.
- (Optional) Delete any existing non-compliant pods to accelerate rollout
For a Deployment:
kubectl delete pod -n <NAMESPACE> -l app=<APP_LABEL>
Adjust the label selector to match your workload.
- Verification
After the rollout completes, rerun the benchmark audit command from any machine with kubectl access:
kubectl get pods --all-namespaces -o json | jq -r '
[ .items[]
| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| (.spec.nodeName // "") as $node
| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
| ((.spec.containers // []) + (.spec.initContainers // []))[]
| (.securityContext.privileged // false) as $priv
| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ (if $node == "" then "" else " node=\($node)" end)
+ (if $labels == "" then "" else " labels=\($labels)" end)
+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
+ " container=\(.name) image=\(.image) privileged=\($priv)"
+ " is_compliant=\(if $priv then "false" else "true" end)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
Confirm that all listed containers show privileged=false and is_compliant=true, or that the output is is_compliant=true.
Automation
#!/usr/bin/env bash
# Purpose: Remove privileged=true from all non-system Pods (and their controllers)
# Platform: GKE (any machine with kubectl access)
set -euo pipefail
# REQUIREMENTS:
# - kubectl configured with sufficient RBAC to get/patch resources cluster-wide
# - jq installed
echo "[INFO] Discovering privileged pods (excluding kube-system, kube-public, kube-node-lease)..."
privileged_pods_json="$(
kubectl get pods --all-namespaces -o json | jq '
.items[]
| select(.metadata.namespace as $n
| ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| . as $pod
| ((.spec.containers // []) + (.spec.initContainers // []))[]
| select(.securityContext.privileged == true)
| {
ns: $pod.metadata.namespace,
pod: $pod.metadata.name,
ownerRefs: ($pod.metadata.ownerReferences // []),
container: .name
}'
)"
if [[ -z "$privileged_pods_json" ]]; then
echo "[INFO] No privileged containers found."
else
echo "[INFO] Found privileged containers. Grouping by owning controller..."
fi
# Build a unique list of owner controllers and standalone pods
# Output format: kind ns name
controllers_file="$(mktemp)"
pods_file="$(mktemp)"
# Iterate over each privileged container record
jq -c '.' <<< "$privileged_pods_json" | while read -r rec; do
ns=$(jq -r '.ns' <<< "$rec")
pod=$(jq -r '.pod' <<< "$rec")
# Determine if pod has a controller
owner_json=$(kubectl get pod "$pod" -n "$ns" -o json | jq -c '
[(.metadata.ownerReferences // [])[] | select(.controller == true)] | first // null
')
if [[ "$owner_json" == "null" ]]; then
echo "Pod $ns/$pod" >> "$pods_file"
else
kind=$(jq -r '.kind' <<< "$owner_json")
name=$(jq -r '.name' <<< "$owner_json")
echo "$kind $ns $name" >> "$controllers_file"
fi
done
# De-duplicate
if [[ -s "$controllers_file" ]]; then
sort -u "$controllers_file" -o "$controllers_file"
fi
if [[ -s "$pods_file" ]]; then
sort -u "$pods_file" -o "$pods_file"
fi
patch_template="$(mktemp)"
cat > "$patch_template" <<'EOF'
{
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "__ALL__",
"securityContext": {
"privileged": false
}
}
],
"initContainers": [
{
"name": "__ALL__",
"securityContext": {
"privileged": false
}
}
]
}
}
}
}
EOF
echo "[INFO] Patching controller objects to remove privileged=true..."
# Helper: patch a controller
patch_controller() {
local kind="$1" ns="$2" name="$3"
# Use jsonpatch to avoid wiping other fields; set privileged to false where present.
# For idempotence, failures (e.g., resource gone) are tolerated.
kubectl get "$kind" "$name" -n "$ns" -o json | \
jq '
(.. | objects | select(has("securityContext")) | select(has("privileged"))) |=
(.privileged = false)
' | kubectl apply -f - >/dev/null 2>&1 || true
}
if [[ -s "$controllers_file" ]]; then
while read -r kind ns name; do
echo "[INFO] Patching controller: kind=$kind ns=$ns name=$name"
patch_controller "$kind" "$ns" "$name"
done < "$controllers_file"
else
echo "[INFO] No controllers to patch."
fi
echo "[INFO] Patching standalone pods to remove privileged=true (will not affect controller-managed pods)."
if [[ -s "$pods_file" ]]; then
while read -r _ns_pod; do
ns="${_ns_pod%% *}"
pod="${_ns_pod##* }"
# Patch the live Pod only. This is best-effort; controller will override on reschedule.
kubectl get pod "$pod" -n "$ns" -o json | \
jq '
(.. | objects | select(has("securityContext")) | select(has("privileged"))) |=
(.privileged = false)
' | kubectl apply -f - >/dev/null 2>&1 || true
echo "[INFO] Patched standalone pod: ns=$ns pod=$pod"
done < <(sed 's/^Pod //g' "$pods_file")
else
echo "[INFO] No standalone pods to patch."
fi
rm -f "$controllers_file" "$pods_file" "$patch_template"
echo "[INFO] Waiting for updated workloads to roll out..."
kubectl rollout status deployment --all --all-namespaces >/dev/null 2>&1 || true
kubectl rollout status daemonset --all --all-namespaces >/dev/null 2>&1 || true
kubectl rollout status statefulset --all --all-namespaces >/dev/null 2>&1 || true
echo "[INFO] Verification: re-running privileged container audit..."
kubectl get pods --all-namespaces -o json | jq -r '
[ .items[]
| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| (.spec.nodeName // "") as $node
| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
| ((.spec.containers // []) + (.spec.initContainers // []))[]
| (.securityContext.privileged // false) as $priv
| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ (if $node == "" then "" else " node=\($node)" end)
+ (if $labels == "" then "" else " labels=\($labels)" end)
+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
+ " container=\(.name) image=\(.image) privileged=\($priv)"
+ " is_compliant=\(if $priv then "false" else "true" end)"
] as $rows
| if ($rows | map(select(. | contains("privileged=true"))) | length) == 0
then "is_compliant=true"
else ($rows[] | select(. | contains("privileged=true")))
end
'