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
Remediation
Manual Steps
- 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.
- 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 asdeny-hostnetwork.yamland adjustnamespaceslist):
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"
- Apply the admission policy to the cluster (run on any machine with kubectl access):
kubectl apply -f deny-hostnetwork.yaml
- 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)"'
- 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 namespaceprod-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.
- 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