Skip to main content

Minimize Admission Containers Wishing Share The Host

More Info:

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

Risk Level

High

Address

Security

Compliance Standards

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS Critical Security Controls v8
  • CIS EKS
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • Essential 8
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIS2 Directive
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation

Remediation

Manual Steps
  1. List all namespaces and identify those with user workloads (run on any machine with kubectl access):
kubectl get ns

For each user namespace (for example, prod-apps), proceed with the next steps.

  1. For each user namespace, create or edit a NetworkPolicy-like admission control (e.g., via a ValidatingAdmissionPolicy or external policy engine such as OPA/Gatekeeper or Kyverno) that denies Pods with hostNetwork: true. Example Kyverno policy manifest (save as deny-hostnetwork.yaml and adjust namespaces list):
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-hostnetwork
spec:
validationFailureAction: enforce
rules:
- name: deny-hostnetwork
match:
any:
- resources:
kinds:
- Pod
namespaces:
- prod-apps
- staging-apps
validate:
message: "Using hostNetwork is not allowed in this namespace."
pattern:
spec:
hostNetwork: "false"
  1. Apply the admission policy to the cluster (run on any machine with kubectl access):
kubectl apply -f deny-hostnetwork.yaml
  1. Identify any existing Pods already running with hostNetwork: true (run on any machine with kubectl access):
kubectl get pods --all-namespaces -o json | jq -r '.items[] | select(.spec.hostNetwork == true) | "\(.metadata.namespace) \(.metadata.name)"'
  1. For each listed Pod that is not part of system/control-plane components, edit its manifest (Deployment, StatefulSet, DaemonSet, or Pod) to remove or set hostNetwork: false, then apply the change (example for a Deployment in namespace prod-apps):
kubectl -n prod-apps edit deploy <deployment-name>

In the opened editor, remove the hostNetwork: true line or change it to hostNetwork: false, save, and exit. Kubernetes will roll out updated Pods without host networking.

  1. Verification (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'

Ensure the output is:

NO_HOSTNETWORK
Using kubectl
# 1) Create a baseline restricting hostNetwork in user namespaces
# Run on: any machine with kubectl access

cat << 'EOF' > deny-hostnetwork-psp.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: deny-hostnetwork
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
spec:
privileged: false
hostNetwork: false
hostPID: false
hostIPC: false
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
EOF

kubectl apply -f deny-hostnetwork-psp.yaml
# 2) Create a ClusterRole to use this PodSecurityPolicy

cat << 'EOF' > deny-hostnetwork-psp-clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: use-deny-hostnetwork-psp
rules:
- apiGroups:
- policy
resources:
- podsecuritypolicies
verbs:
- use
resourceNames:
- deny-hostnetwork
EOF

kubectl apply -f deny-hostnetwork-psp-clusterrole.yaml
# 3) Bind the ClusterRole in each user-workload namespace
# Replace <user-namespace> with the actual namespace name.
# Repeat for each user namespace.

NAMESPACE="<user-namespace>"

cat << EOF > psp-deny-hostnetwork-rb-${NAMESPACE}.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: use-deny-hostnetwork-psp
namespace: ${NAMESPACE}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: use-deny-hostnetwork-psp
subjects:
- kind: Group
name: system:serviceaccounts:${NAMESPACE}
apiGroup: rbac.authorization.k8s.io
EOF

kubectl apply -f psp-deny-hostnetwork-rb-${NAMESPACE}.yaml
# 4) Optional: Block hostNetwork with a namespace-level NetworkPolicy (for CNIs that honor it)
# This does not replace PSP / admission control but adds defense-in-depth.

NAMESPACE="<user-namespace>"

cat << EOF > default-deny-hostnetwork-netpol-${NAMESPACE}.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-hostnetwork-traffic
namespace: ${NAMESPACE}
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: []
egress: []
EOF

kubectl apply -f default-deny-hostnetwork-netpol-${NAMESPACE}.yaml
# 5) Verification: confirm no pods are using hostNetwork
# 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

# This script:
# - Detects namespaces that allow pods with hostNetwork: true
# - Applies a restrictive Pod Security Admission label (restricted:latest)
# to user namespaces to prevent hostNetwork pods
# - Verifies that no such pods exist
#
# Run from: any machine with kubectl access and current-context set to the target cluster.

# Namespaces to NEVER touch (system/control-plane)
SYSTEM_NAMESPACES=(
"kube-system"
"kube-public"
"kube-node-lease"
"default" # adjust/remove if you run user workloads here
"kube-monitoring"
"kube-logging"
"kube-security"
)

join_by() { local IFS="$1"; shift; echo "$*"; }

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

echo "Current status: ${HOSTNETWORK_STATUS}"

# Build jq filter to exclude system namespaces
EXCLUDE_FILTER=""
for ns in "${SYSTEM_NAMESPACES[@]}"; do
if [[ -z "${EXCLUDE_FILTER}" ]]; then
EXCLUDE_FILTER=". != \"${ns}\""
else
EXCLUDE_FILTER="${EXCLUDE_FILTER} and . != \"${ns}\""
fi
done

echo "Identifying candidate user namespaces..."
USER_NAMESPACES=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}' \
| tr ' ' '\n' \
| jq -R 'select(length>0)' \
| jq -r "select(${EXCLUDE_FILTER})")

if [[ -z "${USER_NAMESPACES}" ]]; then
echo "No user namespaces detected (after excluding: $(join_by ',' "${SYSTEM_NAMESPACES[@]}"))."
echo "Nothing to change."
else
echo "User namespaces to enforce Pod Security 'restricted:latest':"
echo "${USER_NAMESPACES}"
fi

# Apply Pod Security Admission labels idempotently
for ns in ${USER_NAMESPACES}; do
echo "Patching namespace '${ns}' with Pod Security restricted:latest labels..."
kubectl label namespace "${ns}" \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/audit-version=latest \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/warn-version=latest \
--overwrite
done

echo "Waiting briefly for admission configuration to take effect..."
sleep 5

echo "Verifying that no pods with hostNetwork: true remain..."
VERIFY_STATUS=$(kubectl get pods --all-namespaces -o json \
| jq -r 'if any(.items[]?; .spec.hostNetwork == true) then "HOSTNETWORK_FOUND" else "NO_HOSTNETWORK" end')

if [[ "${VERIFY_STATUS}" = "NO_HOSTNETWORK" ]]; then
echo "Verification passed: NO_HOSTNETWORK"
exit 0
else
echo "Verification failed: HOSTNETWORK_FOUND"
echo "Details of remaining hostNetwork pods:"
kubectl get pods --all-namespaces -o json \
| jq -r '.items[] | select(.spec.hostNetwork == true) | "\(.metadata.namespace) \(.metadata.name)"'
exit 1
fi

Additional Reading: