Skip to main content

Minimize The Admission Of Containers Sharing The Host

More Info:

Containers sharing the host network namespace can access node-level network interfaces and bypass network controls. Restrict admission of hostNetwork containers via namespace policies.

Risk Level

High

Address

Security

Compliance Standards

  • CIS EKS

Triage and Remediation

Remediation

Manual Steps
  1. Identify pods currently using hostNetwork (any machine with kubectl access):

    kubectl get pods --all-namespaces -o json \
    | jq -r '.items[] | select(.spec.hostNetwork == true) | "\(.metadata.namespace) \(.metadata.name)"'
  2. For each affected namespace that should host only user workloads, create or update a Baseline Pod Security admission policy that disallows hostNetwork (any machine with kubectl access). Example for namespace my-namespace:

    kubectl label namespace my-namespace pod-security.kubernetes.io/enforce=baseline --overwrite
    kubectl label namespace my-namespace pod-security.kubernetes.io/enforce-version=latest --overwrite

    If you need finer control (e.g., allow for specific system workloads), instead create a custom admission policy (ValidatingAdmissionPolicy/PolicyBinding, Gatekeeper, or Kyverno) that rejects pods with spec.hostNetwork: true except for explicitly allowed service accounts.

  3. Review exceptions before tightening policies (any machine with kubectl access):

    kubectl get pods -A -o wide \
    | awk 'NR==1 || $0 ~ /hostNetwork=true/ {print}'

    For any pod that legitimately requires hostNetwork (e.g., CNI components), ensure it runs in a dedicated “system” namespace with an explicit exception in your admission policy, or is excluded by your policy’s namespace/label selectors.

  4. Update workload manifests to remove hostNetwork: true where not strictly required (any machine with kubectl access). For each affected pod/deployment:

    # Example: edit a deployment to remove hostNetwork
    kubectl -n my-namespace edit deploy my-app

    In the editor, remove or set:

    spec:
    template:
    spec:
    hostNetwork: false
  5. Redeploy any workloads that previously relied on hostNetwork only after confirming they function correctly behind normal Kubernetes networking and network policies (any machine with kubectl access). Use your CI/CD or:

    kubectl -n my-namespace rollout restart deploy my-app
  6. Verification (any machine with kubectl access):

    kubectl get pods --all-namespaces -o json \
    | jq -r 'if any(.items[]?; .spec.hostNetwork == true) then "HOSTNETWORK_FOUND" else "NO_HOSTNETWORK" end'

    Confirm the output is:

    NO_HOSTNETWORK
Using kubectl
# 1) Create a baseline policy that denies hostNetwork by default
# Run on: any machine with kubectl access

cat << 'EOF' > deny-hostnetwork-admission.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: deny-hostnetwork
annotations:
policies.kyverno.io/title: Deny Host Network
policies.kyverno.io/category: Security
policies.kyverno.io/description: >-
Block creation of Pods that set spec.hostNetwork=true.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: deny-hostnetwork
match:
any:
- resources:
kinds:
- Pod
namespaces:
- "*"
exclude:
any:
- resources:
namespaces:
- kube-system
- kube-public
- kube-node-lease
validate:
message: "Use of hostNetwork is not allowed in this namespace."
pattern:
spec:
=(hostNetwork): "false"
EOF

kubectl apply -f deny-hostnetwork-admission.yaml

Note: The above uses Kyverno as an example admission controller. If Kyverno (or a similar admission controller) is not installed, install and configure one first; kubectl alone cannot enforce admission without such a controller.

# 2) (Optional) Namespace-specific policy allowing exceptions if needed
# Replace <namespace-with-user-workloads> with an actual namespace name

cat << 'EOF' > deny-hostnetwork-namespace.yaml
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: deny-hostnetwork
namespace: <namespace-with-user-workloads>
spec:
validationFailureAction: Enforce
background: true
rules:
- name: deny-hostnetwork-in-namespace
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Use of hostNetwork is not allowed in this namespace."
pattern:
spec:
=(hostNetwork): "false"
EOF

# Apply for a specific namespace:
kubectl apply -f deny-hostnetwork-namespace.yaml
# 3) Clean up any existing hostNetwork pods in user namespaces if appropriate
# Review first:
kubectl get pods --all-namespaces -o json \
| jq -r '.items[] | select(.spec.hostNetwork == true) | "\(.metadata.namespace) \(.metadata.name)"'

