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
Remediation
Manual Steps
-
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
defaultservice account.
- On any machine with kubectl access:
-
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
AUTOMOUNTis empty ortrue; these default service accounts can auto-mount tokens.
- On any machine with kubectl access:
-
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).
- For each Pod from step 1, inspect how it uses the service account:
-
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.specit specifies this service account:serviceAccountName: app-saautomountServiceAccountToken: true - Apply the updated manifest from any machine with kubectl access, for example:
kubectl apply -f <workload-manifest>.yaml
- For each namespace where some workloads legitimately need API access, create a dedicated service account (example):
-
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: falseat the Pod spec level for workloads that should not use a token.
- Once workloads that need API access have explicit service accounts, update each namespace’s default service account to disable automounting the token:
-
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
defaultservice account are intended to do so and do not require API access, and automountServiceAccountTokenisfalsefor default service accounts in all applicable namespaces.
- Any remaining Pods using the
- Re-run the evidence commands from steps 1 and 2:
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
defaultservice account where:AUTOMOUNTis empty (inheritstruein most clusters), orAUTOMOUNTis explicitlytrue.
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
defaultaccount (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:
serviceAccountNameempty (will fall back todefault), orserviceAccountNameexplicitly set todefault, 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
defaultservice account bound to:cluster-adminor 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
defaultSA and still mounting aserviceAccountTokenvolume 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.
- The default SA is not set to
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