Skip to main content

Service Account Token Authentication Should Not Be Used For

More Info:

Service account tokens are intended for workloads, not users, and provide weak user authentication. Use OIDC instead.

Risk Level

High

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. Identify any user-facing processes or scripts using service account tokens

    • On any machine with access to your clusters and automation code, search your repos and scripts for common SA token usage patterns:
      rg 'service-account-token|serviceaccount/token|Authorization: Bearer ' / -n --hidden --glob '!*/.git/*' 2>/dev/null
    • Review CI/CD pipelines, jump-host scripts, and local dev scripts for:
      • curl/kubectl using Authorization: Bearer <token> where the token is from /var/run/secrets/kubernetes.io/serviceaccount/token or a long-lived SA secret.
      • Saved token files used for “user logins” to the API server.
  2. List all service accounts and check for suspicious “user-like” usage

    • On any machine with kubectl access, list SAs and related secrets:
      kubectl get serviceaccounts --all-namespaces -o wide
      kubectl get secrets --all-namespaces | grep -i 'service-account'
    • For any SA that looks like a human user (names like alice, devops-john, admin-user), inspect annotations and usage:
      kubectl describe serviceaccount -n <namespace> <serviceaccount-name>
    • Check if those SAs are referenced in kubeconfigs or scripts used by humans.
  3. Verify API server authentication configuration on each control plane node

    • On every control plane node, inspect the API server manifest to understand supported user auth methods:
      sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml
    • In command: flags, note presence/absence of:
      • --oidc-issuer-url, --oidc-client-id, --oidc-username-claim, --oidc-groups-claim
      • Any --token-auth-file or --authentication-token-webhook-config-file indicating token-based user auth
    • This does not by itself prove misuse, but it shows whether OIDC (preferred) is available as an alternative.
  4. Review kubeconfigs and access methods used by human users

    • On any machine with kubectl access, list configured contexts:
      kubectl config get-contexts
      kubectl config view --raw
    • For each user entry, check the user section for:
      • token: fields that are static, long-lived tokens (especially if they match SA tokens from step 2).
      • auth-provider: or exec: sections that use OIDC (preferred) or other stronger mechanisms.
    • Any kubeconfig distributed to humans that directly embeds an SA token should be flagged for remediation.
  5. Plan and implement migration from service account token usage to OIDC (or other approved user auth)

    • For each identified human user or script using an SA token:
      • Create or map to an identity in your IdP (e.g., OIDC provider).
      • Configure OIDC on the API server if not already present by adding flags in /etc/kubernetes/manifests/kube-apiserver.yaml (this will restart the API server when saved):
        - --oidc-issuer-url=<https://your-issuer-url>
        - --oidc-client-id=<your-client-id>
        - --oidc-username-claim=email
        - --oidc-groups-claim=groups
      • Update RBAC to bind roles to OIDC identities/groups instead of SAs:
        kubectl create clusterrolebinding oidc-user-admin \
        --clusterrole=cluster-admin \
        --user=<user@yourdomain.com>
      • Regenerate kubeconfigs or client auth flows so humans authenticate via OIDC (or your chosen strong method), not via SA tokens.
  6. Verify no human access depends on service account tokens

    • After migration, search again for SA token usage in user tools:
      rg 'Authorization: Bearer ' / -n --hidden --glob '!*/.git/*' 2>/dev/null
      kubectl config view --raw | grep -i 'token:' -n
    • Confirm that any remaining token: entries are:
      • Bound only to non-human workloads, and
      • Not distributed outside the workload environment.
    • Optionally, rotate or delete any SA secrets previously used by humans:
      kubectl delete secret -n <namespace> <service-account-token-secret>
Using kubectl

kubectl cannot fix this finding because it requires changing the kube-apiserver configuration on each control plane node, specifically /etc/kubernetes/manifests/kube-apiserver.yaml, and configuring an alternative auth mechanism such as OIDC. Refer to the Manual Steps section for host-level remediation guidance on disabling user access via service account tokens and enabling OIDC-based authentication.

Automation
#!/usr/bin/env bash
#
# Purpose:
# Report potential *user* use of service account tokens across the cluster.
# This is for review only; it does NOT attempt to fix or definitively prove compliance.
#
# Requirements:
# - Run on any machine with kubectl access and cluster-wide RBAC rights to:
# * list serviceaccounts, secrets, pods, events, and audit logs (if enabled)
# - kubectl must be configured (KUBECONFIG or in-cluster config).

set -euo pipefail

NL=$'\n'

header() {
echo
echo "=== $1 ==="
}

###############################################################################
# 1. Detect SA tokens mounted into Pods that look like *interactive user* pods
###############################################################################
header "1) Pods with service account tokens mounted (cluster-wide)"

# This shows all pods with the standard SA token mount path.
# Service accounts are expected for workloads; flag namespaces/pods that look like user environments.
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\t"}{range .spec.volumes[*]}{.name}{"="}{.projected.sources[*].serviceAccountToken.path}{";"}{end}{"\n"}{end}' \
| awk -F'\t' '
NF==4 {
ns=$1; pod=$2; sa=$3; vols=$4
if (vols ~ /token/ || vols ~ /serviceaccount/ || vols ~ /sa-token/) {
print ns "\t" pod "\t" sa "\t" vols
}
}
' \
| column -t || true

cat <<EOF

Review guidance:
- NORMAL for workloads to have SA tokens.
- POTENTIAL ISSUE if:
- Namespace or pod names suggest interactive/user use, e.g.: "user", "dev-shell", "jumpbox", "bastion",
"notebook", "jupyter", "workspace", or personal names.
- ServiceAccountName suggests user identity, e.g. "alice", "bob", "team-x-user".
These indicate service account tokens might be used as user credentials instead of OIDC.
EOF

###############################################################################
# 2. ServiceAccounts that appear to represent *users*
###############################################################################
header "2) ServiceAccounts that look like user identities"

kubectl get sa -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.metadata.annotations}{"\n"}{end}' \
| awk -F'\t' '
{
ns=$1; sa=$2; ann=$3
lower=tolower(sa)
if (lower ~ /user/ || lower ~ /login/ || lower ~ /human/ ||
lower ~ /jupyter/ || lower ~ /notebook/ ||
lower ~ /alice/ || lower ~ /bob/ || lower ~ /dev-.*-shell/ ) {
print ns "\t" sa "\t" ann
}
}
' \
| column -t || true

cat <<EOF

Review guidance:
- NORMAL for SAs to be workload-specific names (e.g. "nginx", "controller", "ci-runner").
- POTENTIAL ISSUE if SAs appear to model human users (names, emails, "user", "login", etc.),
especially when bound to broad RBAC roles. This suggests user identities may rely on SA tokens.
EOF

###############################################################################
# 3. Secrets that are or were service account tokens
###############################################################################
header "3) ServiceAccount token secrets (cluster-wide)"

# Kubernetes v1.24+ typically uses projected SA tokens; older clusters used Secret-type tokens.
# This lists any classic token secrets that could be directly exfiltrated/used.
kubectl get secrets -A \
-o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.type}{"\t"}{.metadata.annotations}{"\n"}{end}' \
| awk -F'\t' '
{
ns=$1; name=$2; type=$3; ann=$4
if (type ~ /kubernetes.io\/service-account-token/) {
print ns "\t" name "\t" type "\t" ann
}
}
' \
| column -t || true

cat <<EOF

Review guidance:
- NORMAL in older clusters to have many kubernetes.io/service-account-token secrets.
- POTENTIAL ISSUE if:
- These secrets are referenced outside Pods (e.g., given directly to users, external tools,
or checked into source control).
- Annotations or names suggest human users or external non-workload usage.
Manual investigation needed to see if humans are authenticating with these tokens.
EOF

###############################################################################
# 4. (If audit logs available) Look for bearer-token auth using SA tokens
###############################################################################
header "4) (Optional) Audit log indications of SA token authentication"

cat <<'EOF'
If Kubernetes API audit logs are enabled and accessible, search for use of service
account bearer tokens as user authentication.

Examples (run on the machine that has access to audit logs, adjust paths/commands):

# Example: GREP for 'system:serviceaccount' subjects in audit logs
sudo find /var/log -type f -name '*kube-apiserver-audit*' -print0 \
| xargs -0 grep -H "system:serviceaccount" | head

# Example: If logs in JSON, extract subjects that might represent "users" via jq
sudo find /var/log -type f -name '*kube-apiserver-audit*' -print0 \
| xargs -0 jq -r '
select(.user.username | startswith("system:serviceaccount:")) |
.user.username
' | sort -u

Review guidance:
- NORMAL: service accounts are used by workloads; you will see system:serviceaccount:* identities.
- POTENTIAL ISSUE:
- Requests from unusual source IPs corresponding to user networks/laptops/VPNs.
- Long-lived tokens reused from many locations that map to "user-like" SAs.
- Evidence that humans are using curl/kubectl with "Authorization: Bearer <SA token>" instead of OIDC.

There is no one-shot automated remediation:
- Use this evidence to decide:
- Which service accounts must be migrated off for human use.
- How to introduce/require OIDC for user authentication.
EOF

###############################################################################
# Summary of what indicates a PROBLEM
###############################################################################
header "5) Quick interpretation summary"

cat <<EOF
Potential problems to investigate further:

1) Pods in user-like namespaces or with user-like names that have service account tokens mounted.
2) ServiceAccounts with names/annotations that represent human users or logins.
3) ServiceAccount token secrets used outside normal pod mounting (e.g., handed to users/tools).
4) Audit log entries showing serviceaccount identities coming from user endpoints
or being used as long-lived user credentials.

This script only surfaces *signals*; you must manually confirm whether humans are
authenticating with service account tokens instead of using OIDC.
EOF