> ## 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 Disallow Privilege Escalation

### More Info:

Verifies allowPrivilegeEscalation is false on every container. It defaults to true, letting a process gain more privileges than its parent.

### Risk Level

High

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. List all non-system pods and identify non-compliant ones (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl get pods --all-namespaces \
             --field-selector metadata.namespace!=kube-system,metadata.namespace!=kube-public,metadata.namespace!=kube-node-lease \
             -o json | jq -r '
             .items[]
             | .metadata as $m
             | ((.spec.containers // []) + (.spec.initContainers // []))[]
             | select(.securityContext.allowPrivilegeEscalation != false)
             | "ns=\($m.namespace) pod=\($m.name) container=\(.name) ape=\(if .securityContext.allowPrivilegeEscalation==null then "unset" else .securityContext.allowPrivilegeEscalation end)"'
           ```

        2. For a pod you need to fix, find its owning controller (Deployment, StatefulSet, DaemonSet, Job, etc.) (run on any machine with kubectl access):
           ```bash theme={null}
           NAMESPACE="<namespace-of-pod>"
           POD_NAME="<pod-name>"

           kubectl get pod "${POD_NAME}" -n "${NAMESPACE}" -o jsonpath='{.metadata.ownerReferences}'
           ```
           Note the `kind` and `name`. You will edit that controller, not the pod itself.

        3. Edit the owning controller manifest to set `allowPrivilegeEscalation: false` on every container (run on any machine with kubectl access):
           ```bash theme={null}
           KIND="<Deployment|StatefulSet|DaemonSet|Job|CronJob>"
           NAME="<controller-name>"
           NAMESPACE="<namespace>"

           kubectl get "${KIND}" "${NAME}" -n "${NAMESPACE}" -o yaml > /tmp/${KIND}-${NAME}.yaml
           ```
           Open `/tmp/${KIND}-${NAME}.yaml` in an editor and, under each container (and initContainer) in `spec.template.spec.containers` and `spec.template.spec.initContainers`, ensure:
           ```yaml theme={null}
           securityContext:
             allowPrivilegeEscalation: false
           ```
           If `securityContext` exists, add only the `allowPrivilegeEscalation: false` line; do not remove other fields.

        4. Apply the updated controller manifest so new pods are compliant (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl apply -f /tmp/${KIND}-${NAME}.yaml
           ```

        5. (Optional but recommended) Rotate existing non-compliant pods so they are recreated from the updated controller (run on any machine with kubectl access):
           ```bash theme={null}
           # For a Deployment
           kubectl rollout restart deployment "${NAME}" -n "${NAMESPACE}"

           # For a DaemonSet
           # kubectl rollout restart daemonset "${NAME}" -n "${NAMESPACE}"

           # For a StatefulSet
           # kubectl rollout restart statefulset "${NAME}" -n "${NAMESPACE}"
           ```

        6. Verification (run on 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.allowPrivilegeEscalation == false) as $ok
             | "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)"
               + " allowPrivilegeEscalation=\(if .securityContext.allowPrivilegeEscalation == null then "unset" else .securityContext.allowPrivilegeEscalation end)"
               + " is_compliant=\(if $ok then "true" else "false" end)"
             ] as $rows
             | if ($rows | map(select(. | contains("is_compliant=false"))) | length) == 0
               then "is_compliant=true"
               else $rows[]
               end'
           ```
           Confirm the output is `is_compliant=true` or that all listed containers show `is_compliant=true`.
      </Accordion>

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

        1. Identify non-compliant Pods (for context)

        ```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.allowPrivilegeEscalation == false) as $ok
          | select($ok | not)
          | "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)"
            + " allowPrivilegeEscalation=\(if .securityContext.allowPrivilegeEscalation == null then "unset" else .securityContext.allowPrivilegeEscalation end)"
            + " is_compliant=false"
          ][]'
        ```

        2. Edit the owning workload manifest to set `allowPrivilegeEscalation: false` on every container.

        Example `Deployment` spec snippet (applies to any workload kind: Deployment, StatefulSet, DaemonSet, Job, CronJob, etc.):

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: example
          namespace: default
        spec:
          template:
            spec:
              containers:
                - name: app
                  image: nginx:1.27
                  securityContext:
                    allowPrivilegeEscalation: false
              initContainers:
                - name: init-app
                  image: busybox:1.36
                  securityContext:
                    allowPrivilegeEscalation: false
        ```

        Apply the updated manifest:

        ```bash theme={null}
        kubectl apply -f deployment-example.yaml
        ```

        Repeat for every workload so that each `spec.template.spec.containers[]` and `spec.template.spec.initContainers[]` entry has:

        ```yaml theme={null}
        securityContext:
          allowPrivilegeEscalation: false
        ```

        3. For naked Pods created directly (no controller), recreate them from a manifest that sets the field:

        ```yaml theme={null}
        apiVersion: v1
        kind: Pod
        metadata:
          name: example-pod
          namespace: default
        spec:
          containers:
            - name: app
              image: nginx:1.27
              securityContext:
                allowPrivilegeEscalation: false
          initContainers:
            - name: init-app
              image: busybox:1.36
              securityContext:
                allowPrivilegeEscalation: false
        ```

        Apply:

        ```bash theme={null}
        kubectl delete pod -n default example-pod --wait=true
        kubectl apply -f pod-example.yaml
        ```

        4. Verification (on 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.allowPrivilegeEscalation == false) as $ok
          | "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)"
            + " allowPrivilegeEscalation=\(if .securityContext.allowPrivilegeEscalation == null then "unset" else .securityContext.allowPrivilegeEscalation end)"
            + " is_compliant=\(if $ok then "true" else "false" end)"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
        ```

        You are compliant when the output is `is_compliant=true` or all listed containers show `is_compliant=true`.
      </Accordion>

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

        # Applies securityContext.allowPrivilegeEscalation: false
        # to every container and initContainer in non-system Pods
        # by patching their owning workload objects via kubectl.
        #
        # Run on: any machine with kubectl access to the OKE cluster.
        # Requirements: kubectl, jq, yq (https://github.com/mikefarah/yq)

        if ! command -v kubectl >/dev/null 2>&1; then
          echo "kubectl not found in PATH" >&2
          exit 1
        fi
        if ! command -v jq >/dev/null 2>&1; then
          echo "jq not found in PATH" >&2
          exit 1
        fi
        if ! command -v yq >/dev/null 2>&1; then
          echo "yq not found in PATH (https://github.com/mikefarah/yq)" >&2
          exit 1
        fi

        WORKDIR="$(mktemp -d)"
        trap 'rm -rf "$WORKDIR"' EXIT

        # 1. Discover non-compliant Pods and their controllers
        echo "Discovering non-compliant Pods and their controllers..."
        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.allowPrivilegeEscalation != false)
          | select(.image != "k8s.gcr.io/pause:3.2") # ignore pause-type containers if any
          | {
              ns: $m.namespace,
              pod: $m.name,
              ownerKind: ($own.kind // "Pod"),
              ownerName: ($own.name // $m.name)
            }
          ] | unique
          | .[]
          | @tsv "\(.ns)\t\(.pod)\t\(.ownerKind)\t\(.ownerName)"
        ' > "$WORKDIR/non_compliant_pods.tsv" || true

        if [ ! -s "$WORKDIR/non_compliant_pods.tsv" ]; then
          echo "No non-compliant Pods found."
        else
          echo "Found non-compliant Pods:"
          cat "$WORKDIR/non_compliant_pods.tsv"
        fi

        # 2. Build a unique list of owner workloads to patch
        echo
        echo "Building workload list to patch..."
        awk -F'\t' '{print $1"\t"$3"\t"$4}' "$WORKDIR/non_compliant_pods.tsv" 2>/dev/null | sort -u > "$WORKDIR/owners.tsv" || true

        if [ ! -s "$WORKDIR/owners.tsv" ]; then
          echo "No controller workloads to patch (likely only bare Pods)."
        fi

        # Helper: patch a single workload manifest (stdin) to set allowPrivilegeEscalation: false
        # for all containers and initContainers. Outputs patched manifest to stdout.
        patch_manifest() {
          yq '
            # Ensure securityContext exists for each container
            (.spec.template.spec.containers // []) |=
              map(
                .securityContext.allowPrivilegeEscalation = false
              )
            |
            (.spec.template.spec.initContainers // []) |=
              map(
                .securityContext.allowPrivilegeEscalation = false
              )
          '
        }

        # 3. Patch owners (Deployments, DaemonSets, StatefulSets, Jobs, CronJobs, etc.)
        echo
        echo "Patching controller workloads to disallow privilege escalation..."
        while IFS=$'\t' read -r NS KIND NAME; do
          [ -z "$NS" ] && continue
          echo "Processing $KIND $NS/$NAME..."

          # Detect API group (apps/v1 or batch/v1 / batch/v1beta1 etc.) via kubectl get
          # and then re-fetch with -o yaml.
          if ! kubectl get "$KIND" -n "$NS" "$NAME" >/dev/null 2>&1; then
            echo "  Skipping: $KIND $NS/$NAME not found anymore."
            continue
          fi

          ORIG_YAML="$WORKDIR/${NS}_${KIND}_${NAME}_orig.yaml"
          PATCHED_YAML="$WORKDIR/${NS}_${KIND}_${NAME}_patched.yaml"

          kubectl get "$KIND" -n "$NS" "$NAME" -o yaml > "$ORIG_YAML"

          # Some kinds (like CronJob) have spec.jobTemplate.spec.template; handle generically:
          # Try to patch spec.template first; if missing, try spec.jobTemplate.spec.template.
          if yq '.spec.template' "$ORIG_YAML" >/dev/null 2>&1; then
            patch_manifest < "$ORIG_YAML" > "$PATCHED_YAML"
          else
            # Fallback for CronJob-style objects
            yq '
              (.spec.jobTemplate.spec.template.spec.containers // []) |=
                map(.securityContext.allowPrivilegeEscalation = false)
              |
              (.spec.jobTemplate.spec.template.spec.initContainers // []) |=
                map(.securityContext.allowPrivilegeEscalation = false)
            ' < "$ORIG_YAML" > "$PATCHED_YAML"
          fi

          # Apply only if changed (idempotent)
          if diff -q "$ORIG_YAML" "$PATCHED_YAML" >/dev/null 2>&1; then
            echo "  No changes needed (already compliant)."
            continue
          fi

          kubectl apply -n "$NS" -f "$PATCHED_YAML"
          echo "  Patched and applied."
        done < "$WORKDIR/owners.tsv"

        # 4. Handle standalone Pods (no controller) by patching Pod spec directly
        echo
        echo "Patching standalone Pods (no controller)..."
        awk -F'\t' '$3=="Pod" {print $1"\t"$2}' "$WORKDIR/non_compliant_pods.tsv" 2>/dev/null | sort -u > "$WORKDIR/standalone_pods.tsv" || true

        if [ -s "$WORKDIR/standalone_pods.tsv" ]; then
          while IFS=$'\t' read -r NS POD; do
            [ -z "$NS" ] && continue
            echo "Processing standalone Pod $NS/$POD..."

            if ! kubectl get pod -n "$NS" "$POD" >/dev/null 2>&1; then
              echo "  Skipping: Pod $NS/$POD not found anymore."
              continue
            fi

            ORIG_POD_YAML="$WORKDIR/pod_${NS}_${POD}_orig.yaml"
            PATCHED_POD_YAML="$WORKDIR/pod_${NS}_${POD}_patched.yaml"

            kubectl get pod -n "$NS" "$POD" -o yaml > "$ORIG_POD_YAML"

            yq '
              (.spec.containers // []) |=
                map(.securityContext.allowPrivilegeEscalation = false)
              |
              (.spec.initContainers // []) |=
                map(.securityContext.allowPrivilegeEscalation = false)
            ' < "$ORIG_POD_YAML" > "$PATCHED_POD_YAML"

            if diff -q "$ORIG_POD_YAML" "$PATCHED_POD_YAML" >/dev/null 2>&1; then
              echo "  No changes needed (already compliant)."
              continue
            fi

            kubectl replace -n "$NS" -f "$PATCHED_POD_YAML"
            echo "  Patched Pod."
          done < "$WORKDIR/standalone_pods.tsv"
        else
          echo "No standalone Pods to patch."
        fi

        # 5. Verification using the authoritative audit command
        echo
        echo "Re-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.allowPrivilegeEscalation == false) as $ok
          | "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)"
            + " allowPrivilegeEscalation=\(if .securityContext.allowPrivilegeEscalation == null then "unset" else .securityContext.allowPrivilegeEscalation end)"
            + " is_compliant=\(if $ok then "true" else "false" end)"
          ] as $rows
          | if ($rows | map(select(. | contains("is_compliant=false"))) | length) == 0
              then "is_compliant=true"
              else ($rows[] | select(. | contains("is_compliant=false")))
            end
        '
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
