> ## 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
           * Run on any machine with kubectl access:
             ```sh 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)"
               ][]'
             ```

        2. For each offending pod, locate and edit the owning workload manifest
           * Run on any machine with kubectl access, example for a Deployment owner from step 1:
             ```sh theme={null}
             # Get the current manifest
             kubectl -n <NAMESPACE> get deployment <DEPLOYMENT_NAME> -o yaml > /tmp/deployment-<DEPLOYMENT_NAME>.yaml
             ```
           * In `/tmp/deployment-<DEPLOYMENT_NAME>.yaml`, under the affected container or initContainer, remove or change:
             ```yaml theme={null}
             securityContext:
               privileged: true
             ```
           * If specific kernel access is required, add only needed capabilities instead of privileged:
             ```yaml theme={null}
             securityContext:
               capabilities:
                 add:
                   - NET_ADMIN    # example; adjust to actual need
             ```

        3. Apply the updated manifest
           * Run on any machine with kubectl access:
             ```sh theme={null}
             kubectl apply -f /tmp/deployment-<DEPLOYMENT_NAME>.yaml
             ```

        4. If the pod is not managed by a higher-level controller (standalone Pod)
           * Run on any machine with kubectl access:
             ```sh theme={null}
             kubectl -n <NAMESPACE> get pod <POD_NAME> -o yaml > /tmp/pod-<POD_NAME>.yaml
             ```
           * Edit `/tmp/pod-<POD_NAME>.yaml` to remove `securityContext.privileged: true` (and optionally add minimal `securityContext.capabilities.add` as needed), then re-create the pod:
             ```sh theme={null}
             kubectl -n <NAMESPACE> delete pod <POD_NAME>
             kubectl apply -f /tmp/pod-<POD_NAME>.yaml
             ```

        5. Repeat for all other controllers (DaemonSet, StatefulSet, Job, CronJob)
           * Use the owner information from step 1 (e.g. `owner=DaemonSet/ns/name/uid`) and follow steps 2–3, replacing `deployment` with the appropriate kind:
             ```sh theme={null}
             kubectl -n <NAMESPACE> get daemonset <DAEMONSET_NAME> -o yaml > /tmp/daemonset-<DAEMONSET_NAME>.yaml
             # edit to remove privileged and optionally add only required capabilities
             kubectl apply -f /tmp/daemonset-<DAEMONSET_NAME>.yaml
             ```

        6. Verify no containers are still privileged
           * Run on any machine with kubectl access:
             ```sh 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.containers // []) + (.spec.initContainers // []))[]
               | (.securityContext.privileged // false) as $priv
               | "privileged=\($priv)"
               ] as $rows
               | if ([.[] | select(. == "privileged=true")] | length) == 0
                 then "is_compliant=true"
                 else $rows[]
                 end'
             ```
           * Confirm the output is `is_compliant=true` and that no line shows `privileged=true`.
      </Accordion>

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

        1. Identify the offending pod and its owner

        ```sh 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
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | select(.securityContext.privileged == true)
          | "ns=\($m.namespace) pod=\($m.name) ownerKind=\($own.kind) ownerName=\($own.name)"
          ][]'
        ```

        Decide whether to:

        * Edit a standalone Pod, or
        * Edit the owning controller (Deployment/DaemonSet/StatefulSet/Job/CronJob, etc.).

        2. Remove `securityContext.privileged: true` from the controller manifest

        Example: Deployment named `my-deployment` in namespace `my-namespace`:

        ```sh theme={null}
        kubectl -n my-namespace get deploy my-deployment -o yaml > /tmp/my-deployment.yaml
        ```

        Edit `/tmp/my-deployment.yaml` and, for each offending container (including `initContainers`), remove or change:

        ```yaml theme={null}
        securityContext:
          privileged: true
        ```

        If specific capabilities are required, replace with a minimal capability set, for example:

        ```yaml theme={null}
        securityContext:
          capabilities:
            add:
              - NET_ADMIN
              - SYS_TIME
        ```

        Apply the updated manifest:

        ```sh theme={null}
        kubectl apply -f /tmp/my-deployment.yaml
        ```

        Repeat this pattern for other controller types, e.g.:

        ```sh theme={null}
        kubectl -n my-namespace get daemonset my-daemonset -o yaml > /tmp/my-daemonset.yaml
        # edit to remove privileged: true, then:
        kubectl apply -f /tmp/my-daemonset.yaml
        ```

        For a standalone Pod that is not managed by a controller (uncommon in production), recreate it without `securityContext.privileged: true`:

        ```sh theme={null}
        kubectl -n my-namespace get pod my-pod -o yaml > /tmp/my-pod.yaml
        # edit: remove privileged: true and delete fields status, metadata.uid, metadata.resourceVersion, metadata.creationTimestamp, metadata.managedFields
        kubectl delete pod -n my-namespace my-pod
        kubectl apply -f /tmp/my-pod.yaml
        ```

        3. Verification

        On any machine with kubectl access, re-run the compliance check:

        ```sh 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'
        ```

        The cluster is compliant when the output is only:

        ```text theme={null}
        is_compliant=true
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Remediation: Ensure no container runs with securityContext.privileged: true
        # Scope: Any machine with kubectl access and cluster-admin privileges
        #
        # Behavior:
        # - Scans all non-system namespaces for Pods with privileged containers/initContainers
        # - For each Pod, identifies its owning controller (Deployment/StatefulSet/DaemonSet/Job/CronJob/ReplicaSet/ReplicationController)
        # - Patches the controller template to remove privileged=true and optionally keep/add capabilities
        # - Deletes the non-compliant Pod so the controller recreates it from the fixed template
        # - Skips Pods without a controller (requires manual review)
        # - Re-runs the audit command at the end
        #
        # Idempotency:
        # - Patches only containers that currently have privileged=true
        # - Safe to re-run; no changes are made when nothing is privileged

        set -euo pipefail

        # --- Configuration ---

        # Namespace filter for system namespaces we should NOT touch
        SYSTEM_NAMESPACES=("kube-system" "kube-public" "kube-node-lease")

        # Temporary working directory
        WORKDIR="$(mktemp -d /tmp/k8s-privileged-fix.XXXXXX)"
        trap 'rm -rf "$WORKDIR"' EXIT

        # --- Helper functions ---

        is_system_namespace() {
          local ns="$1"
          for s in "${SYSTEM_NAMESPACES[@]}"; do
            if [[ "$ns" == "$s" ]]; then
              return 0
            fi
          done
          return 1
        }

        jq_filter_remove_privileged='
          # Given a containers array, remove privileged=true; preserve any capabilities
          map(
            if (.securityContext // null) != null and (.securityContext.privileged // false) == true then
              .securityContext |= (
                .privileged = false
                | with_entries(select(.key != "privileged"))
              )
            else
              .
            end
          )
        '

        # --- Discover privileged pods ---

        echo "[*] Discovering non-compliant pods (privileged containers) ..."

        audit_output_file="${WORKDIR}/audit.json"

        kubectl get pods --all-namespaces -o json > "${audit_output_file}"

        non_compliant_pods_json="$(
          jq --argjson sysns "$(printf '%s\n' "${SYSTEM_NAMESPACES[@]}" | jq -R . | jq -s .)" '
            .items[]
            | select(.metadata.namespace as $n | $sysns | index($n) | not)
            | select(
                ((.spec.containers // []) + (.spec.initContainers // []))
                | map(.securityContext.privileged // false)
                | any(. == true)
              )
          ' "${audit_output_file}"
        )"

        if [[ -z "$non_compliant_pods_json" ]]; then
          echo "[*] No privileged containers found outside system namespaces. Cluster appears compliant."
          echo
          echo "[*] Running verification audit:"
          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'
          exit 0
        fi

        echo "[*] Privileged pods detected. Beginning remediation ..."

        # Save detailed list for possible manual review
        echo "${non_compliant_pods_json}" > "${WORKDIR}/non_compliant_pods.json"

        # --- Patch controllers ---

        # Extract unique owner references (controller) for non-compliant pods
        owners_file="${WORKDIR}/owners.json"

        jq '
          [
            .
            | {
                ns: .metadata.namespace,
                pod: .metadata.name,
                owner: ([ (.metadata.ownerReferences // [])[] | select(.controller) ] | first)
              }
            | select(.owner != null)
          ]
        ' "${WORKDIR}/non_compliant_pods.json" > "${owners_file}"

        # Build a unique set of controllers to patch
        controllers_file="${WORKDIR}/controllers.json"
        jq '
          map({kind: .owner.kind, name: .owner.name, ns: .ns})
          | unique
        ' "${owners_file}" > "${controllers_file}"

        controllers_count="$(jq 'length' "${controllers_file}")"

        if [[ "${controllers_count}" -eq 0 ]]; then
          echo "[!] All privileged pods lack a controlling workload (e.g., standalone Pods)."
          echo "    These must be fixed manually by editing their Pod specs/manifests to remove securityContext.privileged: true."
        else
          echo "[*] Found ${controllers_count} owning controllers to patch."
        fi

        # Function to patch a single controller
        patch_controller() {
          local kind="$1"
          local name="$2"
          local ns="$3"

          echo "[*] Processing controller: ${kind} ${ns}/${name}"

          # Determine the path to the Pod template based on kind
          local template_jsonpath=''
          case "$kind" in
            Deployment)
              template_jsonpath='.spec.template'
              ;;
            StatefulSet)
              template_jsonpath='.spec.template'
              ;;
            DaemonSet)
              template_jsonpath='.spec.template'
              ;;
            ReplicaSet)
              template_jsonpath='.spec.template'
              ;;
            ReplicationController)
              template_jsonpath='.spec.template'
              ;;
            Job)
              template_jsonpath='.spec.template'
              ;;
            CronJob)
              # v1 CronJob
              template_jsonpath='.spec.jobTemplate.spec.template'
              ;;
            *)
              echo "[!] Unsupported controller kind: ${kind}. Skipping."
              return
              ;;
          esac

          # Fetch the controller as JSON
          local ctrl_file="${WORKDIR}/${kind}_${ns}_${name}.json"
          if ! kubectl get "${kind}" "${name}" -n "${ns}" -o json > "${ctrl_file}" 2>/dev/null; then
            echo "[!] Failed to fetch ${kind} ${ns}/${name}. It may have been deleted. Skipping."
            return
          fi

          # Patch containers and initContainers in the template
          local patched_file="${WORKDIR}/${kind}_${ns}_${name}.patched.json"
          jq --argjson emptyobj '{}' "
            . as \$root
            | ${template_jsonpath} as \$tmpl
            | \$root
            | ${template_jsonpath}.spec.containers |= (${jq_filter_remove_privileged})
            | if (${template_jsonpath}.spec.initContainers // null) != null then
                ${template_jsonpath}.spec.initContainers |= (${jq_filter_remove_privileged})
              else
                .
              end
          " "${ctrl_file}" > "${patched_file}"

          # Check if there is any diff; if no change, skip apply
          if diff -q "${ctrl_file}" "${patched_file}" >/dev/null 2>&1; then
            echo "    No privileged containers found in template for ${kind} ${ns}/${name}; nothing to patch."
            return
          fi

          echo "    Applying patched template to ${kind} ${ns}/${name} ..."
          kubectl apply -f "${patched_file}"
        }

        # Patch all controllers
        for i in $(seq 0 $((controllers_count - 1))); do
          kind="$(jq -r ".[$i].kind" "${controllers_file}")"
          name="$(jq -r ".[$i].name" "${controllers_file}")"
          ns="$(jq -r ".[$i].ns" "${controllers_file}")"
          patch_controller "${kind}" "${name}" "${ns}"
        done

        # --- Handle standalone pods (no controller) ---

        standalone_pods_file="${WORKDIR}/standalone_pods.json"
        jq '
          [
            .
            | {
                ns: .metadata.namespace,
                name: .metadata.name
              }
          ]
        ' "${WORKDIR}/non_compliant_pods.json" > "${standalone_pods_file}.tmp"

        # Filter out pods that had a controller
        jq '
          . as $pods
          | $pods
          | map(
              . as $p
              | if (input // []) as $owners
                | any(.ns == $p.ns and .pod == $p.name; .)
                then empty
                else $p
                end
            )
        ' "${standalone_pods_file}.tmp" "${owners_file}" > "${standalone_pods_file}"

        standalone_count="$(jq 'length' "${standalone_pods_file}")"

        if [[ "${standalone_count}" -gt 0 ]]; then
          echo "[!] Detected ${standalone_count} privileged Pod(s) without a controlling workload."
          echo "    These must be remediated manually by updating their manifests or recreating them without privileged=true."
          echo "    List of such pods:"
          jq -r '.[] | "- ns=\(.ns) name=\(.name)"' "${standalone_pods_file}"
        fi

        # --- Delete existing non-compliant pods so controllers recreate them ---

        echo "[*] Deleting non-compliant pods so patched controllers can recreate them ..."

        pods_to_delete_file="${WORKDIR}/pods_to_delete.txt"
        jq -r '[.[] | "\(.metadata.namespace) \(.metadata.name)"] | .[]' "${WORKDIR}/non_compliant_pods.json" > "${pods_to_delete_file}"

        while read -r ns name; do
          echo "    Deleting pod ${ns}/${name} ..."
          kubectl delete pod "${name}" -n "${ns}" --ignore-not-found
        done < "${pods_to_delete_file}"

        echo "[*] Waiting for replacements to become Ready ..."
        sleep 10

        # --- Verification (re-run audit) ---

        echo
        echo "[*] Verification: re-running privileged container audit ..."
        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'

        echo
        echo "[*] Automation completed. Review any remaining non-compliant lines above and fix manually (e.g., standalone Pods)."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
