Skip to main content

Minimize Wildcard Use In Roles And Cluster Roles

More Info:​

Kubernetes Roles and ClusterRoles provide access to resources based on sets of objects and actions that can be taken on those objects. It is possible to set either of these to be the wildcard * which matches all items.

Risk Level​

Medium

Address​

Security

Compliance Standards​

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS Critical Security Controls v8
  • CIS GKE
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • Essential 8
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIS2 Directive
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • Securities and Exchange Board of India (SEBI) - Cloud Security Adoption Framework
  • UK NCSC Cyber Assessment Framework

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 '
    .items[] | select(
    .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
    ) | {kind, namespace: .metadata.namespace, name: .metadata.name, rules: .rules}' | less

    kubectl get clusterroles -o json | jq '
    .items[] | select(
    .rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
    ) | {kind, name: .metadata.name, rules: .rules}' | less
  2. For each Role with wildcards, decide the minimum required permissions

    • For each Role listed above, determine:
      • The exact verbs actually needed (e.g., get, list, watch, create, update, patch, delete).
      • The exact resources (e.g., pods, deployments, configmaps) and apiGroups (e.g., "", "apps") required.
    • Document these decisions before editing so you can justify that wildcards are required only where strictly necessary (if ever).
  3. Edit Roles to replace wildcards with specific verbs/resources/apiGroups

    • Run on: any machine with kubectl access
    • For each affected Role, edit it interactively:
    kubectl edit role <role-name> -n <namespace>
    • In the opened YAML, locate .rules and replace:
      • verbs: ["*"] with an explicit list, for example:
        verbs: ["get", "list", "watch"]
      • resources: ["*"] with only needed resources, for example:
        resources: ["pods", "pods/log"]
      • apiGroups: ["*"] with specific groups, for example:
        apiGroups: [""]
    • Save and exit the editor to apply the change.
  4. Edit ClusterRoles to replace wildcards with specific verbs/resources/apiGroups

    • Run on: any machine with kubectl access
    • For each affected ClusterRole, edit it interactively:
    kubectl edit clusterrole <clusterrole-name>
    • Adjust .rules the same way as in step 3, replacing any * in verbs, resources, or apiGroups with the minimum specific values needed.
    • For built‑in or add‑on ClusterRoles managed by the platform or an operator, review carefully; if changing them might break functionality, prefer:
      • Creating a new, more restrictive ClusterRole.
      • Updating your RoleBindings/ClusterRoleBindings to use the new role instead of the wildcarded one.
  5. For GitOps or manifest‑managed clusters, update source manifests

    • Run on: any machine with access to your IaC/Git repo
    • Locate Role and ClusterRole definitions containing wildcards, for example with:
    grep -R --include='*.yaml' -n 'verbs: \["*"\]\|resources: \["*"\]\|apiGroups: \["*"\]' /absolute/path/to/your/manifests
    • Edit those YAML files to match the changes you applied via kubectl edit (specific verbs/resources/apiGroups).
    • Commit and push the changes so the GitOps/IaC source of truth matches the live cluster.
  6. Verification: confirm wildcards are no longer present

    • 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
  1. Identify the specific ClusterRoles using wildcards
    Run on: any machine with kubectl access
kubectl get clusterroles -o json | jq -r '
.items[]
| select(
.rules[]? |
(.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)
| .metadata.name
'

For each ClusterRole name returned (example: example-clusterrole), proceed with the next steps.

  1. Export the existing ClusterRole manifest for editing
    Run on: any machine with kubectl access
kubectl get clusterrole example-clusterrole -o yaml > example-clusterrole.yaml
  1. Edit the manifest to remove wildcards
    Open example-clusterrole.yaml in an editor and, under .rules, replace each * with the minimum required specific values. For example, change:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

to something like:

rules:
- apiGroups: [""] # core API group
resources:
- pods
- services
verbs:
- get
- list
- watch

Adjust apiGroups, resources, and verbs to the least-privilege set actually needed for that ClusterRole.

  1. Apply the updated ClusterRole
    Run on: any machine with kubectl access
kubectl apply -f example-clusterrole.yaml

Repeat steps 2–4 for each ClusterRole flagged in step 1.

  1. 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
Automation
#!/usr/bin/env bash
#
# Minimize wildcard use in Roles and ClusterRoles (CIS GKE 4.1.3)
#
# Scope: run on any machine with kubectl access and jq installed.
# This script is ASSISTED / SEMI-AUTOMATIC:
# - It does NOT blindly rewrite RBAC.
# - It finds Roles/ClusterRoles using wildcards, exports them, and opens them for
# manual, least-privilege editing, then reapplies them.
#
# Idempotency:
# - Safe to re-run; it will re-check, re-export, and allow re-editing the same objects.
# - Only 'kubectl apply' is used; no deletes.
#
# OPERATIONAL NOTE:
# - Changing RBAC may immediately remove access from users/service accounts.
# - Carefully review each object and test access for affected subjects.

set -euo pipefail

# CONFIG
OUTPUT_DIR="${PWD}/rbac-wildcards-$(date +%Y%m%d-%H%M%S)"
EDITOR_CMD="${EDITOR:-vi}" # Change to your preferred editor if desired

mkdir -p "${OUTPUT_DIR}/roles" "${OUTPUT_DIR}/clusterroles"

echo "Discovering Roles and ClusterRoles that use wildcards in rules..."

# Function: list wildcard RBAC objects in JSON
list_wildcard_objects() {
local kind="$1" # roles or clusterroles
if [[ "${kind}" == "roles" ]]; then
kubectl get roles --all-namespaces -o json
else
kubectl get clusterroles -o json
fi
}

# Extract Roles with wildcards
roles_json="$(list_wildcard_objects roles || echo '{}')"
clusterroles_json="$(list_wildcard_objects clusterroles || echo '{}')"

roles_with_wildcards=$(echo "${roles_json}" | jq -r '
.items[]? |
select(.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")) |
"\(.metadata.namespace),\(.metadata.name)"
')

clusterroles_with_wildcards=$(echo "${clusterroles_json}" | jq -r '
.items[]? |
select(.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")) |
.metadata.name
')

if [[ -z "${roles_with_wildcards}" && -z "${clusterroles_with_wildcards}" ]]; then
echo "No Roles or ClusterRoles with wildcard rules found."
exit 0
fi

echo "Exporting affected Roles and ClusterRoles to ${OUTPUT_DIR} for review..."

# Export Roles
if [[ -n "${roles_with_wildcards}" ]]; then
echo "Roles with wildcards:"
echo "${roles_with_wildcards}" | while IFS=, read -r ns name; do
echo " - ${ns}/${name}"
out="${OUTPUT_DIR}/roles/${ns}__${name}.yaml"
kubectl get role "${name}" -n "${ns}" -o yaml > "${out}"
done
else
echo "No roles with wildcards."
fi

# Export ClusterRoles
if [[ -n "${clusterroles_with_wildcards}" ]]; then
echo "ClusterRoles with wildcards:"
echo "${clusterroles_with_wildcards}" | while read -r name; do
[[ -z "${name}" ]] && continue
echo " - ${name}"
out="${OUTPUT_DIR}/clusterroles/${name}.yaml"
kubectl get clusterrole "${name}" -o yaml > "${out}"
done
else
echo "No clusterroles with wildcards."
fi

cat <<'EOF'

MANUAL REMEDIATION REQUIRED (per RBAC best practices):
- For each exported YAML in the output directory:
* Identify any occurrence of:
verbs: ["*"] or verbs: - "*"
resources: ["*"] or resources: - "*"
apiGroups: ["*"] or apiGroups: - "*"
* Replace "*" with the minimal explicit list of verbs/resources/apiGroups
actually required for that Role/ClusterRole.
* Be cautious: over-restricting may break workloads; under-restricting defeats this control.

You will now be dropped into an editor, one file at a time, to make those changes.
Save and exit the editor to move to the next file.

EOF

read -r -p "Press Enter to start editing affected YAML files, or Ctrl+C to abort..."

# Edit and apply Roles
for f in "${OUTPUT_DIR}/roles/"*.yaml; do
[[ ! -e "${f}" ]] && break
echo "Editing Role manifest: ${f}"
"${EDITOR_CMD}" "${f}"

echo "Applying Role manifest: ${f}"
kubectl apply -f "${f}"
done

# Edit and apply ClusterRoles
for f in "${OUTPUT_DIR}/clusterroles/"*.yaml; do
[[ ! -e "${f}" ]] && break
echo "Editing ClusterRole manifest: ${f}"
"${EDITOR_CMD}" "${f}"

echo "Applying ClusterRole manifest: ${f}"
kubectl apply -f "${f}"
done

echo "Re-running wildcard audit to verify remediation..."

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

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

total=$((wildcards + wildcards_clusterroles))

if [ "${total}" -gt 0 ]; then
echo "wildcards_present"
echo "Remaining objects with wildcards detected: ${total}"
echo "Re-run the script and further tighten RBAC, or manually inspect with:"
echo " kubectl get roles --all-namespaces -o yaml | grep -n \"- \\\"*\\\"\" -n"
echo " kubectl get clusterroles -o yaml | grep -n \"- \\\"*\\\"\" -n"
exit 1
fi

echo "No remaining Roles or ClusterRoles with wildcard verbs/resources/apiGroups."
echo "Remediation for CIS GKE 4.1.3 appears complete."