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 AKS
  • CIS Critical Security Controls v8
  • 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 all default service accounts and their token settings

    • Run on: any machine with kubectl access
    kubectl get serviceaccounts --all-namespaces \
    -o jsonpath='{range .items[?(@.metadata.name=="default")]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.automountServiceAccountToken}{"\n"}{end}'
    • Review which namespaces have default SAs with automountServiceAccountToken not set or set to true.
  2. Find pods currently using the default service account

    • Run on: any machine with kubectl access
    kubectl get pods --all-namespaces \
    -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\n"}{end}' \
    | awk '$3=="default"'
    • Review each listed pod and determine whether it should instead use a dedicated service account.
  3. Review permissions granted to default service accounts

    • Run on: any machine with kubectl access
    # ClusterRoleBindings involving default SAs
    kubectl get clusterrolebindings -o yaml | grep -B4 -A6 "kind: ServiceAccount" | grep -B10 -A10 "name: default"

    # RoleBindings involving default SAs, per namespace
    for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
    echo "### Namespace: $ns"
    kubectl get rolebindings -n "$ns" -o yaml | grep -B4 -A6 "kind: ServiceAccount" | grep -B10 -A10 "name: default" || true
    done
    • For each binding involving a default SA, assess whether those permissions are necessary or should be moved to a custom service account.
  4. Plan and create custom service accounts and bindings where needed

    • For each workload that should not use the default SA, design a least-privilege service account and roles. Example (adjust names and rules):
    # Run on: any machine with kubectl access
    kubectl create serviceaccount app-sa -n my-namespace

    cat <<'EOF' | kubectl apply -f -
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    name: app-sa-role
    namespace: my-namespace
    rules:
    - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: app-sa-rb
    namespace: my-namespace
    subjects:
    - kind: ServiceAccount
    name: app-sa
    namespace: my-namespace
    roleRef:
    kind: Role
    name: app-sa-role
    apiGroup: rbac.authorization.k8s.io
    EOF
    • Update pod manifests / deployments to use the new service account (spec.serviceAccountName: app-sa) and re-deploy them.
  5. Disable token automount on default service accounts

    • After moving workloads off the default SAs, disable token mounting as per the remediation. Run on: any machine with kubectl access
    for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
    kubectl patch serviceaccount default -n "$ns" \
    -p '{"automountServiceAccountToken": false}' || true
    done
    • If any namespace must legitimately use the default SA, document the justification and consider leaving automountServiceAccountToken enabled only there.
  6. Verify no active workloads rely on default service accounts

    • Run on: any machine with kubectl access
    # Confirm automount is disabled on default SAs
    kubectl get serviceaccounts --all-namespaces \
    -o jsonpath='{range .items[?(@.metadata.name=="default")]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.automountServiceAccountToken}{"\n"}{end}'

    # Confirm no pods (or only documented exceptions) use default SA
    kubectl get pods --all-namespaces \
    -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\n"}{end}' \
    | awk '$3=="default"'
    • Investigate and resolve any remaining pods using default SAs that do not have an explicit, documented justification.
Using kubectl

Using kubectl

1. List all namespaces and their default service accounts

Run on: any machine with kubectl access.

kubectl get ns

For each namespace, check the default service account and whether it auto-mounts tokens:

# Example loop to inspect all namespaces
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
echo "Namespace: $ns"
kubectl get sa default -n "$ns" -o yaml | grep -E '^(kind:|metadata:| name:|automountServiceAccountToken:)' || echo " (no default SA found)"
echo
done

Indicates a problem: In any namespace, default service account shows automountServiceAccountToken: true or has no automountServiceAccountToken field (which means it may inherit a default true from pod spec or namespace).

2. Find pods that are using the default service account

# All pods and their serviceAccounts
kubectl get pods -A -o custom-columns='NAMESPACE:.metadata.namespace,POD:.metadata.name,SA:.spec.serviceAccountName' | sort

Indicates a problem: Any workload pod (non-system, non-addon) shows SA as default in its namespace.

You can narrow this down by excluding well-known system namespaces:

kubectl get pods -A \
--field-selector=status.phase=Running \
-o custom-columns='NAMESPACE:.metadata.namespace,POD:.metadata.name,SA:.spec.serviceAccountName' \
| egrep -v '^(kube-system|kube-public|kube-node-lease|default)[[:space:]]' \
| sort

Then manually review pods still showing SA as default.

3. Inspect specific pods that might be problematic

