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

Pods that do not need to access the Kubernetes API should not mount service account tokens. Disabling token automounting reduces the blast radius of a compromised pod.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS AKS

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **List service accounts with default token behavior**
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get serviceaccounts -A -o jsonpath='{range .items[?(@.automountServiceAccountToken!=false)]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{end}'
           ```
           * This shows service accounts that are not explicitly set to `automountServiceAccountToken: false` (including those inheriting the cluster default).

        2. **Identify workloads that will mount tokens via their service accounts**
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\t"}{.spec.automountServiceAccountToken}{"\n"}{end}' \
           | sort
           ```
           * Note: If `.spec.automountServiceAccountToken` is empty, the pod uses the behavior of its referenced service account (or namespace/cluster default).

        3. **Review which pods actually need API access**
           * For a sample of pods from step 2 (start with those in non-system namespaces), inspect their full spec and containers to determine if they interact with the Kubernetes API (client libraries, kubeconfig usage, `KUBERNETES_SERVICE_HOST`/`KUBERNETES_PORT` env usage, etc.):
           ```bash theme={null}
           # Example for a specific pod
           kubectl -n <namespace> get pod <pod-name> -o yaml
           ```
           * Decide per workload: “Requires Kubernetes API access” vs “Does NOT require Kubernetes API access.”

        4. **Update workload templates to disable token automount where not needed**
           * For each deployment/statefulset/daemonset/job whose pods do NOT need API access, edit its manifest (or use `kubectl edit`) to add `automountServiceAccountToken: false` to the pod template:
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl -n <namespace> edit deployment <deployment-name>
           ```
           * In the opened YAML, under `spec.template.spec`, ensure:
           ```yaml theme={null}
           spec:
             automountServiceAccountToken: false
           ```
           * Save and exit; the controller will recreate pods with tokens disabled.

        5. **Optionally harden service accounts for non-API workloads**
           * For service accounts used only by non-API workloads, set `automountServiceAccountToken: false` on the service account itself (so future pods using it do not mount tokens by default):
           ```bash theme={null}
           kubectl -n <namespace> patch serviceaccount <sa-name> \
             -p '{"automountServiceAccountToken": false}'
           ```

        6. **Verify that service account tokens are not mounted where unnecessary**
           * Re-run the pod inspection to confirm `automountServiceAccountToken: false` is set on pod specs that should not have tokens:
           ```bash theme={null}
           kubectl get pods -n <namespace> -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.automountServiceAccountToken}{"\n"}{end}'
           ```
           * For a spot check, confirm the token volume is absent inside a pod that should not have a token:
           ```bash theme={null}
           POD=<pod-name>; NS=<namespace>
           kubectl -n "$NS" exec "$POD" -- ls -R /var/run/secrets/kubernetes.io 2>/dev/null
           ```
           * No files found there (or the directory missing) indicates the token is not mounted.
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1) List all ServiceAccounts and see which have automountServiceAccountToken explicitly set
        # Run on: any machine with kubectl access

        kubectl get serviceaccounts -A -o yaml \
          | sed -n '/^apiVersion: v1$/,/^status:/{/kind: ServiceAccount/p;/metadata:/p;/automountServiceAccountToken:/p}' \
          | sed '/^$/d'
        ```

        Review guidance:

        * If a ServiceAccount has `automountServiceAccountToken: false`, pods using it will not auto-mount tokens unless overridden at pod level.
        * If the field is missing or `true`, pods using that ServiceAccount will auto-mount tokens unless the pod spec disables it.

        ***

        ```bash theme={null}
        # 2) Find pods that explicitly control automountServiceAccountToken
        kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\t"}{.spec.automountServiceAccountToken}{"\n"}{end}' \
          | column -t
        ```

        Review guidance:

        * The 4th column is the pod-level setting:
          * `false` → token will not be mounted for that pod (good for workloads that do not need API access).
          * `true` → token will be mounted (needs review: does this pod really need API access?).
          * `<no value>` (blank) → pod inherits behavior from its ServiceAccount; see step 1.

        Focus review on:

        * Pods whose function clearly does not need Kubernetes API access but show `true` or blank with a `true`/unset ServiceAccount.
        * High-privilege or internet-facing workloads where reducing token exposure is especially important.

        ***

        ```bash theme={null}
        # 3) For a specific workload type (example: Deployments), inspect pod templates
        # Replace NAMESPACE and DEPLOYMENT with real names

        kubectl -n NAMESPACE get deployment DEPLOYMENT -o yaml \
          | sed -n '/spec:/,/status:/{/serviceAccountName:/p;/automountServiceAccountToken:/p}'
        ```

        Review guidance:

        * In `.spec.template.spec` of the workload:
          * `automountServiceAccountToken: false` → template is configured to avoid mounting tokens.
          * Missing or `true` → template will mount tokens (or inherit from ServiceAccount), and should be reviewed for necessity.

        Use this pattern for other controllers (StatefulSet, DaemonSet, CronJob, Job) by swapping the resource kind in the command.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Report pods and serviceaccounts that may be unnecessarily mounting service account tokens.
        # Run on any machine with kubectl context for the target cluster.

        set -euo pipefail

        echo "=== 1) Namespaces with default ServiceAccount automount set to true or unset (defaults to true) ==="
        echo "NAMESPACE,SA,SA_AUTOMOUNT"
        kubectl get sa --all-namespaces -o json \
          | jq -r '
            .items[]
            | {
                ns: .metadata.namespace,
                sa: .metadata.name,
                automount: ( .automountServiceAccountToken // "default(true)" )
              }
            | select(.automount == true or .automount == "default(true)")
            | "\(.ns),\(.sa),\(.automount)"
          ' | sort

        echo
        echo "=== 2) Pods that explicitly automount the service account token ==="
        echo "NAMESPACE,POD,CONTROLLER_KIND,CONTROLLER_NAME,POD_AUTOMOUNT,SA,SA_AUTOMOUNT"
        kubectl get pods --all-namespaces -o json \
          | jq -r '
            .items[]
            | {
                ns: .metadata.namespace,
                pod: .metadata.name,
                sa: (.spec.serviceAccountName // "default"),
                podAutomount: (
                  .spec.automountServiceAccountToken
                  // "inherit-from-SA/namespace-default"
                ),
                owner: (
                  .metadata.ownerReferences[0]
                  // {kind:"",name:""}
                )
              }
            | . + {
                saAutomount: (
                  .sa as $sa
                  | .ns as $ns
                  | (input | .items[] | select(.metadata.namespace==$ns and .metadata.name==$sa) |
                     (.automountServiceAccountToken // "default(true)"))
                )
              }
          ' <(kubectl get sa --all-namespaces -o json) \
          | awk -F'\t' 'BEGIN{OFS=","}{
              # jq outputs tab-separated fields in this order due to object literal:
              # ns, pod, sa, podAutomount, owner.kind, owner.name, saAutomount
              print $1,$2,$5,$6,$4,$3,$7
          }' | sort

        echo
        echo "=== 3) Pods that are LIKELY NOT needing API access: no known API client in image/args (heuristic ONLY) ==="
        echo "NAMESPACE,POD,CONTROLLER_KIND,CONTROLLER_NAME,POD_AUTOMOUNT,SA,SA_AUTOMOUNT,IMAGES"
        kubectl get pods --all-namespaces -o json \
          | jq -r '
            .items[]
            | {
                ns: .metadata.namespace,
                pod: .metadata.name,
                sa: (.spec.serviceAccountName // "default"),
                podAutomount: (
                  .spec.automountServiceAccountToken
                  // "inherit-from-SA/namespace-default"
                ),
                owner: (
                  .metadata.ownerReferences[0]
                  // {kind:"",name:""}
                ),
                images: (
                  ([.spec.containers[], (.spec.initContainers // [])[]?]
                   | map(.image)
                   | unique
                   | join(";"))
                ),
                cmd: ([.spec.containers[], (.spec.initContainers // [])[]?]
                      | map((.command + .args) // [])
                      | add // []
                )
              }
            | . + {
                saAutomount: (
                  .sa as $sa
                  | .ns as $ns
                  | (input | .items[] | select(.metadata.namespace==$ns and .metadata.name==$sa) |
                     (.automountServiceAccountToken // "default(true)"))
                )
              }
            # crude heuristic: filter pods that have SA token auto-mounted (explicitly true or default),
            # and whose command/args do NOT mention kubectl, kube, k8s, or in-cluster client libs.
            | select(
                (
                  .podAutomount == true
                  or .podAutomount == "inherit-from-SA/namespace-default"
                  or .podAutomount == "default(true)"
                )
                and (
                  ([.cmd[]?] | map(tostring | ascii_downcase) | join(" "))
                  | test("kubectl|kube-|k8s|kubernetes|incluster|client-go|kubernetes-client") | not
                )
              )
            | "\(.ns),\(.pod),\(.owner.kind // \"\"),\(.owner.name // \"\"),\(.podAutomount),\(.sa),\(.saAutomount),\(.images)"
          ' <(kubectl get sa --all-namespaces -o json) | sort
        ```

        How to interpret the output:

        * Section 1: `SA_AUTOMOUNT` of `true` or `default(true)` means pods using that ServiceAccount will mount tokens unless they override `spec.automountServiceAccountToken: false`. Review these ServiceAccounts; for workloads that do not need API access, consider setting `automountServiceAccountToken: false` on the ServiceAccount and/or in the pod template.

        * Section 2: Lists all pods, showing:
          * `POD_AUTOMOUNT`: `true` or `inherit-from-SA/namespace-default` means the pod is mounting or likely mounting the token.
          * `SA_AUTOMOUNT`: `true` or `default(true)` means the ServiceAccount is also allowing mounts.
            Focus review on pods where:
          * `POD_AUTOMOUNT` is `true` or `inherit-from-SA/namespace-default`, and
          * You know the workload does not need to talk to the Kubernetes API.

        * Section 3: Heuristic list of pods that likely do NOT need API access but are auto-mounting tokens (based on simple text matches in command/args). This is only a starting point for human review; it can miss or misclassify workloads. For each candidate, inspect its deployment/statefulset/cronjob and decide whether to set:
          * `spec.automountServiceAccountToken: false` in the pod template, and/or
          * `automountServiceAccountToken: false` on the ServiceAccount.

        This script does not attempt to change any resources; all remediation decisions and edits must be made manually in the workload manifests.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
