Skip to main content

Ensure Default Service Accounts Are Not Actively Used

More Info:

The default service account should not be used to ensure that rights granted to applications can be more easily audited and reviewed.

Risk Level

Medium

Address

Security

Compliance Standards

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS Critical Security Controls v8
  • CIS OKE
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • Essential 8
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIS2 Directive
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation

Remediation

Manual Steps
  1. Identify namespaces where the default service account is used by running Pods

    • On any machine with kubectl access:
      kubectl get pods --all-namespaces -o jsonpath='{range .items[?(@.spec.serviceAccountName=="default")]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{end}'
    • Record namespaces and Pod names that appear; these workloads are currently using the default service account.
  2. Check whether default service accounts auto-mount tokens

    • On any machine with kubectl access:
      kubectl get serviceaccount default --all-namespaces -o custom-columns='NAMESPACE:.metadata.namespace,AUTOMOUNT:.automountServiceAccountToken'
    • Note any rows where AUTOMOUNT is empty or true; these default service accounts can auto-mount tokens.
  3. Review whether affected workloads actually need Kubernetes API access

    • For each Pod from step 1, inspect how it uses the service account:
      kubectl describe pod <pod-name> -n <namespace>
    • Examine container images, args/commands, and environment variables to determine if the application should call the Kubernetes API. If it does not, plan to disable token automount for the default service account in that namespace (or for the Pod itself).
  4. Create explicit service accounts where API access is needed

    • For each namespace where some workloads legitimately need API access, create a dedicated service account (example):
      kubectl create serviceaccount app-sa -n <namespace>
    • Update each relevant workload manifest (Deployment/StatefulSet/Job, etc.) so that under spec.template.spec it specifies this service account:
      serviceAccountName: app-sa
      automountServiceAccountToken: true
    • Apply the updated manifest from any machine with kubectl access, for example:
      kubectl apply -f <workload-manifest>.yaml
  5. Harden default service accounts by disabling token automount

    • Once workloads that need API access have explicit service accounts, update each namespace’s default service account to disable automounting the token:
      kubectl patch serviceaccount default -n <namespace> \
      --type merge \
      -p '{"automountServiceAccountToken": false}'
    • For extra safety, also set automountServiceAccountToken: false at the Pod spec level for workloads that should not use a token.
  6. Verify that default service accounts are not actively used with tokens

    • Re-run the evidence commands from steps 1 and 2:
      kubectl get pods --all-namespaces -o jsonpath='{range .items[?(@.spec.serviceAccountName=="default")]}{.metadata.namespace}{"\t"}{.metadata.name}{"\n"}{end}'
      kubectl get serviceaccount default --all-namespaces -o custom-columns='NAMESPACE:.metadata.namespace,AUTOMOUNT:.automountServiceAccountToken'
    • Confirm that:
      • Any remaining Pods using the default service account are intended to do so and do not require API access, and
      • automountServiceAccountToken is false for default service accounts in all applicable namespaces.
Using kubectl

Using kubectl

Run these commands from any machine with kubectl access.

1. List all default service accounts and check token automount

kubectl get serviceaccounts --all-namespaces \
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,AUTOMOUNT:.automountServiceAccountToken' \
| grep '\sdefault\s'

Indicates a problem:

  • Any default service account where:
    • AUTOMOUNT is empty (inherits true in most clusters), or
    • AUTOMOUNT is explicitly true.

You are aiming for AUTOMOUNT to be false for all default service accounts unless there is a documented exception.

To see full YAML for a specific namespace’s default SA:

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

Look for:

automountServiceAccountToken: false

If it is missing or set to true, it is a candidate for review.

2. Find workloads that use the default service account

Check all pods currently using the default service account:

kubectl get pods --all-namespaces \
-o custom-columns='NAMESPACE:.metadata.namespace,POD:.metadata.name,SA:.spec.serviceAccountName' \
| awk '$3 == "default"'

Indicates a problem:

  • Any application (non-system) pods using service account default, especially in production namespaces.
  • Multiple unrelated workloads sharing the same default account (hard to audit/least-privilege).

Check which controllers (deployments, etc.) are configured to use default:

kubectl get deploy,sts,ds,job,cronjob --all-namespaces \
-o jsonpath='{range .items[*]}{.kind}{"\t"}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.template.spec.serviceAccountName}{"\n"}{end}' \
| awk '($4 == "" || $4 == "default")'

