Skip to main content

API Server Should Disable Anonymous Authentication

More Info:

Verifies that the API server --anonymous-auth argument is set to false. Allowing anonymous requests lets unauthenticated users access the API and is a critical exposure.

Risk Level

Critical

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. Identify how users currently authenticate

    • On any machine with kubectl access, list all ClusterRoleBinding and RoleBinding objects to see which subjects (users, groups, service accounts) have access:
      kubectl get clusterrolebindings.authorization.k8s.io -o yaml > /tmp/clusterrolebindings.yaml
      kubectl get rolebindings.authorization.k8s.io --all-namespaces -o yaml > /tmp/rolebindings.yaml
    • Review these files for kind: ServiceAccount used as long‑lived “users” (e.g., bound to broad cluster roles like cluster-admin or used outside pods).
  2. Locate and review OIDC (or other user auth) configuration

    • On every control plane node, inspect the API server static pod manifest to see what authentication mechanisms are configured:
      sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml
    • In the command: section, look for flags such as --oidc-issuer-url, --oidc-client-id, --oidc-username-claim, --oidc-groups-claim, or webhook/token file auth flags.
    • If no non–service-account user auth mechanism is configured, plan an OIDC or other supported auth integration before removing any service-account-based user access.
  3. Inventory service accounts used as “users”

    • On any machine with kubectl access, list all service accounts and their tokens/secrets:
      kubectl get serviceaccounts --all-namespaces -o yaml > /tmp/serviceaccounts.yaml
      kubectl get secrets --all-namespaces -o yaml > /tmp/secrets.yaml
    • Correlate service accounts that:
      • Are referenced in out-of-cluster automation, scripts, or CI/CD as bearer tokens.
      • Have powerful bindings (e.g., cluster-admin) from step 1.
    • Document which external systems rely on these tokens.
  4. Plan and, if appropriate, migrate those “user” identities to OIDC (or another user auth)

    • Work with your identity team to create proper user or group identities in your IdP and configure Kubernetes API server OIDC flags in /etc/kubernetes/manifests/kube-apiserver.yaml (or equivalent mechanism) according to your platform’s guidance.
    • For each external system identified in step 3, update its configuration to use OIDC (or other non–service-account) auth instead of embedding a service account token.
    • Apply any manifest changes to /etc/kubernetes/manifests/kube-apiserver.yaml; be aware that editing this file will restart the API server pod on that control plane node.
  5. Tighten or remove service account–based user access

    • Once alternate auth is working, on any machine with kubectl access, remove or reduce broad RBAC bindings that treat service accounts as users (example for a specific binding):
      kubectl delete clusterrolebinding <binding-name>
    • Optionally delete no-longer-needed service accounts and their secrets:
      kubectl delete serviceaccount <name> -n <namespace>
    • Ensure remaining service accounts are only used by in-cluster workloads and have least-privilege roles.
  6. Verify current state and document residual exceptions

    • Re-run evidence collection to confirm that no service accounts are bound as generic user identities:
      kubectl get clusterrolebindings.authorization.k8s.io -o yaml | grep -n "kind: ServiceAccount" -n
      kubectl get rolebindings.authorization.k8s.io --all-namespaces -o yaml | grep -n "kind: ServiceAccount" -n
    • For any remaining intentional uses (e.g., machine-to-machine automation that cannot yet use OIDC), document the justification, scope, and planned remediation timeline, as this check is inherently manual and risk-based.
Using kubectl

kubectl cannot change API server process flags or the static pod manifest at /etc/kubernetes/manifests/kube-apiserver.yaml on control plane nodes. To address this finding, make the change directly on each control plane node’s host configuration as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Purpose: Report where service account tokens are being used as user credentials,
# so you can manually review and decide if they are appropriate.
#
# Requirements:
# - Run on any machine with:
# * kubectl installed
# * KUBECONFIG pointing at the cluster
# - kubectl must have sufficient RBAC to:
# * list secrets, serviceaccounts, pods, configmaps
# * list clusterrolebindings and rolebindings

set -euo pipefail

echo "=== 1. ServiceAccount tokens and where they are mounted into Pods ==="
echo

# List all serviceaccount token secrets (Kubernetes 1.24+ uses bound tokens; older clusters may still have 'kubernetes.io/service-account-token')
kubectl get secrets --all-namespaces -o jsonpath='{range .items[?(@.type=="kubernetes.io/service-account-token")]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.metadata.annotations["kubernetes.io/service-account.name"]}{"\n"}{end}' \
| sort || true

echo
echo "Above columns: NAMESPACE SECRET_NAME SERVICEACCOUNT_NAME"
echo "These secrets represent long-lived service account tokens. They are risky if used by humans or external systems as 'user' credentials."
echo

echo "=== 2. Pods with service account token volume mounts or automount enabled ==="
echo

