> ## 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 Default Service Accounts Are Not Actively Used

### More Info:

Default service accounts should not be used by workloads and should not auto-mount tokens. Explicit, purpose-built service accounts should be created for workloads needing API access.

### 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. Disable token automount on all default ServiceAccounts
           * Run on: any machine with kubectl access
           ```bash theme={null}
           # Patch every default ServiceAccount in every namespace
           kubectl get sa --all-namespaces -o json \
           | jq -r '.items[] | select(.metadata.name=="default") | "\(.metadata.namespace)"' \
           | sort -u \
           | xargs -I{} kubectl patch sa default -n {} -p '{"automountServiceAccountToken": false}'
           ```

        2. Identify pods using the default ServiceAccount
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json \
           | jq -r '.items[]
             | select(.spec.serviceAccountName=="default")
             | "\(.metadata.namespace) \(.metadata.name)"' \
           | sort
           ```

        3. For each affected workload, create a dedicated ServiceAccount
           * Run on: any machine with kubectl access
           * Example for namespace `my-namespace` and app `my-app` (repeat per app/namespace as needed):
           ```bash theme={null}
           kubectl create serviceaccount my-app-sa -n my-namespace
           ```

        4. Update workloads to use the new ServiceAccount
           * Run on: any machine with kubectl access
           * Example for a Deployment (repeat for DaemonSets/StatefulSets/Jobs, etc.):
           ```bash theme={null}
           # Edit the workload to set spec.template.spec.serviceAccountName
           kubectl -n my-namespace edit deployment my-app
           ```
           Set:
           ```yaml theme={null}
           spec:
             template:
               spec:
                 serviceAccountName: my-app-sa
           ```
           Save and exit to trigger a rolling update.

        5. Recreate or roll pods that were using the default ServiceAccount directly
           * Run on: any machine with kubectl access
           * For pods managed by a controller, the edit in step 4 is enough.
           * For bare pods (no controller), delete and recreate them with an explicit `serviceAccountName`:
           ```bash theme={null}
           kubectl -n my-namespace delete pod POD_NAME
           # Recreate from manifest after adding:
           # spec:
           #   serviceAccountName: my-app-sa
           ```

        6. Verify remediation
           * Run on: any machine with kubectl access
           ```bash theme={null}
           echo "🔹 Default Service Accounts with automountServiceAccountToken enabled:"
           default_sa_count=$(kubectl get serviceaccounts --all-namespaces -o json | jq '
             [.items[] | select(.metadata.name == "default" and (.automountServiceAccountToken != false))] | length')
           if [ "$default_sa_count" -gt 0 ]; then
             echo "default_sa_not_auto_mounted"
           else
             echo "OK: all default SAs have automountServiceAccountToken=false"
           fi

           echo "\n🔹 Pods using default ServiceAccount:"
           pods_using_default_sa=$(kubectl get pods --all-namespaces -o json | jq '
             [.items[] | select(.spec.serviceAccountName == "default")] | length')
           if [ "$pods_using_default_sa" -gt 0 ]; then
             echo "default_sa_used_in_pods"
           else
             echo "OK: no pods are using the default ServiceAccount"
           fi
           ```
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1) Disable token automount on all existing *default* ServiceAccounts
        # Run on: any machine with kubectl access

        kubectl get sa default --all-namespaces -o json | \
        jq -r '.items[] |
          "cat <<\"EOF\" | kubectl apply -f -\n" +
          "---\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: default\n  namespace: " + .metadata.namespace + "\nautomountServiceAccountToken: false\nEOF\n"' | \
        bash

        # 2) (Recommended) Ensure new workloads use explicit, non-default ServiceAccounts
        # Example: create a namespace-scoped service account for a workload

        # Create a dedicated ServiceAccount in a chosen namespace (example: app-namespace)
        kubectl create namespace app-namespace --dry-run=client -o yaml | kubectl apply -f -

        cat <<'EOF' | kubectl apply -f -
        apiVersion: v1
        kind: ServiceAccount
        metadata:
          name: app-sa
          namespace: app-namespace
        EOF

        # Patch an existing deployment to use the explicit ServiceAccount (example)
        kubectl -n app-namespace patch deploy my-app-deployment \
          -p '{"spec":{"template":{"spec":{"serviceAccountName":"app-sa"}}}}'

        # 3) Verification

        # 3a) Confirm all default ServiceAccounts have automountServiceAccountToken: false
        kubectl get serviceaccounts --all-namespaces -o json | jq '
          [.items[] | select(.metadata.name == "default")] |
          map({ns: .metadata.namespace, name: .metadata.name, automount: .automountServiceAccountToken})'

        # 3b) Re-run the benchmark audit logic to ensure no failures
        echo "🔹 Default Service Accounts with automountServiceAccountToken enabled:"
        default_sa_count=$(kubectl get serviceaccounts --all-namespaces -o json | jq '
          [.items[] | select(.metadata.name == "default" and (.automountServiceAccountToken != false))] | length')
        if [ "$default_sa_count" -gt 0 ]; then
          echo "default_sa_not_auto_mounted"
        else
          echo "OK: all default ServiceAccounts have automountServiceAccountToken=false"
        fi

        echo "\n🔹 Pods using default ServiceAccount:"
        pods_using_default_sa=$(kubectl get pods --all-namespaces -o json | jq '
          [.items[] | select(.spec.serviceAccountName == "default")] | length')
        if [ "$pods_using_default_sa" -gt 0 ]; then
          echo "default_sa_used_in_pods"
        else
          echo "OK: no pods using the default ServiceAccount (new pods)"
        fi
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        set -euo pipefail

        # This script:
        # 1. Sets automountServiceAccountToken: false on every "default" ServiceAccount in all namespaces.
        # 2. Finds pods using the default SA and generates workload-specific ServiceAccounts for them.
        # 3. Patches those workloads to use the new ServiceAccounts.
        # 4. Verifies the benchmark checks pass.
        #
        # Requirements:
        # - Run on any machine with kubectl + jq + bash.
        # - kubectl must be configured with cluster-admin-like privileges.

        # Fail early if required tools are missing
        for bin in kubectl jq; do
          if ! command -v "$bin" >/dev/null 2>&1; then
            echo "ERROR: $bin is required but not found in PATH" >&2
            exit 1
          fi
        done

        echo "=== Step 1: Disable automountServiceAccountToken on all default ServiceAccounts ==="

        # Get all namespaces
        namespaces=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}')

        for ns in $namespaces; do
          # Check if default SA exists in the namespace
          if kubectl get sa default -n "$ns" >/dev/null 2>&1; then
            echo "Processing default ServiceAccount in namespace: $ns"

            # Patch the default SA to ensure automountServiceAccountToken: false
            # This is idempotent; re-running will not cause issues.
            kubectl patch sa default -n "$ns" \
              --type merge \
              -p '{"automountServiceAccountToken": false}' >/dev/null

          fi
        done

        echo "=== Step 2: Create explicit ServiceAccounts for workloads using default SA and patch them ==="

        # Helper: safely get kind/name from ownerReferences
        get_owner_ref() {
          jq -r '
            .metadata.ownerReferences // [] |
            map(select(.controller == true) | "\(.kind)/\(.name)") |
            .[0] // ""
          '
        }

        # Work through all pods using the default SA
        pods_json=$(kubectl get pods --all-namespaces -o json)

        echo "$pods_json" | jq -c '.items[] | select(.spec.serviceAccountName == "default" or .spec.serviceAccountName == null)' | while read -r pod; do
          ns=$(echo "$pod" | jq -r '.metadata.namespace')
          pod_name=$(echo "$pod" | jq -r '.metadata.name')

          owner_ref=$(echo "$pod" | get_owner_ref)

          if [[ -z "$owner_ref" ]]; then
            echo "Skipping naked Pod $ns/$pod_name (no controller owner). Review manually if needed."
            continue
          fi

          owner_kind=${owner_ref%%/*}
          owner_name=${owner_ref##*/}

          # We only handle common workload controllers. Others should be reviewed manually.
          case "$owner_kind" in
            Deployment|StatefulSet|DaemonSet|ReplicaSet|Job|CronJob)
              ;;
            *)
              echo "Skipping $owner_kind $ns/$owner_name (not an automated workload type handled by this script)."
              continue
              ;;
          esac

          # Construct a deterministic ServiceAccount name per workload
          sa_name="sa-${owner_kind,,}-${owner_name}"

          echo "Namespace: $ns | Workload: $owner_kind/$owner_name | Pod: $pod_name | Target SA: $sa_name"

          # Create the ServiceAccount if it does not exist (idempotent)
          if ! kubectl get sa "$sa_name" -n "$ns" >/dev/null 2>&1; then
            echo "  - Creating ServiceAccount $ns/$sa_name"
            kubectl create sa "$sa_name" -n "$ns" >/dev/null
          else
            echo "  - ServiceAccount $ns/$sa_name already exists; reusing"
          fi

          # Patch the workload to use the explicit ServiceAccount
          case "$owner_kind" in
            Deployment|StatefulSet|DaemonSet|ReplicaSet|Job)
              echo "  - Patching $owner_kind/$owner_name to use $sa_name"
              kubectl patch "$owner_kind" "$owner_name" -n "$ns" \
                --type merge \
                -p "{\"spec\":{\"template\":{\"spec\":{\"serviceAccountName\":\"$sa_name\",\"automountServiceAccountToken\":true}}}}" >/dev/null
              ;;
            CronJob)
              echo "  - Patching CronJob/$owner_name to use $sa_name"
              kubectl patch CronJob "$owner_name" -n "$ns" \
                --type merge \
                -p "{\"spec\":{\"jobTemplate\":{\"spec\":{\"template\":{\"spec\":{\"serviceAccountName\":\"$sa_name\",\"automountServiceAccountToken\":true}}}}}}" >/dev/null
              ;;
          esac

        done

        echo "=== Step 3: Verification (matches benchmark audit) ==="

        echo "🔹 Default Service Accounts with automountServiceAccountToken enabled:"
        default_sa_count=$(kubectl get serviceaccounts --all-namespaces -o json | jq '
          [.items[] | select(.metadata.name == "default" and (.automountServiceAccountToken != false))] | length')
        if [ "$default_sa_count" -gt 0 ]; then
          echo "default_sa_not_auto_mounted (remaining count: $default_sa_count)"
        else
          echo "OK: All default ServiceAccounts have automountServiceAccountToken set to false."
        fi

        echo
        echo "🔹 Pods using default ServiceAccount:"
        pods_using_default_sa=$(kubectl get pods --all-namespaces -o json | jq '
          [.items[] | select(.spec.serviceAccountName == "default" or .spec.serviceAccountName == null)] | length')
        if [ "$pods_using_default_sa" -gt 0 ]; then
          echo "default_sa_used_in_pods (remaining count: $pods_using_default_sa)"
          echo "NOTE: Some pods still use the default ServiceAccount (e.g., naked Pods or uncommon controllers)."
          echo "      Review and update these workloads manually."
        else
          echo "OK: No pods are using the default ServiceAccount."
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
