Minimize The Admission Of Containers Sharing The Host
More Info:
Sharing the host network namespace gives a container access to host network interfaces and local services, bypassing network controls. Restrict it.
Risk Level
High
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List all Pods using
hostNetwork(run on any machine with kubectl access):kubectl get pods --all-namespaces -o jsonpath='{range .items[?(@.spec.hostNetwork==true)]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}' -
For each affected Pod, identify its owning workload (run on any machine with kubectl access). Replace NAMESPACE and POD with real values from step 1:
NAMESPACE=example-namespacePOD=example-podkubectl get pod "$POD" -n "$NAMESPACE" -o jsonpath='{.metadata.ownerReferences}' | jqUse the
kindandnamefields to determine if it is controlled by a Deployment, DaemonSet, StatefulSet, Job, etc., or is a standalone Pod. -
For each standalone Pod that should not use host networking, edit the Pod spec and plan a recreation (run on any machine with kubectl access):
# Export the Pod spec (without cluster-assigned fields)kubectl get pod "$POD" -n "$NAMESPACE" -o yaml \| sed '/^\s*uid:/d;/^\s*resourceVersion:/d;/^\s*selfLink:/d;/^\s*creationTimestamp:/d;/^\s*status:/d' \> "/tmp/${NAMESPACE}-${POD}-pod.yaml"# Edit hostNetwork to false (or remove the field)sed -i 's/hostNetwork: true/hostNetwork: false/' "/tmp/${NAMESPACE}-${POD}-pod.yaml"# Delete and recreate the Pod from the modified manifestkubectl delete pod "$POD" -n "$NAMESPACE"kubectl apply -f "/tmp/${NAMESPACE}-${POD}-pod.yaml" -
For each controller-managed workload (Deployment/DaemonSet/StatefulSet/Job) that should not use host networking, edit the controller spec (run on any machine with kubectl access). Example for a Deployment:
NAMESPACE=example-namespaceDEPLOYMENT=example-deploymentkubectl -n "$NAMESPACE" get deployment "$DEPLOYMENT" -o yaml > "/tmp/${NAMESPACE}-${DEPLOYMENT}.yaml"sed -i 's/hostNetwork: true/hostNetwork: false/' "/tmp/${NAMESPACE}-${DEPLOYMENT}.yaml"kubectl apply -f "/tmp/${NAMESPACE}-${DEPLOYMENT}.yaml"Repeat with
deploymentreplaced bydaemonset,statefulset, orjobas appropriate. -
Add a restrictive admission policy in each user-workload namespace to prevent new
hostNetworkPods (run on any machine with kubectl access). Example using a Kubernetes-nativePodSecurityPolicy-like Gatekeeper constraint is cluster-specific; if you do not have an admission controller already in place, document and implement one via your chosen policy engine (e.g., Kyverno, Gatekeeper) that rejects Pods withspec.hostNetwork: truein user namespaces, with explicit exceptions for justified system workloads. -
Verify no remaining Pods use
hostNetwork(run on any machine with kubectl access):kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | while read -r pod_name pod_namespacedopod_hostnetwork=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostNetwork}' 2>/dev/null)if [ -z "${pod_hostnetwork}" ]; thenpod_hostnetwork="false"fiecho "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: $([ "${pod_hostnetwork}" = "true" ] && echo false || echo true)"done | grep 'is_pod_hostnetwork: true' || echo "All pods compliant (no hostNetwork: true)"
Using kubectl
# 1) Identify pods using hostNetwork (any machine with kubectl access)
kubectl get pods --all-namespaces -o=jsonpath='{range .items[?(@.spec.hostNetwork==true)]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}'
# 2) For each NON‑system namespace with hostNetwork pods, create a restrictive NetworkPolicy or AdmissionPolicy.
# Example: deny hostNetwork in a user namespace "production"
# 2a) (Preferred, if you have a validating admission controller such as Kyverno)
# Save this as deny-hostnetwork-kyverno.yaml and apply it.
cat << 'EOF' > deny-hostnetwork-kyverno.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-hostnetwork
spec:
validationFailureAction: enforce
background: true
rules:
- name: disallow-hostnetwork
match:
any:
- resources:
kinds:
- Pod
namespaces:
- production
validate:
message: "Use of hostNetwork is not allowed in this namespace."
pattern:
spec:
=(hostNetwork): "false"
EOF
kubectl apply -f deny-hostnetwork-kyverno.yaml
# 2b) Example Gatekeeper (OPA) constraint to disallow hostNetwork in a namespace
# (Assumes the corresponding ConstraintTemplate exists in the cluster.)
cat << 'EOF' > k8sdisallowhostnetwork-constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowHostNetwork
metadata:
name: disallow-hostnetwork-production
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- production
EOF
kubectl apply -f k8sdisallowhostnetwork-constraint.yaml
# 3) Remove or edit existing manifests that set hostNetwork: true (any machine with kubectl access)
# Example: export, edit, and reapply one offending pod in a user namespace:
kubectl get pod <POD_NAME> -n <NAMESPACE> -o yaml > /tmp/pod-no-hostnetwork.yaml
# Edit /tmp/pod-no-hostnetwork.yaml:
# - Remove `hostNetwork: true` or change it to `hostNetwork: false`
# - Remove fields not allowed on Pod create (status, metadata.resourceVersion, etc.)
# Then delete and recreate:
kubectl delete pod <POD_NAME> -n <NAMESPACE>
kubectl apply -f /tmp/pod-no-hostnetwork.yaml
# 4) Verification (any machine with kubectl access)
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{" "}{.metadata.name}{" hostNetwork="}{.spec.hostNetwork}{"\n"}{end}' \
| grep "hostNetwork=true" || echo "No pods with hostNetwork=true found"
Automation
#!/usr/bin/env bash
#
# Restrict hostNetwork usage with PodSecurityStandard policies.
# - Labels all non-system namespaces with a high-privilege policy that disallows hostNetwork.
# - Safe to re-run: all operations are idempotent.
#
# Requirements:
# - Run on any machine with kubectl access and cluster-admin privileges.
# - Kubernetes v1.25+ with Pod Security Admission enabled (standard for recent clusters).
set -euo pipefail
# ---------- Configuration ----------
# Namespaces to IGNORE (typically system/control-plane)
IGNORED_NAMESPACES=(
kube-system
kube-public
kube-node-lease
default # remove this if you intentionally run user workloads in "default"
)
# Pod Security Admission labels that prohibit hostNetwork
PSA_LEVEL="restricted"
PSA_VERSION="latest"
# ---------- Helper functions ----------
ns_ignored() {
local ns="$1"
for ign in "${IGNORED_NAMESPACES[@]}"; do
if [[ "$ns" == "$ign" ]]; then
return 0
fi
done
return 1
}
label_namespace_psa() {
local ns="$1"
echo "Ensuring Pod Security Admission labels on namespace: ${ns}"
# Add or update labels; these commands are idempotent
kubectl label namespace "${ns}" \
"pod-security.kubernetes.io/enforce=${PSA_LEVEL}" \
--overwrite >/dev/null
kubectl label namespace "${ns}" \
"pod-security.kubernetes.io/enforce-version=${PSA_VERSION}" \
--overwrite >/dev/null
kubectl label namespace "${ns}" \
"pod-security.kubernetes.io/warn=${PSA_LEVEL}" \
--overwrite >/dev/null
kubectl label namespace "${ns}" \
"pod-security.kubernetes.io/warn-version=${PSA_VERSION}" \
--overwrite >/dev/null
}
# ---------- Main: apply policy ----------
echo "Discovering namespaces..."
ALL_NAMESPACES=$(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
for ns in ${ALL_NAMESPACES}; do
if ns_ignored "${ns}"; then
echo "Skipping ignored namespace: ${ns}"
continue
fi
label_namespace_psa "${ns}"
done
# ---------- Verification ----------
echo
echo "Verifying that no pods are running with spec.hostNetwork=true..."
NON_COMPLIANT=false
# Reuse the benchmark-style audit to list pods with hostNetwork=true
kubectl get pods --all-namespaces -o custom-columns=POD_NAME:.metadata.name,POD_NAMESPACE:.metadata.namespace --no-headers | \
while read -r pod_name pod_namespace; do
pod_hostnetwork=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostNetwork}' 2>/dev/null || true)
if [[ "${pod_hostnetwork}" == "true" ]]; then
NON_COMPLIANT=true
echo "NON-COMPLIANT: pod_name=${pod_name} pod_namespace=${pod_namespace} hostNetwork=true"
fi
done
# Note: the subshell above cannot modify NON_COMPLIANT in the parent directly.
# Run a second, direct check that is easy to parse for automation purposes.
HOSTNETWORK_PODS=$(kubectl get pods --all-namespaces \
-o jsonpath='{range .items[?(@.spec.hostNetwork==true)]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}')
echo
if [[ -z "${HOSTNETWORK_PODS}" ]]; then
echo "Result: COMPLIANT - no pods currently have spec.hostNetwork=true."
else
echo "Result: NON-COMPLIANT - the following pods still have spec.hostNetwork=true:"
echo "${HOSTNETWORK_PODS}"
echo
echo "Note: Pod Security Admission prevents NEW hostNetwork pods in labeled namespaces,"
echo "but existing pods with hostNetwork=true must be manually reviewed/updated or deleted."
fi