# After deciding which user workloads should be changed, edit or patch each Pod/Deployment/etc.
# Example for a Deployment in a user namespace:
kubectl -n <user-namespace> edit deployment <deployment-name>
# and manually remove or set:
# hostNetwork: false
# under spec.template.spec
# 4) Verification (same logic as the audit)
# Run on: any machine with kubectl access

kubectl get pods --all-namespaces -o json \
| jq -r 'if any(.items[]?; .spec.hostNetwork == true) then "HOSTNETWORK_FOUND" else "NO_HOSTNETWORK" end'
Automation
#!/usr/bin/env bash
set -euo pipefail

# Purpose:
# - For every non-system namespace, ensure a policy exists that denies pods
# using hostNetwork.
# - Uses a Kyverno ClusterPolicy; idempotent and safe to re-run.
#
# Prerequisites:
# - Run on any machine with kubectl access and sufficient RBAC to create
# cluster-wide policies (ClusterPolicy).

# 1) Check access
kubectl auth can-i create clusterpolicies.kyverno.io >/dev/null 2>&1 || {
echo "ERROR: This script requires permission to create Kyverno ClusterPolicies." >&2
exit 1
}

# 2) Ensure Kyverno is installed (simple presence check)
if ! kubectl get ns kyverno >/dev/null 2>&1; then
echo "ERROR: Namespace 'kyverno' not found. Install Kyverno admission controller first." >&2
exit 1
fi

# 3) Apply a ClusterPolicy that denies hostNetwork pods in all non-system namespaces
# This single policy satisfies the requirement "Add policies to each namespace
# in the cluster which has user workloads to restrict the admission of hostNetwork containers."
cat <<'EOF' | kubectl apply -f -
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: deny-hostnetwork-pods
annotations:
policies.kyverno.io/title: Deny hostNetwork Pods
policies.kyverno.io/category: Security
policies.kyverno.io/severity: high
policies.kyverno.io/description: >
Deny Pods that request hostNetwork in all non-system namespaces.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: deny-hostnetwork
match:
any:
- resources:
kinds:
- Pod
namespaces:
- "*"
exclude:
any:
- resources:
namespaces:
- kube-system
- kube-public
- kube-node-lease
- kyverno
# Add any other infrastructure or system namespaces here if needed
validate:
message: "Use of hostNetwork is not allowed in this namespace."
pattern:
spec:
hostNetwork: "false"
EOF

echo "ClusterPolicy 'deny-hostnetwork-pods' applied."

# 4) Verification

echo "Verifying policy existence..."
if ! kubectl get clusterpolicy deny-hostnetwork-pods -o yaml >/dev/null 2>&1; then
echo "ERROR: ClusterPolicy 'deny-hostnetwork-pods' not found after apply." >&2
exit 1
fi

echo "Verifying that new hostNetwork pods are rejected in a test namespace..."

# Create a temporary test namespace (non-system)
TEST_NS="hostnetwork-deny-test"
kubectl get ns "${TEST_NS}" >/dev/null 2>&1 || kubectl create ns "${TEST_NS}" >/dev/null

# Try to create a pod with hostNetwork: true (should be denied)
set +e
kubectl -n "${TEST_NS}" apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: test-hostnetwork-denied
spec:
hostNetwork: true
containers:
- name: pause
image: registry.k8s.io/pause:3.9
EOF
APPLY_RC=$?
set -e

if [ "${APPLY_RC}" -eq 0 ]; then
echo "ERROR: Test pod with hostNetwork was admitted; policy is not effective." >&2
# Clean up in case it was actually created
kubectl -n "${TEST_NS}" delete pod test-hostnetwork-denied --ignore-not-found >/dev/null 2>&1 || true
exit 1
fi

echo "Policy enforcement verified: hostNetwork pod creation was rejected."

# 5) Final cluster-wide check for existing hostNetwork pods (informational)
echo "Current cluster state (existing pods may still use hostNetwork until recreated):"
kubectl get pods --all-namespaces -o json \
| jq -r 'if any(.items[]?; .spec.hostNetwork == true) then "HOSTNETWORK_FOUND" else "NO_HOSTNETWORK" end'