Avoid Non-Default Bindings To System:authenticated
More Info:​
Non-default bindings to the group system:authenticated grant permissions to every authenticated user in the cluster. They should be removed or replaced with narrowly scoped, user-defined groups.
Risk Level​
Critical
Address​
Security
Compliance Standards​
- CIS GKE
Triage and Remediation​
- Remediation
Remediation​
Manual Steps
-
List all non-default bindings to
system:authenticated(any machine with kubectl access):(kubectl get clusterrolebindings -o json | jq -r '.items[]| select((.metadata.name != "system:basic-user") and (.metadata.name != "system:discovery"))| select((.subjects | length) > 0)| select(any(.subjects[]?;.kind=="Group" and .name=="system:authenticated"))| "FOUND_AUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"';kubectl get rolebindings -A -o json | jq -r '.items[]| select((.subjects | length) > 0)| select(any(.subjects[]?;.kind=="Group" and .name=="system:authenticated"))| "FOUND_AUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"') | (grep -q "^FOUND_AUTH:" && cat || echo "NO_NON_DEFAULT_AUTH_BINDINGS") -
For each
FOUND_AUTH:ClusterRoleBinding:...entry, inspect the binding and the referenced role to understand impact (any machine with kubectl access):# Example: replace <CRB_NAME> with the name reported in step 1kubectl get clusterrolebinding <CRB_NAME> -o yaml# Inspect the referenced ClusterRole# (from the ROLE=ClusterRole/<ROLE_NAME> part)kubectl get clusterrole <ROLE_NAME> -o yaml -
For each
FOUND_AUTH:RoleBinding:...entry, inspect the binding and the referenced role (any machine with kubectl access):# Example: replace <NAMESPACE> and <RB_NAME> with values from step 1kubectl get rolebinding <RB_NAME> -n <NAMESPACE> -o yaml# Inspect the referenced Role or ClusterRole# (from the ROLE=<KIND>/<ROLE_NAME> part)kubectl get <KIND> <ROLE_NAME> -n <NAMESPACE> -o yaml -
Decide safer replacements before deletion (any machine with kubectl access):
-
Identify or create user-defined groups in your IdP (e.g.,
devs,ops-team) and map them to Kubernetes groups via your authentication setup (GKE RBAC, OIDC, etc.). -
Create least-privilege
Role/ClusterRoleobjects and bind them to those user-defined groups instead ofsystem:authenticated. Example pattern:# Example: create a least-privilege ClusterRolekubectl apply -f - << 'EOF'apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:name: custom-read-onlyrules:- apiGroups: [""]resources: ["pods"]verbs: ["get", "list", "watch"]EOF# Bind it to a user-defined group, not system:authenticatedkubectl apply -f - << 'EOF'apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:name: custom-read-only-bindingsubjects:- kind: Groupname: my-organization-devs # user-defined group from your IdPapiGroup: rbac.authorization.k8s.ioroleRef:kind: ClusterRolename: custom-read-onlyapiGroup: rbac.authorization.k8s.ioEOF -
Validate with application/cluster owners that these new bindings cover all legitimate access needs before you remove the unsafe ones.
-
-
Remove each non-default binding to
system:authenticatedonce a safer alternative exists and operational impact is accepted (any machine with kubectl access):# Delete unsafe ClusterRoleBindings# Repeat for each <CRB_NAME> identified in step 1 that you have approved for removalkubectl delete clusterrolebinding <CRB_NAME># Delete unsafe RoleBindings# Repeat for each <NAMESPACE> and <RB_NAME> identified in step 1 that you have approved for removalkubectl delete rolebinding <RB_NAME> -n <NAMESPACE> -
Verification (any machine with kubectl access):
(kubectl get clusterrolebindings -o json | jq -r '.items[]| select((.metadata.name != "system:basic-user") and (.metadata.name != "system:discovery"))| select((.subjects | length) > 0)| select(any(.subjects[]?;.kind=="Group" and .name=="system:authenticated"))| "FOUND_AUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"';kubectl get rolebindings -A -o json | jq -r '.items[]| select((.subjects | length) > 0)| select(any(.subjects[]?;.kind=="Group" and .name=="system:authenticated"))| "FOUND_AUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"') | (grep -q "^FOUND_AUTH:" && cat || echo "NO_NON_DEFAULT_AUTH_BINDINGS")
Using kubectl
On any machine with kubectl access:
- List all non-default bindings to
system:authenticated(from the audit)
(
kubectl get clusterrolebindings -o json | jq -r '
.items[]
| select((.metadata.name != "system:basic-user") and (.metadata.name != "system:discovery"))
| select((.subjects | length) > 0)
| select(any(.subjects[]?;
.kind=="Group" and .name=="system:authenticated"
))
| "FOUND_AUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"
';
kubectl get rolebindings -A -o json | jq -r '
.items[]
| select((.subjects | length) > 0)
| select(any(.subjects[]?;
.kind=="Group" and .name=="system:authenticated"
))
| "FOUND_AUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"
'
)
Review each listed binding and decide whether it is truly required. For any binding that is not strictly necessary, delete it.
- Delete unsafe ClusterRoleBindings to
system:authenticated
Replace CLUSTER_ROLE_BINDING_NAME with each offending ClusterRoleBinding name you chose to remove:
kubectl delete clusterrolebinding CLUSTER_ROLE_BINDING_NAME
- Delete unsafe RoleBindings to
system:authenticated
Replace ROLE_BINDING_NAMESPACE and ROLE_BINDING_NAME with the namespace and name of each offending RoleBinding you chose to remove:
kubectl delete rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE
- (Optional) Replace with safer, user-defined group bindings
Create or edit bindings so they refer to a narrower, non-default group you manage (example):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: myteam-readonly
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: myteam@example.com
Apply:
kubectl apply -f myteam-readonly.yaml
- Verification (run on any machine with kubectl)
(
kubectl get clusterrolebindings -o json | jq -r '
.items[]
| select((.metadata.name != "system:basic-user") and (.metadata.name != "system:discovery"))
| select((.subjects | length) > 0)
| select(any(.subjects[]?;
.kind=="Group" and .name=="system:authenticated"
))
| "FOUND_AUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"
';
kubectl get rolebindings -A -o json | jq -r '
.items[]
| select((.subjects | length) > 0)
| select(any(.subjects[]?;
.kind=="Group" and .name=="system:authenticated"
))
| "FOUND_AUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"
'
) | (grep -q "^FOUND_AUTH:" && cat || echo "NO_NON_DEFAULT_AUTH_BINDINGS")
Automation
#!/usr/bin/env bash
#
# Remediate non-default bindings to group "system:authenticated"
# Scope: any machine with kubectl access (requires jq)
#
# Behavior:
# - Lists all ClusterRoleBindings and RoleBindings that bind system:authenticated
# - Skips GKE default CRBs: system:basic-user, system:discovery
# - Prompts for each binding to delete (default: no)
# - Safe to re-run; already-deleted objects are ignored
# - Prints final verification matching the benchmark audit
set -euo pipefail
# -------- configuration --------
# Set to "true" to delete without prompting
AUTO_APPROVE="${AUTO_APPROVE:-false}"
# -------------------------------
if ! command -v kubectl >/dev/null 2>&1; then
echo "ERROR: kubectl not found in PATH" >&2
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo "ERROR: jq not found in PATH" >&2
exit 1
fi
echo "Discovering non-default bindings to group 'system:authenticated'..."
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
CRB_CANDIDATES_FILE="${TMP_DIR}/crb_candidates.txt"
RB_CANDIDATES_FILE="${TMP_DIR}/rb_candidates.txt"
# Find offending ClusterRoleBindings (excluding allowed defaults)
kubectl get clusterrolebindings -o json | jq -r '
.items[]
| select((.metadata.name != "system:basic-user") and (.metadata.name != "system:discovery"))
| select((.subjects // [] | length) > 0)
| select(any(.subjects[]?;
.kind=="Group" and .name=="system:authenticated"
))
| .metadata.name
' | sort -u > "${CRB_CANDIDATES_FILE}"
# Find offending RoleBindings (all namespaces)
kubectl get rolebindings -A -o json | jq -r '
.items[]
| select((.subjects // [] | length) > 0)
| select(any(.subjects[]?;
.kind=="Group" and .name=="system:authenticated"
))
| .metadata.namespace + ":" + .metadata.name
' | sort -u > "${RB_CANDIDATES_FILE}"
CRB_COUNT="$(wc -l < "${CRB_CANDIDATES_FILE}" | tr -d ' ')"
RB_COUNT="$(wc -l < "${RB_CANDIDATES_FILE}" | tr -d ' ')"
if [[ "${CRB_COUNT}" -eq 0 && "${RB_COUNT}" -eq 0 ]]; then
echo "No non-default bindings to 'system:authenticated' found."
else
echo "Found ${CRB_COUNT} offending ClusterRoleBindings and ${RB_COUNT} offending RoleBindings."
fi
# Helper: ask for confirmation (unless AUTO_APPROVE)
confirm_delete() {
local desc="$1"
if [[ "${AUTO_APPROVE}" == "true" ]]; then
return 0
fi
while true; do
read -r -p "Delete ${desc}? [y/N]: " ans
case "${ans:-}" in
[Yy]*) return 0 ;;
[Nn]*|"") return 1 ;;
*) echo "Please answer y or n." ;;
esac
done
}
# Process ClusterRoleBindings
if [[ -s "${CRB_CANDIDATES_FILE}" ]]; then
echo
echo "Reviewing ClusterRoleBindings binding to 'system:authenticated' (excluding defaults)..."
while read -r crb; do
[[ -z "${crb}" ]] && continue
echo
echo "----- ClusterRoleBinding: ${crb} -----"
kubectl get clusterrolebinding "${crb}" -o yaml || {
echo " (Already deleted or not found, skipping.)"
continue
}
# Show the roleRef and subjects for quick assessment
echo
echo "Summary (roleRef and subjects):"
kubectl get clusterrolebinding "${crb}" -o json | jq '{roleRef, subjects}'
if confirm_delete "ClusterRoleBinding '${crb}'"; then
echo "Deleting ClusterRoleBinding '${crb}'..."
if ! kubectl delete clusterrolebinding "${crb}" >/dev/null 2>&1; then
echo "WARNING: Failed to delete ClusterRoleBinding '${crb}'. Continuing." >&2
fi
else
echo "Skipping deletion of ClusterRoleBinding '${crb}'."
fi
done < "${CRB_CANDIDATES_FILE}"
fi
# Process RoleBindings
if [[ -s "${RB_CANDIDATES_FILE}" ]]; then
echo
echo "Reviewing RoleBindings binding to 'system:authenticated'..."
while read -r entry; do
[[ -z "${entry}" ]] && continue
ns="${entry%%:*}"
rb="${entry#*:}"
echo
echo "----- RoleBinding: ${ns}/${rb} -----"
kubectl get rolebinding "${rb}" -n "${ns}" -o yaml || {
echo " (Already deleted or not found, skipping.)"
continue
}
# Show the roleRef and subjects for quick assessment
echo
echo "Summary (roleRef and subjects):"
kubectl get rolebinding "${rb}" -n "${ns}" -o json | jq '{roleRef, subjects}'
if confirm_delete "RoleBinding '${ns}/${rb}'"; then
echo "Deleting RoleBinding '${ns}/${rb}'..."
if ! kubectl delete rolebinding "${rb}" -n "${ns}" >/dev/null 2>&1; then
echo "WARNING: Failed to delete RoleBinding '${ns}/${rb}'. Continuing." >&2
fi
else
echo "Skipping deletion of RoleBinding '${ns}/${rb}'."
fi
done < "${RB_CANDIDATES_FILE}"
fi
echo
echo "Verification: re-running audit for non-default bindings to 'system:authenticated'..."
(
kubectl get clusterrolebindings -o json | jq -r '
.items[]
| select((.metadata.name != "system:basic-user") and (.metadata.name != "system:discovery"))
| select((.subjects | length) > 0)
| select(any(.subjects[]?;
.kind=="Group" and .name=="system:authenticated"
))
| "FOUND_AUTH:ClusterRoleBinding:\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"
';
kubectl get rolebindings -A -o json | jq -r '
.items[]
| select((.subjects | length) > 0)
| select(any(.subjects[]?;
.kind=="Group" and .name=="system:authenticated"
))
| "FOUND_AUTH:RoleBinding:\(.metadata.namespace):\(.metadata.name):ROLE=\(.roleRef.kind)/\(.roleRef.name)"
'
) | (grep -q "^FOUND_AUTH:" && cat || echo "NO_NON_DEFAULT_AUTH_BINDINGS")
echo
echo "Completed."
Usage (run on any machine with kubectl access and jq):
chmod +x ./fix-authenticated-bindings.sh
# Interactive review & delete
./fix-authenticated-bindings.sh
# Or non-interactive (auto-delete all offending bindings)
AUTO_APPROVE=true ./fix-authenticated-bindings.sh