Indicates a problem:

  • Any non-system workload with:
    • serviceAccountName empty (will fall back to default), or
    • serviceAccountName explicitly set to default, without a clear justification and documentation.

3. Check if default SAs are bound to powerful roles

List role bindings referencing the default service account:

# Namespaced rolebindings
kubectl get rolebinding --all-namespaces -o json \
| jq -r '.items[]
| select(.subjects[]? | select(.kind=="ServiceAccount" and .name=="default"))
| [.metadata.namespace, .metadata.name, .roleRef.kind, .roleRef.name] | @tsv'

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

Indicates a problem:

  • Any default service account bound to:
    • cluster-admin or other highly-privileged ClusterRoles, or
    • Broad namespace Roles (e.g., full CRUD on many resources), especially in application namespaces.

4. Verify automountServiceAccountToken behavior on pods

For a pod that uses the default SA in a given namespace:

kubectl get pod <pod-name> -n <namespace> -o yaml \
| grep -A3 serviceAccountToken

Or simply check for the token volume:

kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.volumes[*].projected.sources[*].serviceAccountToken}\n'

Indicates a problem:

  • Pods using default SA and still mounting a serviceAccountToken volume when they do not need API access.
  • This typically means either:
    • The default SA is not set to automountServiceAccountToken: false, and/or
    • The pod spec explicitly enables token automount.
Automation
#!/usr/bin/env bash
# Check usage of default service accounts and their token mounting across the cluster

set -euo pipefail

echo "=== 1) List all default service accounts and whether they auto-mount tokens ==="
kubectl get serviceaccount -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.automountServiceAccountToken}{"\n"}{end}' \
| column -t \
| sort

cat <<'EOF'

Interpretation:
- Any line where the second column is "default" and the third column is "true" is a finding.
Example problematic line:
my-namespace default true
- Desired state for all default serviceaccounts:
<namespace> default false

EOF

echo "=== 2) Show workloads that are using the default service account and/or auto-mounting tokens ==="
echo "--- Pods using the *default* service account (any automount setting) ---"
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\t"}{.spec.automountServiceAccountToken}{"\n"}{end}' \
| column -t \
| sort \
| awk '$3 == "default" {print}'

cat <<'EOF'

Interpretation:
- Every line here shows a pod currently using the "default" service account.
- For each such pod you should:
- Prefer creating and using a dedicated ServiceAccount instead of "default".
- Ensure that the default ServiceAccount in this namespace has automountServiceAccountToken=false
unless there is a strong, reviewed reason otherwise.

EOF

echo "--- Pods (any SA) that explicitly auto-mount service account tokens ---"
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\t"}{.spec.automountServiceAccountToken}{"\n"}{end}' \
| column -t \
| sort \
| awk '$4 == "true" {print}'

cat <<'EOF'

Interpretation:
- Lines here show pods where spec.automountServiceAccountToken=true.
- If these pods use the "default" service account (3rd column == "default"), this is especially high priority to review.
- Even for non-default SAs, consider whether they truly need an API token mounted.

EOF

echo "=== 3) Summaries (counts) to help focus review ==="

echo "--- Count of pods per namespace using the default service account ---"
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.spec.serviceAccountName}{"\n"}{end}' \
| awk '$2=="default"{print $1}' \
| sort \
| uniq -c \
| sort -nr

cat <<'EOF'

Interpretation:
- Any non-zero count means that namespace has workloads using the default ServiceAccount.
- Prioritize namespaces with the highest counts for remediation.

EOF

echo "--- Default ServiceAccounts that still auto-mount tokens (problematic) ---"
kubectl get serviceaccount -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.automountServiceAccountToken}{"\n"}{end}' \
| awk '$2=="default" && $3=="true"{print}' \
| column -t \
| sort

cat <<'EOF'

Interpretation of PROBLEM output:
- Any line printed in the block above indicates a default ServiceAccount that:
- Exists in the listed namespace, and
- Has automountServiceAccountToken=true
- This contradicts the benchmark recommendation; these are concrete findings to review.
- For each such namespace:
- Decide whether workloads should instead use dedicated ServiceAccounts, and
- Whether the default ServiceAccount can have automountServiceAccountToken set to false.

Note:
- This script only reports current state; it does not make any changes.
- Use these results to drive a manual review and then update manifests and ServiceAccounts accordingly.

EOF

Additional Reading: