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

# Ensure Service Account Tokens Are Only Mounted Where Necessary

### More Info:

Automatically mounting service account tokens into pods that do not call the API server expands the attack surface. Disable token mounting on service accounts and pods that do not need it.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS OKE

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Identify service accounts and pods that auto-mount tokens**
           * Run on any machine with kubectl access:
             ```bash theme={null}
             kubectl get serviceaccounts --all-namespaces -o json \
               | jq -r '.items[] | [.metadata.namespace, .metadata.name,
                 ( .automountServiceAccountToken // "default(true)" )] | @tsv'
             ```
             ```bash theme={null}
             kubectl get pods --all-namespaces -o json \
               | jq -r '.items[] | [.metadata.namespace, .metadata.name,
                 ( .spec.automountServiceAccountToken // "inherit" ),
                 .spec.serviceAccountName] | @tsv'
             ```

        2. **Select candidates that do not need API access**
           * Focus on namespaces and workloads that are not control‑plane or infrastructure (e.g., non‑`kube-system`, non‑monitoring/ingress).
           * For each candidate deployment/statefulset/cronjob, inspect its containers for API use:
             ```bash theme={null}
             kubectl get deploy -A -o yaml > /tmp/all-deployments.yaml
             ```
           * Review `/tmp/all-deployments.yaml` (or per‑namespace exports) for images and arguments that clearly do not interact with the Kubernetes API (simple stateless apps, frontends, batch jobs with no cluster logic).

        3. **Confirm with application owners whether API access is required**
           * For each candidate workload, provide: namespace, resource name, serviceAccountName, and current `automountServiceAccountToken` behavior (from step 1).
           * Ask owners if the pod ever: uses in‑cluster config, lists/watches Kubernetes objects, or needs token‑based authentication. If not, mark it safe to disable token mounting.

        4. **Disable token auto-mount on service accounts where safe**
           * For a specific service account confirmed as not needing API access (example: namespace `prod`, service account `web-sa`), run:
             ```bash theme={null}
             kubectl patch serviceaccount web-sa -n prod \
               -p '{"automountServiceAccountToken": false}'
             ```
           * For new service accounts, ensure manifests include:
             ```yaml theme={null}
             apiVersion: v1
             kind: ServiceAccount
             metadata:
               name: web-sa
               namespace: prod
             automountServiceAccountToken: false
             ```

        5. **Override token auto-mount at pod spec level where needed**
           * For workloads using shared service accounts where some pods need tokens and others do not, set `automountServiceAccountToken: false` on the pod template of the specific workload. Examples:
             ```bash theme={null}
             kubectl -n prod patch deploy web-frontend \
               --type merge \
               -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
             ```
           * Persist this in the source manifests/Helm charts so it is not reverted by future deployments.

        6. **Verify changes cluster-wide**
           * Re-run the cluster-wide inspection:
             ```bash theme={null}
             kubectl get serviceaccounts --all-namespaces -o json \
               | jq -r '.items[] | [.metadata.namespace, .metadata.name,
                 ( .automountServiceAccountToken // "default(true)" )] | @tsv'
             ```
             ```bash theme={null}
             kubectl get pods --all-namespaces -o json \
               | jq -r '.items[] | [.metadata.namespace, .metadata.name,
                 ( .spec.automountServiceAccountToken // "inherit" ),
                 .spec.serviceAccountName] | @tsv'
             ```
           * Confirm that pods and service accounts identified as not needing API access now have `automountServiceAccountToken` set to `false` either on the service account or on the pod spec.
      </Accordion>

      <Accordion title="Using kubectl">
        ### Using kubectl

        #### 1. List all ServiceAccounts and check their token mounting behavior

        Run on: any machine with kubectl access.

        ```bash theme={null}
        kubectl get serviceaccounts --all-namespaces -o yaml > /tmp/all-serviceaccounts.yaml
        ```

        Review `/tmp/all-serviceaccounts.yaml` and look for `automountServiceAccountToken`:

        * If **missing** on a ServiceAccount, it inherits the namespace/pod default (usually `true` unless overridden).
        * If explicitly set to `true`, that ServiceAccount’s pods will mount tokens by default.
        * For ServiceAccounts used by workloads that **do not need to call the API server**, `automountServiceAccountToken: true` or unset is a potential problem; those are candidates to set `automountServiceAccountToken: false` after review.

        To focus on ServiceAccounts that explicitly enable token mounting:

        ```bash theme={null}
        kubectl get serviceaccounts --all-namespaces -o json \
          | jq -r '.items[]
            | select(.automountServiceAccountToken == true)
            | [.metadata.namespace, .metadata.name, .automountServiceAccountToken]
            | @tsv'
        ```

        Output columns:

        1. Namespace
        2. ServiceAccount name
        3. `true`

        Each line represents a ServiceAccount that **forces** token mounting; verify whether its pods truly need API access.

        #### 2. Find pods that explicitly control token mounting

        Run on: any machine with kubectl access.

        ```bash theme={null}
        kubectl get pods --all-namespaces -o yaml > /tmp/all-pods.yaml
        ```

        Inspect `/tmp/all-pods.yaml` and look for `automountServiceAccountToken`:

        * If `automountServiceAccountToken: true` at pod spec level, the pod will mount a token even if the ServiceAccount is more restrictive.
        * If `automountServiceAccountToken: false`, the pod is explicitly protected and will not mount a token.
        * Pods with `automountServiceAccountToken: true` that **do not need** to call the API server are potential problems.

        To list pods that explicitly enable token mounting:

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json \
          | jq -r '.items[]
            | select(.spec.automountServiceAccountToken == true)
            | [.metadata.namespace, .metadata.name, .spec.serviceAccountName, .spec.automountServiceAccountToken]
            | @tsv'
        ```

        Output columns:

        1. Namespace
        2. Pod name
        3. ServiceAccount name
        4. `true`

        Each line is a pod that **forces** token mounting; review the workload purpose to decide if that is justified.

        #### 3. Identify pods that implicitly mount tokens (likely default behavior)

        These pods do not set `automountServiceAccountToken` but are still likely getting a token via default behavior.

        Run on: any machine with kubectl access.

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json \
          | jq -r '.items[]
            | select(.spec.automountServiceAccountToken == null)
            | [.metadata.namespace, .metadata.name, .spec.serviceAccountName]
            | @tsv'
        ```

        Output columns:

        1. Namespace
        2. Pod name
        3. ServiceAccount name

        Interpretation:

        * These pods do **not** override token mounting; whether they receive a token depends on:
          * The ServiceAccount’s `automountServiceAccountToken` field, or
          * The namespace/global defaults.
        * For workloads that do not need API calls, confirm whether their ServiceAccounts are unnecessarily allowing token mounting.

        #### 4. Check if pods actually have token files mounted

        This helps confirm effective behavior, independent of spec fields.

        Run on: any machine with kubectl access.

        ```bash theme={null}
        for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
          for pod in $(kubectl get pods -n "$ns" --no-headers -o custom-columns=':metadata.name'); do
            echo "=== $ns/$pod ==="
            kubectl exec -n "$ns" "$pod" -- sh -c 'ls -l /var/run/secrets/kubernetes.io/serviceaccount 2>/dev/null || echo "no token directory"'
          done
        done
        ```

        Interpretation:

        * If you see files such as `token`, `ca.crt`, and `namespace` under `/var/run/secrets/kubernetes.io/serviceaccount`, that pod **has** a service account token mounted.
        * For pods that do not need to interact with the Kubernetes API, the presence of this directory and token file indicates unnecessary exposure.

        #### 5. Verification after making manual changes

        After you manually update manifests/ServiceAccounts (outside of this section), re-run:

        ```bash theme={null}
        kubectl get serviceaccounts --all-namespaces -o yaml > /tmp/all-serviceaccounts.yaml
        kubectl get pods --all-namespaces -o yaml > /tmp/all-pods.yaml
        ```

        Confirm that:

        * ServiceAccounts and pods that do **not** need API access either:
          * have `automountServiceAccountToken: false`, and/or
          * no longer show a token directory when checked with the command in step 4.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Audit service account token mounting across the whole cluster
        # Run on: any machine with kubectl access and current context set

        set -euo pipefail

        echo "=== 1) Cluster-wide ServiceAccount defaults (automountServiceAccountToken) ==="
        echo
        echo "# Namespaces where the default for ServiceAccounts is NOT explicitly disabled (potentially risky):"
        kubectl get sa --all-namespaces -o json \
          | jq -r '
            .items[]
            | {
                ns: .metadata.namespace,
                name: .metadata.name,
                automount: ( .automountServiceAccountToken // "inherited" )
              }
            | [ .ns, .name, .automount ]
            | @tsv' \
          | column -t

        cat <<'EOF'

        Interpretation:
        - If "automount" is "true": this ServiceAccount will mount a token into pods by default (higher risk).
        - If "automount" is "false": this ServiceAccount will NOT mount a token by default (more secure).
        - If "automount" is "inherited": it follows the namespace or cluster default; review pods using it.

        Risk indicators:
        - ServiceAccounts used by workloads that do NOT need to call the API server but show "true" or "inherited".
        EOF

        echo
        echo "=== 2) Pods and their effective token-mount setting ==="
        echo
        echo "# Listing all pods with the effective automount behavior and what it is derived from:"
        kubectl get pods --all-namespaces -o json \
          | jq -r '
            .items[]
            | . as $pod
            | $pod.spec.serviceAccountName as $sa
            | $pod.metadata.namespace as $ns
            | $pod.metadata.name as $podname
            | (
                if $pod.spec.automountServiceAccountToken != null then
                  {source:"pod", value:$pod.spec.automountServiceAccountToken}
                else
                  (
                    (input_filename | .) as $f
                  )
                end
              )' 2>/dev/null || true
        ```

        The above `jq` block is complex to inline correctly across all clusters; instead, use the following more explicit two-step approach:

        ```bash theme={null}
        #!/usr/bin/env bash
        set -euo pipefail

        echo "=== 2) Pods and their effective token-mount setting (explicit view) ==="
        echo
        # For each pod, show:
        # - namespace, pod name
        # - serviceAccountName
        # - pod-level automountServiceAccountToken (if set)
        # - ServiceAccount-level automountServiceAccountToken (if set)
        # Note: effective behavior is:
        #   pod.automount (if not null) else SA.automount (if not null) else namespace/cluster default (usually true)
        kubectl get pods --all-namespaces -o json \
          | jq -r '
            .items[]
            | .metadata.namespace as $ns
            | .metadata.name as $podname
            | .spec.serviceAccountName as $sa
            | .spec.automountServiceAccountToken as $pod_auto
            | {
                ns: $ns,
                pod: $podname,
                sa: $sa,
                pod_auto: (if $pod_auto==null then "unset" else $pod_auto|tostring end)
              }
            | @base64' \
          | while read -r line; do
              obj=$(echo "$line" | base64 -d)
              ns=$(echo "$obj" | jq -r '.ns')
              pod=$(echo "$obj" | jq -r '.pod')
              sa=$(echo "$obj" | jq -r '.sa')
              pod_auto=$(echo "$obj" | jq -r '.pod_auto')

              # Look up SA-level automount for this pod
              sa_auto=$(kubectl get sa "$sa" -n "$ns" -o json 2>/dev/null \
                | jq -r '.automountServiceAccountToken // "unset"' || echo "missing-SA")

              printf "%s\t%s\t%s\tpod_auto=%s\tsa_auto=%s\n" "$ns" "$pod" "$sa" "$pod_auto" "$sa_auto"
            done \
          | column -t | sed '1iNAMESPACE  POD  SERVICEACCOUNT  POD_AUTOMOUNT  SA_AUTOMOUNT'

        cat <<'EOF'

        Interpretation:
        - POD_AUTOMOUNT=unset and SA_AUTOMOUNT=unset:
          - Pod inherits namespace/cluster default (commonly true) -> likely mounts a token.
        - POD_AUTOMOUNT=true:
          - Pod will mount a token regardless of SA/namespace defaults (higher risk if API not needed).
        - POD_AUTOMOUNT=false:
          - Pod will NOT mount a token even if SA/namespace defaults are true (more secure).
        - SA_AUTOMOUNT=true:
          - All pods using this SA (that do not override at pod level) will mount a token.
        - SA_AUTOMOUNT=false:
          - Pods using this SA will not mount a token unless they explicitly set POD_AUTOMOUNT=true.

        Risk indicators (potential problems to review):
        - Pods that do NOT need to talk to the Kubernetes API but show:
          - POD_AUTOMOUNT=true, OR
          - POD_AUTOMOUNT=unset and SA_AUTOMOUNT=true or unset (i.e., likely mounting a token).
        - Namespaces or teams where *every* ServiceAccount has automount=true or unset by default.
        EOF
        ```

        To verify the script:

        * Run it from any machine with `kubectl` and `jq` installed.
        * Confirm it completes without errors and prints:
          * A table of ServiceAccounts and their `automountServiceAccountToken`.
          * A table of pods showing pod-level and ServiceAccount-level automount values for review.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
