> ## 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 the non‑compliant pods and pick the one(s) to fix:
           ```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) container=\(.name) image=\(.image)"
           ][]'
           ```

        2. For a pod created from a higher‑level controller (Deployment, DaemonSet, StatefulSet, Job, CronJob), edit the controller manifest so the setting persists. For example, for a Deployment (run on any kubectl machine):
           ```bash theme={null}
           kubectl -n NAMESPACE edit deployment DEPLOYMENT_NAME
           ```
           Under `spec.template.spec`, add or update a pod‑level security context:
           ```yaml theme={null}
           spec:
             template:
               spec:
                 securityContext:
                   runAsNonRoot: true
           ```
           If you need per‑container overrides instead (e.g., only some containers must be non‑root), set on each container:
           ```yaml theme={null}
           spec:
             template:
               spec:
                 containers:
                   - name: CONTAINER_NAME
                     securityContext:
                       runAsNonRoot: true
           ```
           Save and exit; Kubernetes will roll out new pods.

        3. For standalone Pods not managed by a controller, first export the manifest (any kubectl machine):
           ```bash theme={null}
           kubectl -n NAMESPACE get pod POD_NAME -o yaml > /tmp/pod-POD_NAME.yaml
           ```
           In `/tmp/pod-POD_NAME.yaml`, delete runtime‑only fields like `status:`, `metadata.resourceVersion`, `metadata.uid`, `metadata.creationTimestamp`, and `metadata.managedFields`. Under `spec`, add either:
           ```yaml theme={null}
           spec:
             securityContext:
               runAsNonRoot: true
           ```
           or, per container:
           ```yaml theme={null}
           spec:
             containers:
               - name: CONTAINER_NAME
                 securityContext:
                   runAsNonRoot: true
           ```

        4. Apply the corrected standalone Pod manifest and recreate the pod (any kubectl machine):
           ```bash theme={null}
           kubectl -n NAMESPACE delete pod POD_NAME
           kubectl apply -f /tmp/pod-POD_NAME.yaml
           ```

        5. For OKE workloads managed via IaC or GitOps, mirror the same `securityContext.runAsNonRoot: true` changes in the source manifests or Helm charts (e.g., in your Git repository) so future deployments do not revert the setting. Commit and redeploy via your normal pipeline.

        6. Verify all pods now comply (any kubectl machine):
           ```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'
           ```
           Confirm that either the output is exactly `is_compliant=true` or every listed container line ends with `is_compliant=true`.
      </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.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. For a pod controlled by a Deployment/ReplicaSet/DaemonSet/Job/etc., patch the controller (example: Deployment)

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

        Edit `/tmp/deployment-<name>.yaml` and under `spec.template.spec` add (or adjust) the pod-level security context:

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: <name>
          namespace: <namespace>
        spec:
          template:
            spec:
              securityContext:
                runAsNonRoot: true
              containers:
                - name: <container-name>
                  image: <image>
                  # container-specific securityContext is optional if pod-level is set
                  # securityContext:
                  #   runAsNonRoot: true
        ```

        Apply the updated manifest:

        ```bash theme={null}
        kubectl apply -f /tmp/deployment-<name>.yaml
        ```

        If you prefer to set it per container instead of pod-level:

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

        3. For standalone Pods (no ownerReferences.controller), edit and re-apply

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

        Edit `/tmp/pod-<pod-name>.yaml` so the pod spec has one of:

        Pod-level:

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

        Or per container:

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

        Then recreate the pod:

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

        4. Verification (same audit logic as check)

        ```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
          | "kind=Pod ns=\($m.namespace) name=\($m.name) container=\(.name) 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="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Enforce securityContext.runAsNonRoot: true on all non-excluded Pods
        # by updating their owning workload manifests via kubectl.
        #
        # Runs from: any machine with kubectl access to the OKE cluster.
        # Requirements: bash, kubectl, jq, yq (https://github.com/mikefarah/yq)
        #
        # Notes:
        # - This is idempotent: re-running only re-applies the same settings.
        # - It patches the OWNER workload (Deployment, StatefulSet, DaemonSet,
        #   Job, CronJob, ReplicaSet) rather than the live Pod.
        # - Pods in kube-system, kube-public, kube-node-lease are skipped,
        #   matching the audit command.
        # - You MUST review and test in non‑prod first; some images may REQUIRE root.
        #

        set -euo pipefail

        # Fail fast if required tools not present
        for bin in kubectl jq yq; do
          if ! command -v "$bin" >/dev/null 2>&1; then
            echo "ERROR: $bin not found in PATH. Please install it before running this script." >&2
            exit 1
          fi
        done

        # Get non-compliant pod rows from the audit query
        echo "Discovering non-compliant containers (runAsNonRoot=false or unset)..."
        mapfile -t NON_COMPLIANT_ROWS < <(
          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"
            ] as $rows
            | if ($rows | length) == 0 then empty else $rows[] end
          '
        )

        if [ "${#NON_COMPLIANT_ROWS[@]}" -eq 0 ]; then
          echo "No non-compliant containers found. Nothing to do."
          exit 0
        fi

        echo "Found ${#NON_COMPLIANT_ROWS[@]} non-compliant container entries."

        # Build a unique list of owners (kind, namespace, name)
        declare -A OWNERS
        for row in "${NON_COMPLIANT_ROWS[@]}"; do
          # owner=Kind/ns/name/uid
          owner_field=$(grep -o 'owner=[^ ]*' <<<"$row" || true)
          if [ -z "$owner_field" ]; then
            # Standalone Pod (no controller) – patch the Pod template directly
            # Key format: Pod|<namespace>|<name>
            ns=$(grep -o 'ns=[^ ]*' <<<"$row" | cut -d= -f2)
            podname=$(grep -o 'name=[^ ]*' <<<"$row" | cut -d= -f2)
            key="Pod|${ns}|${podname}"
          else
            owner_val=${owner_field#owner=}
            IFS=/ read -r owner_kind owner_ns owner_name _uid <<<"$owner_val"
            key="${owner_kind}|${owner_ns}|${owner_name}"
          fi
          OWNERS["$key"]=1
        done

        echo "Will patch ${#OWNERS[@]} owning resources (Pods/Controllers)."

        # Function: patch a workload YAML to set runAsNonRoot: true
        patch_manifest_run_as_non_root() {
          local yaml_file=$1
          # Pod spec path may vary by kind
          # - Pod, Deployment, DaemonSet, StatefulSet, ReplicaSet, Job: .spec.template.spec or .spec
          # - CronJob: .spec.jobTemplate.spec.template.spec
          # We’ll set both pod-level and per-container for safety.

          yq -i '
            # Detect base path for pod spec
            ( .kind == "Pod" )
            as $isPod |

            ( .kind == "CronJob" )
            as $isCron |

            # Helper for setting runAsNonRoot on a pod spec node
            def set_runnr(path):
              . as $root
              | (path | $root) as $pod
              | if $pod == null then $root
                else
                  $root
                  | (path + ".securityContext.runAsNonRoot") |= (true)
                  | (path + ".containers[]?.securityContext.runAsNonRoot") |= (true)
                  | (path + ".initContainers[]?.securityContext.runAsNonRoot") |= (true)
                end;

            # For Pod
            if $isPod then
              set_runnr(".spec")
            # For CronJob: jobTemplate.spec.template.spec
            elif $isCron then
              set_runnr(".spec.jobTemplate.spec.template.spec")
            # For other workload types: spec.template.spec
            else
              set_runnr(".spec.template.spec")
            end
          ' "$yaml_file"
        }

        # Process each unique owner
        for key in "${!OWNERS[@]}"; do
          IFS='|' read -r kind ns name <<<"$key"

          echo "Processing owner: kind=${kind} ns=${ns} name=${name}"

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

          if [ "$kind" = "Pod" ]; then
            # Standalone Pod: get and patch the Pod manifest
            if ! kubectl get pod "$name" -n "$ns" -o yaml >"$tmp_yaml"; then
              echo "WARNING: Pod ${ns}/${name} not found, skipping."
              continue
            fi
          else
            if ! kubectl get "$kind" "$name" -n "$ns" -o yaml >"$tmp_yaml"; then
              echo "WARNING: ${kind} ${ns}/${name} not found, skipping."
              continue
            fi
          fi

          # Patch the manifest locally
          patch_manifest_run_as_non_root "$tmp_yaml"

          # Apply back to the cluster
          echo "  Applying patched manifest for ${kind} ${ns}/${name}..."
          kubectl apply -f "$tmp_yaml"

          rm -f "$tmp_yaml"
          trap - EXIT
        done

        echo "Patching complete. Waiting for workloads to reconcile..."
        sleep 10

        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.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
        ' | tee /tmp/runAsNonRoot_verification.txt

        echo "Verification output saved to /tmp/runAsNonRoot_verification.txt"
        echo "Review any lines with is_compliant=false; those require manual analysis (image may require root or owner kind unsupported by this script)."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
