Skip to main content

Minimize Admission Containers Wishing Share Host Process Id

More Info:

Do not generally permit containers to be run with the hostPID 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. Identify namespaces with user workloads (on any machine with kubectl access):

    kubectl get ns

    Decide which are “user workload” namespaces (exclude kube-system, kube-public, kube-node-lease, and provider/system namespaces unless you explicitly run workloads there).

  2. Create a baseline policy manifest file locally (on any machine with kubectl access), e.g. disallow-hostpid-policy.yaml, using either ValidatingAdmissionPolicy (Kubernetes ≥1.30 with Kubernetes-native admission) or Gatekeeper/PSP/OPA if already standardized in your environment. Example using a ValidatingAdmissionPolicy that denies hostPID in all non-exempt namespaces:

    apiVersion: admissionregistration.k8s.io/v1
    kind: ValidatingAdmissionPolicy
    metadata:
    name: deny-hostpid
    spec:
    failurePolicy: Fail
    matchConstraints:
    resourceRules:
    - apiGroups: [""]
    apiVersions: ["v1"]
    operations: ["CREATE", "UPDATE"]
    resources: ["pods"]
    matchConditions:
    - name: exclude-system-namespaces
    expression: '!(object.metadata.namespace in ["kube-system","kube-public","kube-node-lease"])'
    validations:
    - expression: '!(object.spec.hostPID == true)'
    message: "Pods must not be created with hostPID: true"
    ---
    apiVersion: admissionregistration.k8s.io/v1
    kind: ValidatingAdmissionPolicyBinding
    metadata:
    name: deny-hostpid-binding
    spec:
    policyName: deny-hostpid
    validationActions: ["Deny"]
  3. Review and, if needed, adjust the namespace exclusion list and scope (on any machine with kubectl access), for example by changing exclude-system-namespaces to also exclude any operational namespaces where hostPID is required, or by narrowing the policy to a label selector if you only want to enforce it on specific user namespaces.

  4. Apply the policy to the cluster (on any machine with kubectl access):

    kubectl apply -f disallow-hostpid-policy.yaml
  5. Test that hostPID pods are now blocked in at least one user namespace (on any machine with kubectl access), replacing user-namespace with a real namespace:

    cat << 'EOF' | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
    name: hostpid-test-deny
    namespace: user-namespace
    spec:
    hostPID: true
    containers:
    - name: sleep
    image: busybox:1.36
    command: ["sleep", "3600"]
    EOF

    Confirm that creation is rejected with a validation/admission error.

  6. Verify that no pods with hostPID: true remain (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'
Using kubectl
# Apply this to each namespace that runs user workloads (edit metadata.name).
# Example for namespace "apps":
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: disallow-hostpid
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
spec:
privileged: false
hostPID: false
hostIPC: false
hostNetwork: false
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: disallow-hostpid-psp-user
namespace: apps
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['disallow-hostpid']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: disallow-hostpid-psp-binding
namespace: apps
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: disallow-hostpid-psp-user
subjects:
# Adjust to your user/groups/serviceaccounts that run workloads in this namespace
- kind: Group
name: system:serviceaccounts:apps
apiGroup: rbac.authorization.k8s.io

Apply the policy
Run on: any machine with kubectl access.

kubectl apply -f disallow-hostpid-psp-apps.yaml

Repeat for each user-workload namespace, changing metadata.name (namespace) and file name accordingly.

If your cluster uses the Pod Security Admission (PSA) labels instead of PodSecurityPolicy, you cannot express hostPID explicitly; you must instead rely on higher-level Pod Security standards and/or an external admission controller (see Manual Steps section for review and design – no kubectl-only deterministic fix exists in that model).

Verification (no pods using hostPID)
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
#
# Block pods that request hostPID=true in all user namespaces
# by applying a restricted PodSecurity admission label.
#
# Requirements:
# - Run on any machine with kubectl access and cluster-admin rights.
# - jq must be installed.
#
# Idempotent: safe to re-run; uses kubectl apply / label --overwrite.

set -euo pipefail

# 1) Identify namespaces that currently allow hostPID pods (informational)
echo "Scanning for pods with hostPID=true..."
HOSTPID_PODS=$(kubectl get pods --all-namespaces -o json | \
jq -r '.items[] | select(.spec.hostPID == true) | "\(.metadata.namespace)/\(.metadata.name)"' || true)

if [ -n "${HOSTPID_PODS}" ]; then
echo "Found pods with hostPID=true:"
echo "${HOSTPID_PODS}"
else
echo "No existing pods with hostPID=true found."
fi

echo
echo "Applying restrictive PodSecurity labels to user namespaces..."

# 2) Select target namespaces
# Adjust the filter as needed to exclude system namespaces.
SYSTEM_NS_REGEX='^(kube-system|kube-public|kube-node-lease|default)$'

# Get all namespaces and filter out system ones
USER_NAMESPACES=$(kubectl get ns -o json | \
jq -r '.items[]
| select(.metadata.name | test("'"${SYSTEM_NS_REGEX}"'") | not)
| .metadata.name')

if [ -z "${USER_NAMESPACES}" ]; then
echo "No user namespaces found to label. Exiting."
exit 0
fi

# 3) Apply PodSecurity labels that disallow hostPID by enforcing restricted policy
# This uses Kubernetes Pod Security Admission (built-in to recent clusters).
for ns in ${USER_NAMESPACES}; do
echo "Labeling namespace: ${ns}"
# enforce restricted in baseline version; adjust version as needed for your cluster
kubectl label ns "${ns}" \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
--overwrite >/dev/null
done

echo
echo "Namespace labels after update:"
kubectl get ns --show-labels

# 4) Verification: ensure no pods with hostPID=true are admitted moving forward
# (this checks current state; admission prevention affects future creates/updates).
echo
echo "Verifying: checking again for pods with hostPID=true..."
VERIFY_OUTPUT=$(kubectl get pods --all-namespaces -o json | \
jq -r 'if any(.items[]?; .spec.hostPID == true) then "HOSTPID_FOUND" else "NO_HOSTPID" end')

echo "Verification result: ${VERIFY_OUTPUT}"
echo "Note: If HOSTPID_FOUND is reported, those pods were created before the policy change."
echo "New pods requesting hostPID=true in labeled namespaces should now be rejected."

Additional Reading: