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
-
List the 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' -
For each violating namespaced RoleBinding you want to remove, delete it (on any machine with kubectl access), replacing
<namespace>and<name>with values from step 1:kubectl delete rolebinding <name> -n <namespace> -
For each violating ClusterRoleBinding you want to remove, delete it (on any machine with kubectl access), replacing
<name>with the value from step 1:kubectl delete clusterrolebinding <name> -
If you need to preserve access for specific authenticated identities, recreate appropriate bindings without
system:anonymousorsystem:unauthenticatedas subjects (on any machine with kubectl access). For example, bind a ClusterRole to a specific Azure AD group:kubectl apply -f - << 'EOF'apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:name: example-aks-auth-group-bindingsubjects:- kind: Groupname: 00000000-0000-0000-0000-000000000000 # Azure AD group object IDapiGroup: rbac.authorization.k8s.ioroleRef:kind: ClusterRolename: viewapiGroup: rbac.authorization.k8s.ioEOF -
Re-run the verification command to confirm there are no remaining bindings that reference
system:anonymousorsystem:unauthenticated(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'The output must include only:
is_compliant=true
Using kubectl
On any machine with kubectl access:
- Identify violating 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) uid=\($m.uid)"
+ (if ($api // "") == "" then "" else " apiVersion=\($api)" end)
+ (if ($m.creationTimestamp // "") == "" else " created=\($m.creationTimestamp)" end)
+ " subject=\(.name) is_compliant=false"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
Note each violating object’s kind, name, and, if present, ns= namespace.
- Delete each violating RoleBinding
For namespaced RoleBindings (lines with kind=RoleBinding ns=<namespace> name=<name>):
kubectl delete rolebinding <name> -n <namespace>
Example:
kubectl delete rolebinding public-read -n default
- Delete each violating ClusterRoleBinding
For cluster-wide bindings (lines with kind=ClusterRoleBinding name=<name> and no ns=):
kubectl delete clusterrolebinding <name>
Example:
kubectl delete clusterrolebinding allow-anonymous-access
- (Optional, declarative) Remove from GitOps/manifests
If these bindings are managed declaratively (e.g., Helm, Kustomize, or IaC), remove the offending RoleBinding / ClusterRoleBinding definitions from the source manifests and re-apply:
kubectl apply -f <your-cleaned-manifest>.yaml
- Verify remediation
Re-run the audit command; it should return only is_compliant=true:
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 // "") == "" 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
#
# Automation for CBP C2.4 on AKS:
# Delete any RoleBinding/ClusterRoleBinding that grants access to
# system:anonymous or system:unauthenticated.
#
# Run on: any machine with kubectl access and jq installed.
# Safe to re-run (idempotent).
set -euo pipefail
echo "=== CBP C2.4 remediation: removing RoleBindings/ClusterRoleBindings with anonymous/unauthenticated subjects ==="
# Ensure requirements
command -v kubectl >/dev/null 2>&1 || { echo "kubectl not found in PATH"; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "jq not found in PATH"; exit 1; }
# Get all violating bindings (kind, namespace, name) in a parseable form
violations_json="$(kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | jq -c '
.items[]
| .kind as $kind
| .metadata.name as $name
| (.metadata.namespace // "") as $ns
| ((.subjects // [])[]
| select(.name == "system:anonymous" or .name == "system:unauthenticated")
) as $subject
| {
kind: $kind,
namespace: $ns,
name: $name,
subject: $subject.name
}
' || true)"
if [[ -z "$violations_json" ]]; then
echo "No rolebindings/clusterrolebindings with anonymous/unauthenticated subjects found."
else
echo "Found the following bindings granting access to anonymous/unauthenticated subjects:"
echo "$violations_json" | jq -r '. | "\(.kind) ns=\(.namespace) name=\(.name) subject=\(.subject)"' | sort -u
# Deduplicate by kind/namespace/name to avoid deleting the same object multiple times
echo
echo "Deleting violating bindings..."
echo "$violations_json" | jq -r '
{kind, namespace, name}
| @tsv
' | sort -u | while IFS=$'\t' read -r kind namespace name; do
if [[ -z "$name" ]]; then
continue
fi
if [[ "$kind" == "ClusterRoleBinding" ]]; then
echo "kubectl delete clusterrolebinding ${name}"
kubectl delete clusterrolebinding "${name}" --ignore-not-found
elif [[ "$kind" == "RoleBinding" ]]; then
if [[ -z "$namespace" ]]; then
# Should not occur for RoleBinding, but guard anyway
echo "Skipping RoleBinding ${name} with empty namespace"
continue
fi
echo "kubectl delete rolebinding ${name} -n ${namespace}"
kubectl delete rolebinding "${name}" -n "${namespace}" --ignore-not-found
fi
done
fi
echo
echo "=== Verification (should print only 'is_compliant=true') ==="
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'