Skip to main content

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

Manual Steps
  1. 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}'
  2. 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-namespace
    POD=example-pod

    kubectl get pod "$POD" -n "$NAMESPACE" -o jsonpath='{.metadata.ownerReferences}' | jq

    Use the kind and name fields to determine if it is controlled by a Deployment, DaemonSet, StatefulSet, Job, etc., or is a standalone Pod.

  3. 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 manifest
    kubectl delete pod "$POD" -n "$NAMESPACE"
    kubectl apply -f "/tmp/${NAMESPACE}-${POD}-pod.yaml"
  4. 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-namespace
    DEPLOYMENT=example-deployment

    kubectl -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 deployment replaced by daemonset, statefulset, or job as appropriate.

  5. Add a restrictive admission policy in each user-workload namespace to prevent new hostNetwork Pods (run on any machine with kubectl access). Example using a Kubernetes-native PodSecurityPolicy-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 with spec.hostNetwork: true in user namespaces, with explicit exceptions for justified system workloads.

  6. 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_namespace
    do
    pod_hostnetwork=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostNetwork}' 2>/dev/null)
    if [ -z "${pod_hostnetwork}" ]; then
    pod_hostnetwork="false"
    fi
    echo "***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