> ## 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. Identify non-compliant pods (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.securityContext.runAsNonRoot // false) as $podNonRoot
             | ((.spec.containers // []) + (.spec.initContainers // []))[]
             | ($podNonRoot or (.securityContext.runAsNonRoot // 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) runAsNonRoot=\($ok)"
               + " is_compliant=false"
             ][]'
           ```

        2. For standalone Pods (not controlled by a higher-level object), export, edit, and re-apply the manifest (run on any machine with kubectl access). Example for one pod:
           ```bash theme={null}
           kubectl -n NAMESPACE get pod POD_NAME -o yaml > /tmp/pod-POD_NAME.yaml
           ```
           Edit `/tmp/pod-POD_NAME.yaml` and add or adjust at the pod spec level:
           ```yaml theme={null}
           spec:
             securityContext:
               runAsNonRoot: true
           ```
           or, if you cannot set it at pod level, add it to every container and initContainer:
           ```yaml theme={null}
           spec:
             containers:
               - name: app
                 image: your-image
                 securityContext:
                   runAsNonRoot: true
             initContainers:
               - name: init
                 image: your-init-image
                 securityContext:
                   runAsNonRoot: true
           ```
           Then re-create the pod (Pods are immutable):
           ```bash theme={null}
           kubectl -n NAMESPACE delete pod POD_NAME
           kubectl -n NAMESPACE apply -f /tmp/pod-POD_NAME.yaml
           ```

        3. For pods owned by a controller (Deployment/ReplicaSet/StatefulSet/DaemonSet/Job/CronJob), edit the controller so all future pods are compliant (run on any machine with kubectl access). Example for a Deployment:
           ```bash theme={null}
           kubectl -n NAMESPACE edit deployment DEPLOYMENT_NAME
           ```
           In the editor, under `spec.template.spec`, set:
           ```yaml theme={null}
           spec:
             template:
               spec:
                 securityContext:
                   runAsNonRoot: true
           ```
           If needed, also set on each container:
           ```yaml theme={null}
           spec:
             template:
               spec:
                 containers:
                   - name: app
                     image: your-image
                     securityContext:
                       runAsNonRoot: true
           ```
           Save and exit; Kubernetes will roll out updated pods.

        4. Repeat step 3 for other controllers (StatefulSet, DaemonSet, Job, CronJob) that own non-compliant pods, using `kubectl edit` on the appropriate resource type and setting `spec.template.spec.securityContext.runAsNonRoot: true` or per-container `securityContext.runAsNonRoot: true`.

        5. If any image fails to start with `runAsNonRoot: true`, review that image on your image build system: ensure the container’s default user is non-root (e.g., via a `USER` directive in the Dockerfile) or explicitly set a non-root `runAsUser` together with `runAsNonRoot: true` in the pod spec, then redeploy.

        6. Verify compliance (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.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"
               + " container=\(.name) image=\(.image) runAsNonRoot=\($ok)"
               + " 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'
           ```
      </Accordion>

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

        1. Identify non-compliant Pods

        ```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
          | select($ok|not)
          | "ns=\($m.namespace) name=\($m.name)"
          ] | unique[]'
        ```

        2. Edit each affected workload manifest and set pod-level `runAsNonRoot: true` (preferred). For example, for a Deployment owning the Pod:

        ```bash theme={null}
        kubectl -n NAMESPACE edit deployment DEPLOYMENT_NAME
        ```

        Under `spec.template.spec`, ensure:

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

        If you cannot set it at pod level (e.g., mixed containers), set it on each container instead:

        ```yaml theme={null}
        spec:
          template:
            spec:
              containers:
                - name: app
                  securityContext:
                    runAsNonRoot: true
              initContainers:
                - name: init
                  securityContext:
                    runAsNonRoot: true
        ```

        Apply the same pattern to other controllers (StatefulSet, DaemonSet, Job, CronJob) using:

        ```bash theme={null}
        kubectl -n NAMESPACE edit statefulset STATEFULSET_NAME
        kubectl -n NAMESPACE edit daemonset DAEMONSET_NAME
        kubectl -n NAMESPACE edit job JOB_NAME
        kubectl -n NAMESPACE edit cronjob CRONJOB_NAME
        ```

        For standalone Pods managed directly (not recommended in GKE production), edit and re-apply:

        ```bash theme={null}
        kubectl -n NAMESPACE get pod POD_NAME -o yaml > /tmp/pod.yaml
        ```

        Edit `/tmp/pod.yaml` to include:

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

        Then delete and recreate:

        ```bash theme={null}
        kubectl -n NAMESPACE delete pod POD_NAME
        kubectl -n NAMESPACE apply -f /tmp/pod.yaml
        ```

        3. Verification

        Run the benchmark audit command again 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.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'
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Remediates: Ensure Pods/containers set securityContext.runAsNonRoot: true
        # Scope: any machine with kubectl access to the cluster
        # Requirements: kubectl, jq, yq (v4+)

        set -euo pipefail

        # ---- Config ----
        # Namespaces to exclude (system namespaces managed by GKE)
        EXCLUDED_NS_REGEX='^(kube-system|kube-public|kube-node-lease)$'

        # ---- Functions ----

        need_bin() {
          if ! command -v "$1" >/dev/null 2>&1; then
            echo "ERROR: '$1' is required but not found in PATH" >&2
            exit 1
          fi
        }

        # Return 0 if the resource kind is namespaced
        is_namespaced_kind() {
          local kind="$1"
          case "$kind" in
            Pod|Deployment|ReplicaSet|StatefulSet|DaemonSet|Job|CronJob)
              return 0
              ;;
            *)
              return 1
              ;;
          esac
        }

        # Patch a single workload manifest to enforce runAsNonRoot
        patch_manifest_run_as_non_root() {
          local file="$1"

          # 1) ensure pod-level securityContext.runAsNonRoot: true
          # 2) ensure every (init)container has securityContext.runAsNonRoot: true
          yq eval '
            .spec.template.spec |= (
              .securityContext //= {} |
              .securityContext.runAsNonRoot = true |
              (.containers // []) |=
                map(.securityContext //= {} | .securityContext.runAsNonRoot = true) |
              (.initContainers // []) |=
                map(.securityContext //= {} | .securityContext.runAsNonRoot = true)
            )
          ' "$file"
        }

        # For plain Pod manifests (no template)
        patch_pod_manifest_run_as_non_root() {
          local file="$1"

          yq eval '
            .spec |= (
              .securityContext //= {} |
              .securityContext.runAsNonRoot = true |
              (.containers // []) |=
                map(.securityContext //= {} | .securityContext.runAsNonRoot = true) |
              (.initContainers // []) |=
                map(.securityContext //= {} | .securityContext.runAsNonRoot = true)
            )
          ' "$file"
        }

        # ---- Pre-flight ----
        need_bin kubectl
        need_bin jq
        need_bin yq

        # ---- Discover non-compliant Pods ----
        echo "Discovering non-compliant Pods (excluding kube-system, kube-public, kube-node-lease)..."

        non_compliant_json="$(kubectl get pods --all-namespaces -o json | jq --arg re "$EXCLUDED_NS_REGEX" '
          .items[]
          | select(.metadata.namespace | test($re) | not)
          | . as $pod
          | (.spec.securityContext.runAsNonRoot // false) as $podNonRoot
          | ((.spec.containers // []) + (.spec.initContainers // []))[]
          | ($podNonRoot or (.securityContext.runAsNonRoot // false)) as $ok
          | select($ok | not)
          | {
              pod_namespace: $pod.metadata.namespace,
              pod_name: $pod.metadata.name,
              container_name: .name,
              owner: ([$pod.metadata.ownerReferences[]? | select(.controller)] | first // null)
            }
        ' 2>/dev/null || true)"

        if [[ -z "$non_compliant_json" ]]; then
          echo "No Pods found."
          exit 0
        fi

        non_compliant_count="$(echo "$non_compliant_json" | jq -s 'length')"
        if [[ "$non_compliant_count" -eq 0 ]]; then
          echo "All Pods are compliant."
        else
          echo "Found $non_compliant_count non-compliant container instances."
        fi

        # ---- Group by owner to identify manageable workloads ----
        # We will patch only supported, namespaced controllers with server-side apply.
        echo "Identifying parent workloads to patch..."

        # Build list of unique (kind, namespace, name) for owners
        owners_json="$(
          echo "$non_compliant_json" | jq -s '
            map(select(.owner != null))
            | map({
                kind: .owner.kind,
                namespace: .pod_namespace,
                name: .owner.name
              })
            | unique
          '
        )"

        # Also collect standalone Pods (no controller) to patch or flag for manual fix
        standalone_pods_json="$(
          echo "$non_compliant_json" | jq -s '
            map(select(.owner == null))
            | map({
                kind: "Pod",
                namespace: .pod_namespace,
                name: .pod_name
              })
            | unique
          '
        )"

        echo "Workloads with non-compliant containers:"
        echo "$owners_json" | jq -r '.[] | "\(.kind) \(.namespace)/\(.name)"' 2>/dev/null || true

        echo "Standalone Pods with non-compliant containers (may not be safe to patch automatically):"
        echo "$standalone_pods_json" | jq -r '.[] | "\(.kind) \(.namespace)/\(.name)"' 2>/dev/null || true

        # ---- Patch controllers (Deployments, etc.) via kubectl ----
        echo "Patching parent workloads via kubectl..."

        echo "$owners_json" | jq -c '.[]' | while read -r owner; do
          kind="$(echo "$owner" | jq -r '.kind')"
          ns="$(echo "$owner" | jq -r '.namespace')"
          name="$(echo "$owner" | jq -r '.name')"

          if ! is_namespaced_kind "$kind"; then
            echo "Skipping unsupported owner kind: $kind $ns/$name"
            continue
          fi

          echo "Processing $kind $ns/$name ..."

          tmpfile="$(mktemp)"
          trap 'rm -f "$tmpfile"' EXIT

          # Export manifest
          if ! kubectl get "$kind" "$name" -n "$ns" -o yaml > "$tmpfile"; then
            echo "  WARN: could not get $kind $ns/$name, skipping."
            rm -f "$tmpfile"
            trap - EXIT
            continue
          fi

          # Patch manifest in-place, depending on kind
          case "$kind" in
            Pod)
              patched="$(patch_pod_manifest_run_as_non_root "$tmpfile")"
              ;;
            *)
              patched="$(patch_manifest_run_as_non_root "$tmpfile")"
              ;;
          esac

          echo "$patched" > "$tmpfile"

          # Apply with server-side apply (idempotent)
          if ! kubectl apply -f "$tmpfile" --server-side --force-conflicts >/dev/null; then
            echo "  WARN: failed to apply patched manifest for $kind $ns/$name"
          else
            echo "  Patched $kind $ns/$name"
          fi

          rm -f "$tmpfile"
          trap - EXIT
        done

        # ---- Standalone Pods ----
        # These are often ephemeral or manually managed. We only print guidance.
        standalone_count="$(echo "$standalone_pods_json" | jq -s 'length' 2>/dev/null || echo 0)"
        if [[ "$standalone_count" -gt 0 ]]; then
          echo
          echo "NOTE: Standalone Pods listed above are still non-compliant."
          echo "To fix them, update their Pod specs or the process that creates them to set:"
          echo "  spec.securityContext.runAsNonRoot: true"
          echo "  spec.containers[].securityContext.runAsNonRoot: true"
        fi

        # ---- Verification ----
        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.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 | map(select(. | contains("is_compliant=false"))) | length) == 0
            then "is_compliant=true"
            else ($rows[] | select(. | contains("is_compliant=false")))
            end
        '
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