# Pods that explicitly mount service account token secrets as volumes
echo "-- Pods explicitly mounting SA token secrets as volumes --"
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
sa: .spec.serviceAccountName,
volumes: (.spec.volumes // [])
}
| select([.volumes[]? | select(.secret != null) | .secret.secretName] | length > 0)
| "\(.ns)\t\(.pod)\t\(.sa)"' 2>/dev/null | sort || true

echo
echo "Columns: NAMESPACE POD_NAME SERVICEACCOUNT"
echo "Pods listed here are explicitly mounting secrets (which may be service account tokens)."
echo "Investigate each secret to ensure it is not being used as a user credential outside the pod."
echo

# Pods with automountServiceAccountToken explicitly true
echo "-- Pods with automountServiceAccountToken=true --"
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| select(.spec.automountServiceAccountToken == true)
| "\(.metadata.namespace)\t\(.metadata.name)\t\(.spec.serviceAccountName)"' 2>/dev/null | sort || true

echo
echo "Columns: NAMESPACE POD_NAME SERVICEACCOUNT"
echo "These pods automatically receive a service account token. Confirm that the token is used only for in-cluster workload auth, not as a user credential."
echo

echo "=== 3. ServiceAccounts with automountServiceAccountToken not disabled ==="
echo

kubectl get serviceaccounts --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
sa: .metadata.name,
auto: (if .automountServiceAccountToken == false then "false" else "true-or-default" end)
}
| "\(.ns)\t\(.sa)\t\(.auto)"' 2>/dev/null | sort || true

echo
echo "Columns: NAMESPACE SERVICEACCOUNT AUTOMOUNT_SA_TOKEN"
echo "'true-or-default' means the pod will usually get a token mounted unless disabled at the Pod spec."
echo "Risk is higher for service accounts used by humans or external automation as a substitute for proper user/OIDC auth."
echo

echo "=== 4. ClusterRoleBindings / RoleBindings referencing ServiceAccounts ==="
echo

echo "-- ClusterRoleBindings with ServiceAccount subjects --"
kubectl get clusterrolebindings.rbac.authorization.k8s.io -o json \
| jq -r '
.items[]
| select(.subjects != null)
| {
name: .metadata.name,
role: .roleRef.name,
subjects: [.subjects[] | select(.kind=="ServiceAccount") | "\(.namespace)/\(.name)"]
}
| select(.subjects | length > 0)
| "\(.name)\t\(.role)\t\(.subjects | join(","))"' 2>/dev/null | sort || true

echo
echo "Columns: CLUSTERROLEBINDING CLUSTERROLE SERVICEACCOUNT_SUBJECTS"
echo "These service accounts have cluster-scoped permissions. If any are used by humans via tokens, that violates the intent of this control."
echo

echo "-- RoleBindings with ServiceAccount subjects --"
kubectl get rolebindings.rbac.authorization.k8s.io --all-namespaces -o json \
| jq -r '
.items[]
| select(.subjects != null)
| {
ns: .metadata.namespace,
name: .metadata.name,
role: .roleRef.name,
subjects: [.subjects[] | select(.kind=="ServiceAccount") | "\(.namespace // "'"'"'")/\(.name)"]
}
| select(.subjects | length > 0)
| "\(.ns)\t\(.name)\t\(.role)\t\(.subjects | join(","))"' 2>/dev/null | sort || true

echo
echo "Columns: NAMESPACE ROLEBINDING ROLE SERVICEACCOUNT_SUBJECTS"
echo "Again, if any of these service accounts are used directly as user credentials, that is a problem."
echo

echo "=== 5. ConfigMaps that might be distributing SA tokens as credentials (heuristic) ==="
echo

# Heuristic: look for strings that look like bearer tokens in ConfigMaps (very rough; may have false positives)
kubectl get configmaps --all-namespaces -o yaml | \
awk '
/^apiVersion: v1/ {ns="";name=""}
/^ namespace:/ {ns=$2}
/^ name:/ {name=$2}
/eyJhbGciOi/ {
print ns "\t" name "\t" "possible-JWT-like-string"
}' | sort || true

echo
echo "Columns: NAMESPACE CONFIGMAP NOTE"
echo "This is a heuristic for JWT-like strings (e.g., SA tokens) stored in ConfigMaps, possibly to be used as credentials by humans or external systems."
echo

echo "=== Interpretation guidance ==="
cat <<'EOF'

The following outputs indicate a potential problem that requires manual review:

- Any long-lived 'kubernetes.io/service-account-token' secrets used:
* outside the cluster (e.g., in CI/CD, scripts, local kubeconfigs), or
* by humans as if they were user credentials.
- Pods or automation that mount SA token secrets and then expose them to users or external systems.
- ServiceAccounts with broad RBAC (especially cluster-admin) where their token is used interactively.
- ConfigMaps or other artifacts distributing SA tokens for use as client credentials.

This script does NOT automatically fix anything.
Use it to:
1. Identify high-privilege or widely used ServiceAccounts.
2. Confirm how their tokens are used.
3. Migrate those use cases to proper user authentication (e.g., OIDC, SSO, short-lived tokens)
instead of relying on service account tokens for user access.

EOF