Skip to main content

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​

Manual Steps
  1. 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'
  2. For each offending RoleBinding, inspect details and confirm it is safe to remove (any machine with kubectl access):

    # example: namespace-scoped RoleBinding
    kubectl get rolebinding <ROLEBINDING_NAME> -n <NAMESPACE> -o yaml

    # example: ClusterRoleBinding
    kubectl get clusterrolebinding <CLUSTERROLEBINDING_NAME> -o yaml
  3. Delete offending RoleBindings that reference system:anonymous or system:unauthenticated (any machine with kubectl access):

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

    # ClusterRoleBinding
    kubectl delete clusterrolebinding <CLUSTERROLEBINDING_NAME>
  4. 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>
  5. If bindings are managed via manifests or GitOps, update the source manifests to remove any subjects named system:anonymous or system:unauthenticated so they are not re-applied (edit locally, then apply from any machine with kubectl access):

    kubectl apply -f <UPDATED_MANIFEST_FILE>.yaml
  6. 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:

  1. 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'
  1. 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
  1. 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