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

Service accounts tokens should not be mounted in pods except where the workload running in the pod explicitly needs to communicate with the API server

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify pods and service accounts that are not compliant
           * Run on any machine with kubectl access:
             ```bash theme={null}
             kubectl get pods --all-namespaces -o custom-columns=POD_NAMESPACE:.metadata.namespace,POD_NAME:.metadata.name,POD_SERVICE_ACCOUNT:.spec.serviceAccount,POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken
             do
               svacc_is_automountserviceaccounttoken=$(kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json | jq -r '.automountServiceAccountToken' | sed -e 's/<none>/notset/g' -e 's/null/notset/g')
               pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's/<none>/notset/g' -e 's/null/notset/g')
               if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && ( [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ] ); then
                 is_compliant="true"
               elif [ "${svacc_is_automountserviceaccounttoken}" = "true" ] && [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then
                 is_compliant="true"
               else
                 is_compliant="false"
               fi
               echo "**namespace: ${pod_namespace} pod_name: ${pod_name} service_account: ${pod_service_account} pod_is_automountserviceaccounttoken: ${pod_is_automountserviceaccounttoken} svacc_is_automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken} is_compliant: ${is_compliant}"
             done | grep 'is_compliant: false'
             ```
           * Review the non-compliant pods and decide which workloads actually need API server access. Only those should keep `automountServiceAccountToken: true` or `notset` as appropriate.

        2. Disable token automount at the ServiceAccount level where API access is not needed
           * For each namespace and service account that should not get tokens by default, run on any machine with kubectl access:
             ```bash theme={null}
             kubectl -n NAMESPACE get serviceaccount SERVICEACCOUNT -o yaml > /tmp/serviceaccount-NAMESPACE-SERVICEACCOUNT.yaml
             ```
           * Edit `/tmp/serviceaccount-NAMESPACE-SERVICEACCOUNT.yaml` and ensure this field is present under `metadata` (top level of the ServiceAccount):
             ```yaml theme={null}
             automountServiceAccountToken: false
             ```
           * Apply the change:
             ```bash theme={null}
             kubectl apply -f /tmp/serviceaccount-NAMESPACE-SERVICEACCOUNT.yaml
             ```

        3. Override specific Pods to *allow* token mount when needed
           * For workloads that truly require API access and use a ServiceAccount you have set to `automountServiceAccountToken: false`, explicitly set pod-level `automountServiceAccountToken: true` so they keep a token:
             ```bash theme={null}
             kubectl -n NAMESPACE get deployment DEPLOYMENT_NAME -o yaml > /tmp/deployment-NAMESPACE-DEPLOYMENT_NAME.yaml
             ```
           * Edit `/tmp/deployment-NAMESPACE-DEPLOYMENT_NAME.yaml` and, under `spec.template.spec`, add or set:
             ```yaml theme={null}
             automountServiceAccountToken: true
             ```
           * Apply the change:
             ```bash theme={null}
             kubectl apply -f /tmp/deployment-NAMESPACE-DEPLOYMENT_NAME.yaml
             ```
           * This will trigger a rollout; existing pods in that workload will be recreated.

        4. Disable token automount at the Pod level for workloads that do not need API access
           * For each non-compliant workload you determined does *not* need API access, edit the controller manifest (Deployment/StatefulSet/DaemonSet/CronJob, etc.):
             ```bash theme={null}
             kubectl -n NAMESPACE get deployment DEPLOYMENT_NAME -o yaml > /tmp/deployment-NAMESPACE-DEPLOYMENT_NAME.yaml
             ```
           * Under `spec.template.spec`, add or set:
             ```yaml theme={null}
             automountServiceAccountToken: false
             ```
           * Apply the manifest:
             ```bash theme={null}
             kubectl apply -f /tmp/deployment-NAMESPACE-DEPLOYMENT_NAME.yaml
             ```
           * This will recreate pods for that workload without mounting service account tokens.

        5. For bare Pods (not managed by a controller), recreate them with the correct setting
           * Export and edit the Pod definition:
             ```bash theme={null}
             kubectl -n NAMESPACE get pod POD_NAME -o yaml --export=false > /tmp/pod-NAMESPACE-POD_NAME.yaml
             ```
           * Remove `metadata.resourceVersion`, `metadata.uid`, `metadata.creationTimestamp`, `status`, and other server-generated fields from the file.
           * Under `spec`, set:
             ```yaml theme={null}
             automountServiceAccountToken: false
             ```
           * Delete and recreate the Pod:
             ```bash theme={null}
             kubectl -n NAMESPACE delete pod POD_NAME
             kubectl apply -f /tmp/pod-NAMESPACE-POD_NAME.yaml
             ```

        6. Verification
           * After changes have rolled out and pods are Running, re-run the audit on any machine with kubectl access:
             ```bash theme={null}
             kubectl get pods --all-namespaces -o custom-columns=POD_NAMESPACE:.metadata.namespace,POD_NAME:.metadata.name,POD_SERVICE_ACCOUNT:.spec.serviceAccount,POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken
             do
               svacc_is_automountserviceaccounttoken=$(kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json | jq -r '.automountServiceAccountToken' | sed -e 's/<none>/notset/g' -e 's/null/notset/g')
               pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's/<none>/notset/g' -e 's/null/notset/g')
               if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && ( [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ] ); then
                 is_compliant="true"
               elif [ "${svacc_is_automountserviceaccounttoken}" = "true" ] && [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then
                 is_compliant="true"
               else
                 is_compliant="false"
               fi
               echo "**namespace: ${pod_namespace} pod_name: ${pod_name} service_account: ${pod_service_account} pod_is_automountserviceaccounttoken: ${pod_is_automountserviceaccounttoken} svacc_is_automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken} is_compliant: ${is_compliant}"
             done | grep 'is_compliant: false' || echo "All pods compliant with 5.1.6"
             ```
      </Accordion>

      <Accordion title="Using kubectl">
        On any machine with kubectl access:

        1. Identify non-compliant Pods and ServiceAccounts (for review)

        ```sh theme={null}
        kubectl get pods --all-namespaces -o wide
        kubectl get serviceaccounts --all-namespaces
        ```

        2. Patch a ServiceAccount so that tokens are not mounted by default

        ```sh theme={null}
        kubectl patch serviceaccount default \
          -n your-namespace \
          --type merge \
          -p '{"automountServiceAccountToken": false}'
        ```

        Replace `default` and `your-namespace` with the specific ServiceAccount and namespace that do not need API access.

        Declarative example (ServiceAccount manifest):

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

        Apply:

        ```sh theme={null}
        kubectl apply -f serviceaccount-example-sa.yaml
        ```

        3. Override at Pod/Workload level where the Pod must NOT mount the token

        For individual Pods:

        ```yaml theme={null}
        apiVersion: v1
        kind: Pod
        metadata:
          name: example-pod
          namespace: your-namespace
        spec:
          serviceAccountName: example-sa
          automountServiceAccountToken: false
          containers:
          - name: app
            image: nginx:1.27
        ```

        ```sh theme={null}
        kubectl apply -f pod-example-pod.yaml
        ```

        For Deployments (or similar controllers):

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: example-deployment
          namespace: your-namespace
        spec:
          replicas: 2
          selector:
            matchLabels:
              app: example
          template:
            metadata:
              labels:
                app: example
            spec:
              serviceAccountName: example-sa
              automountServiceAccountToken: false
              containers:
              - name: app
                image: nginx:1.27
        ```

        ```sh theme={null}
        kubectl apply -f deployment-example.yaml
        ```

        4. Override at Pod/Workload level where the Pod MUST mount the token (explicitly true)

        If you have set `automountServiceAccountToken: false` on a ServiceAccount but a particular Pod using it needs API access, set it to true in the Pod spec (Pod takes precedence):

        ```yaml theme={null}
        spec:
          serviceAccountName: example-sa
          automountServiceAccountToken: true
          containers:
          - name: app
            image: your-image
        ```

        Apply with `kubectl apply -f ...`.

        5. Verification (on any machine with kubectl access)

        Re-run the benchmark-style audit and review that `is_compliant` is `true` for Pods that should not mount the token:

        ```sh theme={null}
        kubectl get pods --all-namespaces -o custom-columns=POD_NAMESPACE:.metadata.namespace,POD_NAME:.metadata.name,POD_SERVICE_ACCOUNT:.spec.serviceAccount,POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken
        do
          svacc_is_automountserviceaccounttoken=$(kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json | jq -r '.automountServiceAccountToken' | sed -e 's/<none>/notset/g' -e 's/null/notset/g')
          pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's/<none>/notset/g' -e 's/null/notset/g')
          if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && ( [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ] ); then
            is_compliant="true"
          elif [ "${svacc_is_automountserviceaccounttoken}" = "true" ] && [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then
            is_compliant="true"
          else
            is_compliant="false"
          fi
          echo "**namespace: ${pod_namespace} pod_name: ${pod_name} service_account: ${pod_service_account} pod_is_automountserviceaccounttoken: ${pod_is_automountserviceaccounttoken} svacc_is_automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken} is_compliant: ${is_compliant}"
        done
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automate CIS Kubernetes 5.1.6 remediation:
        # Ensure Service Account Tokens Are Only Mounted Where Necessary
        #
        # Requirements:
        # - Run on any machine with kubectl access and jq installed.
        # - Assumes cluster-admin (or equivalent) privileges.
        #
        # Behavior:
        # - For each Pod using a real ServiceAccount (not "default"):
        #   - If Pod.spec.automountServiceAccountToken is "true" or not set,
        #     patch the ServiceAccount to automountServiceAccountToken=false
        #     (if not already false).
        #   - If Pod.spec.automountServiceAccountToken is "true",
        #     patch the Pod to automountServiceAccountToken=false.
        # - Skips "kube-system" namespace and DaemonSets/StatefulSets/Deployments,
        #   because those should be fixed at controller/manifest level.
        # - Safe to re-run (uses server-side apply / merge patches).
        # - Finishes by re-running the benchmark audit logic to show compliance.

        set -euo pipefail

        # CONFIGURABLE: namespaces to exclude from automatic changes
        EXCLUDED_NAMESPACES_REGEX='^(kube-system|kube-public|kube-node-lease)$'

        echo "Discovering pods and service accounts..."
        kubectl get pods --all-namespaces -o custom-columns=\
        POD_NAMESPACE:.metadata.namespace,\
        POD_NAME:.metadata.name,\
        POD_SERVICE_ACCOUNT:.spec.serviceAccount,\
        POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken \
        --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken
        do
          # Skip excluded namespaces
          if [[ "${pod_namespace}" =~ ${EXCLUDED_NAMESPACES_REGEX} ]]; then
            continue
          fi

          # Skip pods without a service account or with default SA if you prefer to handle defaults manually
          if [ -z "${pod_service_account}" ] || [ "${pod_service_account}" = "<none>" ]; then
            continue
          fi

          # Normalize pod automount value
          pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's/<none>/notset/g' -e 's/null/notset/g')

          # Fetch current SA automount value (may be null)
          svacc_is_automountserviceaccounttoken=$(
            kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json \
            | jq -r '.automountServiceAccountToken // "notset"' \
            | sed -e 's/<none>/notset/g' -e 's/null/notset/g'
          )

          # Determine owning controller to avoid patching pods that will be recreated immediately
          owner_kind=$(kubectl get pod "${pod_name}" -n "${pod_namespace}" -o jsonpath='{.metadata.ownerReferences[0].kind}' 2>/dev/null || echo "")
          if [ -n "${owner_kind}" ] && [[ "${owner_kind}" =~ ^(DaemonSet|StatefulSet|ReplicaSet|Deployment|Job|CronJob)$ ]]; then
            # These should be fixed by editing the controller manifests instead
            continue
          fi

          echo "------------------------------------------------------------"
          echo "Namespace: ${pod_namespace}"
          echo "Pod: ${pod_name}"
          echo "ServiceAccount: ${pod_service_account}"
          echo "Pod automountServiceAccountToken: ${pod_is_automountserviceaccounttoken}"
          echo "SA  automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken}"

          # Decide if remediation is needed by reproducing compliance logic

          is_compliant="false"
          if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && \
             { [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ]; }; then
            is_compliant="true"
          elif { [ "${svacc_is_automountserviceaccounttoken}" = "true" ] || [ "${svacc_is_automountserviceaccounttoken}" = "notset" ]; } && \
               [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then
            is_compliant="true"
          fi

          if [ "${is_compliant}" = "true" ]; then
            echo "Status: already compliant, skipping."
            continue
          fi

          echo "Status: NON-COMPLIANT, applying remediation..."

          # 1) Ensure ServiceAccount has automountServiceAccountToken=false
          if [ "${svacc_is_automountserviceaccounttoken}" != "false" ]; then
            echo " - Patching ServiceAccount ${pod_service_account} in ${pod_namespace} to automountServiceAccountToken=false"
            kubectl patch serviceaccount "${pod_service_account}" \
              -n "${pod_namespace}" \
              --type='merge' \
              -p '{"automountServiceAccountToken": false}'
          else
            echo " - ServiceAccount already has automountServiceAccountToken=false"
          fi

          # 2) If Pod spec explicitly sets automountServiceAccountToken to true, patch it to false
          if [ "${pod_is_automountserviceaccounttoken}" = "true" ]; then
            echo " - Patching Pod ${pod_name} in ${pod_namespace} to automountServiceAccountToken=false"
            kubectl patch pod "${pod_name}" \
              -n "${pod_namespace}" \
              --type='merge' \
              -p '{"spec":{"automountServiceAccountToken": false}}'
          elif [ "${pod_is_automountserviceaccounttoken}" = "notset" ]; then
            # With SA set to false, Pod not setting automount is compliant; no need to patch Pod.
            echo " - Pod does not set automountServiceAccountToken; SA=false is sufficient"
          else
            echo " - Pod already has automountServiceAccountToken=false"
          fi

        done

        echo
        echo "Verification: re-running compliance evaluation..."
        echo

        kubectl get pods --all-namespaces -o custom-columns=\
        POD_NAMESPACE:.metadata.namespace,\
        POD_NAME:.metadata.name,\
        POD_SERVICE_ACCOUNT:.spec.serviceAccount,\
        POD_IS_AUTOMOUNTSERVICEACCOUNTTOKEN:.spec.automountServiceAccountToken \
        --no-headers | while read -r pod_namespace pod_name pod_service_account pod_is_automountserviceaccounttoken
        do
          svacc_is_automountserviceaccounttoken=$(kubectl get serviceaccount -n "${pod_namespace}" "${pod_service_account}" -o json 2>/dev/null \
            | jq -r '.automountServiceAccountToken // "notset"' \
            | sed -e 's/<none>/notset/g' -e 's/null/notset/g')
          pod_is_automountserviceaccounttoken=$(echo "${pod_is_automountserviceaccounttoken}" | sed -e 's/<none>/notset/g' -e 's/null/notset/g')

          if [ "${svacc_is_automountserviceaccounttoken}" = "false" ] && \
             { [ "${pod_is_automountserviceaccounttoken}" = "false" ] || [ "${pod_is_automountserviceaccounttoken}" = "notset" ]; }; then
            is_compliant="true"
          elif { [ "${svacc_is_automountserviceaccounttoken}" = "true" ] || [ "${svacc_is_automountserviceaccounttoken}" = "notset" ]; } && \
               [ "${pod_is_automountserviceaccounttoken}" = "false" ]; then
            is_compliant="true"
          else
            is_compliant="false"
          fi

          echo "**namespace: ${pod_namespace} pod_name: ${pod_name} service_account: ${pod_service_account} pod_is_automountserviceaccounttoken: ${pod_is_automountserviceaccounttoken} svacc_is_automountServiceAccountToken: ${svacc_is_automountserviceaccounttoken} is_compliant: ${is_compliant}"
        done

        echo
        echo "Automation complete. Review any remaining non-compliant workloads (likely controller-managed) and update their manifests accordingly."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
