Minimize The Admission Of Containers Sharing The Host
More Info:
Sharing the host PID namespace lets a container see and interact with all host processes, aiding escape and escalation. Restrict hostPID pods.
Risk Level
High
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List and review current Pods using
hostPID(any machine with kubectl access)kubectl get pods --all-namespaces -o jsonpath='{range .items[?(@.spec.hostPID==true)]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{end}'For each listed Pod, decide if host PID access is strictly required. If not, plan to remove it; if yes, note the namespace so you can design exception policies (e.g., via labels).
-
Create a restrictive
PodSecurityPolicyorPod Security Standards-style control (if PSP still in use) (any machine with kubectl access)
If you still use PodSecurityPolicy, create one that forbids hostPID:cat << 'EOF' | kubectl apply -f -apiVersion: policy/v1beta1kind: PodSecurityPolicymetadata:name: restricted-no-hostpidspec:hostPID: falseprivileged: falserunAsUser:rule: 'MustRunAsNonRoot'seLinux:rule: 'RunAsAny'fsGroup:rule: 'RunAsAny'supplementalGroups:rule: 'RunAsAny'volumes:- 'configMap'- 'emptyDir'- 'projected'- 'secret'- 'downwardAPI'- 'persistentVolumeClaim'EOFIf your cluster instead uses Pod Security Admission (PSA), apply a namespace-level policy that disallows host namespaces. For example, enforce
restrictedon a namespace:kubectl label namespace my-workload-namespace \pod-security.kubernetes.io/enforce=restricted \--overwriteRepeat for each user-workload namespace, adjusting names as needed.
-
Bind the policy to user-workload namespaces (if using PSP) (any machine with kubectl access)
cat << 'EOF' | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:name: use-restricted-no-hostpid-psprules:- apiGroups: ['policy']resources: ['podsecuritypolicies']verbs: ['use']resourceNames: ['restricted-no-hostpid']---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: use-restricted-no-hostpid-pspnamespace: my-workload-namespacesubjects:- kind: Groupname: system:serviceaccounts:my-workload-namespaceapiGroup: rbac.authorization.k8s.ioroleRef:kind: ClusterRolename: use-restricted-no-hostpid-pspapiGroup: rbac.authorization.k8s.ioEOFRepeat the
RoleBindingstanza for each user-workload namespace by changingnamespace:and thesystem:serviceaccounts:<namespace>group. -
Tighten admission using a validating admission policy (if PSA/PSP are not applicable) (any machine with kubectl access)
If you rely on a generic admission controller (e.g., ValidatingAdmissionPolicy in newer Kubernetes), create a policy that rejects hostPID Pods, then selectively exempt namespaces that truly require it:cat << 'EOF' | kubectl apply -f -apiVersion: admissionregistration.k8s.io/v1kind: ValidatingWebhookConfigurationmetadata:name: deny-hostpid-podswebhooks:- name: deny-hostpid-pods.example.comadmissionReviewVersions: ["v1"]sideEffects: NonefailurePolicy: Failrules:- apiGroups: [""]apiVersions: ["v1"]operations: ["CREATE","UPDATE"]resources: ["pods"]namespaceSelector:matchExpressions:- key: allow-hostpidoperator: NotInvalues: ["true"]clientConfig:# POINT THIS TO YOUR ACTUAL WEBHOOK SERVICE IMPLEMENTATIONservice:namespace: policy-systemname: hostpid-deny-webhookpath: /validateEOFImplement the webhook to reject any Pod with
.spec.hostPID == true. Label only the namespaces that genuinely require hostPID withallow-hostpid=trueto exempt them. -
Update or recreate non-essential hostPID Pods without hostPID (any machine with kubectl access)
For any Pod/Deployment/DaemonSet/Job where hostPID is not strictly required, edit the manifest to remove or sethostPID: false, then apply:kubectl -n <namespace> get deployment <name> -o yaml > /tmp/deploy-no-hostpid.yamlsed -i 's/hostPID: true/hostPID: false/' /tmp/deploy-no-hostpid.yamlkubectl apply -f /tmp/deploy-no-hostpid.yamlFor standalone Pods, delete and recreate them from updated manifests without
hostPID: true. -
Verify no non-exempt Pods use hostPID (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_namespacedopod_hostpid=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostPID}' 2>/dev/null)if [ -z "${pod_hostpid}" ]; thenpod_hostpid="false"echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: true"elseecho "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: false"fidoneConfirm that any
is_compliant: falsePods exist only in namespaces you have explicitly designated as allowed to use hostPID and that they are strictly necessary.
Using kubectl
# 1) Create a baseline restrictive PodSecurityPolicy (if your cluster still uses PSP)
# This denies hostPID by default.
# Run on: any machine with kubectl access.
cat <<'EOF' > psp-deny-hostpid.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: deny-hostpid
spec:
privileged: false
hostPID: false
hostIPC: false
hostNetwork: false
seLinux:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
volumes:
- '*'
EOF
kubectl apply -f psp-deny-hostpid.yaml
# 2) Bind this PSP to a namespace with user workloads (example: "production").
# Replace "production" with each target namespace.
# Run on: any machine with kubectl access.
NAMESPACE=production
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: use-deny-hostpid-psp
namespace: ${NAMESPACE}
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['deny-hostpid']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: use-deny-hostpid-psp
namespace: ${NAMESPACE}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: use-deny-hostpid-psp
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:serviceaccounts:${NAMESPACE}
EOF
# 3) (Alternative / additional) Use Pod Security Admission via labels to restrict hostPID.
# Use "restricted" or stricter for all user namespaces (example: "production").
# Run on: any machine with kubectl access.
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
--overwrite
# 4) (Optional) Example: secure Pod manifest WITHOUT hostPID for user workloads.
# Ensure all application manifests in user namespaces omit hostPID or set it to false.
cat <<'EOF' > example-app-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: example-app
namespace: production
spec:
hostPID: false
containers:
- name: app
image: nginx:stable
EOF
kubectl apply -f example-app-pod.yaml
# 5) Verification: list all Pods with hostPID=true to confirm none exist in user namespaces.
# 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) hostPID=true"' || true
Automation
#!/usr/bin/env bash
set -euo pipefail
# This script:
# - Creates a Pod Security Policy–style restriction using Pod Security Admission (PSA) via labels/Policy/PSP replacement
# - For each namespace with user workloads, it:
# * Labels the namespace with a restricted Pod Security level (baseline or restricted)
# * Creates/patches a validating admission policy (if supported) or a validating webhook
#
# NOTE:
# - This is a MANUAL benchmark control: different clusters use different admission stacks.
# - This script implements a conservative, Kubernetes-native control using Pod Security Admission labels
# and a validating admission policy if available. Review carefully before using in production.
# -------- CONFIGURATION --------
# Namespaces to ignore (system and infrastructure)
EXCLUDED_NAMESPACES=(
"kube-system"
"kube-public"
"kube-node-lease"
"local-path-storage"
"default" # remove this if you run user workloads in 'default'
)
# Pod Security level to enforce; choose "baseline" or "restricted"
POD_SECURITY_LEVEL="baseline"
# Label key for Pod Security Admission
POD_SECURITY_ENFORCE_LABEL="pod-security.kubernetes.io/enforce"
# ValidatingAdmissionPolicy name (for clusters >=1.26 supporting it)
VAP_NAME="deny-hostpid-pods"
VAP_BINDING_NAME="deny-hostpid-pods-binding"
# -------- FUNCTIONS --------
is_excluded_ns() {
local ns="$1"
for e in "${EXCLUDED_NAMESPACES[@]}"; do
if [[ "$ns" == "$e" ]]; then
return 0
fi
done
return 1
}
kubectl_safe() {
kubectl "$@" 2>/dev/null
}
# -------- PRECHECKS --------
echo "Checking kubectl access..."
kubectl version --short >/dev/null
echo "Determining cluster support for ValidatingAdmissionPolicy..."
if kubectl_safe api-resources | grep -q "^validatingadmissionpolicies"; then
HAS_VAP="true"
else
HAS_VAP="false"
fi
echo "ValidatingAdmissionPolicy supported: ${HAS_VAP}"
# -------- STEP 1: Label namespaces with Pod Security Admission --------
# Run on: any machine with kubectl access
echo "Labeling namespaces with Pod Security Admission level: ${POD_SECURITY_LEVEL}"
ALL_NAMESPACES=$(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
while read -r NS; do
[[ -z "$NS" ]] && continue
if is_excluded_ns "$NS"; then
echo "Skipping excluded namespace: ${NS}"
continue
fi
# Skip namespaces without user pods
POD_COUNT=$(kubectl get pods -n "$NS" --no-headers 2>/dev/null | wc -l | tr -d ' ')
if [[ "$POD_COUNT" -eq 0 ]]; then
echo "Skipping namespace with no pods: ${NS}"
continue
fi
CURRENT_LABEL=$(kubectl get ns "$NS" -o jsonpath="{.metadata.labels.${POD_SECURITY_ENFORCE_LABEL}}" 2>/dev/null || echo "")
if [[ "$CURRENT_LABEL" == "${POD_SECURITY_LEVEL}" ]]; then
echo "Namespace ${NS} already labeled ${POD_SECURITY_ENFORCE_LABEL}=${POD_SECURITY_LEVEL}"
else
echo "Labeling namespace ${NS} with ${POD_SECURITY_ENFORCE_LABEL}=${POD_SECURITY_LEVEL}"
kubectl label ns "$NS" "${POD_SECURITY_ENFORCE_LABEL}=${POD_SECURITY_LEVEL}" --overwrite
fi
done <<< "${ALL_NAMESPACES}"
# -------- STEP 2: Create/Update ValidatingAdmissionPolicy (if supported) --------
# Run on: any machine with kubectl access
if [[ "${HAS_VAP}" == "true" ]]; then
echo "Applying ValidatingAdmissionPolicy to deny hostPID pods..."
cat <<'EOF' | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: deny-hostpid-pods
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "!(has(object.spec.hostPID) && object.spec.hostPID == true)"
message: "Creating pods with hostPID=true is not allowed by policy"
reason: "Forbidden"
EOF
cat <<EOF | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: ${VAP_BINDING_NAME}
spec:
policyName: ${VAP_NAME}
matchResources:
namespaceSelector: {}
validationActions: ["Deny"]
EOF
else
echo "Cluster does not support ValidatingAdmissionPolicy;"
echo "you must manually implement an alternative admission control (e.g. Gatekeeper/PSP replacement)."
fi
# -------- STEP 3: Verification --------
# Run on: any machine with kubectl access
echo
echo "Verifying that existing pods do not use hostPID=true..."
NONCOMPLIANT=false
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_hostpid=$(kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o jsonpath='{.spec.hostPID}' 2>/dev/null || true)
if [[ -n "${pod_hostpid}" && "${pod_hostpid}" == "true" ]]; then
echo "***pod_name: ${pod_name} pod_namespace: ${pod_namespace} is_pod_hostpid: ${pod_hostpid} is_compliant: false"
NONCOMPLIANT=true
fi
done
if [[ "${NONCOMPLIANT}" == "false" ]]; then
echo "All existing pods are compliant (hostPID not set or false)."
else
echo "Some existing pods are non-compliant; they must be manually reviewed and recreated without hostPID=true."
fi
echo
echo "To test enforcement, try creating a pod with spec.hostPID=true in a labeled user namespace; it should be denied."