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

# Minimize Access To Service Account Token Creation

### More Info:

The token sub-resource of service accounts can mint tokens that impersonate those accounts. Limit who can create service account tokens.

### 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 roles/clusterroles that can create service account tokens**
           * Run on: any machine with kubectl access
           ```sh theme={null}
           kubectl get clusterroles -o json | jq -r '
             .items[] |
             select(.rules[]? |
               .resources[]? == "serviceaccounts/token"
               and (.verbs[]? | IN("create","*"))
             ) |
             .metadata.name
           '
           kubectl get roles -A -o json | jq -r '
             .items[] |
             select(.rules[]? |
               .resources[]? == "serviceaccounts/token"
               and (.verbs[]? | IN("create","*"))
             ) |
             [.metadata.namespace, .metadata.name] | @tsv
           '
           ```

        2. **Review which subjects are bound to those roles/clusterroles**
           * For each ClusterRole name from step 1:
           ```sh theme={null}
           CLUSTERROLE_NAME="<name from step 1>"
           kubectl get clusterrolebindings -o yaml | yq '
             .items[] |
             select(.roleRef.kind == "ClusterRole" and .roleRef.name == "'"$CLUSTERROLE_NAME"'")
           '
           ```
           * For each Role (namespace, name) from step 1:
           ```sh theme={null}
           NAMESPACE="<namespace>"
           ROLE_NAME="<role name>"
           kubectl get rolebindings -n "$NAMESPACE" -o yaml | yq '
             .items[] |
             select(.roleRef.kind == "Role" and .roleRef.name == "'"$ROLE_NAME"'")
           '
           ```
           * Manually decide whether each subject (user/group/serviceaccount) truly needs to mint tokens.

        3. **Inspect the exact permissions granted on serviceaccounts/token**
           * For each role/clusterrole from step 1:
           ```sh theme={null}
           # ClusterRole
           CLUSTERROLE_NAME="<name>"
           kubectl get clusterrole "$CLUSTERROLE_NAME" -o yaml

           # Namespaced Role
           NAMESPACE="<namespace>"
           ROLE_NAME="<name>"
           kubectl get role -n "$NAMESPACE" "$ROLE_NAME" -o yaml
           ```
           * Confirm whether `resources: ["serviceaccounts/token"]` is necessary and whether `verbs` can be reduced or removed.

        4. **Tighten or remove token-creation permissions**
           * If token creation is not needed, edit and remove the `serviceaccounts/token` rule:
           ```sh theme={null}
           # ClusterRole
           CLUSTERROLE_NAME="<name>"
           kubectl edit clusterrole "$CLUSTERROLE_NAME"

           # Role
           NAMESPACE="<namespace>"
           ROLE_NAME="<name>"
           kubectl edit role -n "$NAMESPACE" "$ROLE_NAME"
           ```
           * In the editor, delete only the rule(s) that contain `resources: ["serviceaccounts/token"]` (or that list it among resources), or at least remove `create` from `verbs` if you must retain other verbs.

        5. **Re-assess bindings; re-scope if needed**
           * Where token creation is required but too broadly granted, create a new, minimal Role/ClusterRole and bind it only to the specific service accounts/users that need it:
           ```sh theme={null}
           cat <<'EOF' > sa-token-minimal-clusterrole.yaml
           apiVersion: rbac.authorization.k8s.io/v1
           kind: ClusterRole
           metadata:
             name: sa-token-minimal
           rules:
           - apiGroups: [""]
             resources: ["serviceaccounts/token"]
             verbs: ["create"]
           EOF

           kubectl apply -f sa-token-minimal-clusterrole.yaml

           # Example binding to a single service account
           kubectl create clusterrolebinding sa-token-minimal-binding \
             --clusterrole=sa-token-minimal \
             --serviceaccount=default:example-sa
           ```
           * Then remove broader bindings discovered in step 2 that no longer need this access.

        6. **Verify that access to serviceaccounts/token is minimized**
           * Re-run the discovery from step 1 and confirm only intentionally authorized roles remain:
           ```sh theme={null}
           kubectl get clusterroles -o json | jq -r '
             .items[] |
             select(.rules[]? |
               .resources[]? == "serviceaccounts/token"
               and (.verbs[]? | IN("create","*"))
             ) |
             .metadata.name
           '
           kubectl get roles -A -o json | jq -r '
             .items[] |
             select(.rules[]? |
               .resources[]? == "serviceaccounts/token"
               and (.verbs[]? | IN("create","*"))
             ) |
             [.metadata.namespace, .metadata.name] | @tsv
           '
           ```
           * Optionally verify for a given subject whether it can still create a token:
           ```sh theme={null}
           kubectl auth can-i create serviceaccounts/token --as=<user-or-sa> -n <namespace>
           ```
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1) List all Roles and ClusterRoles that mention 'serviceaccounts/token'

        # Run on: any machine with kubectl access
        kubectl get clusterroles -o yaml | grep -nA5 -B5 "serviceaccounts/token" || echo "No ClusterRoles grant serviceaccounts/token"
        kubectl get roles -A -o yaml | grep -nA5 -B5 "serviceaccounts/token" || echo "No Roles grant serviceaccounts/token"
        ```

        **What to look for (problematic cases):**

        Any `rules` entry that includes:

        ```yaml theme={null}
        resources:
          - serviceaccounts/token
        verbs:
          - create        # most critical
          - '*'           # also implies create
          # or any list that includes 'create'
        ```

        ***

        ```bash theme={null}
        # 2) Show full definitions of Roles/ClusterRoles that reference 'serviceaccounts/token'

        # ClusterRoles
        kubectl get clusterroles -o jsonpath='{range .items[?(@.rules)]}{.metadata.name}{"\n"}{range .rules[?(@.resources && (index .resources 0)=="serviceaccounts/token" || "serviceaccounts/token" in @.resources)]}{"  resources: "}{.resources}{"\n  verbs: "}{.verbs}{"\n"}{end}{"\n"}{end}'

        # Namespaced Roles
        kubectl get roles -A -o jsonpath='{range .items[?(@.rules)]}{.metadata.namespace}{"/"}{.metadata.name}{"\n"}{range .rules[?(@.resources && (index .resources 0)=="serviceaccounts/token" || "serviceaccounts/token" in @.resources)]}{"  resources: "}{.resources}{"\n  verbs: "}{.verbs}{"\n"}{end}{"\n"}{end}'
        ```

        **What to look for (problematic cases):**

        * Any entry where `verbs` contains `create` directly or via `*`.
        * Wide or sensitive scope, for example:
          * ClusterRole used cluster‑wide by many subjects.
          * Roles/ClusterRoles bound to:
            * `system:serviceaccounts` (all service accounts in a namespace or cluster).
            * `system:authenticated` or `system:authenticated:oauth`.
            * Broad user groups (e.g., corporate SSO groups) not specifically needing token minting.

        ***

        ```bash theme={null}
        # 3) Find which subjects are bound to those Roles/ClusterRoles

        # First, list all RoleBindings/ClusterRoleBindings that reference Roles/ClusterRoles
        # mentioning 'serviceaccounts/token'. This is two-step: get names, then describe bindings.

        # 3a) Get names of ClusterRoles with serviceaccounts/token
        clusterroles_with_token=$(kubectl get clusterroles -o jsonpath='{range .items[?(@.rules)]}{.metadata.name}{" "}{end}' | xargs -n1 -I{} sh -c 'kubectl get clusterrole {} -o yaml | grep -q "serviceaccounts/token" && echo {}')

        # 3b) Get names of Roles with serviceaccounts/token (namespaced)
        roles_with_token=$(kubectl get roles -A -o jsonpath='{range .items[?(@.rules)]}{.metadata.namespace}{"/"}{.metadata.name}{" "}{end}' | xargs -n1 -I{} sh -c 'ns="${%%/*}"; name="${##*/}"; kubectl get role -n "$ns" "$name" -o yaml | grep -q "serviceaccounts/token" && echo "$ns/$name"' 2>/dev/null)

        echo "ClusterRoles with serviceaccounts/token:"
        echo "$clusterroles_with_token"
        echo
        echo "Roles with serviceaccounts/token:"
        echo "$roles_with_token"

        # 3c) For each such ClusterRole, show its bindings and subjects
        for cr in $clusterroles_with_token; do
          echo "=== ClusterRole: $cr ==="
          kubectl get clusterrolebinding -o jsonpath="{range .items[?(@.role.name=='$cr')]}{.metadata.name}{'\n  Subjects: '}{range .subjects}{.kind}{'/'}{.namespace}{'/'}{.name}{', '}{end}{'\n\n'}{end}"
          kubectl get rolebinding -A -o jsonpath="{range .items[?(@.role.kind=='ClusterRole' && @.role.name=='$cr')]}{.metadata.namespace}{'/'}{.metadata.name}{'\n  Subjects: '}{range .subjects}{.kind}{'/'}{.namespace}{'/'}{.name}{', '}{end}{'\n\n'}{end}"
        done

        # 3d) For each namespaced Role, show its bindings and subjects
        for r in $roles_with_token; do
          ns="${r%%/*}"
          name="${r##*/}"
          echo "=== Role: $ns/$name ==="
          kubectl get rolebinding -n "$ns" -o jsonpath="{range .items[?(@.role.kind=='Role' && @.role.name=='$name')]}{.metadata.name}{'\n  Subjects: '}{range .subjects}{.kind}{'/'}{.namespace}{'/'}{.name}{', '}{end}{'\n\n'}{end}"
        done
        ```

        **What to look for (problematic cases):**

        * Bindings where subjects are:
          * `Group` = `system:authenticated`, `system:serviceaccounts`, or large identity provider groups.
          * `ServiceAccount` that do not clearly need to mint tokens (generic app SAs).
          * `User` accounts that should not impersonate arbitrary service accounts.

        ***

        ```bash theme={null}
        # 4) Focused inspection for 'create' verb on serviceaccounts/token

        # ClusterRoles
        kubectl get clusterroles -o json | jq -r '
          .items[]
          | {name: .metadata.name, rules: .rules}
          | select(.rules != null)
          | select(.rules[]
              | select(.resources != null and (.resources | index("serviceaccounts/token")))
              | select(.verbs | index("create") or index("*"))
            )
          | .name
        ' | while read cr; do
          echo "=== ClusterRole needing review: $cr ==="
          kubectl get clusterrole "$cr" -o yaml
          echo
        done

        # Roles
        kubectl get roles -A -o json | jq -r '
          .items[]
          | {ns: .metadata.namespace, name: .metadata.name, rules: .rules}
          | select(.rules != null)
          | select(.rules[]
              | select(.resources != null and (.resources | index("serviceaccounts/token")))
              | select(.verbs | index("create") or index("*"))
            )
          | "\(.ns)/\(.name)"
        ' | while read rn; do
          ns="${rn%%/*}"
          name="${rn##*/}"
          echo "=== Role needing review: $ns/$name ==="
          kubectl get role -n "$ns" "$name" -o yaml
          echo
        done
        ```

        **What to look for (problematic cases):**

        * Roles/ClusterRoles from this list that:
          * Are not strictly necessary for components that really need to mint service account tokens.
          * Are bound to broad or sensitive subjects as described above.

        ***

        ```bash theme={null}
        # 5) Verification after any manual changes (re‑run core discovery)

        # Run on: any machine with kubectl access
        kubectl get clusterroles -o yaml | grep -nA5 -B5 "serviceaccounts/token" || echo "No ClusterRoles grant serviceaccounts/token"
        kubectl get roles -A -o yaml | grep -nA5 -B5 "serviceaccounts/token" || echo "No Roles grant serviceaccounts/token"
        ```

        If this output shows no `serviceaccounts/token` with `verbs` including `create` (or `*`), or only tightly scoped, explicitly justified roles, the risk from this control has been reduced.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Audit serviceaccount token subresource usage in RBAC
        # Run on: any machine with kubectl access

        set -euo pipefail

        echo "=== Searching for RBAC rules that allow 'create' on 'serviceaccounts/token' ==="
        echo

        # 1) ClusterRoles
        echo "--- ClusterRoles with 'create' on serviceaccounts/token ---"
        kubectl get clusterroles -o json \
          | jq -r '
            .items[]
            | .metadata.name as $name
            | [
                .rules[]
                | select(.resources != null)
                | select((.resources | index("serviceaccounts/token")) != null)
                | select((.verbs | index("create")) != null)
              ] as $rules
            | select($rules | length > 0)
            | "ClusterRole: \($name)\n" +
              (
                $rules[]
                | "  apiGroups: \(.apiGroups // [])\n" +
                  "  resources: \(.resources // [])\n" +
                  "  verbs:     \(.verbs // [])\n"
              )
          '

        echo
        # 2) Roles (namespaced)
        echo "--- Roles with 'create' on serviceaccounts/token ---"
        kubectl get roles -A -o json \
          | jq -r '
            .items[]
            | .metadata.name as $name
            | .metadata.namespace as $ns
            | [
                .rules[]
                | select(.resources != null)
                | select((.resources | index("serviceaccounts/token")) != null)
                | select((.verbs | index("create")) != null)
              ] as $rules
            | select($rules | length > 0)
            | "Role: \($name) (namespace: \($ns))\n" +
              (
                $rules[]
                | "  apiGroups: \(.apiGroups // [])\n" +
                  "  resources: \(.resources // [])\n" +
                  "  verbs:     \(.verbs // [])\n"
              )
          '

        echo
        # 3) Show which subjects get these permissions (ClusterRoleBindings)
        echo "--- ClusterRoleBindings that reference those ClusterRoles ---"
        kubectl get clusterrolebindings -o json \
          | jq -r '
            .items[]
            | .metadata.name as $crb_name
            | .roleRef.name as $role_name
            | .subjects as $subs
            | select($subs != null and ($subs | length > 0))
            | "ClusterRoleBinding: \($crb_name)\n  roleRef: \($role_name)\n  subjects:\n" +
              (
                $subs[]
                | "    kind=\(.kind) name=\(.name) namespace=\(.namespace // "-")\n"
              )
          ' \
          | awk '
              BEGIN { show=0 }
              /^ClusterRoleBinding:/ { show=0 }
              /roleRef:/ {
                role=$2
                # Defer filtering; we will check against actual risky ClusterRoles below
                print_line=$0
              }
              { print }
            ' >/dev/null  # This section is informational; see step 4 for precise mapping

        # 4) Precise mapping: cluster-wide subjects that can create serviceaccounts/token
        echo "--- Cluster-wide subjects with 'create' on serviceaccounts/token ---"
        kubectl get clusterroles -o json \
          | jq -r '
            .items[]
            | select(
                any(.rules[]?; 
                  (.resources // []) | index("serviceaccounts/token")
                  and (.verbs // []) | index("create")
                )
              )
            | .metadata.name
          ' \
          | sort -u \
          | while read -r cr; do
              [ -z "$cr" ] && continue
              echo "ClusterRole: $cr"
              kubectl get clusterrolebindings -o json \
                | jq -r --arg cr "$cr" '
                  .items[]
                  | select(.roleRef.kind == "ClusterRole" and .roleRef.name == $cr)
                  | "  ClusterRoleBinding: \(.metadata.name)\n" +
                    (
                      (.subjects // [])
                      | map("    kind=\(.kind) name=\(.name) namespace=\(.namespace // \"-\")")
                      | join("\n")
                    )
                '
              echo
            done

        # 5) Precise mapping: namespaced subjects that can create serviceaccounts/token
        echo "--- Namespaced subjects with 'create' on serviceaccounts/token ---"
        kubectl get roles -A -o json \
          | jq -r '
            .items[]
            | select(
                any(.rules[]?; 
                  (.resources // []) | index("serviceaccounts/token")
                  and (.verbs // []) | index("create")
                )
              )
            | "\(.metadata.namespace) \(.metadata.name)"
          ' \
          | sort -u \
          | while read -r ns role; do
              [ -z "$ns" ] && continue
              echo "Role: $role (namespace: $ns)"
              kubectl get rolebindings -n "$ns" -o json \
                | jq -r --arg role "$role" '
                  .items[]
                  | select(.roleRef.kind == "Role" and .roleRef.name == $role)
                  | "  RoleBinding: \(.metadata.name)\n" +
                    (
                      (.subjects // [])
                      | map("    kind=\(.kind) name=\(.name) namespace=\(.namespace // \"-\")")
                      | join("\n")
                    )
                '
              echo
            done

        echo "=== Done. Review output above. ==="
        ```

        **How to interpret the output**

        * A **potential problem** is indicated whenever you see:
          * A `ClusterRole` or `Role` listing `resources: ["serviceaccounts/token"]` (or including it in the list) **and** `verbs` including `"create"`.
          * Corresponding `ClusterRoleBinding` or `RoleBinding` sections that attach these roles to:
            * `subjects` with `kind=User` or `Group` representing broad human populations (e.g. `system:authenticated`, large SSO groups), or
            * `subjects` with `kind=ServiceAccount` that do not strictly require minting tokens for other workloads.

        * These cases should be manually reviewed; where possible, remove `create` on `serviceaccounts/token` from those roles or refactor the access to narrower, justified subjects.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
