> ## 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.

# Containers Should Not Run In Privileged Mode

### More Info:

Verifies no container sets securityContext.privileged=true. A privileged container can compromise the node and every other pod scheduled on it.

### Risk Level

Critical

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify all privileged containers (any machine with kubectl access)
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json | jq -r '
             [ .items[]
             | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
             | .metadata as $m
             | (.spec.nodeName // "") as $node
             | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
             | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
             | ((.spec.containers // []) + (.spec.initContainers // []))[]
             | (.securityContext.privileged // false) as $priv
             | select($priv == true)
             | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
               + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
               + (if $node   == ""   then "" else " node=\($node)" end)
               + (if $labels == ""   then "" else " labels=\($labels)" end)
               + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
               + " container=\(.name) image=\(.image) privileged=\($priv)"
               + " is_compliant=false"
             ][]'
           ```

        2. Determine the owning workload for each offending pod (any machine with kubectl access)\
           For a specific pod from the list (replace placeholders with actual values from step 1):
           ```bash theme={null}
           NAMESPACE="example-namespace"
           POD="example-pod"
           kubectl get pod "$POD" -n "$NAMESPACE" -o jsonpath='{.metadata.ownerReferences}'
           ```
           * If there is an ownerReference (Deployment, StatefulSet, DaemonSet, Job, etc.), plan to edit that controller.
           * If there is no ownerReference, the pod is standalone; edit or recreate that Pod manifest.

        3. Edit the controller or pod manifest to remove privileged and optionally add specific capabilities (any machine with kubectl access)\
           a) For a Deployment (similar for StatefulSet/DaemonSet/Job, adjust kind):
           ```bash theme={null}
           kubectl -n example-namespace edit deployment example-deployment
           ```
           In the opened manifest, for each affected container (including any `initContainers`):
           * Locate and remove or change:
             ```yaml theme={null}
             securityContext:
               privileged: true
             ```
           * If the workload needs specific kernel capabilities, replace with only those capabilities, for example:
             ```yaml theme={null}
             securityContext:
               privileged: false
               capabilities:
                 add:
                   - NET_ADMIN
                   - SYS_TIME
             ```
           b) For a standalone Pod (not recommended for long-lived workloads but sometimes present):
           ```bash theme={null}
           kubectl -n example-namespace get pod example-pod -o yaml > /tmp/example-pod.yaml
           ```
           Edit `/tmp/example-pod.yaml` and, for each offending container, remove `securityContext.privileged: true` and optionally add minimal required capabilities as above. Then recreate (pods themselves are immutable):
           ```bash theme={null}
           kubectl -n example-namespace delete pod example-pod
           kubectl -n example-namespace apply -f /tmp/example-pod.yaml
           ```

        4. For Helm-managed workloads, update values instead of live-editing (any machine with kubectl and helm access)
           * Identify Helm release and chart:
             ```bash theme={null}
             kubectl -n example-namespace get pod example-pod -o jsonpath='{.metadata.labels.helm\.sh/release}'
             ```
           * Fetch current values, update to remove any `privileged: true` setting and replace with a minimal `securityContext.capabilities.add` block if needed:
             ```bash theme={null}
             helm -n example-namespace get values example-release > /tmp/example-release-values.yaml
             # Edit /tmp/example-release-values.yaml to remove privileged: true and add only necessary capabilities
             helm -n example-namespace upgrade example-release example-chart-repo/example-chart \
               -f /tmp/example-release-values.yaml
             ```

        5. For workloads that genuinely require broad host interaction, review design instead of defaulting to privileged (any machine with kubectl access)
           * Inspect container permissions and behavior to see what it actually needs:
             ```bash theme={null}
             kubectl -n example-namespace exec -it example-pod -c example-container -- id
             kubectl -n example-namespace exec -it example-pod -c example-container -- capsh --print || true
             ```
           * Prefer combinations of:
             * Specific `securityContext.capabilities.add` entries.
             * `hostPath` volumes with tight `path` and `readOnly: true` where possible.
             * `runAsNonRoot: true`, `readOnlyRootFilesystem: true` when compatible.\
               Only retain privileged mode if a documented, risk-accepted exception is granted.

        6. Verify no remaining privileged containers (any machine with kubectl access)
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json | jq -r '
             [ .items[]
             | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
             | .metadata as $m
             | (.spec.nodeName // "") as $node
             | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
             | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
             | ((.spec.containers // []) + (.spec.initContainers // []))[]
             | (.securityContext.privileged // false) as $priv
             | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
               + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
               + (if $node   == ""   then "" else " node=\($node)" end)
               + (if $labels == ""   then "" else " labels=\($labels)" end)
               + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
               + " container=\(.name) image=\(.image) privileged=\($priv)"
               + " is_compliant=\(if $priv then "false" else "true" end)"
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
           ```
           Confirm that either the output is exactly `is_compliant=true` or that all listed lines end with `privileged=false is_compliant=true`.
      </Accordion>

      <Accordion title="Using kubectl">
        On any machine with kubectl access:

        1. Identify the offending pod and its controller

        Use the audit output line for the failing pod to see the `owner=` field. Example:

        ```text theme={null}
        kind=Pod ns=app-namespace name=app-pod-123 ... owner=Deployment/app-namespace/app-deploy/...
        ```

        If `owner=` is present, you must edit that owner (Deployment, StatefulSet, DaemonSet, Job, CronJob). If there is no owner, edit the Pod manifest directly (or the Git/IaC source that owns it).

        2. Export the current manifest for the owner

        Example for a Deployment:

        ```bash theme={null}
        kubectl -n app-namespace get deploy app-deploy -o yaml > app-deploy.yaml
        ```

        (StatefulSet: `get statefulset`, DaemonSet: `get daemonset`, Job: `get job`, CronJob: `get cronjob`. For an unmanaged Pod: `get pod`.)

        3. Edit the manifest to remove privileged and, if needed, add fine-grained capabilities

        Open the file and, for every affected container (including `initContainers`), remove the `privileged: true` setting under `securityContext`. Optionally add only the specific capabilities required.

        Example patch inside the Deployment spec:

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: app-deploy
          namespace: app-namespace
        spec:
          template:
            spec:
              containers:
                - name: app-container
                  image: myregistry.azurecr.io/app:1.0
                  securityContext:
                    # REMOVE this line:
                    # privileged: true
                    # OPTIONAL: replace with only required capabilities:
                    capabilities:
                      add:
                        - NET_ADMIN
                        - SYS_TIME
              initContainers:
                - name: init-sidecar
                  image: myregistry.azurecr.io/init:1.0
                  securityContext:
                    # REMOVE this line:
                    # privileged: true
                    capabilities:
                      add:
                        - NET_RAW
        ```

        Ensure there is no remaining `privileged: true` under any `containers` or `initContainers`.

        4. Apply the updated manifest

        ```bash theme={null}
        kubectl apply -f app-deploy.yaml
        ```

        For an unmanaged Pod:

        ```bash theme={null}
        kubectl delete -n app-namespace pod app-pod-123
        kubectl apply -f app-pod.yaml
        ```

        (Deleting and recreating is required because Pods are immutable.)

        5. Verification

        After the controllers have recreated pods, re-run the audit from any machine with kubectl access:

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | (.spec.nodeName // "") as $node
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | (.securityContext.privileged // false) as $priv
          | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
            + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
            + (if $node   == ""   then "" else " node=\($node)" end)
            + (if $labels == ""   then "" else " labels=\($labels)" end)
            + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
            + " container=\(.name) image=\(.image) privileged=\($priv)"
            + " is_compliant=\(if $priv then "false" else "true" end)"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
        ```

        Confirm that all listed containers show `privileged=false` and the final output is `is_compliant=true`.
      </Accordion>

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

        # Remediation for: Containers Should Not Run In Privileged Mode (CBP C1.1)
        # Scope: Any machine with kubectl access to the AKS cluster
        # Requirement: kubectl, jq, and yq (v4, https://github.com/mikefarah/yq) installed and in PATH

        # This script:
        # 1. Finds all pods (excluding system namespaces) with privileged containers.
        # 2. Identifies the owning workload (Deployment/DaemonSet/StatefulSet/Job/CronJob) where possible.
        # 3. Patches those workloads to remove securityContext.privileged=true from containers and initContainers.
        # 4. Prints manual follow-up for pods that cannot be auto-fixed (e.g., bare Pods).
        # 5. Re-runs the audit command to verify.

        # ----- config -----
        WORK_DIR="$(pwd)/privileged-remediation-$(date +%Y%m%d-%H%M%S)"
        mkdir -p "${WORK_DIR}"

        echo "Working directory: ${WORK_DIR}"

        # ----- helper: run audit and capture list of offending pods -----
        echo "Discovering pods with privileged containers..."
        AUDIT_JSON="${WORK_DIR}/pods.json"
        kubectl get pods --all-namespaces -o json > "${AUDIT_JSON}"

        # Build a JSON list of offending containers with ownership data
        OFFENDERS_JSON="${WORK_DIR}/offenders.json"
        jq '
          .items[]
          | select(.metadata.namespace as $n
                   | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | [ ($m.ownerReferences // [])[] | select(.controller) ] | first as $own
          | (.spec.containers // [] + .spec.initContainers // [])[]
          | select((.securityContext.privileged // false) == true)
          | {
              podNamespace: $m.namespace,
              podName: $m.name,
              containerName: .name,
              ownerKind: ($own.kind // null),
              ownerName: ($own.name // null)
            }
        ' "${AUDIT_JSON}" | jq -s '.' > "${OFFENDERS_JSON}"

        if [[ "$(jq 'length' "${OFFENDERS_JSON}")" -eq 0 ]]; then
          echo "No privileged containers found outside system namespaces. Cluster is compliant."
          exit 0
        fi

        echo "Found $(jq 'length' "${OFFENDERS_JSON}") privileged container entries. Beginning remediation..."

        # ----- function: patch a specific workload to remove privileged -----
        patch_workload() {
          local ns="$1"
          local kind="$2"
          local name="$3"

          local base="${WORK_DIR}/${ns}-${kind}-${name}"
          local orig_yaml="${base}-orig.yaml"
          local patched_yaml="${base}-patched.yaml"

          # Retrieve the workload manifest
          if ! kubectl get "${kind}" "${name}" -n "${ns}" -o yaml > "${orig_yaml}" 2>/dev/null; then
            echo "WARN: Failed to get ${kind}/${ns}/${name}; skipping."
            return
          fi

          cp "${orig_yaml}" "${patched_yaml}"

          # Remove .securityContext.privileged from all containers and initContainers
          # in .spec.template.spec (covers Deployments, DS, SS, Jobs, CronJobs)
          yq eval '
            (.. | select(has("containers")).containers[]? // {}) |= (
              .securityContext |= ( . // {} | with(.privileged; . = null) | with_entries(select(.value != null)))
            ) |
            (.. | select(has("initContainers")).initContainers[]? // {}) |= (
              .securityContext |= ( . // {} | with(.privileged; . = null) | with_entries(select(.value != null)))
            )
          ' "${patched_yaml}" > "${patched_yaml}.tmp" && mv "${patched_yaml}.tmp" "${patched_yaml}"

          # If no change, skip apply
          if diff -q "${orig_yaml}" "${patched_yaml}" >/dev/null; then
            echo "No privileged fields found in ${kind}/${ns}/${name}; nothing to patch."
            return
          fi

          echo "Patching ${kind}/${ns}/${name} to remove privileged=true from containers..."
          kubectl apply -f "${patched_yaml}"
        }

        # ----- main remediation loop -----
        # Collect unique owner objects to patch
        OWNERS_JSON="${WORK_DIR}/owners.json"
        jq '
          map(select(.ownerKind != null and .ownerName != null))
          | map({podNamespace, ownerKind, ownerName})
          | unique
        ' "${OFFENDERS_JSON}" > "${OWNERS_JSON}"

        OWNERS_COUNT="$(jq 'length' "${OWNERS_JSON}")"
        if [[ "${OWNERS_COUNT}" -gt 0 ]]; then
          echo "Patching ${OWNERS_COUNT} owning workloads (Deployments/DaemonSets/StatefulSets/Jobs/CronJobs)..."
          for i in $(seq 0 $((OWNERS_COUNT - 1))); do
            ns="$(jq -r ".[$i].podNamespace" "${OWNERS_JSON}")"
            kind="$(jq -r ".[$i].ownerKind" "${OWNERS_JSON}")"
            name="$(jq -r ".[$i].ownerName" "${OWNERS_JSON}")"

            # Only patch expected workload kinds
            case "${kind}" in
              Deployment|DaemonSet|StatefulSet|Job|CronJob)
                patch_workload "${ns}" "${kind}" "${name}"
                ;;
              *)
                echo "WARN: Unsupported owner kind ${kind} for ${ns}/${name}; manual review required."
                ;;
            esac
          done
        else
          echo "No controller-owned workloads detected; all offending pods appear to be bare Pods."
        fi

        # ----- manual follow-up for bare Pods or unsupported owners -----
        BARE_PODS_TXT="${WORK_DIR}/manual-bare-pods.txt"
        jq -r '
          map(select(.ownerKind == null or .ownerName == null))
          | group_by(.podNamespace, .podName)
          | .[]
          | "Pod " + .[0].podNamespace + "/" + .[0].podName
        ' "${OFFENDERS_JSON}" | sort -u > "${BARE_PODS_TXT}" || true

        if [[ -s "${BARE_PODS_TXT}" ]]; then
          echo
          echo "The following Pods have privileged containers but are not owned by a supported controller."
          echo "They must be fixed manually by editing/recreating the Pod manifests:"
          cat "${BARE_PODS_TXT}"
          echo
          echo "For each listed Pod, obtain the manifest, remove securityContext.privileged: true from"
          echo "all containers and initContainers (and, if needed, add only specific capabilities via"
          echo "securityContext.capabilities.add), then recreate the Pod from a controller or as needed."
        fi

        # ----- verification (re-run the authoritative audit) -----
        echo
        echo "Re-running compliance audit to verify remediation..."
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | (.spec.nodeName // "") as $node
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | (.securityContext.privileged // false) as $priv
          | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
            + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
            + (if $node   == ""   then "" else " node=\($node)" end)
            + (if $labels == ""   then "" else " labels=\($labels)" end)
            + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
            + " container=\(.name) image=\(.image) privileged=\($priv)"
            + " is_compliant=\(if $priv then "false" else "true" end)"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
