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
Remediation
Manual Steps
-
Identify namespaces using the default service account
- Run on: any machine with kubectl access
- Command (lists Pods that rely on the
defaultServiceAccount):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'
-
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' ServiceAccountskubectl 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.
-
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: falseif it doesn’t need any Kubernetes API access.
- A new ServiceAccount with no extra permissions (
- For each
-
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: v1kind: ServiceAccountmetadata:name: app-sanamespace: NAMESPACE---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:name: app-sa-rolenamespace: NAMESPACErules:# TODO: replace with minimal required rules- apiGroups: [""]resources: ["pods"]verbs: ["get", "list"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: app-sa-bindingnamespace: NAMESPACEsubjects:- kind: ServiceAccountname: app-sanamespace: NAMESPACEroleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: app-sa-roleEOF - Update each workload manifest to set
spec.serviceAccountName: app-saand, where applicable,automountServiceAccountToken: falseif no API access is needed.
-
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}'); dokubectl patch serviceaccount default -n "$ns" \-p '{"automountServiceAccountToken": false}' --type=merge || truedone
- Verify:
kubectl get sa default --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.automountServiceAccountToken}{"\n"}{end}'
-
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 oftrue): 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
SERVICEACCOUNTisdefault(or empty, which means it defaults todefaultSA) 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.serviceAccountNameor.spec.serviceAccount:defaultor 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
trueor is blank (and the namespace/SA defaults totrue), 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
RoleBindingorClusterRoleBindingthat grants non-trivial permissions (e.g.,edit,admin, custom roles with many verbs/resources) toServiceAccount/defaultis 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