> ## 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 Run As Non-Root

### More Info:

Verifies runAsNonRoot is set at pod or container level. Running as root inside a container widens the impact of a container escape.

### 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. On any machine with kubectl access, list non-compliant pods using the audit command and capture the output for reference:
           ```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.securityContext.runAsNonRoot // false) as $podNonRoot
             | ((.spec.containers // []) + (.spec.initContainers // []))[]
             | ($podNonRoot or (.securityContext.runAsNonRoot // 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) runAsNonRoot=\($ok)"
               + " is_compliant=\(if $ok then "true" else "false" end)"
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end' \
           | grep 'is_compliant=false' || true
           ```

        2. For each non-compliant pod that is managed by a higher-level controller (Deployment, StatefulSet, DaemonSet, Job, CronJob), identify its owner and edit that resource to set `runAsNonRoot: true` at the pod level. Example for a Deployment owner in namespace `my-namespace` named `my-app` (replace with actual values from `owner=` in the audit output):
           ```bash theme={null}
           kubectl -n my-namespace edit deployment my-app
           ```
           In the opened manifest, under `spec.template.spec`, add or update:
           ```yaml theme={null}
           spec:
             template:
               spec:
                 securityContext:
                   runAsNonRoot: true
           ```
           Save and exit to apply the change.

        3. If you prefer to patch instead of interactive edit, on any machine with kubectl access run a JSON patch for each affected controller. Example for the same Deployment (adjust kind/name/namespace per resource):
           ```bash theme={null}
           kubectl -n my-namespace patch deployment my-app \
             --type merge \
             -p '{
               "spec": {
                 "template": {
                   "spec": {
                     "securityContext": {
                       "runAsNonRoot": true
                     }
                   }
                 }
               }
             }'
           ```

        4. For non-compliant pods that are not controlled by a higher-level resource (no `owner=` field in the audit output), retrieve the current pod manifest and recreate it with `runAsNonRoot: true` set. On any machine with kubectl access:
           ```bash theme={null}
           kubectl -n my-namespace get pod my-pod -o yaml > /tmp/my-pod.yaml
           ```
           Edit `/tmp/my-pod.yaml` and under `spec` add:
           ```yaml theme={null}
           spec:
             securityContext:
               runAsNonRoot: true
           ```
           Then delete and recreate the pod (it will not be automatically recreated because it has no controller):
           ```bash theme={null}
           kubectl -n my-namespace delete pod my-pod
           kubectl -n my-namespace apply -f /tmp/my-pod.yaml
           ```

        5. If any container must explicitly override the pod-level setting, ensure each container and initContainer that should be non-root has `securityContext.runAsNonRoot: true` defined. In the relevant controller or pod manifest, under each container:
           ```yaml theme={null}
           spec:
             template:
               spec:
                 containers:
                   - name: my-container
                     image: my-image
                     securityContext:
                       runAsNonRoot: true
           ```
           Apply the updated manifest using:
           ```bash theme={null}
           kubectl -n my-namespace apply -f <updated-manifest>.yaml
           ```

        6. After changes have rolled out and pods are running with updated specs, verify compliance from any machine with kubectl access by re-running the audit command and confirming no `is_compliant=false` lines remain:
           ```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.securityContext.runAsNonRoot // false) as $podNonRoot
             | ((.spec.containers // []) + (.spec.initContainers // []))[]
             | ($podNonRoot or (.securityContext.runAsNonRoot // 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) runAsNonRoot=\($ok)"
               + " is_compliant=\(if $ok then "true" else "false" end)"
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end' \
           | grep 'is_compliant=false' || echo "All checked pods are compliant (runAsNonRoot=true)."
           ```
      </Accordion>

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

        1. Identify non-compliant pods (from the audit output you already have) and note their controllers (Deployment/StatefulSet/DaemonSet/Job/CronJob) if present in the `owner=` field. Always fix the controller, not the live Pod.

        2. Edit the owning controller manifest and add `runAsNonRoot: true` under `securityContext` at the pod level (preferred) or per container.

        Example: Deployment (pod-level `securityContext`)

        ```bash theme={null}
        kubectl -n <namespace> get deploy <name> -o yaml > /tmp/deploy-nonroot.yaml
        ```

        Edit `/tmp/deploy-nonroot.yaml` and under `spec.template.spec` add:

        ```yaml theme={null}
        spec:
          template:
            spec:
              securityContext:
                runAsNonRoot: true
              containers:
              - name: <container-name>
                image: <image>
                # existing fields...
        ```

        Apply:

        ```bash theme={null}
        kubectl apply -f /tmp/deploy-nonroot.yaml
        ```

        Example: Deployment (container-level `securityContext` if you cannot set pod-level)

        ```yaml theme={null}
        spec:
          template:
            spec:
              containers:
              - name: <container-name>
                image: <image>
                securityContext:
                  runAsNonRoot: true
        ```

        3. Repeat the same pattern for other controller types:

        StatefulSet:

        ```bash theme={null}
        kubectl -n <namespace> get statefulset <name> -o yaml > /tmp/sts-nonroot.yaml
        # edit as above, then:
        kubectl apply -f /tmp/sts-nonroot.yaml
        ```

        DaemonSet:

        ```bash theme={null}
        kubectl -n <namespace> get daemonset <name> -o yaml > /tmp/ds-nonroot.yaml
        # edit as above, then:
        kubectl apply -f /tmp/ds-nonroot.yaml
        ```

        Job:

        ```bash theme={null}
        kubectl -n <namespace> get job <name> -o yaml > /tmp/job-nonroot.yaml
        # edit under spec.template.spec, then:
        kubectl apply -f /tmp/job-nonroot.yaml
        ```

        CronJob:

        ```bash theme={null}
        kubectl -n <namespace> get cronjob <name> -o yaml > /tmp/cronjob-nonroot.yaml
        # edit under spec.jobTemplate.spec.template.spec, then:
        kubectl apply -f /tmp/cronjob-nonroot.yaml
        ```

        4. For standalone Pods (no `owner=` in audit output), recreate them with a manifest that includes `runAsNonRoot: true`:

        ```bash theme={null}
        kubectl -n <namespace> get pod <pod-name> -o yaml > /tmp/pod-nonroot.yaml
        ```

        Edit `/tmp/pod-nonroot.yaml` to add:

        ```yaml theme={null}
        spec:
          securityContext:
            runAsNonRoot: true
        ```

        or per container:

        ```yaml theme={null}
        spec:
          containers:
          - name: <container-name>
            image: <image>
            securityContext:
              runAsNonRoot: true
        ```

        Delete fields that must not be reused (`status`, `metadata.uid`, `metadata.resourceVersion`, `metadata.creationTimestamp`, `metadata.managedFields`), then:

        ```bash theme={null}
        kubectl delete pod -n <namespace> <pod-name>
        kubectl apply -f /tmp/pod-nonroot.yaml
        ```

        5. Verification (on any machine with kubectl):

        ```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.securityContext.runAsNonRoot // false) as $podNonRoot
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | ($podNonRoot or (.securityContext.runAsNonRoot // false)) as $ok
          | select($ok | not)
          ] | if (length)==0 then "is_compliant=true" else . end'
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Enforce runAsNonRoot=true on all non-system Pods in an AKS cluster
        # Scope: any machine with kubectl access
        #
        # Behavior:
        # - Skips kube-system, kube-public, kube-node-lease
        # - Patches only Pods/containers that lack runAsNonRoot=true
        # - Adds pod.spec.securityContext.runAsNonRoot=true when possible
        # - If Pod already has a pod-level runAsUser or runAsGroup, you must
        #   review manually – this script will not change user IDs.
        # - Safe to re-run (idempotent): patches only where needed.

        set -euo pipefail

        # Ensure dependencies
        command -v kubectl >/dev/null 2>&1 || {
          echo "kubectl not found in PATH" >&2
          exit 1
        }
        command -v jq >/dev/null 2>&1 || {
          echo "jq not found in PATH" >&2
          exit 1
        }

        echo "Discovering non-compliant Pods (this may take a moment)..."

        NON_COMPLIANT_JSON=$(
          kubectl get pods --all-namespaces -o json | jq -c '
            .items[]
            | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
            as $pod
            | ($pod.spec.securityContext.runAsNonRoot // false) as $podNonRoot
            | (($pod.spec.containers // []) + ($pod.spec.initContainers // [])) as $allContainers
            | if ($allContainers | length) == 0 then empty else
                # Container-level compliance check
                ($allContainers
                  | map(($podNonRoot or (.securityContext.runAsNonRoot // false)))
                  | all) as $all_ok
                | if $all_ok and $podNonRoot then
                    empty   # fully compliant
                  else
                    $pod    # needs attention
                  end
              end
          '
        )

        if [[ -z "${NON_COMPLIANT_JSON}" ]]; then
          echo "All relevant Pods are already compliant (runAsNonRoot=true)."
          exit 0
        fi

        echo "Patching Pods to set spec.securityContext.runAsNonRoot=true where missing..."

        # Helper: patch a single Pod to set pod-level runAsNonRoot=true
        patch_pod_run_as_non_root() {
          local ns="$1"
          local name="$2"

          # Get current pod.spec.securityContext (may be null)
          local current_sc
          current_sc="$(kubectl -n "${ns}" get pod "${name}" -o json | jq -c '.spec.securityContext')"

          if [[ "${current_sc}" == "null" ]]; then
            # No pod-level securityContext; create one with runAsNonRoot=true
            kubectl -n "${ns}" patch pod "${name}" \
              --type merge \
              -p '{"spec":{"securityContext":{"runAsNonRoot":true}}}' >/dev/null
          else
            # Merge in runAsNonRoot=true preserving other fields
            # jq is used client-side to prepare a merge patch
            local patched
            patched="$(jq -c '. + {"runAsNonRoot":true}' <<< "${current_sc}")"
            # Apply as a merge patch to spec.securityContext
            kubectl -n "${ns}" patch pod "${name}" \
              --type merge \
              -p "{\"spec\":{\"securityContext\":${patched}}}" >/dev/null
          fi
        }

        # Iterate over non-compliant Pods and patch them
        while IFS= read -r pod_json; do
          ns=$(jq -r '.metadata.namespace' <<< "${pod_json}")
          name=$(jq -r '.metadata.name' <<< "${pod_json}")

          echo "Processing Pod ${ns}/${name}..."

          # Inspect existing pod-level securityContext for manual review needs
          pod_sc="$(jq -c '.spec.securityContext // {}' <<< "${pod_json}")"
          has_run_as_user="$(jq -r 'has("runAsUser")' <<< "${pod_sc}")"
          has_run_as_group="$(jq -r 'has("runAsGroup")' <<< "${pod_sc}")"

          if [[ "${has_run_as_user}" == "true" || "${has_run_as_group}" == "true" ]]; then
            echo "  WARNING: Pod ${ns}/${name} has pod-level runAsUser/runAsGroup."
            echo "           Review this Pod manually to ensure the user is non-root."
            echo "           Skipping automatic patch for this Pod."
            continue
          fi

          patch_pod_run_as_non_root "${ns}" "${name}"
          echo "  Patched: set spec.securityContext.runAsNonRoot=true"

        done <<< "${NON_COMPLIANT_JSON}"

        echo
        echo "Re-running compliance audit to verify..."

        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.securityContext.runAsNonRoot // false) as $podNonRoot
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | ($podNonRoot or (.securityContext.runAsNonRoot // false)) as $ok
          | "ns=\($m.namespace) name=\($m.name) container=\(.name) runAsNonRoot=\($ok)"
            + " is_compliant=\(if $ok then "true" else "false" end)"
          ] as $rows
          | if ($rows | all(contains("is_compliant=true"))) then
              "is_compliant=true"
            else
              $rows[]
            end
        '

        echo "Automation run completed."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
