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:​