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
-
Identify offending RoleBindings
- Run on 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'
- Run on any machine with kubectl access:
-
Review each violating binding and its purpose
- For a namespaced RoleBinding (replace NAMESPACE and NAME):
kubectl get rolebinding NAME -n NAMESPACE -o yaml
- For a ClusterRoleBinding (cluster-scoped):
kubectl get clusterrolebinding NAME -o yaml
- Determine if the access is actually needed. If it is not required, plan to delete the binding. If access is required, design an alternative using authenticated identities (e.g., specific Kubernetes ServiceAccounts, IAM-authenticated users/groups via aws-auth).
- For a namespaced RoleBinding (replace NAMESPACE and NAME):
-
Safely back up the offending RoleBindings before deletion
- Namespaced RoleBinding:
kubectl get rolebinding NAME -n NAMESPACE -o yaml > NAME-rolebinding-backup.yaml
- ClusterRoleBinding:
kubectl get clusterrolebinding NAME -o yaml > NAME-clusterrolebinding-backup.yaml
- Namespaced RoleBinding:
-
Delete RoleBindings that reference
system:anonymousorsystem:unauthenticated- Namespaced RoleBinding:
kubectl delete rolebinding NAME -n NAMESPACE
- ClusterRoleBinding:
kubectl delete clusterrolebinding NAME
- Namespaced RoleBinding:
-
(Optional but recommended) Recreate least-privilege bindings for authenticated subjects
- Example for a ServiceAccount in namespace NAMESPACE (replace placeholders):
kubectl create rolebinding NAME \--namespace NAMESPACE \--role EXISTING_ROLE_NAME \--serviceaccount NAMESPACE:SERVICEACCOUNT_NAME
- Or for an AWS IAM-mapped group (from aws-auth ConfigMap), use that group name as the subject instead of unauthenticated groups in a ClusterRoleBinding manifest you apply with:
kubectl apply -f updated-clusterrolebinding.yaml
- Example for a ServiceAccount in namespace NAMESPACE (replace placeholders):
-
Verify the cluster is compliant
- Run on 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'
- Confirm the output is exactly:
is_compliant=true
- Run on any machine with kubectl access:
Using kubectl
On any machine with kubectl access to the cluster:
- Identify the offending RoleBindings and 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)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
- For each noncompliant RoleBinding, delete it (namespaced)
Example (replace <namespace> and <name> with values from step 1):
kubectl delete rolebinding <name> -n <namespace>
- For each noncompliant ClusterRoleBinding, delete it (cluster-scoped)
Example (replace <name> with value from step 1):
kubectl delete clusterrolebinding <name>
- If you manage these bindings via manifests (GitOps/IaC), remove the corresponding
RoleBinding/ClusterRoleBindingobjects or edit theirsubjectsto no longer includesystem:anonymousor thesystem:unauthenticatedgroup, then apply:
kubectl apply -f <your-manifest-file>.yaml
- Verification (cluster should now report compliant)
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
# Purpose: Remove any RoleBinding or ClusterRoleBinding that grants access to
# system:anonymous or the system:unauthenticated group.
# Scope: Run on any machine with kubectl access to the EKS cluster.
# Safety: Idempotent; safe to re-run.
set -euo pipefail
# Ensure kubectl is available and can talk to the cluster
kubectl version --request-timeout=10s >/dev/null
echo "=== Identifying RoleBindings and ClusterRoleBindings referencing anonymous/unauthenticated subjects ==="
# Function to delete a binding by type/name/namespace
delete_binding() {
local kind="$1" # RoleBinding or ClusterRoleBinding
local ns="$2" # namespace or "-" for cluster-scoped
local name="$3"
if [[ "${kind}" == "RoleBinding" ]]; then
# Namespaced
echo "Processing RoleBinding: ${ns}/${name}"
if kubectl get rolebinding "${name}" -n "${ns}" >/dev/null 2>&1; then
echo " Deleting RoleBinding ${ns}/${name}"
kubectl delete rolebinding "${name}" -n "${ns}" --wait=true
else
echo " RoleBinding ${ns}/${name} not found (already removed)"
fi
elif [[ "${kind}" == "ClusterRoleBinding" ]]; then
# Cluster-scoped
echo "Processing ClusterRoleBinding: ${name}"
if kubectl get clusterrolebinding "${name}" >/dev/null 2>&1; then
echo " Deleting ClusterRoleBinding ${name}"
kubectl delete clusterrolebinding "${name}" --wait=true
else
echo " ClusterRoleBinding ${name} not found (already removed)"
fi
fi
}
# Build a unique list of offending RoleBindings and ClusterRoleBindings
# Output format: "<kind> <namespace_or_-_for_cluster> <name>"
OFFENDERS=$(kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | jq -r '
.items[]
| .kind as $kind
| .metadata.name as $name
| (.metadata.namespace // "-") as $ns
| ((.subjects // [])[]
| select(.name == "system:anonymous" or .name == "system:unauthenticated"))
| "\($kind) \($ns) \($name)"
' | sort -u || true)
if [[ -z "${OFFENDERS}" ]]; then
echo "No RoleBindings or ClusterRoleBindings found with system:anonymous or system:unauthenticated."
else
echo "The following bindings reference anonymous/unauthenticated subjects and will be deleted:"
echo "${OFFENDERS}"
echo
# Delete each offending binding
while read -r kind ns name; do
[[ -z "${kind}" ]] && continue
delete_binding "${kind}" "${ns}" "${name}"
done <<< "${OFFENDERS}"
fi
echo
echo "=== Verification: re-running compliance 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'