> ## 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 a standalone Pod (no owner/controller), edit the Pod manifest to set `runAsNonRoot: true` at pod level (preferred) or per container (run on any machine with kubectl access):
           ```bash theme={null}
           kubectl -n <NAMESPACE> get pod <POD_NAME> -o yaml > /tmp/pod-fixed.yaml
           ```
           Edit `/tmp/pod-fixed.yaml`:
           * Under `spec:`, add or update:
             ```yaml theme={null}
             securityContext:
               runAsNonRoot: true
             ```
           * Remove `metadata.resourceVersion`, `metadata.uid`, `metadata.creationTimestamp`, and `status:` block.
             Apply the fixed manifest:
           ```bash theme={null}
           kubectl -n <NAMESPACE> delete pod <POD_NAME>
           kubectl apply -f /tmp/pod-fixed.yaml
           ```

        3. For pods managed by a controller (Deployment/StatefulSet/DaemonSet/Job/CronJob), patch the controller spec so all current and future pods inherit `runAsNonRoot: true` (run on any machine with kubectl access). Example for a Deployment:
           ```bash theme={null}
           kubectl -n <NAMESPACE> patch deployment <DEPLOYMENT_NAME> \
             --type merge \
             -p '{
               "spec": {
                 "template": {
                   "spec": {
                     "securityContext": {
                       "runAsNonRoot": true
                     }
                   }
                 }
               }
             }'
           ```
           If you must set it per container (e.g., pod-level context not desired), patch each container by name:
           ```bash theme={null}
           kubectl -n <NAMESPACE> patch deployment <DEPLOYMENT_NAME> \
             --type json \
             -p '[{
               "op": "add",
               "path": "/spec/template/spec/containers/0/securityContext",
               "value": { "runAsNonRoot": true }
             }]'
           ```

        4. If initContainers are present and not covered by pod-level `securityContext`, ensure they also have `runAsNonRoot: true` (run on any machine with kubectl access). Example for the first initContainer:
           ```bash theme={null}
           kubectl -n <NAMESPACE> patch deployment <DEPLOYMENT_NAME> \
             --type json \
             -p '[{
               "op": "add",
               "path": "/spec/template/spec/initContainers/0/securityContext",
               "value": { "runAsNonRoot": true }
             }]'
           ```

        5. For GitOps or manifest-driven environments, update the source manifests instead of live objects (run on any machine with repo access): in each PodTemplate (Deployment/StatefulSet/DaemonSet/Job/CronJob), set:
           ```yaml theme={null}
           spec:
             template:
               spec:
                 securityContext:
                   runAsNonRoot: true
           ```
           Commit and apply via your normal pipeline so changes persist.

        6. Verification (run on any machine with kubectl access): re-run the audit and confirm only `is_compliant=true` remains, or no `is_compliant=false` lines:
           ```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 non-excluded pods are compliant"
           ```
      </Accordion>

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

        1. Edit the affected Pod’s owning workload (Deployment, StatefulSet, etc.) manifest and set `runAsNonRoot: true` at pod level.

        Example for a Deployment:

        ```bash theme={null}
        kubectl -n <NAMESPACE> get deploy <DEPLOYMENT_NAME> -o yaml > /tmp/deploy-run-as-non-root.yaml
        ```

        Edit `/tmp/deploy-run-as-non-root.yaml` and ensure the pod template has:

        ```yaml theme={null}
        spec:
          template:
            spec:
              securityContext:
                runAsNonRoot: true
              containers:
                - name: <CONTAINER_NAME>
                  image: <IMAGE>
                  # per-container override if desired:
                  # securityContext:
                  #   runAsNonRoot: true
        ```

        Apply the updated manifest:

        ```bash theme={null}
        kubectl apply -f /tmp/deploy-run-as-non-root.yaml
        ```

        For a naked Pod (no controller), edit and apply similarly:

        ```bash theme={null}
        kubectl -n <NAMESPACE> get pod <POD_NAME> -o yaml > /tmp/pod-run-as-non-root.yaml
        # edit /tmp/pod-run-as-non-root.yaml as above
        kubectl delete pod -n <NAMESPACE> <POD_NAME>
        kubectl apply -f /tmp/pod-run-as-non-root.yaml
        ```

        Note: Updating the pod template for controllers will recreate pods; direct Pod changes require deletion and re-creation.

        Verification (same 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.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
        #
        # Enforce securityContext.runAsNonRoot: true on Pods and template owners
        # Scope: any machine with kubectl access to the cluster
        # Requirements: kubectl, jq, yq (https://mikefarah.gitbook.io/yq)

        set -euo pipefail

        if ! command -v kubectl >/dev/null 2>&1; then
          echo "ERROR: kubectl not found in PATH" >&2
          exit 1
        fi

        if ! command -v jq >/dev/null 2>&1; then
          echo "ERROR: jq not found in PATH" >&2
          exit 1
        fi

        if ! command -v yq >/dev/null 2>&1; then
          echo "ERROR: yq not found in PATH (mikefarah yq v4+)" >&2
          exit 1
        fi

        # Namespaces to exclude (as per audit command)
        EXCLUDED_NS_REGEX='^(kube-system|kube-public|kube-node-lease)$'

        timestamp() {
          date -u +"%Y%m%dT%H%M%SZ"
        }

        BACKUP_DIR="runAsNonRoot-backups-$(timestamp)"
        mkdir -p "${BACKUP_DIR}"

        echo "Backing up original manifests to: ${BACKUP_DIR}"

        # Helper: patch any workload with a Pod template, setting runAsNonRoot at pod level
        patch_workload() {
          local ns kind name
          ns="$1"
          kind="$2"
          name="$3"

          echo "Processing ${kind}/${ns}/${name}"

          local backup_file="${BACKUP_DIR}/${ns}-${kind}-${name}.yaml"
          kubectl get "${kind}" "${name}" -n "${ns}" -o yaml > "${backup_file}"

          # Use yq to ensure .spec.template.spec.securityContext.runAsNonRoot: true
          # Idempotent: setting the same value repeatedly is safe
          yq -y '
            .spec.template.spec.securityContext //= {} |
            .spec.template.spec.securityContext.runAsNonRoot = true
          ' "${backup_file}" > "${backup_file}.patched"

          # Apply patched manifest
          kubectl apply -f "${backup_file}.patched"
        }

        # 1. Fix template-based owners (Deployments, StatefulSets, DaemonSets, Jobs, CronJobs, ReplicaSets, ReplicationControllers)
        echo "Discovering namespaced workloads with Pod templates (excluding system namespaces)..."

        kubectl api-resources --namespaced=true --verbs=list --output=name | while read -r res; do
          # Filter for common workload types that own Pod templates
          case "${res}" in
            deployments.apps|statefulsets.apps|daemonsets.apps|jobs.batch|cronjobs.batch|replicasets.apps|replicationcontrollers)
              :
              ;;
            *)
              continue
              ;;
          esac

          kubectl get "${res}" --all-namespaces -o json | \
            jq -r --arg re "${EXCLUDED_NS_REGEX}" '
              .items[]
              | select(.metadata.namespace | test($re) | not)
              | [.metadata.namespace, .kind, .metadata.name]
              | @tsv
            ' | while IFS=$'\t' read -r ns kind name; do
              patch_workload "${ns}" "${kind}" "${name}"
            done
        done

        # 2. For standalone Pods without an owning controller, patch the Pod spec directly
        echo "Discovering standalone Pods (no controller ownerReferences) to patch..."

        kubectl get pods --all-namespaces -o json | \
          jq -r --arg re "${EXCLUDED_NS_REGEX}" '
            .items[]
            | select(.metadata.namespace | test($re) | not)
            | select(((.metadata.ownerReferences // []) | map(select(.controller == true)) | length) == 0)
            | [.metadata.namespace, .metadata.name]
            | @tsv
          ' | while IFS=$'\t' read -r ns name; do
            echo "Patching standalone Pod ${ns}/${name}"
            kubectl patch pod "${name}" -n "${ns}" --type='merge' -p '
            {
              "spec": {
                "securityContext": {
                  "runAsNonRoot": true
                }
              }
            }' || echo "WARNING: Failed to patch Pod ${ns}/${name}; it may be terminating or immutable."
          done

        echo "Initial remediation complete. Verifying using benchmark audit..."

        # 3. Verification: run the provided audit command and show non-compliant lines (if any)
        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' | \
          awk '/is_compliant=false/ || /is_compliant=true/'

        echo
        echo "If any lines above show is_compliant=false, investigate those workloads and update their manifests in source control to include:"
        echo "  spec:"
        echo "    template:"
        echo "      spec:"
        echo "        securityContext:"
        echo "          runAsNonRoot: true"
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
