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
Remediation
Manual Steps
-
Identify how users currently authenticate
- On any machine with
kubectlaccess, list allClusterRoleBindingandRoleBindingobjects to see which subjects (users, groups, service accounts) have access:kubectl get clusterrolebindings.authorization.k8s.io -o yaml > /tmp/clusterrolebindings.yamlkubectl get rolebindings.authorization.k8s.io --all-namespaces -o yaml > /tmp/rolebindings.yaml - Review these files for
kind: ServiceAccountused as long‑lived “users” (e.g., bound to broad cluster roles likecluster-adminor used outside pods).
- On any machine with
-
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.
- On every control plane node, inspect the API server static pod manifest to see what authentication mechanisms are configured:
-
Inventory service accounts used as “users”
- On any machine with
kubectlaccess, list all service accounts and their tokens/secrets:kubectl get serviceaccounts --all-namespaces -o yaml > /tmp/serviceaccounts.yamlkubectl 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.
- On any machine with
-
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.
- Work with your identity team to create proper user or group identities in your IdP and configure Kubernetes API server OIDC flags in
-
Tighten or remove service account–based user access
- Once alternate auth is working, on any machine with
kubectlaccess, 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.
- Once alternate auth is working, on any machine with
-
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" -nkubectl 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.
- Re-run evidence collection to confirm that no service accounts are bound as generic user identities:
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