Minimize The Admission Of Containers Sharing The Host
More Info:​
Containers sharing the host PID namespace can view and interact with all processes on the node. Restrict admission of hostPID containers via namespace policies.
Risk Level​
High
Address​
Security
Compliance Standards​
- CIS EKS
Triage and Remediation​
- Remediation
Remediation​
Manual Steps
-
List all namespaces that contain user workloads (exclude system namespaces)
Run on: any machine with kubectl accesskubectl get nsDecide which namespaces should be protected (e.g., everything except kube-system, kube-public, kube-node-lease, etc.).
-
For each target namespace, create or update a Pod Security admission label set to
restricted(which forbidshostPID: true)
Run on: any machine with kubectl access
Replace<NAMESPACE>with the actual namespace name:kubectl label namespace <NAMESPACE> \pod-security.kubernetes.io/enforce=restricted \pod-security.kubernetes.io/enforce-version=latest \--overwrite -
Optionally add warnings/audit labels (recommended to catch violations before enforcement)
Run on: any machine with kubectl accesskubectl label namespace <NAMESPACE> \pod-security.kubernetes.io/warn=restricted \pod-security.kubernetes.io/warn-version=latest \pod-security.kubernetes.io/audit=restricted \pod-security.kubernetes.io/audit-version=latest \--overwrite -
For clusters not using Pod Security Admission and instead using an existing Validating/MutatingWebhookPolicy controller (e.g., OPA Gatekeeper, Kyverno), create a policy in that system denying
spec.hostPID: truein the target namespaces.
Run on: any machine with kubectl access
Example Kyverno policy manifest (save asdeny-hostpid.yamland apply):apiVersion: kyverno.io/v1kind: ClusterPolicymetadata:name: disallow-hostpidspec:validationFailureAction: enforcerules:- name: deny-hostpidmatch:any:- resources:kinds:- Podvalidate:message: "Sharing the host PID namespace (hostPID) is not allowed."pattern:spec:hostPID: "false"Apply it:
kubectl apply -f deny-hostpid.yaml -
Review and update existing manifests or Helm charts for user workloads to remove
hostPID: truewhere it is not strictly required.- Search manifests locally:
grep -Rni "hostPID" .
- For any Pod/Deployment/DaemonSet/etc. manifest that contains
hostPID: true, edit the file and set:spec:hostPID: false
Then re-apply:
kubectl apply -f <UPDATED_MANIFEST_FILE> - Search manifests locally:
-
Verification (no pods with
hostPID: trueshould exist)
Run on: any machine with kubectl accesskubectl get pods --all-namespaces -o json | \jq -r 'if any(.items[]?; .spec.hostPID == true) then "HOSTPID_FOUND" else "NO_HOSTPID" end'Confirm the output is:
NO_HOSTPID
Using kubectl
# 1) Create a baseline Pod Security Admission configuration that disallows hostPID
# Run on: any machine with kubectl access
cat << 'EOF' > psa-hostpid-restrict.yaml
apiVersion: policy/v1
kind: PodSecurityPolicy
metadata:
name: restricted-no-hostpid
labels:
app.kubernetes.io/name: restricted-no-hostpid
spec:
privileged: false
hostPID: false
hostIPC: false
hostNetwork: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
fsGroup:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
supplementalGroups:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
EOF
# Apply the PodSecurityPolicy (only works if PSP admission is enabled in your cluster)
kubectl apply -f psa-hostpid-restrict.yaml
# 2) Bind the PSP to each namespace with user workloads
# Replace <NAMESPACE> with each user-workload namespace name.
# Run on: any machine with kubectl access
NAMESPACE=<NAMESPACE>
kubectl create role psp-restricted-no-hostpid \
--verb=use \
--resource=podsecuritypolicies \
--resource-name=restricted-no-hostpid \
-n "$NAMESPACE"
kubectl create rolebinding psp-restricted-no-hostpid-binding \
--role=psp-restricted-no-hostpid \
--group=system:serviceaccounts:"$NAMESPACE" \
-n "$NAMESPACE"
# 3) (Alternative / in addition) Enforce Pod Security Admission labels to restrict hostPID
# For each user-workload namespace, label with at least "restricted" policy
# Run on: any machine with kubectl access
NAMESPACE=<NAMESPACE>
kubectl label namespace "$NAMESPACE" \
pod-security.kubernetes.io/enforce=restricted \
--overwrite
kubectl label namespace "$NAMESPACE" \
pod-security.kubernetes.io/audit=restricted \
--overwrite
kubectl label namespace "$NAMESPACE" \
pod-security.kubernetes.io/warn=restricted \
--overwrite
# 4) Optional: Immediately remove any existing pods that are using hostPID
# WARNING: This deletes running pods; review carefully before running.
# Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o json | \
jq -r '.items[] | select(.spec.hostPID == true) | [.metadata.namespace, .metadata.name] | @tsv' | \
while IFS=$'\t' read -r ns name; do
kubectl delete pod "$name" -n "$ns"
done
# 5) Verification (from the benchmark audit)
# Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o json | \
jq -r 'if any(.items[]?; .spec.hostPID == true) then "HOSTPID_FOUND" else "NO_HOSTPID" end'
Automation
#!/usr/bin/env bash
set -euo pipefail
# This script:
# - Adds or updates a PodSecurityPolicy and/or Namespace-level policy object
# that forbids hostPID usage in all user namespaces.
# - Skips system/control-plane namespaces.
# - Verifies that no pods with hostPID=true remain (or reports if any do).
#
# Requirements:
# - Run on any machine with kubectl access to the cluster.
# - kubectl must be configured with sufficient RBAC to create/patch the used objects.
#
# NOTE:
# - This implements the benchmark guidance by restricting admission of hostPID
# containers via namespace-level policy resources.
# - Two mechanisms are covered, use whichever your cluster supports:
# * Pod Security Admission (PSA) labels on Namespaces (v1.23+ clusters using PSA)
# * Or, as a fallback, a restrictive PodSecurityPolicy bound per namespace
#
# The script is idempotent and safe to re-run.
# ------------------------
# Configuration
# ------------------------
# Namespaces to ignore (system/control-plane)
EXCLUDE_NAMESPACES=(
kube-system
kube-public
kube-node-lease
default
)
# Label values for Pod Security Admission (most strict: restricted:enforce)
PSA_ENFORCE_LEVEL="restricted"
PSA_AUDIT_LEVEL="restricted"
PSA_WARN_LEVEL="restricted"
# Names for PSP resources (if PSP is enabled in your cluster)
PSP_NAME="restrict-hostpid-psp"
PSP_CLUSTERROLE_NAME="restrict-hostpid-psp-role"
# ------------------------
# Helpers
# ------------------------
ns_excluded() {
local ns="$1"
for e in "${EXCLUDE_NAMESPACES[@]}"; do
if [[ "$e" == "$ns" ]]; then
return 0
fi
done
return 1
}
have_psa() {
# Heuristic: if PodSecurity admission plugin is present, Namespaces usually
# accept pod-security.kubernetes.io/* labels; there's no direct API.
# We simply try to label a dummy in dry-run mode.
if kubectl label namespace default \
pod-security.kubernetes.io/enforce="${PSA_ENFORCE_LEVEL}" \
--dry-run=server >/dev/null 2>&1; then
return 0
fi
return 1
}
have_psp() {
if kubectl api-resources | grep -q "^podsecuritypolicies"; then
return 0
fi
return 1
}
# ------------------------
# Step 1: Create/update cluster-wide objects for PSP path (if applicable)
# ------------------------
setup_psp() {
# Create or update the PodSecurityPolicy that forbids hostPID
cat <<'EOF' | kubectl apply -f -
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restrict-hostpid-psp
spec:
privileged: false
hostPID: false
hostIPC: false
hostNetwork: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
EOF
# Create a ClusterRole that allows use of this PSP
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ${PSP_CLUSTERROLE_NAME}
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['${PSP_NAME}']
EOF
}
bind_psp_to_namespace() {
local ns="$1"
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: use-${PSP_NAME}
namespace: ${ns}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ${PSP_CLUSTERROLE_NAME}
subjects:
- kind: Group
name: system:serviceaccounts:${ns}
apiGroup: rbac.authorization.k8s.io
EOF
}
# ------------------------
# Step 2: Apply namespace-level restrictions
# ------------------------
main() {
echo "Discovering namespaces..."
mapfile -t namespaces < <(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
use_psa=false
use_psp=false
if have_psa; then
echo "Pod Security Admission detected; will enforce restricted profile via namespace labels."
use_psa=true
elif have_psp; then
echo "PodSecurityPolicy detected; will create PSP and bind per namespace."
use_psp=true
setup_psp
else
echo "WARNING: Neither Pod Security Admission nor PodSecurityPolicy appear to be available."
echo "You must implement an alternative admission control (e.g. OPA/Gatekeeper, Kyverno)"
echo "to enforce 'hostPID: false' per namespace. This script cannot enforce that automatically."
fi
for ns in "${namespaces[@]}"; do
if ns_excluded "$ns"; then
echo "Skipping excluded namespace: ${ns}"
continue
fi
echo "Processing namespace: ${ns}"
if $use_psa; then
# Label namespace with restrictive PSA; idempotent.
kubectl label namespace "${ns}" \
"pod-security.kubernetes.io/enforce=${PSA_ENFORCE_LEVEL}" \
"pod-security.kubernetes.io/audit=${PSA_AUDIT_LEVEL}" \
"pod-security.kubernetes.io/warn=${PSA_WARN_LEVEL}" \
--overwrite
fi
if $use_psp; then
bind_psp_to_namespace "${ns}"
fi
done
# ------------------------
# Step 3: Verification
# ------------------------
echo "Verifying that no pods with hostPID=true remain admitted..."
result="$(
kubectl get pods --all-namespaces -o json | \
jq -r 'if any(.items[]?; .spec.hostPID == true) then "HOSTPID_FOUND" else "NO_HOSTPID" end'
)"
if [[ "$result" == "NO_HOSTPID" ]]; then
echo "Verification PASSED: no pods with hostPID=true are present."
else
echo "Verification WARNING: existing pods with hostPID=true are still present."
echo "New hostPID pods should now be rejected in protected namespaces, but you must:"
echo "- Identify and delete/migrate those pods, or"
echo "- Explicitly allow hostPID only where absolutely required."
fi
}
main "$@"