> ## 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 automount service account tokens without needing them expand the attack surface. Token mounting should be disabled where API access is not required.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS GKE

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify pods and service accounts that do not need API access
           * On any machine with kubectl access:
             ```sh theme={null}
             kubectl get pods --all-namespaces -o wide
             kubectl get sa --all-namespaces
             ```
           * Based on application knowledge, list pods and service accounts that never call the Kubernetes API.

        2. Disable token automount on service accounts that do not need API access
           * On any machine with kubectl access, for each such service account:
             ```sh theme={null}
             kubectl patch sa SERVICEACCOUNT_NAME \
               -n NAMESPACE \
               --type merge \
               -p '{"automountServiceAccountToken": false}'
             ```
           * For GitOps or manifest-based management, edit the ServiceAccount manifest and add:
             ```yaml theme={null}
             automountServiceAccountToken: false
             ```
             then apply:
             ```sh theme={null}
             kubectl apply -f serviceaccount.yaml
             ```

        3. Disable token automount on pods that do not need API access (per‑pod override)
           * For standalone pods:
             ```sh theme={null}
             kubectl patch pod POD_NAME \
               -n NAMESPACE \
               --type merge \
               -p '{"spec":{"automountServiceAccountToken":false}}'
             ```
             (This affects the existing pod; ensure your controllers or manifests won’t immediately recreate it without the setting.)

        4. Disable token automount on controllers (Deployments, StatefulSets, DaemonSets, Jobs, CronJobs)
           * On any machine with kubectl access, for each controller whose workloads do not require API access:
             ```sh theme={null}
             kubectl get deployment DEPLOYMENT_NAME -n NAMESPACE -o yaml > deployment.yaml
             ```
           * Edit `deployment.yaml` and under `spec.template.spec` add:
             ```yaml theme={null}
             automountServiceAccountToken: false
             ```
           * Apply the change:
             ```sh theme={null}
             kubectl apply -f deployment.yaml
             ```
           * Repeat similarly for other controller kinds:
             ```sh theme={null}
             kubectl get statefulset STATEFULSET_NAME -n NAMESPACE -o yaml > ss.yaml
             kubectl get daemonset  DAEMONSET_NAME  -n NAMESPACE -o yaml > ds.yaml
             kubectl get job        JOB_NAME        -n NAMESPACE -o yaml > job.yaml
             kubectl get cronjob    CRONJOB_NAME    -n NAMESPACE -o yaml > cj.yaml
             ```
             Edit each file under `spec.template.spec` and add `automountServiceAccountToken: false`, then:
             ```sh theme={null}
             kubectl apply -f ss.yaml
             kubectl apply -f ds.yaml
             kubectl apply -f job.yaml
             kubectl apply -f cj.yaml
             ```

        5. Review for exceptions where API access is required
           * Confirm you did not change service accounts or pods that legitimately need to call the Kubernetes API (e.g., controllers, operators, metrics collectors).
           * For critical namespaces (like `kube-system`), review individually before applying changes:
             ```sh theme={null}
             kubectl get pods -n kube-system -o wide
             kubectl get sa -n kube-system -o yaml
             ```

        6. Verification
           * On any machine with kubectl access, re-run a refined audit to list only pods still mounting tokens:
             ```sh theme={null}
             kubectl get pods --all-namespaces -o json | jq '
               .items[]
               | select(.spec.automountServiceAccountToken != false)
               | {namespace: .metadata.namespace, name: .metadata.name, automountServiceAccountToken: .spec.automountServiceAccountToken}'
             ```
           * Confirm that pods and service accounts which do not need API access no longer appear with `automountServiceAccountToken` set (either missing or `true`).
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1) Identify pods that explicitly mount a service account token
        # Run on: any machine with kubectl access
        kubectl get pods --all-namespaces -o jsonpath='{range .items[?(@.spec.automountServiceAccountToken!=false)]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}'

        # 2) Identify service accounts that explicitly allow token automount
        kubectl get serviceaccounts --all-namespaces -o jsonpath='{range .items[?(@.automountServiceAccountToken!=false)]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}'
        ```

        Update service accounts (preferred, so all attached pods default to no token unless overridden):

        ```bash theme={null}
        # Example: disable token automount for a specific service account
        # Replace NAMESPACE and SERVICEACCOUNT with real values
        kubectl -n NAMESPACE patch serviceaccount SERVICEACCOUNT \
          --type merge \
          -p '{"automountServiceAccountToken": false}'
        ```

        Update pod specs via their owning workload (Deployment/DaemonSet/StatefulSet/CronJob/Job):

        ```bash theme={null}
        # Example: patch a Deployment to disable token automount for its pods
        # Replace NAMESPACE and DEPLOYMENT with real values
        kubectl -n NAMESPACE patch deployment DEPLOYMENT \
          --type merge \
          -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}'
        ```

        Declarative manifest snippets (for future changes):

        ```yaml theme={null}
        # ServiceAccount manifest
        apiVersion: v1
        kind: ServiceAccount
        metadata:
          name: example-sa
          namespace: example-namespace
        automountServiceAccountToken: false
        ```

        ```yaml theme={null}
        # Pod template in a Deployment (same pattern for other controllers)
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: example-deployment
          namespace: example-namespace
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: example
          template:
            metadata:
              labels:
                app: example
            spec:
              serviceAccountName: example-sa
              automountServiceAccountToken: false
              containers:
              - name: app
                image: nginx:1.27
        ```

        Apply manifests:

        ```bash theme={null}
        # Run on: any machine with kubectl access
        kubectl apply -f /absolute/path/to/your-manifest.yaml
        ```

        Verification (adapted from the audit):

        ```bash theme={null}
        # Run on: any machine with kubectl access
        echo "🔹 Pods with automountServiceAccountToken enabled:"
        pods_with_token_mount=$(kubectl get pods --all-namespaces -o json | jq '
          [.items[] | select(.spec.automountServiceAccountToken != false)] | length')

        if [ "$pods_with_token_mount" -gt 0 ]; then
          echo "automountServiceAccountToken"
        else
          echo "No pods with automountServiceAccountToken enabled (all are false or unset via SA defaults)."
        fi
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Purpose:
        #   Disable automountServiceAccountToken where it is not explicitly needed by
        #   setting:
        #     - Pod spec.automountServiceAccountToken: false
        #     - ServiceAccount automountServiceAccountToken: false
        #
        # Scope:
        #   - Runs from any machine with kubectl access and sufficient RBAC.
        #   - Idempotent: safe to re-run.
        #
        # WARNING:
        #   This is a conservative baseline hardening script. It will:
        #     * Set automountServiceAccountToken: false on ALL ServiceAccounts
        #       that do not already have it explicitly set to true.
        #     * Set automountServiceAccountToken: false on ALL Pods (workloads)
        #       that do not already have it explicitly set to true.
        #   Review before use and consider scoping to selected namespaces/labels
        #   if some workloads need default token mounting.

        set -euo pipefail

        #------------ Configuration ------------#

        KUBECTL_BIN="${KUBECTL_BIN:-kubectl}"

        # Optional: limit to specific namespaces, space-separated (leave empty for all)
        NAMESPACE_FILTER="${NAMESPACE_FILTER:-}"

        #------------ Helper Functions ------------#

        require_cmd() {
          if ! command -v "$1" >/dev/null 2>&1; then
            echo "ERROR: required command '$1' not found in PATH" >&2
            exit 1
          fi
        }

        run_kubectl() {
          # Wrapper for kubectl to centralize binary selection
          "$KUBECTL_BIN" "$@"
        }

        #------------ Pre-flight Checks ------------#

        require_cmd "$KUBECTL_BIN"
        require_cmd jq

        echo "Using kubectl: $KUBECTL_BIN"
        echo "Namespace filter: ${NAMESPACE_FILTER:-<all namespaces>}"

        # Determine namespaces to operate on
        if [ -n "$NAMESPACE_FILTER" ]; then
          read -r -a NAMESPACES <<<"$NAMESPACE_FILTER"
        else
          mapfile -t NAMESPACES < <(run_kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
        fi

        echo "Target namespaces:"
        printf '  %s\n' "${NAMESPACES[@]}"

        #------------ Function: Patch ServiceAccounts ------------#

        harden_serviceaccounts() {
          local ns="$1"
          echo "Processing ServiceAccounts in namespace: $ns"

          # Get all SAs as JSON
          local sa_json
          if ! sa_json=$(run_kubectl get sa -n "$ns" -o json 2>/dev/null); then
            echo "  Skipping namespace $ns (unable to list ServiceAccounts)"
            return
          fi

          # For each SA where .automountServiceAccountToken is null or not set or false,
          # we will explicitly set it to false (if not already true).
          # This preserves any explicit true values.
          echo "$sa_json" \
            | jq -r '
              .items[]
              | select(.automountServiceAccountToken != true)
              | .metadata.name
            ' \
            | while read -r sa_name; do
                [ -z "$sa_name" ] && continue
                echo "  Patching ServiceAccount/$sa_name: set automountServiceAccountToken=false"
                run_kubectl patch sa "$sa_name" -n "$ns" \
                  --type merge \
                  -p '{"automountServiceAccountToken": false}' >/dev/null
              done
        }

        #------------ Function: Patch Workloads (Pods via controllers) ------------#

        # This section adjusts controllers, not live Pods directly:
        # - Deployments
        # - StatefulSets
        # - DaemonSets
        # - ReplicaSets
        # - Jobs
        # - CronJobs

        harden_workloads() {
          local ns="$1"
          echo "Processing workloads in namespace: $ns"

          # Helper to patch a controller kind
          local kind
          for kind in deployment statefulset daemonset replicaset job cronjob; do
            # Skip kinds not present in this namespace
            if ! run_kubectl get "$kind" -n "$ns" >/dev/null 2>&1; then
              continue
            fi

            local items_json
            if ! items_json=$(run_kubectl get "$kind" -n "$ns" -o json 2>/dev/null); then
              echo "  Unable to list $kind in $ns; skipping"
              continue
            fi

            # For each object whose pod template does NOT explicitly set
            # spec.template.spec.automountServiceAccountToken to true,
            # we set it to false.
            echo "$items_json" \
              | jq -r '
                .items[]
                | select(.spec.template.spec.automountServiceAccountToken != true)
                | .metadata.name
              ' \
              | while read -r name; do
                  [ -z "$name" ] && continue
                  echo "  Patching ${kind}/${name}: set spec.template.spec.automountServiceAccountToken=false"
                  run_kubectl patch "$kind" "$name" -n "$ns" \
                    --type merge \
                    -p '{"spec":{"template":{"spec":{"automountServiceAccountToken":false}}}}' >/dev/null
                done
          done
        }

        #------------ Main: Apply Fixes ------------#

        for ns in "${NAMESPACES[@]}"; do
          harden_serviceaccounts "$ns"
          harden_workloads "$ns"
        done

        #------------ Verification ------------#

        echo
        echo "Verification: listing pods with automountServiceAccountToken not explicitly false."
        echo "This should ideally return only pods that explicitly require token mounting."

        pods_with_token_mount=$(run_kubectl get pods --all-namespaces -o json \
          | jq '[.items[] | select(.spec.automountServiceAccountToken != false)] | length')

        echo "Pods with automountServiceAccountToken enabled or unset: $pods_with_token_mount"

        if [ "$pods_with_token_mount" -eq 0 ]; then
          echo "SUCCESS: All pods now have automountServiceAccountToken explicitly set to false."
        else
          echo "NOTICE: Some pods still have automountServiceAccountToken enabled or unset."
          echo "         Review them manually to confirm they genuinely need API access."
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
