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 all RoleBindings and ClusterRoleBindings that reference
system:anonymousorsystem:unauthenticated(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 binding you plan to remove, inspect it to understand what access it grants and whether an authenticated subject (e.g., specific user/group/service account) should replace it (run on any machine with kubectl access). Example for a namespaced RoleBinding:
kubectl get rolebinding <ROLEBINDING_NAME> -n <NAMESPACE> -o yamlExample for a ClusterRoleBinding:
kubectl get clusterrolebinding <CLUSTERROLEBINDING_NAME> -o yaml -
If you decide the binding is not required, delete the offending RoleBinding or ClusterRoleBinding (run on any machine with kubectl access). Example for a namespaced RoleBinding:
kubectl delete rolebinding <ROLEBINDING_NAME> -n <NAMESPACE>Example for a ClusterRoleBinding:
kubectl delete clusterrolebinding <CLUSTERROLEBINDING_NAME> -
If equivalent access is still needed, create or update a binding that grants the same Role/ClusterRole only to authenticated subjects (run on any machine with kubectl access). For example, to bind a ClusterRole to a specific group instead of
system:unauthenticated:cat << 'EOF' | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:name: <NEW_BINDING_NAME>subjects:- kind: Groupname: <APPROVED_AUTHENTICATED_GROUP>apiGroup: rbac.authorization.k8s.ioroleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: <EXISTING_CLUSTERROLE_NAME>EOF -
Repeat steps 2–4 until all RoleBindings and ClusterRoleBindings that reference
system:anonymousorsystem:unauthenticatedhave been deleted or appropriately replaced. -
Verify compliance (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'The output should be:
is_compliant=true
Using kubectl
On any machine with kubectl access:
- Identify 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)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
- For each violating RoleBinding, delete it
- Namespaced RoleBinding (replace
<namespace>and<name>):
kubectl delete rolebinding <name> -n <namespace>
Example:
kubectl delete rolebinding public-read -n default
- For each violating ClusterRoleBinding, delete it
- Cluster-wide ClusterRoleBinding (replace
<name>):
kubectl delete clusterrolebinding <name>
Example:
kubectl delete clusterrolebinding unauthenticated-view
- Re-apply any required access using authenticated subjects only (optional, declarative)
Create a manifest file (for example, rb-authenticated-only.yaml) with safe subjects, then apply:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: authenticated-view
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:authenticated
Apply it:
kubectl apply -f rb-authenticated-only.yaml
- Verification (must return
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'
Automation
#!/usr/bin/env bash
#
# Remediation: Delete any RoleBinding or ClusterRoleBinding that grants access
# to subjects named system:anonymous or system:unauthenticated.
#
# Requirements:
# - Run from any machine with kubectl access and current kube-context set to the OKE cluster.
# - kubectl and jq must be installed and on PATH.
set -euo pipefail
echo "[INFO] Discovering RoleBindings and ClusterRoleBindings targeting system:anonymous or system:unauthenticated..."
# Capture current violations in a temporary file for idempotence and traceability
WORKDIR="$(mktemp -d)"
VIOLATIONS_JSON="${WORKDIR}/violations.json"
kubectl get rolebindings,clusterrolebindings --all-namespaces -o json > "${VIOLATIONS_JSON}"
# Extract unique binding identifiers (kind, namespace, name) that reference the bad subjects
mapfile -t BINDINGS_TO_DELETE < <(
jq -r '
.items[]
| .kind as $kind
| .metadata as $m
| .subjects // []
| map(select(.name == "system:anonymous" or .name == "system:unauthenticated"))
| select(length > 0)
| [
$kind,
($m.namespace // ""),
$m.name
]
| @tsv
' "${VIOLATIONS_JSON}" | sort -u
)
if [ "${#BINDINGS_TO_DELETE[@]}" -eq 0 ]; then
echo "[INFO] No RoleBindings or ClusterRoleBindings reference system:anonymous or system:unauthenticated. Cluster is already compliant."
else
echo "[INFO] The following bindings will be deleted:"
printf ' %s\n' "${BINDINGS_TO_DELETE[@]}"
for line in "${BINDINGS_TO_DELETE[@]}"; do
kind="$(echo "${line}" | awk '{print $1}')"
ns="$(echo "${line}" | awk '{print $2}')"
name="$(echo "${line}" | awk '{print $3}')"
# Determine kubectl delete command based on scope
if [ "${kind}" = "RoleBinding" ]; then
if [ -z "${ns}" ]; then
echo "[WARN] RoleBinding ${name} has no namespace; skipping (invalid object)."
continue
fi
echo "[INFO] Deleting RoleBinding ${name} in namespace ${ns}..."
kubectl delete rolebinding "${name}" -n "${ns}" --ignore-not-found
elif [ "${kind}" = "ClusterRoleBinding" ]; then
echo "[INFO] Deleting ClusterRoleBinding ${name}..."
kubectl delete clusterrolebinding "${name}" --ignore-not-found
else
echo "[WARN] Unknown kind ${kind} for ${name}; skipping."
fi
done
fi
echo "[INFO] Verifying compliance after remediation..."
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 "[INFO] Cleanup temporary files..."
rm -rf "${WORKDIR}"
echo "[INFO] Remediation script completed."