No RoleBinding Should Grant Access To Anonymous Or Unauthenticated Users
More Info:​
Verifies no (Cluster)RoleBinding targets system:anonymous or system:unauthenticated. Such bindings grant access to unauthenticated callers.
Risk Level​
Critical
Address​
Security
Compliance Standards​
- Cloudanix Best Practice
Triage and Remediation​
- Remediation
Remediation​
Manual Steps
-
Review current violating bindings (any machine with kubectl access):
kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | jq -r '[ .items[]| .kind as $kind | .apiVersion as $api | .metadata as $m| ((.subjects // [])[] | select(.name == "system:anonymous" or .name == "system:unauthenticated"))| "kind=\($kind)"+ (if ($m.namespace // "") == "" then "" else " ns=\($m.namespace)" end)+ " name=\($m.name) uid=\($m.uid)"+ (if ($api // "") == "" then "" else " apiVersion=\($api)" end)+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)+ " subject=\(.name) is_compliant=false"] as $rows| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end' -
For each offending RoleBinding, inspect details and confirm it is safe to remove (any machine with kubectl access):
# example: namespace-scoped RoleBindingkubectl get rolebinding <ROLEBINDING_NAME> -n <NAMESPACE> -o yaml# example: ClusterRoleBindingkubectl get clusterrolebinding <CLUSTERROLEBINDING_NAME> -o yaml -
Delete offending RoleBindings that reference
system:anonymousorsystem:unauthenticated(any machine with kubectl access):# namespace RoleBindingkubectl delete rolebinding <ROLEBINDING_NAME> -n <NAMESPACE># ClusterRoleBindingkubectl delete clusterrolebinding <CLUSTERROLEBINDING_NAME> -
If a deleted binding was legitimately needed, recreate a safer binding to an authenticated subject (any machine with kubectl access). Example pattern:
kubectl create rolebinding <NEW_BINDING_NAME> \--clusterrole=<EXISTING_CLUSTERROLE_NAME> \--user=<AUTHENTICATED_USER_OR_SERVICEACCOUNT> \-n <NAMESPACE>or for cluster-wide:
kubectl create clusterrolebinding <NEW_BINDING_NAME> \--clusterrole=<EXISTING_CLUSTERROLE_NAME> \--user=<AUTHENTICATED_USER_OR_GROUP> -
If bindings are managed via manifests or GitOps, update the source manifests to remove any subjects named
system:anonymousorsystem:unauthenticatedso they are not re-applied (edit locally, then apply from any machine with kubectl access):kubectl apply -f <UPDATED_MANIFEST_FILE>.yaml -
Verification (any machine with kubectl access):
kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | jq -r '[ .items[]| .kind as $kind | .apiVersion as $api | .metadata as $m| ((.subjects // [])[] | select(.name == "system:anonymous" or .name == "system:unauthenticated"))| "kind=\($kind)"+ (if ($m.namespace // "") == "" then "" else " ns=\($m.namespace)" end)+ " name=\($m.name) uid=\($m.uid)"+ (if ($api // "") == "" then "" else " apiVersion=\($api)" end)+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)+ " subject=\(.name) is_compliant=false"] as $rows| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'The command should output only:
is_compliant=true
Using kubectl
On any machine with kubectl access to the cluster:
- Identify the violating RoleBindings / ClusterRoleBindings
kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | jq -r '
[ .items[]
| .kind as $kind | .apiVersion as $api | .metadata as $m
| ((.subjects // [])[] | select(.name == "system:anonymous" or .name == "system:unauthenticated"))
| "kind=\($kind)"
+ (if ($m.namespace // "") == "" then "" else " ns=\($m.namespace)" end)
+ " name=\($m.name) uid=\($m.uid)"
+ (if ($api // "") == "" then "" else " apiVersion=\($api)" end)
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ " subject=\(.name) is_compliant=false"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
- Delete each violating binding
For a namespaced RoleBinding (note the ns= field from the previous output):
kubectl delete rolebinding <ROLEBINDING_NAME> -n <NAMESPACE>
Example:
kubectl delete rolebinding public-view -n default
For a ClusterRoleBinding (no ns= field):
kubectl delete clusterrolebinding <CLUSTERROLEBINDING_NAME>
Example:
kubectl delete clusterrolebinding unauthenticated-read
- Verification
Re-run the audit to confirm no bindings reference system:anonymous or system:unauthenticated:
kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | jq -r '
[ .items[]
| .kind as $kind | .apiVersion as $api | .metadata as $m
| ((.subjects // [])[] | select(.name == "system:anonymous" or .name == "system:unauthenticated"))
| "kind=\($kind)"
+ (if ($m.namespace // "") == "" then "" else " ns=\($m.namespace)" end)
+ " name=\($m.name) uid=\($m.uid)"
+ (if ($api // "") == "" then "" else " apiVersion=\($api)" end)
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ " subject=\(.name) is_compliant=false"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
Automation
#!/usr/bin/env bash
# Remove any RoleBinding or ClusterRoleBinding that grants access to
# system:anonymous or system:unauthenticated.
#
# Run on: any machine with kubectl access to the cluster.
# Requirements: kubectl, jq; current context points to the target GKE cluster.
set -euo pipefail
echo "[INFO] Discovering RoleBindings and ClusterRoleBindings that target system:anonymous or system:unauthenticated..."
# Get full JSON only once
RB_JSON="$(kubectl get rolebindings,clusterrolebindings --all-namespaces -o json)"
# Build a list of offending bindings as "KIND NAMESPACE NAME" (namespace is "-" for cluster-scoped)
mapfile -t OFFENDERS < <(
jq -r '
.items[]
| .kind as $kind
| .metadata.namespace as $ns
| .metadata.name as $name
| ((.subjects // [])[]
| select(.name == "system:anonymous" or .name == "system:unauthenticated"))
| [$kind, ($ns // "-"), $name] | @tsv
' <<< "$RB_JSON" | sort -u
)
if [ "${#OFFENDERS[@]}" -eq 0 ]; then
echo "[INFO] No offending RoleBindings or ClusterRoleBindings found. Cluster is already compliant."
else
echo "[INFO] Found ${#OFFENDERS[@]} offending binding(s):"
printf ' %s\n' "${OFFENDERS[@]}"
echo
for line in "${OFFENDERS[@]}"; do
KIND="$(awk '{print $1}' <<< "$line")"
NS_OR_DASH="$(awk '{print $2}' <<< "$line")"
NAME="$(awk '{print $3}' <<< "$line")"
if [ "$NS_OR_DASH" = "-" ]; then
# ClusterRoleBinding (cluster-scoped)
echo "[ACTION] Deleting $KIND $NAME (cluster-scoped)..."
kubectl delete "$KIND" "$NAME" --ignore-not-found
else
# Namespaced RoleBinding
echo "[ACTION] Deleting $KIND $NAME in namespace $NS_OR_DASH..."
kubectl delete "$KIND" "$NAME" -n "$NS_OR_DASH" --ignore-not-found
fi
done
fi
echo
echo "[INFO] Verifying compliance..."
VERIFY_OUTPUT="$(kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | jq -r '
[ .items[]
| .kind as $kind | .apiVersion as $api | .metadata as $m
| ((.subjects // [])[] | select(.name == "system:anonymous" or .name == "system:unauthenticated"))
| "kind=\($kind)"
+ (if ($m.namespace // "") == "" then "" else " ns=\($m.namespace)" end)
+ " name=\($m.name) uid=\($m.uid)"
+ (if ($api // "") == "" then "" else " apiVersion=\($api)" end)
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ " subject=\(.name) is_compliant=false"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end')"
echo "$VERIFY_OUTPUT"
if grep -qx 'is_compliant=true' <<< "$VERIFY_OUTPUT"; then
echo "[INFO] Remediation successful: no RoleBinding or ClusterRoleBinding grants access to system:anonymous or system:unauthenticated."
exit 0
else
echo "[WARN] Some offending bindings remain. Please inspect the lines above."
exit 1
fi