Pods Should Not Share Host Namespaces
More Info:
Verifies no pod sets hostPID, hostIPC or hostNetwork. Sharing a host namespace breaks the isolation boundary between the pod and the node.
Risk Level
Critical
Address
Security
Compliance Standards
- Cloudanix Best Practice
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify non-compliant Pods (run on 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.hostPID // false) as $hostPID| (.spec.hostIPC // false) as $hostIPC| (.spec.hostNetwork // false) as $hostNet| "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)+ " hostPID=\($hostPID) hostIPC=\($hostIPC) hostNetwork=\($hostNet)"+ " is_compliant=\(if ($hostPID or $hostIPC or $hostNet) then "false" else "true" end)"][]' | grep 'is_compliant=false' -
For each non-compliant Pod that is controlled by a higher-level object (Deployment, DaemonSet, StatefulSet, Job, etc.), edit the controller manifest to remove host namespace sharing (run on any machine with kubectl access):
# Example for a Deploymentkubectl -n NAMESPACE edit deployment DEPLOYMENT_NAMEIn the opened spec, under
spec.template.spec, ensure:hostPID: false # or remove this line entirelyhostIPC: false # or remove this line entirelyhostNetwork: false # or remove this line entirelySave and exit; Kubernetes will roll out updated Pods.
-
For non-compliant standalone Pods (no controller ownerReferences), patch the Pod spec so it no longer shares host namespaces (run on any machine with kubectl access):
kubectl -n NAMESPACE patch pod POD_NAME --type='merge' -p '{"spec": {"hostPID": false,"hostIPC": false,"hostNetwork": false}}'If the Pod definition is created from a manifest you maintain, also update that manifest’s
specsection in source control to omit these fields or set them tofalse. -
If any non-compliant Pods are part of critical EKS add-ons you manage yourself (for example, custom CNI or monitoring agents), review whether they truly require hostPID/hostIPC/hostNetwork. Where feasible, redesign them to use Kubernetes primitives (e.g., downward API, privileges limited to required resources) instead of host namespace sharing, then update their controller manifests as in step 2.
-
If a Pod genuinely requires a host namespace (for example, a node-level troubleshooting DaemonSet), document the justification, namespace, controller name, and fields used (hostPID/hostIPC/hostNetwork) in your security exceptions register and consider restricting its use to dedicated admin-only namespaces and nodes.
-
Verify all Pods now comply (run on 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.hostPID // false) as $hostPID| (.spec.hostIPC // false) as $hostIPC| (.spec.hostNetwork // false) as $hostNet| "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)+ " hostPID=\($hostPID) hostIPC=\($hostIPC) hostNetwork=\($hostNet)"+ " is_compliant=\(if ($hostPID or $hostIPC or $hostNet) then "false" else "true" end)"] as $rows| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end' \| grep -q 'is_compliant=false' || echo "is_compliant=true"
Using kubectl
On any machine with kubectl access:
- Identify non-compliant pods (for context)
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.hostPID // false) as $hostPID
| (.spec.hostIPC // false) as $hostIPC
| (.spec.hostNetwork // false) as $hostNet
| select($hostPID or $hostIPC or $hostNet)
| "ns=\($m.namespace) name=\($m.name) hostPID=\($hostPID) hostIPC=\($hostIPC) hostNetwork=\($hostNet)"
][]'
- For each affected pod, edit the owning workload (Deployment, DaemonSet, StatefulSet, etc.) and remove or set the host namespace fields to
false.
Example for a Deployment:
kubectl -n NAMESPACE get deploy DEPLOYMENT_NAME -o yaml > deployment-fixed.yaml
Edit deployment-fixed.yaml so the pod template has:
spec:
template:
spec:
hostPID: false # or remove this line entirely
hostIPC: false # or remove this line entirely
hostNetwork: false # or remove this line entirely
containers:
- name: ...
image: ...
Apply the updated manifest:
kubectl apply -f deployment-fixed.yaml
Repeat similarly for other controllers:
# DaemonSet
kubectl -n NAMESPACE get daemonset DAEMONSET_NAME -o yaml > ds-fixed.yaml
# StatefulSet
kubectl -n NAMESPACE get statefulset STATEFULSET_NAME -o yaml > sts-fixed.yaml
# Job / CronJob
kubectl -n NAMESPACE get job JOB_NAME -o yaml > job-fixed.yaml
kubectl -n NAMESPACE get cronjob CRONJOB_NAME -o yaml > cj-fixed.yaml
Edit each file’s spec.template.spec section to remove or set hostPID, hostIPC, and hostNetwork to false, then:
kubectl apply -f ds-fixed.yaml
kubectl apply -f sts-fixed.yaml
kubectl apply -f job-fixed.yaml
kubectl apply -f cj-fixed.yaml
If a pod is standalone (no controller), recreate it from a manifest without these fields:
kubectl -n NAMESPACE get pod POD_NAME -o yaml > pod-fixed.yaml
# edit: delete metadata fields like uid, resourceVersion, status, etc.,
# and ensure spec.hostPID/spec.hostIPC/spec.hostNetwork are absent or false
kubectl -n NAMESPACE delete pod POD_NAME
kubectl apply -f pod-fixed.yaml
- Verification (same machine with
kubectl):
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.hostPID // false) as $hostPID
| (.spec.hostIPC // false) as $hostIPC
| (.spec.hostNetwork // false) as $hostNet
| select($hostPID or $hostIPC or $hostNet)
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
Automation
#!/usr/bin/env bash
#
# Remediate CBP C1.2: ensure pods do not share host PID/IPC/Network namespaces.
#
# Platform: Amazon EKS
# Scope: any machine with kubectl access and jq installed
#
# Behavior:
# - Finds *namespaced* workloads (Deployments, StatefulSets, DaemonSets, ReplicaSets, ReplicationControllers, Jobs, CronJobs)
# outside kube-system, kube-public, kube-node-lease that set hostPID/hostIPC/hostNetwork=true.
# - Patches them to set these fields to false at the pod template level.
# - Does NOT touch standalone Pods (they are usually not long‑lived controllers).
# - Safe to re-run: patches only set fields explicitly to false.
# - Ends with the benchmark audit command to verify.
set -euo pipefail
# --- configuration -----------------------------------------------------------
# Namespaces to ignore (system components)
IGNORED_NAMESPACES=("kube-system" "kube-public" "kube-node-lease")
# Workload kinds to inspect/patch
WORKLOAD_KINDS=(
deployment
statefulset
daemonset
replicaset
replicationcontroller
job
cronjob
)
# --- helper functions -------------------------------------------------------
is_ignored_ns() {
local ns="$1"
for n in "${IGNORED_NAMESPACES[@]}"; do
if [[ "$ns" == "$n" ]]; then
return 0
fi
done
return 1
}
# --- detection & remediation ------------------------------------------------
echo "Discovering workloads that use hostPID/hostIPC/hostNetwork..."
for kind in "${WORKLOAD_KINDS[@]}"; do
# Get all objects of this kind cluster‑wide in JSON
# We allow failure for kinds that may not exist in some clusters.
if ! all_json=$(kubectl get "$kind" --all-namespaces -o json 2>/dev/null); then
continue
fi
# Extract a compact list: ns, name, kind, and flags from pod template
echo "$all_json" | jq -r '
.items[]?
| .metadata.namespace as $ns
| .metadata.name as $name
| .kind as $kind
| (.spec.template.spec.hostPID // false) as $hostPID
| (.spec.template.spec.hostIPC // false) as $hostIPC
| (.spec.template.spec.hostNetwork // false) as $hostNet
| select($hostPID or $hostIPC or $hostNet)
| "\($ns) \($name) \($kind) \($hostPID) \($hostIPC) \($hostNet)"
' | while read -r ns name kind_read hostPID hostIPC hostNet; do
# Skip ignored namespaces
if is_ignored_ns "$ns"; then
continue
fi
echo "Found non-compliant workload: kind=${kind_read} ns=${ns} name=${name} (hostPID=${hostPID}, hostIPC=${hostIPC}, hostNetwork=${hostNet})"
# Build JSON patch setting any of the three fields that are true to false.
patch_ops=()
if [[ "$hostPID" == "true" ]]; then
patch_ops+=('{"op":"replace","path":"/spec/template/spec/hostPID","value":false}')
fi
if [[ "$hostIPC" == "true" ]]; then
patch_ops+=('{"op":"replace","path":"/spec/template/spec/hostIPC","value":false}')
fi
if [[ "$hostNet" == "true" ]]; then
patch_ops+=('{"op":"replace","path":"/spec/template/spec/hostNetwork","value":false}')
fi
# If any of those fields were previously absent and set via defaults,
# there is nothing to patch; but our selection only included "true".
if [[ "${#patch_ops[@]}" -eq 0 ]]; then
continue
fi
patch_json=$(printf '[%s]\n' "$(IFS=,; echo "${patch_ops[*]}")")
echo "Patching ${kind_read}/${ns}/${name} to set hostPID/hostIPC/hostNetwork to false..."
kubectl patch "$kind" "$name" -n "$ns" --type='json' -p "$patch_json"
done
done
echo "Remediation phase completed."
# --- verification -----------------------------------------------------------
echo
echo "Verifying: running benchmark audit command to confirm compliance..."
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.hostPID // false) as $hostPID
| (.spec.hostIPC // false) as $hostIPC
| (.spec.hostNetwork // false) as $hostNet
| "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)
+ " hostPID=\($hostPID) hostIPC=\($hostIPC) hostNetwork=\($hostNet)"
+ " is_compliant=\(if ($hostPID or $hostIPC or $hostNet) then "false" else "true" end)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'