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)"] as $rows| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end' \| grep 'is_compliant=false' -
For each non-compliant pod, determine its owner (run on any machine with kubectl access):
kubectl get pod POD_NAME -n POD_NAMESPACE -o jsonpath='{.metadata.ownerReferences[*].kind}{" "}{.metadata.ownerReferences[*].name}{"\n"}'- If there is no ownerReference, the Pod is standalone and must be fixed directly.
- If there is an owner (Deployment, DaemonSet, StatefulSet, Job, etc.), you must edit that owner resource instead of the Pod.
-
Edit the owning workload (preferred) to remove host namespace sharing (run on any machine with kubectl access):
kubectl -n POD_NAMESPACE edit DEPLOYMENT_OR_DAEMONSET_NAMEIn the opened manifest:
- Locate
.spec.template.spec.hostPID,.spec.template.spec.hostIPC, and.spec.template.spec.hostNetwork. - Set each present field to
false:or delete the lines entirely so they are omitted. Save and exit; Kubernetes will recreate pods from this workload. Be aware this may restart application pods and briefly disrupt traffic.spec:template:spec:hostPID: falsehostIPC: falsehostNetwork: false
- Locate
-
If the Pod is standalone (no controller), edit or recreate it from a manifest (run on any machine with kubectl access):
- If you have a manifest in version control, update it so the pod spec does not set these fields (omit them or set to
false):Then:spec:hostPID: falsehostIPC: falsehostNetwork: falsekubectl apply -f PATH_TO_UPDATED_MANIFEST.yaml - If there is no manifest, export, edit, and re-create:
This will delete and recreate the pod; expect a restart and potential brief downtime.kubectl get pod POD_NAME -n POD_NAMESPACE -o yaml > /tmp/pod-fixed.yamlsed -i '/hostPID:/d;/hostIPC:/d;/hostNetwork:/d' /tmp/pod-fixed.yaml# remove runtime-only fieldssed -i '/^ resourceVersion:/d;/^ uid:/d;/^ creationTimestamp:/d;/^ selfLink:/d;/^ managedFields:/,/^[^ ]/d' /tmp/pod-fixed.yamlsed -i '/status:/,$d' /tmp/pod-fixed.yamlkubectl delete pod POD_NAME -n POD_NAMESPACEkubectl apply -f /tmp/pod-fixed.yaml
- If you have a manifest in version control, update it so the pod spec does not set these fields (omit them or set to
-
Repeat steps 2–4 for all pods reported as
is_compliant=false, ensuring all updated specs havehostPID,hostIPC, andhostNetworkeither omitted or explicitly set tofalse. -
Verify remediation (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.hostPID // false) as $hostPID| (.spec.hostIPC // false) as $hostIPC| (.spec.hostNetwork // false) as $hostNet| "ns=\($m.namespace) name=\($m.name) hostPID=\($hostPID) hostIPC=\($hostIPC) hostNetwork=\($hostNet)"+ " is_compliant=\(if ($hostPID or $hostIPC or $hostNet) then "false" else "true" end)"] as $rows| if ($rows | map(select(test("is_compliant=false"))) | length) == 0then "is_compliant=true"else $rows[]end'Confirm the output is
is_compliant=trueand that no lines showis_compliant=false.
Using kubectl
On any machine with kubectl access:
- Identify offending pods and their controllers (exclude system namespaces per the 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
| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
| (.spec.hostPID // false) as $hostPID
| (.spec.hostIPC // false) as $hostIPC
| (.spec.hostNetwork // false) as $hostNet
| select($hostPID or $hostIPC or $hostNet)
| "\($m.namespace) \($m.name) \((if $own == null then "Pod" else $own.kind end))"
][]'
- For each owning controller (Deployment, DaemonSet, StatefulSet, Job, CronJob, or bare Pod), export its manifest:
# Example for a Deployment
kubectl get deployment <deployment-name> -n <namespace> -o yaml > /tmp/<deployment-name>.yaml
- Edit the manifest to remove or explicitly set the host namespace fields under
spec.template.spec(orspecfor a bare Pod):
spec:
template:
spec:
# Remove these if present, or set them to false:
hostPID: false
hostIPC: false
hostNetwork: false
For bare Pods (not managed by a controller):
spec:
# Remove these if present, or set them to false:
hostPID: false
hostIPC: false
hostNetwork: false
- Apply the updated manifest:
kubectl apply -f /tmp/<deployment-name>.yaml
If you had to delete and recreate a bare Pod:
kubectl delete pod <pod-name> -n <namespace>
kubectl apply -f /tmp/<pod-name>.yaml
- Verification (re-run the audit and confirm all non-system pods are
is_compliant=true):
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
| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
+ " 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'
Automation
#!/usr/bin/env bash
#
# Remediation: Ensure pods do not share host namespaces (hostPID/hostIPC/hostNetwork)
# Scope: Any machine with kubectl access to the OKE cluster
#
# Behavior:
# - Scans all namespaces except kube-system, kube-public, kube-node-lease
# - For pods with hostPID/hostIPC/hostNetwork == true:
# * If controlled by a workload (Deployment/DaemonSet/StatefulSet/ReplicaSet/Job/CronJob):
# - Patch the controller’s pod template to set those fields to false
# - Delete the non-compliant pods so the controller recreates them
# * If not controlled by a workload:
# - Patches the pod in place to set those fields to false
# - Safe to re-run: patches are idempotent; deleting managed pods lets controllers reconcile
# - Verifies compliance at the end using the authoritative audit command
set -euo pipefail
# REQUIREMENTS:
# - kubectl configured to point at the target OKE cluster
# - jq installed
# Helper: run kubectl with stable output (no colors, etc.)
k() {
kubectl "$@"
}
echo "Scanning for non-compliant pods (sharing host namespaces)..."
NON_COMPLIANT_JSON=$(k 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.hostPID // false) as $hostPID
| (.spec.hostIPC // false) as $hostIPC
| (.spec.hostNetwork // false) as $hostNet
| select($hostPID or $hostIPC or $hostNet)
')
if [[ -z "${NON_COMPLIANT_JSON}" ]]; then
echo "No non-compliant pods found. Cluster already compliant."
else
echo "Non-compliant pods detected. Applying fixes..."
# Process each non-compliant pod
echo "${NON_COMPLIANT_JSON}" | jq -c '.' | while read -r POD_JSON; do
NS=$(jq -r '.metadata.namespace' <<< "${POD_JSON}")
NAME=$(jq -r '.metadata.name' <<< "${POD_JSON}")
HOSTPID=$(jq -r '(.spec.hostPID // false) | tostring' <<< "${POD_JSON}")
HOSTIPC=$(jq -r '(.spec.hostIPC // false) | tostring' <<< "${POD_JSON}")
HOSTNET=$(jq -r '(.spec.hostNetwork // false) | tostring' <<< "${POD_JSON}")
echo "Processing pod ${NS}/${NAME} (hostPID=${HOSTPID}, hostIPC=${HOSTIPC}, hostNetwork=${HOSTNET})"
# Detect controlling ownerReference (if any)
OWNER_KIND=$(jq -r '[.metadata.ownerReferences // [] | .[] | select(.controller)] | first.kind // ""' <<< "${POD_JSON}")
OWNER_NAME=$(jq -r '[.metadata.ownerReferences // [] | .[] | select(.controller)] | first.name // ""' <<< "${POD_JSON}")
# Patch payload to disable host namespaces
PATCH='{"spec":{"hostPID":false,"hostIPC":false,"hostNetwork":false}}'
if [[ -n "${OWNER_KIND}" && -n "${OWNER_NAME}" ]]; then
echo " -> Pod is controlled by ${OWNER_KIND}/${OWNER_NAME}. Patching controller template."
# Determine patch path depending on controller kind
case "${OWNER_KIND}" in
Deployment|StatefulSet|DaemonSet|ReplicaSet)
TEMPLATE_PATH="spec.template.spec"
;;
Job)
TEMPLATE_PATH="spec.template.spec"
;;
CronJob)
# For CronJob v1: spec.jobTemplate.spec.template.spec
TEMPLATE_PATH="spec.jobTemplate.spec.template.spec"
;;
*)
echo " !! Unsupported owner kind ${OWNER_KIND}. Skipping controller patch; patching pod directly."
TEMPLATE_PATH=""
;;
esac
if [[ -n "${TEMPLATE_PATH}" ]]; then
# Build a strategic merge patch targeting the pod template
CONTROLLER_PATCH=$(jq -n --arg path "${TEMPLATE_PATH}" '
($path | split(".") ) as $p
| {"spec":{}} as $obj
| reduce range(0; ($p|length)) as $i (
$obj;
if $i == 0 then
.[$p[$i]] //= {}
elif $i == ($p|length - 1) then
path($p) |= . + {"hostPID":false,"hostIPC":false,"hostNetwork":false}
else
path($p[0:$i+1]) //= {}
end
)
')
echo " -> Patching ${OWNER_KIND}/${NS}/${OWNER_NAME}..."
k -n "${NS}" patch "${OWNER_KIND,,}" "${OWNER_NAME}" --type=merge -p "${CONTROLLER_PATCH}" >/dev/null
echo " -> Deleting non-compliant pod so controller recreates it..."
k -n "${NS}" delete pod "${NAME}" --wait=false >/dev/null || true
else
echo " -> Patching pod ${NS}/${NAME} directly..."
k -n "${NS}" patch pod "${NAME}" --type=merge -p "${PATCH}" >/dev/null || true
fi
else
echo " -> Pod has no controlling owner. Patching pod directly."
k -n "${NS}" patch pod "${NAME}" --type=merge -p "${PATCH}" >/dev/null || true
fi
done
fi
echo "Verification: re-running compliance check..."
k 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 | map(select(. | contains("is_compliant=false"))) | length) == 0
then "is_compliant=true"
else $rows[]
end
'