> ## 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 and their owners (run on any machine with kubectl access):
           ```sh 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
             | select(.spec.automountServiceAccountToken != false)
             | "\($m.namespace) \($m.name) \($own.kind // "Pod") \($own.name // $m.name)"
             ][]'
           ```
           Save the list; for each line note: NAMESPACE, POD\_NAME, OWNER\_KIND, OWNER\_NAME.

        2. For each workload, decide if it really needs a service account token (run on any machine with kubectl access):
           * Inspect pod spec and image/command:
             ```sh theme={null}
             kubectl -n NAMESPACE get pod POD_NAME -o yaml
             ```
           * Look for:
             * In-cluster client libraries (e.g., uses `KUBERNETES_SERVICE_HOST`, `kubeconfig`, `client-go`, `@kubernetes/client-node`).
             * Environment variables, volume mounts, or sidecars that clearly talk to the Kubernetes API.
           * If in doubt, consult the application owner; do not disable token automount until you’re confident it does not call the API.

        3. For standalone Pods that do not need the API, patch the Pod spec (run on any machine with kubectl access):
           ```sh theme={null}
           kubectl -n NAMESPACE patch pod POD_NAME \
             --type merge \
             -p '{"spec":{"automountServiceAccountToken":false}}'
           ```
           Note: If the Pod is controlled by a higher-level object (Deployment, DaemonSet, etc.), this change will be lost when the controller recreates the Pod; in that case, change the controller instead (next step).

        4. For controller-managed workloads that do not need the API, patch the controller spec (run on any machine with kubectl access, pick the right kind per workload):

           Deployment example:

           ```sh theme={null}
           kubectl -n NAMESPACE patch deployment OWNER_NAME \
             --type merge \
             -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
           ```

           DaemonSet example:

           ```sh theme={null}
           kubectl -n NAMESPACE patch daemonset OWNER_NAME \
             --type merge \
             -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
           ```

           StatefulSet example:

           ```sh theme={null}
           kubectl -n NAMESPACE patch statefulset OWNER_NAME \
             --type merge \
             -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
           ```

           Job example:

           ```sh theme={null}
           kubectl -n NAMESPACE patch job OWNER_NAME \
             --type merge \
             -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
           ```

           CronJob example:

           ```sh theme={null}
           kubectl -n NAMESPACE patch cronjob OWNER_NAME \
             --type merge \
             -p '{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}}}'
           ```

        5. (Optional) If multiple pods in a namespace share a ServiceAccount and none of their workloads call the API, you may set it once at the ServiceAccount level instead (run on any machine with kubectl access, and only after confirming no user of the ServiceAccount needs the API):
           ```sh theme={null}
           kubectl -n NAMESPACE patch serviceaccount SERVICEACCOUNT_NAME \
             --type merge \
             -p '{"automountServiceAccountToken":false}'
           ```
           Then ensure any workload that *does* need the token either uses a different ServiceAccount or explicitly sets `automountServiceAccountToken: true` in its pod template.

        6. Verify remediation (run on any machine with kubectl access):
           ```sh 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
             | "ns=\($m.namespace) name=\($m.name) automountServiceAccountToken=\(.spec.automountServiceAccountToken // "unset") 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 the pod and owning controller

        ```bash theme={null}
        kubectl get pods --all-namespaces \
          -o wide
        ```

        For each non‑system pod reported as noncompliant, determine its controller (Deployment, StatefulSet, etc.):

        ```bash theme={null}
        kubectl get pod <POD_NAME> -n <NAMESPACE> -o jsonpath='{.metadata.ownerReferences[0].kind}{" "}{.metadata.ownerReferences[0].name}{"\n"}'
        ```

        Do not edit bare Pods that are managed by a controller; changes will be overwritten. Always patch the controller.

        2. Patch the controller template to disable token automount

        Examples (pick the kind that matches the `ownerReferences.kind` you saw):

        **Deployment**

        ```bash theme={null}
        kubectl patch deployment <DEPLOYMENT_NAME> -n <NAMESPACE> \
          --type='merge' \
          -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
        ```

        **StatefulSet**

        ```bash theme={null}
        kubectl patch statefulset <STATEFULSET_NAME> -n <NAMESPACE> \
          --type='merge' \
          -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
        ```

        **DaemonSet**

        ```bash theme={null}
        kubectl patch daemonset <DAEMONSET_NAME> -n <NAMESPACE> \
          --type='merge' \
          -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
        ```

        If the workload legitimately calls the Kubernetes API, review and skip patching instead of forcing this setting.

        3. Optional: set it on the ServiceAccount instead of each pod

        If multiple workloads share a ServiceAccount and none need API access:

        ```bash theme={null}
        kubectl patch serviceaccount <SA_NAME> -n <NAMESPACE> \
          --type='merge' \
          -p '{"automountServiceAccountToken":false}'
        ```

        4. Verification

        After controllers have rolled out updated pods, verify:

        ```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=\(.spec.automountServiceAccountToken // "unset")"
            + " 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
        #
        # Remediate CBP C2.2:
        # For pods that do NOT call the Kubernetes API, set automountServiceAccountToken: false
        # at the pod level (or via their ServiceAccount).
        #
        # NOTE: This script CANNOT safely decide which workloads legitimately call the
        # Kubernetes API. It:
        #   - Lists all noncompliant pods (excluding kube-system, kube-public, kube-node-lease)
        #   - For each, shows the owning controller and current setting
        #   - Generates example kubectl patches you can apply after review
        #
        # Run on: any machine with kubectl and jq configured for the target cluster.
        # Safe to re-run.

        set -euo pipefail

        echo "==> Checking for required tools (kubectl, jq)..."
        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

        echo "==> Discovering noncompliant pods (excluding kube-system, kube-public, kube-node-lease)..."
        NONCOMPLIANT_JSON="$(kubectl get pods --all-namespaces -o json \
          | jq '
            .items
            | map(
                select(.metadata.namespace as $n
                       | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
                | select(.spec.automountServiceAccountToken != false)
              )
          ')"

        COUNT="$(printf '%s\n' "${NONCOMPLIANT_JSON}" | jq 'length')"

        if [ "${COUNT}" -eq 0 ]; then
          echo "==> No noncompliant pods found. Cluster currently compliant for CBP C2.2."
          exit 0
        fi

        echo "==> Found ${COUNT} noncompliant pods."
        echo
        echo "================================ REVIEW REQUIRED ================================"
        echo "The benchmark explicitly says: workloads that legitimately call the API"
        echo "must be reviewed rather than blindly remediated."
        echo
        echo "For each listed pod, determine whether the workload calls the Kubernetes API."
        echo "If it does NOT, you can apply the suggested 'kubectl patch' to set"
        echo "automountServiceAccountToken=false at the controller or pod level."
        echo "==============================================================================="

        echo
        echo "==> Noncompliant pods and suggested remediation commands (for manual review):"
        echo

        printf '%s\n' "${NONCOMPLIANT_JSON}" | jq -r '
          .[] as $pod
          | ($pod.metadata.namespace) as $ns
          | ($pod.metadata.name) as $name
          | ($pod.spec.serviceAccountName // "default") as $sa
          | ($pod.spec.automountServiceAccountToken // "unset") as $am
          | ([ ($pod.metadata.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | "----------------------------------------------------------------------",
            "Pod:      " + $ns + "/" + $name,
            "SA:       " + $sa,
            "Current automountServiceAccountToken: " + ($am|tostring),
            (if $own == null then
               "Owner:   (none - naked Pod)"
             else
               "Owner:   " + $own.kind + " " + $ns + "/" + $own.name
             end),
            "",
            "1) Decide if this workload calls the Kubernetes API (code/config review, logs, etc.).",
            "   - If it DOES call the API: leave token automount enabled (no change).",
            "   - If it does NOT call the API: apply one of the following, depending on owner:",
            "",
            (if $own == null then
               "# Naked Pod: patch the Pod spec directly (non-persistent; will be lost if Pod is re-created):\n" +
               "kubectl patch pod " + $name + " -n " + $ns +
               " --type=merge -p '{\"spec\":{\"automountServiceAccountToken\":false}}'\n"
             else
               (if $own.kind == "Deployment" then
                  "# Deployment owner: patch the Deployment template so all Pods inherit the setting:\n" +
                  "kubectl patch deploy " + $own.name + " -n " + $ns +
                  " --type=merge -p '{\"spec\":{\"template\":{\"spec\":{\"automountServiceAccountToken\":false}}}}'\n"
                elif $own.kind == "StatefulSet" then
                  "# StatefulSet owner: patch the StatefulSet template:\n" +
                  "kubectl patch statefulset " + $own.name + " -n " + $ns +
                  " --type=merge -p '{\"spec\":{\"template\":{\"spec\":{\"automountServiceAccountToken\":false}}}}'\n"
                elif $own.kind == "DaemonSet" then
                  "# DaemonSet owner: patch the DaemonSet template:\n" +
                  "kubectl patch daemonset " + $own.name + " -n " + $ns +
                  " --type=merge -p '{\"spec\":{\"template\":{\"spec\":{\"automountServiceAccountToken\":false}}}}'\n"
                elif $own.kind == "ReplicaSet" then
                  "# ReplicaSet owner (likely managed by a Deployment). Prefer patching the owning Deployment.\n" +
                  "# If this ReplicaSet is the top-level controller, patch it directly:\n" +
                  "kubectl patch rs " + $own.name + " -n " + $ns +
                  " --type=merge -p '{\"spec\":{\"template\":{\"spec\":{\"automountServiceAccountToken\":false}}}}'\n"
                elif $own.kind == "Job" then
                  "# Job owner: patch the Job template (for future pods):\n" +
                  "kubectl patch job " + $own.name + " -n " + $ns +
                  " --type=merge -p '{\"spec\":{\"template\":{\"spec\":{\"automountServiceAccountToken\":false}}}}'\n"
                elif $own.kind == "CronJob" then
                  "# CronJob owner: patch the CronJob jobTemplate:\n" +
                  "kubectl patch cronjob " + $own.name + " -n " + $ns +
                  " --type=merge -p '{\"spec\":{\"jobTemplate\":{\"spec\":{\"template\":{\"spec\":{\"automountServiceAccountToken\":false}}}}}}'\n"
                else
                  "# Owner kind " + $own.kind + " is not explicitly handled; patch its pod template accordingly.\n" +
                  "# Example (adjust resource kind/name as needed):\n" +
                  "kubectl patch " + ($own.kind | ascii_downcase) + " " + $own.name + " -n " + $ns +
                  " --type=merge -p '{\"spec\":{\"template\":{\"spec\":{\"automountServiceAccountToken\":false}}}}'\n"
                end)
             end)
        '

        echo "==> Review the commands above, apply only to workloads that do NOT call the Kubernetes API."
        echo

        echo "==> Verification (post-remediation):"
        echo "After applying patches to the appropriate workloads, re-run this command:"
        echo
        echo "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
          | \"kind=Pod ns=\(\$m.namespace) name=\(\$m.name) is_compliant=\(if \$ok then \"true\" else \"false\" end)\"
          ] as \$rows
          | if (\$rows | length) == 0 then \"is_compliant=true\" else \$rows[] end'"
        echo
        echo "Any remaining lines with is_compliant=false represent pods still mounting a token."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
