Skip to main content

Ensure Default Service Accounts Are Not Actively Used

More Info:

Default service accounts should not be used for workloads and should not automount tokens. Use dedicated service accounts scoped to only the permissions each workload needs.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS AKS

Triage and Remediation

Remediation

Manual Steps
  1. Identify namespaces using the default service account

    • Run on: any machine with kubectl access
    • Command (lists Pods that rely on the default ServiceAccount):
      kubectl get pods --all-namespaces -o json \
      | jq -r '.items[]
      | select(.spec.serviceAccountName == null or .spec.serviceAccountName == "default")
      | [.metadata.namespace, .metadata.name, (.spec.serviceAccountName // "default")] | @tsv'
  2. Review RBAC permissions granted to each namespace’s default ServiceAccount

    • Run on: any machine with kubectl access
    • Commands:
      # List all RoleBindings and ClusterRoleBindings that reference 'default' ServiceAccounts
      kubectl get rolebindings,clusterrolebindings --all-namespaces -o yaml \
      | yq '.items[]
      | select(.subjects[]? | select(.kind == "ServiceAccount" and .name == "default"))
      | {kind, metadata: {name: .metadata.name, namespace: .metadata.namespace}, roleRef, subjects}'
    • Manually decide per namespace whether the default SA’s permissions are broader than needed for the Pods you found in step 1.
  3. For each workload using a default ServiceAccount, design a dedicated ServiceAccount and RBAC

    • For each (namespace, pod) from step 1, decide:
      • What minimal API access (if any) the workload truly needs.
      • Whether some Pods need no API access at all.
    • Plan either:
      • A new ServiceAccount with no extra permissions (Role/ClusterRole & binding only if needed), or
      • The Pod running with automountServiceAccountToken: false if it doesn’t need any Kubernetes API access.
  4. Create dedicated ServiceAccounts and RBAC objects

    • Run on: any machine with kubectl access
    • Example template (adjust NAMESPACE, names, and rules):
      cat <<'EOF' | kubectl apply -f -
      apiVersion: v1
      kind: ServiceAccount
      metadata:
      name: app-sa
      namespace: NAMESPACE
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
      name: app-sa-role
      namespace: NAMESPACE
      rules:
      # TODO: replace with minimal required rules
      - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list"]
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: RoleBinding
      metadata:
      name: app-sa-binding
      namespace: NAMESPACE
      subjects:
      - kind: ServiceAccount
      name: app-sa
      namespace: NAMESPACE
      roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: app-sa-role
      EOF
    • Update each workload manifest to set spec.serviceAccountName: app-sa and, where applicable, automountServiceAccountToken: false if no API access is needed.
  5. Disable token automounting for all default ServiceAccounts

    • Run on: any machine with kubectl access
    • Command:
      for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
      kubectl patch serviceaccount default -n "$ns" \
      -p '{"automountServiceAccountToken": false}' --type=merge || true
      done
    • Verify:
      kubectl get sa default --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.automountServiceAccountToken}{"\n"}{end}'
  6. Re-verify that no workloads rely on default ServiceAccounts and document exceptions

    • Run on: any machine with kubectl access
    • Command:
      kubectl get pods --all-namespaces -o json \
      | jq -r '.items[]
      | select(.spec.serviceAccountName == null or .spec.serviceAccountName == "default")
      | [.metadata.namespace, .metadata.name, (.spec.serviceAccountName // "default")] | @tsv'
    • Investigate any remaining Pods listed; either migrate them to dedicated ServiceAccounts or formally document and approve them as exceptions with a clear justification.
Using kubectl

Using kubectl

Run these on any machine with kubectl access.

1. List all default service accounts and their token automount setting

kubectl get serviceaccount default --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.automountServiceAccountToken}{"\n"}{end}'

Interpretation:

  • true (or blank/null, which means it inherits the namespace/pod default of true): the default SA in that namespace may be used by workloads and will get tokens by default. This is a risk and should be reviewed.
  • false: the default SA is configured not to automount tokens; this is the recommended baseline.

To see the full object for a specific namespace:

kubectl get serviceaccount default -n <namespace> -o yaml

Look for:

automountServiceAccountToken: false

If this field is missing or true, the default SA in that namespace is not hardened.

2. Identify pods that are using the default service account

List all pods with their namespace and service account:

kubectl get pods --all-namespaces -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,SERVICEACCOUNT:.spec.serviceAccountName' | sort

Interpretation:

  • Rows where SERVICEACCOUNT is default (or empty, which means it defaults to default SA) indicate workloads actively using the default service account. Each such pod should be reviewed to see if it should instead use a dedicated, least-privilege service account.

For more detail in a specific namespace:

kubectl get pods -n <namespace> -o wide
kubectl get pod <pod-name> -n <namespace> -o yaml | grep -A5 'serviceAccount'

Check:

  • .spec.serviceAccountName or .spec.serviceAccount:
    • default or omitted → using the default SA (needs review).
    • custom name → confirm that custom SA has only necessary permissions.

3. Check if pods using the default SA are actually mounting tokens

For all pods using the default SA in a namespace:

kubectl get pods -n <namespace> \
-o jsonpath='{range .items[?(@.spec.serviceAccountName=="default" || !@.spec.serviceAccountName)]}{.metadata.name}{"\t"}{.spec.automountServiceAccountToken}{"\n"}{end}'

Interpretation:

  • If the pod line shows true or is blank (and the namespace/SA defaults to true), the pod is getting a service account token mounted; combined with use of the default SA, this is a configuration needing human review.
  • If it shows false, the pod is not mounting a token despite using the default SA (lower risk, but still consider moving to a dedicated SA).

To confirm token volume mounts on a specific pod:

kubectl get pod <pod-name> -n <namespace> -o yaml | grep -A5 'serviceAccount'
kubectl get pod <pod-name> -n <namespace> -o yaml | grep -A5 'service-account-token'

Presence of a projected or secret volume for a service account token indicates the pod is using a token.

4. Review RBAC permissions granted to the default service account

For each namespace with pods using the default SA, inspect RBAC bindings:

# RoleBindings in namespace
kubectl get rolebinding -n <namespace> -o wide

# ClusterRoleBindings cluster-wide
kubectl get clusterrolebinding -o wide

Filter bindings that reference the default SA:

kubectl get rolebinding -n <namespace> -o json \
| jq -r '.items[] | select(.subjects[]? | select(.kind=="ServiceAccount" and .name=="default" and .namespace=="<namespace>")) | .metadata.name'

kubectl get clusterrolebinding -o json \
| jq -r '.items[] | select(.subjects[]? | select(.kind=="ServiceAccount" and .name=="default")) | .metadata.name'

Interpretation:

  • Any RoleBinding or ClusterRoleBinding that grants non-trivial permissions (e.g., edit, admin, custom roles with many verbs/resources) to ServiceAccount/default is a red flag.
  • Such bindings, combined with pods using the default SA, indicate the default SA is actively used with elevated privileges and should be redesigned to use dedicated service accounts.
Automation
#!/usr/bin/env bash
# Run on: any machine with kubectl access
# Purpose: Report use of default service accounts and whether they automount tokens

set -euo pipefail

echo "=== 1) Default ServiceAccount automountServiceAccountToken settings ==="
echo

# List all default ServiceAccounts and their automountServiceAccountToken field
kubectl get sa --all-namespaces -o json \
| jq -r '
.items[]
| select(.metadata.name=="default")
| [
.metadata.namespace,
.metadata.name,
(if .automountServiceAccountToken == true then "true"
elif .automountServiceAccountToken == false then "false"
else "null" end)
] | @tsv
' \
| awk 'BEGIN {printf "%-32s %-16s %-10s\n", "NAMESPACE", "SERVICEACCOUNT", "AUTOMOUNT"}
{printf "%-32s %-16s %-10s\n", $1, $2, $3}'

cat <<'EOF'

Problem indication (automount):
- Any line where:
SERVICEACCOUNT = default
AUTOMOUNT = true or null
means the namespace's default ServiceAccount may mount tokens by default.
Remediation per benchmark: set automountServiceAccountToken: false on default SAs you do not intend to use.

EOF

echo "=== 2) Pods using the default ServiceAccount (current workloads) ==="
echo

# List all pods that are using the default ServiceAccount
# (either explicitly serviceAccountName: default or implicitly by being empty)
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| . as $pod
| .spec.serviceAccountName as $sa
| .spec.serviceAccount as $legacy
| ($sa // $legacy // "default") as $resolvedSA
| select($resolvedSA=="default")
| [
$pod.metadata.namespace,
$pod.metadata.name,
$resolvedSA,
(if $pod.spec.automountServiceAccountToken == true then "true"
elif $pod.spec.automountServiceAccountToken == false then "false"
else "null" end)
] | @tsv
' \
| awk 'BEGIN {printf "%-32s %-40s %-16s %-10s\n", "NAMESPACE", "POD", "SERVICEACCOUNT", "POD_AUTOMOUNT"}
{printf "%-32s %-40s %-16s %-10s\n", $1, $2, $3, $4}'

cat <<'EOF'

Problem indication (pod usage):
- Any line is a workload currently using the default ServiceAccount.
Pay special attention when:
POD_AUTOMOUNT = true or null
because the pod may have access to a service account token.
- For such pods, review:
- Whether they truly need to use the default ServiceAccount.
- Whether they can instead use a dedicated, least-privilege ServiceAccount.
- Whether automountServiceAccountToken should be set to false at the pod/spec level.

NOTE:
- This script is only for visibility and review.
- Because this control is MANUAL, you must decide case-by-case whether each use of the default ServiceAccount is acceptable and then:
- Create and bind a dedicated ServiceAccount for the workload, and
- Set automountServiceAccountToken: false on default ServiceAccounts you do not intend to use.

EOF