> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Minimize Admission Of Privileged Containers

### More Info:

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

### Risk Level

Critical

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify all namespaces with user workloads (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl get ns
           ```
           Decide which namespaces should disallow privileged containers (typically all except core system namespaces like kube-system, kube-public, kube-node-lease, kube-admin equivalents).

        2. Create a baseline policy that denies privileged containers (run once on any machine with kubectl access; use Pod Security Admission via labels as the default, plus optional PodSecurityPolicy or AdmissionPolicy if supported in your cluster). Example using Pod Security Admission “restricted” level:
           ```bash theme={null}
           # Label each target namespace to enforce restricted policy
           for ns in NAMESPACE1 NAMESPACE2 NAMESPACE3; do
             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/warn=restricted \
               --overwrite
           done
           ```
           Replace `NAMESPACE1 NAMESPACE2 NAMESPACE3` with your chosen namespaces.

        3. (If PodSecurityPolicy is still in use in your cluster) Create a PSP that disallows privileged containers and a ClusterRole to use it (run on any machine with kubectl access):
           ```bash theme={null}
           cat << 'EOF' | kubectl apply -f -
           apiVersion: policy/v1beta1
           kind: PodSecurityPolicy
           metadata:
             name: restricted-no-privileged
           spec:
             privileged: false
             allowPrivilegeEscalation: false
             hostPID: false
             hostIPC: false
             hostNetwork: false
             runAsUser:
               rule: 'MustRunAsNonRoot'
             seLinux:
               rule: 'RunAsAny'
             fsGroup:
               rule: 'RunAsAny'
             supplementalGroups:
               rule: 'RunAsAny'
             volumes:
             - 'configMap'
             - 'downwardAPI'
             - 'emptyDir'
             - 'persistentVolumeClaim'
             - 'projected'
             - 'secret'
           ---
           apiVersion: rbac.authorization.k8s.io/v1
           kind: ClusterRole
           metadata:
             name: use-restricted-no-privileged-psp
           rules:
           - apiGroups: ['policy']
             resources: ['podsecuritypolicies']
             verbs: ['use']
             resourceNames: ['restricted-no-privileged']
           EOF
           ```

        4. (If PSP is in use) Bind the PSP to the service accounts that run user workloads in each target namespace (run on any machine with kubectl access):
           ```bash theme={null}
           # Example: bind to all service accounts in a namespace
           for ns in NAMESPACE1 NAMESPACE2 NAMESPACE3; do
             kubectl create rolebinding "use-restricted-no-privileged-psp" \
               --clusterrole=use-restricted-no-privileged-psp \
               --group=system:serviceaccounts:"$ns" \
               --namespace="$ns" \
               --dry-run=client -o yaml | kubectl apply -f -
           done
           ```

        5. Manually review and update existing workloads that currently request privileged containers to either:

           * Remove `securityContext.privileged: true`, or
           * Move them into carefully controlled namespaces with different admission policy and an explicit exception process.

           To locate and inspect offending pods (run on any machine with kubectl access):

           ```bash theme={null}
           # List non-compliant containers
           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
             kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container
             do
               container_name=$(echo ${container} | jq -r '.name')
               container_privileged=$(echo ${container} | jq -r '.securityContext.privileged' | sed -e 's/null/notset/g')
               if [ "${container_privileged}" = "true" ]; then
                 echo "NON-COMPLIANT: pod=${pod_name} ns=${pod_namespace} container=${container_name}"
               fi
             done
           done
           ```

           For each non-compliant Deployment/DaemonSet/StatefulSet/etc., edit the manifest to remove `securityContext.privileged: true` and redeploy:

           ```bash theme={null}
           kubectl -n TARGET_NAMESPACE edit deployment TARGET_DEPLOYMENT
           ```

           Remove or set `privileged: false` under the container’s `securityContext`, save, and let the controller recreate pods.

        6. Verification (run on any machine with kubectl access):
           ```bash theme={null}
           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
             kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container
             do
               container_name=$(echo ${container} | jq -r '.name')
               container_privileged=$(echo ${container} | jq -r '.securityContext.privileged' | sed -e 's/null/notset/g')
               if [ "${container_privileged}" = "false" ] || [ "${container_privileged}" = "notset" ] ; then
                 echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: true"
               else
                 echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: false"
               fi
             done
           done
           ```
           Confirm that no lines show `is_compliant: false` for namespaces where privileged containers should be minimized.
      </Accordion>

      <Accordion title="Using kubectl">
        ```yaml theme={null}
        # Apply once per workload namespace (edit metadata.name as needed)
        apiVersion: kyverno.io/v1
        kind: ClusterPolicy
        metadata:
          name: disallow-privileged-containers
        spec:
          validationFailureAction: Enforce
          background: true
          rules:
            - name: validate-privileged
              match:
                any:
                  - resources:
                      kinds:
                        - Pod
                        - Deployment
                        - ReplicaSet
                        - DaemonSet
                        - StatefulSet
                        - Job
                        - CronJob
              validate:
                message: "Privileged containers are not allowed. Do not set securityContext.privileged: true."
                pattern:
                  spec:
                    containers:
                      - =(securityContext):
                          =(privileged): "false"
                    =(initContainers):
                      - =(securityContext):
                          =(privileged): "false"
        ```

        ```bash theme={null}
        # On any machine with kubectl access: create the policy
        kubectl apply -f disallow-privileged-containers.yaml

        # Optional: dry-run creation of a privileged pod to confirm it is blocked
        kubectl run test-privileged \
          --image=busybox \
          --restart=Never \
          --overrides='{"apiVersion":"v1","kind":"Pod","metadata":{"name":"test-privileged"}, "spec":{"containers":[{"name":"c","image":"busybox","securityContext":{"privileged":true},"command":["sh","-c","sleep 3600"]}]}}' \
          --dry-run=server -o yaml
        ```

        Verification (same style as audit, on any machine with kubectl access):

        ```bash theme={null}
        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
          kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json | jq -c '.spec.containers[]' | while read -r container
          do
            container_name=$(echo ${container} | jq -r '.name')
            container_privileged=$(echo ${container} | jq -r '.securityContext.privileged' | sed -e 's/null/notset/g')
            if [ "${container_privileged}" = "false" ] || [ "${container_privileged}" = "notset" ] ; then
              echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: true"
            else
              echo "***pod_name: ${pod_name} container_name: ${container_name} pod_namespace: ${pod_namespace} is_container_privileged: ${container_privileged} is_compliant: false"
            fi
          done
        done
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        set -euo pipefail

        # Purpose:
        # - For each namespace with user workloads, create/patch a PodSecurityPolicy-like
        #   restriction using Pod Security Admission labels to forbid privileged pods.
        # - This is MANUAL in CIS: script helps you detect + optionally enforce.
        #
        # Requirements:
        # - Run on any machine with kubectl and jq configured for the target cluster.
        # - Kubernetes v1.25+ recommended (PodSecurity Admission GA).
        #
        # OPERATIONAL IMPACT:
        # - After enforcement, any new pod (or updated existing pod) with
        #   securityContext.privileged=true will be rejected in restricted namespaces.
        # - Existing running privileged pods are NOT evicted automatically.

        #---------------------------- Configurable options ----------------------------#

        # Namespaces to exclude from enforcement (space-separated, exact names).
        # Add control-plane / system namespaces and any namespace where privileged
        # containers are explicitly required and approved via risk acceptance.
        EXCLUDED_NAMESPACES=(
          "kube-system"
          "kube-public"
          "kube-node-lease"
          "default"     # remove this if you want default namespace restricted too
          "kube-tools"  # example; remove or change as needed
        )

        # Pod Security Admission level to enforce:
        # Valid: privileged | baseline | restricted
        # Use "restricted" to strongly minimize privileged / insecure workloads.
        PSA_ENFORCEMENT_LEVEL="restricted"

        # If true, only report non-compliance but do NOT change any namespaces.
        DRY_RUN=${DRY_RUN:-"false"}  # set DRY_RUN=true in environment to only audit

        #---------------------------- Helper functions ----------------------------#

        contains() {
          local e match="$1"
          shift
          for e; do [[ "$e" == "$match" ]] && return 0; done
          return 1
        }

        log()  { printf '[INFO] %s\n' "$*" >&2; }
        warn() { printf '[WARN] %s\n' "$*" >&2; }
        err()  { printf '[ERR ] %s\n' "$*" >&2; }

        require_cmd() {
          command -v "$1" >/dev/null 2>&1 || {
            err "Required command '$1' not found in PATH"
            exit 1
          }
        }

        #---------------------------- Pre-flight checks -----------------------------#

        require_cmd kubectl
        require_cmd jq

        # Verify cluster access
        if ! kubectl version --request-timeout='5s' >/dev/null 2>&1; then
          err "kubectl cannot reach the cluster or is not authorized"
          exit 1
        fi

        #---------------------------- Detect privileged pods ------------------------#

        log "Scanning for containers with securityContext.privileged=true ..."

        NON_COMPLIANT_FOUND=0

        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
            kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json \
            | jq -c '.spec.containers[]' \
            | while read -r container; do
                container_name=$(echo "${container}" | jq -r '.name')
                container_privileged=$(echo "${container}" | jq -r '.securityContext.privileged' | sed -e 's/null/notset/g')
                if [ "${container_privileged}" = "true" ]; then
                  printf 'NON_COMPLIANT pod_name=%s container_name=%s pod_namespace=%s privileged=%s\n' \
                    "${pod_name}" "${container_name}" "${pod_namespace}" "${container_privileged}"
                  NON_COMPLIANT_FOUND=1
                fi
              done
          done

        # Note: NON_COMPLIANT_FOUND is in a subshell above and will not propagate here;
        # we instead rely on a verification run later. This block is for operator output.

        log "Initial privileged container scan complete."

        #---------------------------- Enforce namespace policies --------------------#

        log "Discovering namespaces to label with Pod Security Admission..."

        NAMESPACES=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}')

        APPLIED_COUNT=0

        for ns in ${NAMESPACES}; do
          if contains "${ns}" "${EXCLUDED_NAMESPACES[@]}"; then
            log "Skipping excluded namespace: ${ns}"
            continue
          fi

          # Read current labels
          current_enforce=$(kubectl get ns "${ns}" -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce}' 2>/dev/null || true)

          # Decide whether to patch
          if [ "${current_enforce}" = "${PSA_ENFORCEMENT_LEVEL}" ]; then
            log "Namespace ${ns} already has enforce=${PSA_ENFORCEMENT_LEVEL}; no change."
            continue
          fi

          if [ "${DRY_RUN}" = "true" ]; then
            warn "DRY RUN: would set pod-security.kubernetes.io/enforce=${PSA_ENFORCEMENT_LEVEL} on namespace ${ns}"
            continue
          fi

          log "Patching namespace ${ns} to enforce Pod Security level: ${PSA_ENFORCEMENT_LEVEL}"

          kubectl label namespace "${ns}" \
            "pod-security.kubernetes.io/enforce=${PSA_ENFORCEMENT_LEVEL}" \
            "pod-security.kubernetes.io/enforce-version=latest" \
            --overwrite

          APPLIED_COUNT=$((APPLIED_COUNT + 1))
        done

        log "Namespace labeling complete. Namespaces updated: ${APPLIED_COUNT}"

        #---------------------------- Verification ----------------------------------#

        log "Verifying that privileged containers are either absent or blocked by policy."

        # 1) Show current privileged containers (if any remain).
        log "Re-running privileged container audit:"
        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
            kubectl get pod "${pod_name}" --namespace "${pod_namespace}" -o json \
            | jq -c '.spec.containers[]' \
            | while read -r container; do
                container_name=$(echo "${container}" | jq -r '.name')
                container_privileged=$(echo "${container}" | jq -r '.securityContext.privileged' | sed -e 's/null/notset/g')
                if [ "${container_privileged}" = "false" ] || [ "${container_privileged}" = "notset" ]; then
                  printf 'COMPLIANT pod_name=%s container_name=%s pod_namespace=%s privileged=%s\n' \
                    "${pod_name}" "${container_name}" "${pod_namespace}" "${container_privileged}"
                else
                  printf 'NON_COMPLIANT pod_name=%s container_name=%s pod_namespace=%s privileged=%s\n' \
                    "${pod_name}" "${container_name}" "${container_name}" "${container_privileged}"
                fi
              done
          done

        # 2) Show namespaces and their Pod Security enforce level for review.
        log "Current Pod Security enforce labels per namespace:"
        kubectl get ns -o custom-columns=NAME:.metadata.name,ENFORCE:'".metadata.labels.pod-security\.kubernetes\.io/enforce"' --show-labels

        log "Automation completed. Review NON_COMPLIANT lines above and handle any remaining privileged workloads per policy (exceptions, redesign, or migration to excluded namespaces)."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://kubernetes.io/docs/concepts/security/pod-security-standards/](https://kubernetes.io/docs/concepts/security/pod-security-standards/)
