Skip to main content

Minimize Admission Containers Wishing Host Network Namespace

More Info:

Do not generally permit containers to be run with the hostNetwork flag set to true.

Risk Level

High

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. List pods using hostNetwork (discovery)
    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. Identify user workload namespaces that allow hostNetwork (review)
    Run on: any machine with kubectl access
    Review which of the above pods are in user workload namespaces (exclude kube-system and other control-plane/system namespaces). For each user namespace you decide should not allow hostNetwork, proceed to the next step. For namespaces that legitimately need hostNetwork (e.g., CNI, ingress), document the exception instead of applying the restriction.

  3. Create a baseline admission policy to deny hostNetwork (example Kyverno)
    Run on: any machine with kubectl access
    If you use Kyverno, create a ClusterPolicy denying hostNetwork for pods in selected namespaces (replace <NAMESPACE1>,<NAMESPACE2> with a comma‑separated list of target namespaces):

    cat << 'EOF' | kubectl apply -f -
    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
    name: deny-hostnetwork
    spec:
    validationFailureAction: Enforce
    background: true
    rules:
    - name: deny-hostnetwork
    match:
    resources:
    kinds:
    - Pod
    namespaces:
    - NAMESPACE1
    - NAMESPACE2
    validate:
    message: "Use of hostNetwork is not allowed in this namespace."
    pattern:
    spec:
    hostNetwork: "false"
    EOF

    Adapt the tool and syntax to your existing admission controller (e.g., OPA Gatekeeper, ValidatingAdmissionPolicy) but keep the same intent: match Pods in selected namespaces and deny when spec.hostNetwork == true.

  4. Handle existing hostNetwork pods in restricted namespaces (migration)
    Run on: any machine with kubectl access
    For each affected namespace from step 2, list existing hostNetwork pods again and plan replacement without hostNetwork:

    NAMESPACE=<TARGET_NAMESPACE>
    kubectl get pods -n "${NAMESPACE}" -o jsonpath='{range .items[?(@.spec.hostNetwork==true)]}{.metadata.name}{"\n"}{end}'

    For each listed workload (Deployment/DaemonSet/StatefulSet/Job, etc.), edit its manifest to remove or set hostNetwork: false under spec.template.spec, then apply:

    # Example for a single deployment
    kubectl get deploy <DEPLOYMENT_NAME> -n "${NAMESPACE}" -o yaml > /tmp/deploy.yaml
    # Edit /tmp/deploy.yaml: delete or change 'hostNetwork: true' to 'hostNetwork: false'
    kubectl apply -f /tmp/deploy.yaml

    Replace pods created directly (not via controllers) by deleting and recreating them with hostNetwork disabled.

  5. Optionally scope exceptions per-namespace (refine policy)
    Run on: any machine with kubectl access
    For namespaces where specific workloads must use hostNetwork, narrow your policy instead of blanket denying the namespace. Example Kyverno rule that allows hostNetwork only for pods with a specific label:

    cat << 'EOF' | kubectl apply -f -
    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
    name: deny-hostnetwork-except-labeled
    spec:
    validationFailureAction: Enforce
    background: true
    rules:
    - name: deny-hostnetwork-except-labeled
    match:
    resources:
    kinds:
    - Pod
    namespaces:
    - NAMESPACE_REQUIRING_EXCEPTIONS
    preconditions:
    any:
    - key: "{{ request.object.metadata.labels.hostnetwork-allowed || 'false' }}"
    operator: Equals
    value: "false"
    validate:
    message: "Use of hostNetwork is not allowed unless explicitly labeled."
    pattern:
    spec:
    hostNetwork: "false"
    EOF

    Then explicitly label exception pods/workloads:

    kubectl label deploy <DEPLOYMENT_NAME> -n NAMESPACE_REQUIRING_EXCEPTIONS hostnetwork-allowed=true
  6. Verification (re-run the audit with expectation)
    Run on: any machine with kubectl access
    Re-run the audit and confirm no pods with spec.hostNetwork=true exist in namespaces where you applied restrictions:

    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"
    echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: true"
    else
    echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostnetwork: ${pod_hostnetwork} is_compliant: false"
    fi
    done

    For user namespaces meant to be restricted, ensure all lines show is_pod_hostnetwork: false and is_compliant: true; any remaining non-compliant pods should be investigated or migrated.

Using kubectl
# 1) Create a baseline policy to DENY pods that use hostNetwork in a workload namespace
# Run on: any machine with kubectl access

# Example: apply to a single namespace (replace <NAMESPACE> with your workload namespace)
cat << 'EOF' | kubectl apply -f -
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:
- "<NAMESPACE>"
validate:
message: "Use of hostNetwork is not allowed in this namespace."
pattern:
spec:
hostNetwork: "false"
EOF
# 2) (Alternative) If you use Kubernetes-native ValidatingAdmissionPolicy (v1.30+),
# first create the policy definition (cluster-wide), then a Binding for each namespace.

# 2a) Create the ValidatingAdmissionPolicy (cluster-wide)
cat << 'EOF' | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: deny-hostnetwork
spec:
paramKind: {}
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "!(has(object.spec.hostNetwork) && object.spec.hostNetwork == true)"
message: "Use of hostNetwork is not allowed."
EOF
EOF

# 2b) Bind the policy to a specific namespace (replace <NAMESPACE> with your workload namespace)
cat << 'EOF' | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: deny-hostnetwork-<NAMESPACE>
spec:
policyName: deny-hostnetwork
validationActions: ["Deny"]
matchResources:
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: "<NAMESPACE>"
EOF
# 3) Label additional workload namespaces and re‑use the binding pattern as needed
# Run for each workload namespace:
kubectl label namespace PROD-NAMESPACE kubernetes.io/metadata.name=PROD-NAMESPACE --overwrite

# Then apply a binding for that namespace (edit manifest above to use PROD-NAMESPACE)
# 4) Verification: list all pods that still have hostNetwork=true
# 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
Automation
#!/usr/bin/env bash
#
# Purpose:
# Minimize admission of Pods using hostNetwork by applying
# a default-deny hostNetwork Pod Security Admission label
# to all non-system namespaces that run user workloads.
#
# This script is idempotent and safe to re-run.
#
# Run on:
# Any machine with kubectl access and current-context set
# to the target cluster, with cluster-admin privileges.
#
# Notes:
# - This uses Kubernetes built‑in Pod Security Admission (PSA)
# by labeling namespaces with:
# pod-security.kubernetes.io/enforce=restricted
# pod-security.kubernetes.io/enforce-version=latest
# - PSA "restricted" level forbids hostNetwork, hostPID, hostIPC, etc.
# - System / control-plane namespaces are excluded.

set -euo pipefail

# Fail early if kubectl is not available
if ! command -v kubectl >/dev/null 2>&1; then
echo "kubectl not found in PATH. Install kubectl and ensure it can access the cluster."
exit 1
fi

# Ensure we can talk to the cluster
if ! kubectl version --short >/dev/null 2>&1; then
echo "kubectl cannot connect to the cluster using the current context."
exit 1
fi

echo "Discovering namespaces..."

# Define a list of namespaces to exclude (system / infrastructure)
EXCLUDE_NAMESPACES=(
kube-system
kube-public
kube-node-lease
default # adjust if 'default' is used for user workloads
kube-monitoring
istio-system
metallb-system
cert-manager
local-path-storage
)

# Build a regex for exclusion
EXCLUDE_REGEX="$(IFS='|'; echo "${EXCLUDE_NAMESPACES[*]}")"

# Get target namespaces: all except excluded ones
TARGET_NAMESPACES=$(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' \
| grep -Ev "^(${EXCLUDE_REGEX})$" || true)

if [ -z "${TARGET_NAMESPACES}" ]; then
echo "No target namespaces found (non-system namespaces). Nothing to do."
else
echo "Target namespaces (will enforce PSA 'restricted'):"
echo "${TARGET_NAMESPACES}"
fi

# Apply Pod Security Admission labels to each target namespace
for ns in ${TARGET_NAMESPACES}; do
echo "Configuring Pod Security Admission labels on namespace: ${ns}"

# Apply "restricted" enforce level; keep "latest" version for future-proofing
kubectl label namespace "${ns}" \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
--overwrite
done

echo "Namespace labeling completed."

###############################################################################
# VERIFICATION
###############################################################################
echo
echo "Verifying that hostNetwork Pods are disallowed in target namespaces..."

VERIFY_TMP_DIR=$(mktemp -d)
trap 'rm -rf "${VERIFY_TMP_DIR}"' EXIT

for ns in ${TARGET_NAMESPACES}; do
echo
echo "Testing namespace: ${ns}"

# Create a simple Pod manifest that requests hostNetwork
cat > "${VERIFY_TMP_DIR}/test-hostnetwork-pod.yaml" <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: psa-hostnetwork-test
spec:
hostNetwork: true
containers:
- name: pause
image: registry.k8s.io/pause:3.9
EOF

# Try to create the Pod; it SHOULD FAIL if enforcement is working
set +e
CREATE_OUTPUT=$(kubectl apply -n "${ns}" -f "${VERIFY_TMP_DIR}/test-hostnetwork-pod.yaml" 2>&1)
CREATE_RC=$?
set -e

if [ ${CREATE_RC} -eq 0 ]; then
echo "WARNING: hostNetwork Pod was ADMITTED in namespace '${ns}'."
echo " This indicates Pod Security Admission is not enforcing 'restricted' as expected."
echo " Output:"
echo " ${CREATE_OUTPUT}"
# Clean up the test pod if it was created
kubectl delete pod psa-hostnetwork-test -n "${ns}" --ignore-not-found >/dev/null 2>&1 || true
else
echo "OK: hostNetwork Pod was REJECTED in namespace '${ns}' as desired."
echo " Admission error:"
echo " ${CREATE_OUTPUT}" | sed 's/^/ /'
fi
done

echo
echo "Verification complete."
echo "Note: Existing running Pods with hostNetwork=true are not automatically evicted."
echo " They must be reviewed and replaced/removed manually if inappropriate."

Additional Reading: