Skip to main content

Minimize Wildcard Use In Roles And ClusterRoles

More Info:

Wildcards in RBAC verbs, resources or apiGroups grant broad, hard-to-audit permissions. They should be replaced with explicit objects and actions.

Risk Level

Critical

Address

Security

Compliance Standards

  • CIS GKE

Triage and Remediation

Remediation

Manual Steps
  1. List all Roles and ClusterRoles that use wildcards (run on any machine with kubectl access):

    kubectl get roles --all-namespaces -o json | jq -r '
    .items[] |
    select(.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")) |
    "ROLE " + .metadata.namespace + "/" + .metadata.name
    '

    kubectl get clusterroles -o json | jq -r '
    .items[] |
    select(.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")) |
    "CLUSTERROLE " + .metadata.name
    '
  2. For each reported ClusterRole, export it to a manifest for review and editing (example for one ClusterRole named example-clusterrole):

    kubectl get clusterrole example-clusterrole -o yaml > /tmp/example-clusterrole.yaml
  3. Manually review /tmp/example-clusterrole.yaml and replace wildcard entries with explicit values:

    • Replace verbs: ["*"] or - "*" with an explicit verb list such as ["get","list","watch","create","update","patch","delete"] as needed.
    • Replace resources: ["*"] with only the required resource names, for example ["pods","deployments"].
    • Replace apiGroups: ["*"] with the specific API groups needed, for example ["","apps"].
    • Remove any rule entries that are not actually required for the workloads using this ClusterRole.
  4. Apply the edited ClusterRole back to the cluster (example):

    kubectl apply -f /tmp/example-clusterrole.yaml
  5. If any wildcard-bearing Role objects (namespaced) were reported, repeat steps 2–4 for those, substituting kubectl get role -n <namespace> <name> -o yaml > /tmp/<name>.yaml and kubectl apply -f /tmp/<name>.yaml.

  6. Verification (run on any machine with kubectl access):

    wildcards=$(kubectl get roles --all-namespaces -o json | jq '
    .items[] | select(
    .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
    )' | wc -l)

    wildcards_clusterroles=$(kubectl get clusterroles -o json | jq '
    .items[] | select(
    .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
    )' | wc -l)

    total=$((wildcards + wildcards_clusterroles))

    if [ "$total" -gt 0 ]; then
    echo "wildcards_present"
    else
    echo "no_wildcards_in_roles_or_clusterroles"
    fi
Using kubectl

This control is MANUAL. There is no one-shot kubectl fix, because each wildcard must be evaluated and replaced with explicit permissions case‑by‑case.

Below are practical review steps and kubectl commands to help you identify, inspect, and update ClusterRoles.

1. Identify ClusterRoles using wildcards

Run on: any machine with kubectl access.

kubectl get clusterroles -o json | jq '
.items[]
| select(
.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)
| .metadata.name' -r

Optionally, see full definitions:

kubectl get clusterroles -o json | jq '
.items[]
| select(
.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)'

2. Inspect an individual ClusterRole

Pick a ClusterRole name from the list and inspect it:

kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml

Decision guidance during review:

  • Is this a built‑in or system role?
    • Names like cluster-admin, system:*, or cloud‑provider managed roles often legitimately use wildcards and are relied upon by the platform. Avoid changing them unless you fully understand the impact.
  • Is this a custom role for an application or team?
    • Prefer replacing * in verbs, resources, or apiGroups with just the specific items actually needed.
  • Is the role even used?
    • Check ClusterRoleBindings to see if it’s bound to any subjects. If unused, consider deletion instead of tightening.

List bindings that use a given ClusterRole:

kubectl get clusterrolebindings -o json | jq '
.items[]
| select(.roleRef.kind=="ClusterRole" and .roleRef.name=="<CLUSTERROLE_NAME>")
| {name: .metadata.name, subjects: .subjects}'

3. Edit a ClusterRole to remove wildcards

Run on: any machine with kubectl access.

Interactive edit:

kubectl edit clusterrole <CLUSTERROLE_NAME>

In the opened YAML:

  • Replace verbs: ["*"] or verbs: - "*"

    • With an explicit list, e.g.:

      verbs:
      - get
      - list
      - watch
      - create
      - update
      - patch
      - delete
    • Choose only the verbs actually required by the workload.

  • Replace resources: ["*"] or resources: - "*"

    • With explicit resources, for example:

      resources:
      - pods
      - pods/log
      - deployments
      - configmaps
  • Replace apiGroups: ["*"] or apiGroups: - "*"

    • With specific API groups, for example:

      apiGroups:
      - ""
      - apps
      - batch

Repeat for each rule under rules: that uses *.

Declarative approach using manifests (preferred for GitOps/IaC):

  1. Export the current role to a file:

    kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml > clusterrole-<CLUSTERROLE_NAME>.yaml
  2. Edit the file with your editor, replacing * as described above.

  3. Apply the updated manifest:

    kubectl apply -f clusterrole-<CLUSTERROLE_NAME>.yaml

Operational impact: tightening a ClusterRole can break workloads or tooling that previously relied on broad access. After each change, confirm that affected applications and users still function as expected.

4. Optionally remove unused overly‑broad ClusterRoles

If you confirmed a ClusterRole has no bindings and is not needed:

kubectl delete clusterrole <CLUSTERROLE_NAME>

5. Verification

Re-run the audit for ClusterRoles to confirm wildcard usage has been eliminated or intentionally minimized:

wildcards_clusterroles=$(kubectl get clusterroles -o json | jq '
.items[] | select(
.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)' | wc -l)

echo "clusterroles_with_wildcards=${wildcards_clusterroles}"

You can also list remaining offending ClusterRoles by name:

kubectl get clusterroles -o json | jq '
.items[]
| select(
.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)
| .metadata.name' -r
Automation
#!/usr/bin/env bash
#
# Purpose:
# Assist review of wildcard use in ClusterRoles by exporting and listing
# them for manual editing, then verifying reduction of wildcards.
#
# Scope:
# Run on any machine with kubectl access to the cluster.
#
# NOTES:
# - This check is MANUAL in the benchmark: there is no safe, generic way
# to automatically replace "*" with specific verbs/resources/apiGroups.
# - This script does NOT change any RBAC objects. It only:
# * Identifies ClusterRoles using wildcards
# * Exports them into separate YAMLs for human review/edit
# * Helps you re-apply your edited manifests
# * Verifies wildcard usage afterwards
#
# Requirements:
# - kubectl installed and configured with sufficient permissions
# - jq installed

set -euo pipefail

OUT_DIR="${OUT_DIR:-./clusterrole-wildcards-$(date +%Y%m%d%H%M%S)}"

echo "[*] Detecting ClusterRoles that use wildcards in verbs, resources, or apiGroups..."

wildcards_clusterroles_json=$(kubectl get clusterroles -o json | jq '
.items[]
| select(
.rules[]?
| (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)
')

if [[ -z "${wildcards_clusterroles_json}" ]]; then
echo "[*] No ClusterRoles with wildcard verbs/resources/apiGroups found."
exit 0
fi

mkdir -p "${OUT_DIR}"

echo "${wildcards_clusterroles_json}" | jq -r '.metadata.name' | sort -u > "${OUT_DIR}/clusterroles_with_wildcards.txt"

echo "[*] ClusterRoles with wildcards:"
cat "${OUT_DIR}/clusterroles_with_wildcards.txt"
echo
echo "[*] Exporting each affected ClusterRole to individual YAML manifests in: ${OUT_DIR}"

# Export each affected ClusterRole
while read -r cr_name; do
if [[ -z "${cr_name}" ]]; then
continue
fi
out_file="${OUT_DIR}/clusterrole-${cr_name}.yaml"
echo " - Exporting ClusterRole/${cr_name} -> ${out_file}"
kubectl get clusterrole "${cr_name}" -o yaml > "${out_file}"
done < "${OUT_DIR}/clusterroles_with_wildcards.txt"

cat <<'EOF'

[MANUAL ACTION REQUIRED]

1) Review each exported ClusterRole manifest in the output directory.
For each rule, where possible:
- Replace verbs: ["*"] with an explicit list, e.g. ["get","list","watch"]
- Replace resources: ["*"] with explicit resources, e.g. ["pods","deployments"]
- Replace apiGroups: ["*"] with only the required apiGroups, e.g. ["","apps"]

2) Save your edited YAML files.

3) To apply your edited ClusterRoles back to the cluster, run:

# Replace <OUT_DIR> with the directory printed above
kubectl apply -f <OUT_DIR>/

(You can safely re-run this command; it is idempotent.)

EOF

read -rp "[?] Have you already edited the manifests and applied them? [y/N]: " CONFIRM
CONFIRM=${CONFIRM:-n}

if [[ "${CONFIRM}" =~ ^[Yy]$ ]]; then
echo "[*] Verifying wildcard usage after your changes..."

# Re-run the wildcard detection, but scoped to the previously affected ClusterRoles
remaining=$(kubectl get clusterroles -o json | jq --argfile names "${OUT_DIR}/clusterroles_with_wildcards.txt" '
.items[]
| select(.metadata.name as $n | ($names | map(select(. == $n)) | length) > 0)
| select(
.rules[]?
| (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)
| .metadata.name
' | sort -u || true)

if [[ -z "${remaining}" ]]; then
echo "[*] Verification passed: no wildcards remain in the previously flagged ClusterRoles."
else
echo "[!] Verification found remaining wildcards in these ClusterRoles:"
echo "${remaining}"
echo "[!] Please re-check these manifests and further restrict wildcard use where possible."
fi
else
echo "[*] Skipping verification because manifests have not been edited/applied yet."
echo "[*] After you apply your changes, you can re-run this script to re-check."
fi