Skip to main content

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​

Manual Steps
  1. 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'
  2. 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 Deployment
    kubectl -n NAMESPACE edit deployment DEPLOYMENT_NAME

    In the opened spec, under spec.template.spec, ensure:

    hostPID: false # or remove this line entirely
    hostIPC: false # or remove this line entirely
    hostNetwork: false # or remove this line entirely

    Save and exit; Kubernetes will roll out updated Pods.

  3. 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 spec section in source control to omit these fields or set them to false.

  4. 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.

  5. 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.

  6. 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:

  1. 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)"
][]'
  1. 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
  1. 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'