No RoleBinding Should Grant Access To Anonymous Or
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
-
On any machine with kubectl access, list all RoleBindings and ClusterRoleBindings that target
system:anonymousorsystem:unauthenticatedand review them to understand what they grant and whether they are truly needed: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 listed binding, fetch the full YAML and record it (for rollback or to recreate with a safer subject such as a specific Group, User, or ServiceAccount):
# Example for a namespaced RoleBindingkubectl get rolebinding <ROLEBINDING_NAME> -n <NAMESPACE> -o yaml > /tmp/<ROLEBINDING_NAME>.yaml# Example for a ClusterRoleBindingkubectl get clusterrolebinding <CLUSTERROLEBINDING_NAME> -o yaml > /tmp/<CLUSTERROLEBINDING_NAME>.yaml -
Decide whether each binding can simply be deleted (preferred), or whether you must instead rebind the same Role/ClusterRole to an authenticated subject (e.g., a specific service account or group). Document the chosen replacement subject for each binding that must be preserved.
-
On any machine with kubectl access, delete each RoleBinding or ClusterRoleBinding whose subject is
system:anonymousorsystem:unauthenticated:# Namespaced RoleBindingkubectl delete rolebinding <ROLEBINDING_NAME> -n <NAMESPACE># ClusterRoleBindingkubectl delete clusterrolebinding <CLUSTERROLEBINDING_NAME> -
If you need to preserve the permission but for an authenticated subject, recreate an equivalent binding that omits
system:anonymous/system:unauthenticatedand uses your chosen subject instead. For example:cat << 'EOF' | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: <NEW_ROLEBINDING_NAME>namespace: <NAMESPACE>roleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: <EXISTING_ROLE_NAME>subjects:- kind: ServiceAccountname: <SERVICEACCOUNT_NAME>namespace: <NAMESPACE>EOF -
Verification (on any machine with kubectl access): rerun the audit command and confirm that it returns only the compliance sentinel and no bindings with
system:anonymousorsystem: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'
Using kubectl
On any machine with kubectl access to the cluster:
- List offending RoleBindings and ClusterRoleBindings (review before deleting):
kubectl get rolebindings,clusterrolebindings --all-namespaces -o wide \
| grep -E 'system:anonymous|system:unauthenticated' || true
- For each violating RoleBinding, delete it. Example commands (replace with the actual names/namespaces you saw):
# Example RoleBindings
kubectl delete rolebinding rb-anon-access -n default
kubectl delete rolebinding rb-unauthenticated-view -n kube-system
# Example ClusterRoleBindings
kubectl delete clusterrolebinding crb-anon-access
kubectl delete clusterrolebinding crb-unauthenticated-view
If you manage these via manifests (GitOps/IaC), also remove the corresponding RoleBinding or ClusterRoleBinding objects (those whose subjects have name: system:anonymous or name: system:unauthenticated) from your declarative configuration and apply:
kubectl apply -f <your-updated-manifests>.yaml
- Verification (same command as the audit):
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 and current context set.
# Idempotent: safe to re-run; only currently offending bindings are deleted.
set -euo pipefail
# Fail fast if kubectl or jq are missing
command -v kubectl >/dev/null 2>&1 || { echo "kubectl not found in PATH" >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "jq not found in PATH" >&2; exit 1; }
echo "Discovering RoleBindings and ClusterRoleBindings that target system:anonymous or system:unauthenticated..."
# Get offending RoleBindings
mapfile -t RB_TO_DELETE < <(
kubectl get rolebindings --all-namespaces -o json \
| jq -r '
.items[]
| select((.subjects // [])[]?.name == "system:anonymous"
or (.subjects // [])[]?.name == "system:unauthenticated")
| "\(.metadata.namespace),\(.metadata.name)"
' \
| sort -u
)
# Get offending ClusterRoleBindings
mapfile -t CRB_TO_DELETE < <(
kubectl get clusterrolebindings -o json \
| jq -r '
.items[]
| select((.subjects // [])[]?.name == "system:anonymous"
or (.subjects // [])[]?.name == "system:unauthenticated")
| .metadata.name
' \
| sort -u
)
if [[ ${#RB_TO_DELETE[@]} -eq 0 && ${#CRB_TO_DELETE[@]} -eq 0 ]]; then
echo "No offending RoleBindings or ClusterRoleBindings found."
else
echo "Deleting offending RoleBindings..."
for entry in "${RB_TO_DELETE[@]}"; do
ns="${entry%%,*}"
name="${entry##*,}"
if [[ -n "$ns" && -n "$name" ]]; then
echo " - Deleting RoleBinding '$name' in namespace '$ns'"
kubectl delete rolebinding "$name" -n "$ns" --ignore-not-found
fi
done
echo "Deleting offending ClusterRoleBindings..."
for name in "${CRB_TO_DELETE[@]}"; do
if [[ -n "$name" ]]; then
echo " - Deleting ClusterRoleBinding '$name'"
kubectl delete clusterrolebinding "$name" --ignore-not-found
fi
done
fi
echo
echo "Verifying compliance..."
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
'