> ## 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.

# Pods That Do Not Use The API Should Disable Token Automount

### More Info:

Verifies automountServiceAccountToken is false for pods that do not call the Kubernetes API. A mounted token is a ready-made credential for an attacker who lands in the pod.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify noncompliant 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
             | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
             | (.spec.automountServiceAccountToken == false) as $ok
             | select($ok | not)
             | "ns=\($m.namespace) name=\($m.name)"
               + (if $own == null then "" else " owner=\($own.kind)/\($own.name)" end)
             ][]'
           ```

        2. For a standalone Pod: capture the manifest
           * Run on: any machine with kubectl access\
             Replace NAMESPACE and POD\_NAME with values from step 1:
           ```bash theme={null}
           kubectl get pod POD_NAME -n NAMESPACE -o yaml > /tmp/pod-POD_NAME.yaml
           ```

        3. Edit the pod manifest to disable token automount
           * Run on: any machine with kubectl access
           * Open the file and add or update the field under `spec`:
           ```bash theme={null}
           sed -i 's/^spec:$/spec:\n  automountServiceAccountToken: false/' /tmp/pod-POD_NAME.yaml
           ```
           * If `automountServiceAccountToken` already exists under `spec:`, edit it so it reads:
           ```yaml theme={null}
           spec:
             automountServiceAccountToken: false
           ```

        4. Recreate the pod with the updated manifest
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl delete pod POD_NAME -n NAMESPACE --wait=true
           kubectl apply -f /tmp/pod-POD_NAME.yaml
           ```

        5. For controller-managed workloads (e.g., Deployment, DaemonSet, Job): patch the controller instead of the pod
           * Run on: any machine with kubectl access
           * Example for a Deployment; adjust KIND, NAME, and NAMESPACE based on the `owner=` from step 1:
           ```bash theme={null}
           kubectl -n NAMESPACE patch deployment OWNER_NAME \
             --type merge \
             -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
           ```
           * The controller will automatically recreate pods with the new setting.

        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.automountServiceAccountToken == false) as $ok
             | select($ok | not)
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
           ```
      </Accordion>

      <Accordion title="Using kubectl">
        On any machine with kubectl access to the OKE cluster:

        1. Identify a non‑compliant pod and its owner (from the audit output), for example:
           * Namespace: `app-namespace`
           * Pod name: `web-7c8b4f9d9b-xj9lt`
           * Owner: `Deployment/app-namespace/web`

        2. Export the owning workload manifest, edit it locally, and re‑apply (pod spec fields like `automountServiceAccountToken` must be set on the controller, not on the individual pod):

        ```bash theme={null}
        kubectl -n app-namespace get deploy web -o yaml > web-deploy.yaml
        ```

        Edit `web-deploy.yaml` and in `spec.template.spec` add (or change) this field:

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: web
          namespace: app-namespace
        spec:
          template:
            spec:
              automountServiceAccountToken: false
              # ... other fields remain unchanged
        ```

        Apply the updated manifest:

        ```bash theme={null}
        kubectl apply -f web-deploy.yaml
        ```

        Kubernetes will recreate the pods managed by this Deployment with `automountServiceAccountToken: false`. Repeat this pattern for other controller types (StatefulSet, DaemonSet, Job, CronJob) by adjusting the `kind` in the `kubectl get` command and editing the same `spec.template.spec` section.

        3. If the pod is created directly (no controller ownerReference), edit the pod spec and re‑create it:

        ```bash theme={null}
        kubectl -n app-namespace get pod web-standalone -o yaml > web-standalone-pod.yaml
        ```

        Edit `web-standalone-pod.yaml`:

        ```yaml theme={null}
        apiVersion: v1
        kind: Pod
        metadata:
          name: web-standalone
          namespace: app-namespace
        spec:
          automountServiceAccountToken: false
          # ... other fields remain unchanged
        ```

        Delete and recreate the pod:

        ```bash theme={null}
        kubectl -n app-namespace delete pod web-standalone
        kubectl apply -f web-standalone-pod.yaml
        ```

        4. Optional: set this at the ServiceAccount level instead of each pod, if all pods using it do not need API access:

        ```bash theme={null}
        kubectl -n app-namespace get serviceaccount web-sa -o yaml > web-sa.yaml
        ```

        Edit `web-sa.yaml`:

        ```yaml theme={null}
        apiVersion: v1
        kind: ServiceAccount
        metadata:
          name: web-sa
          namespace: app-namespace
        automountServiceAccountToken: false
        ```

        Apply:

        ```bash theme={null}
        kubectl apply -f web-sa.yaml
        ```

        5. Verification (rerun the audit command):

        ```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.automountServiceAccountToken == 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)
            + " serviceAccount=\(.spec.serviceAccountName // "default")"
            + " automountServiceAccountToken=\(if .spec.automountServiceAccountToken == null then "unset" else .spec.automountServiceAccountToken end)"
            + " 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
        #
        # Disable automountServiceAccountToken for pods in OKE that do NOT need
        # the Kubernetes API, by defaulting to a secure ServiceAccount setting.
        #
        # RUN ON: any machine with kubectl access and jq installed.
        #
        # NOTES:
        # - This script does NOT try to guess which pods use the API.
        # - It only operates on namespaces you explicitly approve.
        # - It configures the *ServiceAccount* used by pods:
        #     automountServiceAccountToken: false
        #   so new pods using that SA will not auto-mount tokens.
        # - Existing running pods will keep their current mounts until restarted.

        set -euo pipefail

        # ---------- CONFIGURATION ----------

        # Comma-separated list of namespaces you have reviewed and decided that
        # workloads there do NOT need to call the Kubernetes API.
        # Example: SAFE_NAMESPACES="dev,test,frontend"
        SAFE_NAMESPACES="${SAFE_NAMESPACES:-}"

        if [[ -z "${SAFE_NAMESPACES}" ]]; then
          echo "ERROR: SAFE_NAMESPACES is empty."
          echo "Set SAFE_NAMESPACES to a comma-separated list of non-system namespaces"
          echo "whose pods you have reviewed and determined do NOT need the Kubernetes API."
          echo "Example:"
          echo "  SAFE_NAMESPACES='dev,test,frontend' $0"
          exit 1
        fi

        # Convert SAFE_NAMESPACES to an array
        IFS=',' read -r -a NS_LIST <<< "${SAFE_NAMESPACES}"

        # Namespaces that must always be excluded
        EXCLUDED_NS_REGEX='^(kube-system|kube-public|kube-node-lease)$'

        echo "Using SAFE_NAMESPACES: ${SAFE_NAMESPACES}"
        echo

        # ---------- FUNCTIONS ----------

        ensure_namespace_allowed() {
          local ns="$1"
          if [[ "${ns}" =~ ${EXCLUDED_NS_REGEX} ]]; then
            return 1
          fi
          return 0
        }

        # Patch or create ServiceAccounts with automountServiceAccountToken: false
        harden_service_accounts_in_namespace() {
          local ns="$1"

          if ! ensure_namespace_allowed "${ns}"; then
            echo "Skipping system namespace: ${ns}"
            return
          fi

          echo "Processing namespace: ${ns}"

          # Get all ServiceAccounts in the namespace (including 'default')
          local sa_list
          if ! sa_list=$(kubectl get sa -n "${ns}" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null); then
            echo "  WARNING: Failed to list ServiceAccounts in namespace ${ns}, skipping."
            return
          fi

          if [[ -z "${sa_list}" ]]; then
            echo "  No ServiceAccounts found in namespace ${ns}."
            return
          fi

          while IFS= read -r sa; do
            [[ -z "${sa}" ]] && continue

            # Check current value (may be null/unset)
            local current
            current=$(kubectl get sa "${sa}" -n "${ns}" -o jsonpath='{.automountServiceAccountToken}' 2>/dev/null || echo "")

            if [[ "${current}" == "false" ]]; then
              echo "  SA ${ns}/${sa}: automountServiceAccountToken already false"
              continue
            fi

            echo "  Patching SA ${ns}/${sa}: setting automountServiceAccountToken=false"
            kubectl patch sa "${sa}" -n "${ns}" \
              --type merge \
              -p '{"automountServiceAccountToken": false}' >/dev/null
          done <<< "${sa_list}"
        }

        # Verify by re-running a focused version of the audit logic
        verify_namespace() {
          local ns="$1"

          echo
          echo "Verification for namespace: ${ns}"

          kubectl get pods -n "${ns}" -o json | jq -r '
            [ .items[]
            | .metadata as $m
            | (.spec.automountServiceAccountToken == false) as $ok
            | "kind=Pod ns=\($m.namespace) name=\($m.name) " +
              "serviceAccount=\(.spec.serviceAccountName // "default") " +
              "automountServiceAccountToken=\(if .spec.automountServiceAccountToken == null then "unset" else .spec.automountServiceAccountToken end) " +
              "is_compliant=\(if $ok then "true" else "false" end)"
            ] as $rows
            | if ($rows | length) == 0 then "no_pods_in_namespace" else $rows[] end'
        }

        # ---------- MAIN ----------

        for ns in "${NS_LIST[@]}"; do
          ns_trimmed="$(echo "${ns}" | xargs)"
          [[ -z "${ns_trimmed}" ]] && continue

          if ! kubectl get ns "${ns_trimmed}" >/dev/null 2>&1; then
            echo "Namespace ${ns_trimmed} not found; skipping."
            continue
          fi

          harden_service_accounts_in_namespace "${ns_trimmed}"
          verify_namespace "${ns_trimmed}"
        done

        echo
        echo "Completed. For cluster-wide verification, run:"
        echo "  kubectl get pods --all-namespaces -o json | jq -r '...<full audit expression>...'"
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
