> ## 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 granted permissions or used by workloads. Their tokens should not be auto-mounted.

### 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 all default service accounts and their automount setting
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get serviceaccount --all-namespaces --field-selector metadata.name=default -o wide
           ```

        2. Review workloads currently using default service accounts and decide whether to change them
           * Run on: any machine with kubectl access
           ```bash theme={null}
           # Pods explicitly or implicitly using the default service account in each namespace
           for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
             echo "=== Namespace: $ns ==="
             kubectl get pods -n "$ns" -o wide --field-selector spec.serviceAccountName=default || true
           done
           ```
           * For any pod that is not a system or vendor-managed component and still uses the default service account, plan to create and assign a dedicated ServiceAccount with only the permissions it needs.

        3. Create explicit service accounts for affected workloads (per namespace)
           * Run on: any machine with kubectl access
           * Example for one namespace (replace `NAMESPACE` and `APP-SA` with your values):
           ```bash theme={null}
           kubectl create serviceaccount app-sa -n NAMESPACE
           ```
           * Update the corresponding RBAC (Roles/ClusterRoles and RoleBindings/ClusterRoleBindings) to grant only the minimum required permissions to `app-sa`. For example:
           ```bash theme={null}
           kubectl create rolebinding app-sa-view \
             --clusterrole=view \
             --serviceaccount=NAMESPACE:app-sa \
             -n NAMESPACE
           ```

        4. Update workloads to stop using the default service account
           * Run on: any machine with kubectl access
           * Edit each affected workload (Deployment/StatefulSet/DaemonSet/CronJob/Job/Pod) to use the new explicit service account, e.g.:
           ```bash theme={null}
           kubectl -n NAMESPACE edit deployment DEPLOYMENT_NAME
           ```
           * Under `spec.template.spec`, set:
           ```yaml theme={null}
           serviceAccountName: app-sa
           ```
           * Save and exit the editor; Kubernetes will roll out updated pods using the explicit service account.

        5. Disable token automount on default service accounts
           * Run on: any machine with kubectl access
           * For each namespace that has a `default` service account:
           ```bash theme={null}
           NAMESPACE=example-namespace

           kubectl -n "$NAMESPACE" patch serviceaccount default \
             -p '{"automountServiceAccountToken": false}'
           ```
           * Repeat for all namespaces where you want to prevent automatic token mounting on the default service account.

        6. Verify that default service accounts are no longer actively used and have automount disabled
           * Run on: any machine with kubectl access
           ```bash theme={null}
           # Check automountServiceAccountToken on all default service accounts
           kubectl get serviceaccount --all-namespaces --field-selector metadata.name=default -o=json \
           | jq -r '.items[] | "namespace: \(.metadata.namespace), kind: \(.kind), name: \(.metadata.name), automountServiceAccountToken: \(.automountServiceAccountToken | if . == null then "notset" else . end )"'

           # Confirm that no non-system pods are still using the default service account
           for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
             echo "=== Namespace: $ns ==="
             kubectl get pods -n "$ns" -o json \
               | jq -r '.items[] | select(.spec.serviceAccountName=="default") | .metadata.name'
           done
           ```
      </Accordion>

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

        1. Export all existing default ServiceAccounts to manifests (for review and backup)

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

        2. Patch all existing default ServiceAccounts to disable token auto-mount

        ```bash theme={null}
        kubectl get serviceaccount --all-namespaces \
          --field-selector metadata.name=default \
          -o json | \
          kubectl patch -f - \
          --type merge \
          -p '{"automountServiceAccountToken": false}'
        ```

        3. (Optional) Enforce the setting declaratively for a specific namespace

        Example manifest (save as `sa-default-patch.yaml`):

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

        Apply it:

        ```bash theme={null}
        kubectl apply -f sa-default-patch.yaml
        ```

        4. Ensure new or existing workloads do not rely on the default ServiceAccount

        For each deployment/statefulset/cronjob/etc., set an explicit non-default ServiceAccount and (optionally) disable auto-mount at pod level. Example manifest snippet:

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: example
          namespace: your-namespace
        spec:
          template:
            spec:
              serviceAccountName: example-sa
              automountServiceAccountToken: false
        ```

        Apply:

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

        5. Verification

        ```bash theme={null}
        kubectl get serviceaccount --all-namespaces \
          --field-selector metadata.name=default -o=json | \
          jq -r '.items[] | " namespace: \(.metadata.namespace), kind: \(.kind), name: \(.metadata.name), automountServiceAccountToken: \(.automountServiceAccountToken | if . == null then "notset" else . end )"' | \
          xargs -L 1
        ```
      </Accordion>

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

        # This script must run on any machine with kubectl and jq configured for the target cluster.

        echo "[INFO] Discovering all 'default' ServiceAccounts in the cluster..."
        mapfile -t DEFAULT_SAS < <(
          kubectl get serviceaccount --all-namespaces \
            --field-selector metadata.name=default \
            -o json | jq -r '.items[] | [.metadata.namespace, .metadata.name] | @tsv'
        )

        if [ "${#DEFAULT_SAS[@]}" -eq 0 ]; then
          echo "[INFO] No 'default' ServiceAccounts found."
        else
          echo "[INFO] Found ${#DEFAULT_SAS[@]} 'default' ServiceAccount(s)."
        fi

        for entry in "${DEFAULT_SAS[@]}"; do
          ns=$(echo "$entry" | awk '{print $1}')
          name=$(echo "$entry" | awk '{print $2}')

          echo "[INFO] Processing ServiceAccount '$name' in namespace '$ns'..."

          # Dump current SA as YAML
          tmpfile=$(mktemp)
          kubectl get serviceaccount "$name" -n "$ns" -o yaml > "$tmpfile"

          # Check current automountServiceAccountToken value
          current=$(yq -r '.automountServiceAccountToken // "null"' "$tmpfile")

          if [ "$current" = "false" ]; then
            echo "[INFO]   automountServiceAccountToken already false; no change needed."
            rm -f "$tmpfile"
            continue
          fi

          # Ensure top-level automountServiceAccountToken: false is set
          # yq v4 syntax
          yq -y '.automountServiceAccountToken = false' "$tmpfile" > "${tmpfile}.patched"

          echo "[INFO]   Applying patch to set automountServiceAccountToken: false ..."
          kubectl apply -f "${tmpfile}.patched"

          rm -f "$tmpfile" "${tmpfile}.patched"
        done

        echo "[INFO] Verifying all 'default' ServiceAccounts have automountServiceAccountToken=false ..."

        kubectl get serviceaccount --all-namespaces \
          --field-selector metadata.name=default \
          -o json | jq -r '
            .items[] |
            "namespace=\(.metadata.namespace) name=\(.metadata.name) automountServiceAccountToken=\(.automountServiceAccountToken // "notset")"
          ' | sed 's/^/[RESULT] /'

        # Fail if any default SA is still not set to false
        non_compliant_count=$(
          kubectl get serviceaccount --all-namespaces \
            --field-selector metadata.name=default \
            -o json | jq '[.items[] | select((.automountServiceAccountToken // false) != false)] | length'
        )

        if [ "$non_compliant_count" -ne 0 ]; then
          echo "[ERROR] $non_compliant_count 'default' ServiceAccount(s) still do not have automountServiceAccountToken=false."
          exit 1
        fi

        echo "[INFO] All 'default' ServiceAccounts now have automountServiceAccountToken=false."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