For any suspicious pod:

kubectl get pod <pod-name> -n <namespace> -o yaml

Review:

  • .spec.serviceAccountName — is it default?
  • .spec.automountServiceAccountToken — is it true or omitted (thus possibly inheriting true)?

Indicates a problem: Application pods using serviceAccountName: default and/or auto-mounting tokens when they do not need Kubernetes API access.

4. Check namespace-level default for automountServiceAccountToken

kubectl get ns <namespace> -o yaml | grep -A3 'annotations:'

Review for any namespace annotation like:

  • kubernetes.io/allow-automount-service-account-token: "true"

Indicates a problem: Namespaces where policy or annotations cause broad token auto-mounting, increasing the likelihood that default SAs are used with tokens.

5. Verification after you make manual changes

After you have manually switched workloads to custom service accounts and updated defaults as needed, re-run:

kubectl get pods -A -o custom-columns='NAMESPACE:.metadata.namespace,POD:.metadata.name,SA:.spec.serviceAccountName' | sort

Confirm that application workloads no longer use SA = default, and re-run the default SA inspection loop to confirm automountServiceAccountToken is set according to your chosen policy.

Automation
#!/usr/bin/env bash
# Report default ServiceAccount usage and token automount settings across the cluster.
# Run on: any machine with kubectl access and current-context pointing to the target cluster.

set -euo pipefail

echo "=== 1) Default ServiceAccount automountServiceAccountToken settings ==="
echo
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(inherited)" end)
]
| @tsv' \
| awk 'BEGIN{printf "%-30s %-15s %-25s\n","NAMESPACE","SERVICEACCOUNT","AUTOMOUNT_SA_TOKEN"; print gensub(/./,"-","g",sprintf("%-30s %-15s %-25s"," "," "," "));} {printf "%-30s %-15s %-25s\n",$1,$2,$3;}'

cat <<'EOF'

Interpretation:
- Any row where AUTOMOUNT_SA_TOKEN is "true" or "null(inherited)" indicates a potential problem.
Remediation guidance:
- Prefer: set automountServiceAccountToken: false directly on the default SA in that namespace.
- And: use dedicated ServiceAccounts for workloads instead of the default.

EOF

echo "=== 2) Pods using the default ServiceAccount (explicitly or implicitly) ==="
echo
# List all running/non-terminated Pods and show which SA they use and whether they mount the token.
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| select(.status.phase != "Succeeded" and .status.phase != "Failed")
| [
.metadata.namespace,
.metadata.name,
(.spec.serviceAccountName // "default"),
# detect automount: pod-level override, then SA default (unknown here), then default=true
(if .spec.automountServiceAccountToken == true then "true(pod-override)"
elif .spec.automountServiceAccountToken == false then "false(pod-override)"
else "unknown(SA/default)" end)
]
| @tsv' \
| awk 'BEGIN{printf "%-30s %-45s %-20s %-30s\n","NAMESPACE","POD","SERVICEACCOUNT","AUTOMOUNT_TOKEN_EFFECTIVE?"; print gensub(/./,"-","g",sprintf("%-30s %-45s %-20s %-30s"," "," "," "," "));} {printf "%-30s %-45s %-20s %-30s\n",$1,$2,$3,$4;}'

cat <<'EOF'

Interpretation:
- Any POD where SERVICEACCOUNT == "default" is using the default ServiceAccount.
This is a review finding and usually NOT desired for application workloads.
- The AUTOMOUNT_TOKEN_EFFECTIVE? column:
- "true(pod-override)" means the Pod explicitly mounts a token (high risk).
- "false(pod-override)" means the Pod explicitly disables token mount (lower risk).
- "unknown(SA/default)" means the effective value comes from the ServiceAccount or cluster default.
Cross-check with section (1) above for that namespace.

Focus for review:
- Identify namespaces and Pods where:
- SERVICEACCOUNT == "default"
- and/or default SA in that namespace has automountServiceAccountToken true or null(inherited)
- Decide:
- Should this workload have a dedicated ServiceAccount with scoped RBAC?
- Should automountServiceAccountToken be set to false on:
- the Pod spec, and/or
- the default ServiceAccount in that namespace?

NOTE: This script is read-only and does NOT change any resources. Use the findings to
manually:
- create and bind least-privilege ServiceAccounts for workloads, and
- set automountServiceAccountToken: false on default ServiceAccounts where appropriate.
EOF

Additional Reading: