Skip to main content

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

Manual Steps
  1. On any machine with kubectl access, list all RoleBindings and ClusterRoleBindings that target system:anonymous or system:unauthenticated and 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'
  2. 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 RoleBinding
    kubectl get rolebinding <ROLEBINDING_NAME> -n <NAMESPACE> -o yaml > /tmp/<ROLEBINDING_NAME>.yaml

    # Example for a ClusterRoleBinding
    kubectl get clusterrolebinding <CLUSTERROLEBINDING_NAME> -o yaml > /tmp/<CLUSTERROLEBINDING_NAME>.yaml
  3. 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.

  4. On any machine with kubectl access, delete each RoleBinding or ClusterRoleBinding whose subject is system:anonymous or system:unauthenticated:

    # Namespaced RoleBinding
    kubectl delete rolebinding <ROLEBINDING_NAME> -n <NAMESPACE>

    # ClusterRoleBinding
    kubectl delete clusterrolebinding <CLUSTERROLEBINDING_NAME>
  5. If you need to preserve the permission but for an authenticated subject, recreate an equivalent binding that omits system:anonymous / system:unauthenticated and uses your chosen subject instead. For example:

    cat << 'EOF' | kubectl apply -f -
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: <NEW_ROLEBINDING_NAME>
    namespace: <NAMESPACE>
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: Role
    name: <EXISTING_ROLE_NAME>
    subjects:
    - kind: ServiceAccount
    name: <SERVICEACCOUNT_NAME>
    namespace: <NAMESPACE>
    EOF
  6. 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: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'
Using kubectl

On any machine with kubectl access to the cluster:

  1. List offending RoleBindings and ClusterRoleBindings (review before deleting):
kubectl get rolebindings,clusterrolebindings --all-namespaces -o wide \
| grep -E 'system:anonymous|system:unauthenticated' || true
  1. 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
  1. 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
'