Minimize Wildcard Use In Roles And ClusterRoles
More Info:
Wildcards (*) in verbs, resources, or apiGroups grant broad, unintended permissions. Replacing them with explicit values enforces least privilege.
Risk Level
Critical
Address
Security
Compliance Standards
- CIS AKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List all ClusterRoles that use 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' | sort -uSave this list; these are the ClusterRoles to review.
-
Inspect each flagged ClusterRole’s rules and usage
- Run on: any machine with kubectl access
For each<clusterrole>from step 1:
kubectl get clusterrole <clusterrole> -o yamlkubectl get clusterrolebindings,rolebindings --all-namespaces \-o jsonpath='{range .items[?(@.roleRef.kind=="ClusterRole" && @.roleRef.name=="<clusterrole>")]}{.kind}{";"}{.metadata.name}{";"}{.metadata.namespace}{"\n"}{end}'Use this to understand who/what is using the ClusterRole and in what context.
- Run on: any machine with kubectl access
-
Determine the minimal required verbs, resources, and apiGroups
- Review the consuming subjects (users, groups, service accounts) and their applications’ actual needs:
- Check application docs/helm charts/manifest comments for required permissions.
- If necessary, enable/consult audit logs (if available) to see which API calls are made by these identities.
- Review the consuming subjects (users, groups, service accounts) and their applications’ actual needs:
-
Edit the ClusterRole to replace wildcards with explicit values
- Run on: any machine with kubectl access
For each ClusterRole confirmed as overly broad:
kubectl edit clusterrole <clusterrole>In the editor, for each
rulesentry:- Replace
verbs: ["*"]with only the required verbs (e.g.["get","list","watch"]). - Replace
resources: ["*"]with only the needed resources (e.g.["pods","configmaps"]). - Replace
apiGroups: ["*"]with only the necessary groups (e.g.["","apps"]).
Save and exit to apply.
- Run on: any machine with kubectl access
-
If a wildcard is truly required, document and isolate it
- For any ClusterRole where
*cannot be safely removed (e.g., break-glass admin):- Keep the wildcard but ensure it is bound only to tightly controlled identities (e.g., specific admin group).
- Optionally, create a new, narrower ClusterRole for regular use and update bindings to use that instead:
kubectl get clusterrole <clusterrole> -o yaml > <clusterrole>-narrow.yaml# edit file offline to remove wildcards and rename metadata.namekubectl apply -f <clusterrole>-narrow.yaml# update bindings to point to the new role as appropriate
- For any ClusterRole where
-
Verify no unintended wildcards remain
- Run on: any machine with kubectl access
kubectl get clusterroles -o json \| jq -r '.items[]| select(.rules[]| ( .verbs[]? == "*" )or ( .resources[]? == "*" )or ( .apiGroups[]? == "*" ))| .metadata.name' | sort -uConfirm that:
- Only ClusterRoles with a conscious, documented justification still appear.
- All others have been corrected to use explicit verbs, resources, and apiGroups.
Using kubectl
Run these commands from any machine with kubectl access.
1. List all ClusterRoles that contain any wildcard
kubectl get clusterrole -o json \
| jq -r '
.items[]
| select(
.rules[]?
| (
(.verbs[]? == "*")
or (.resources[]? == "*")
or (.apiGroups[]? == "*")
)
)
| .metadata.name
' | sort -u
Problem indication: Any name in the output is a ClusterRole that uses at least one * in verbs, resources, or apiGroups and needs manual review.
If you don’t have jq, use:
kubectl get clusterrole -o yaml | grep -E '^\s*(verbs|resources|apiGroups):' -n -C3
Problem indication: Lines under rules: where verbs:, resources:, or apiGroups: include - "*" are suspect and must be reviewed.
2. Inspect a specific ClusterRole in detail
For any ClusterRole name identified above, review it:
kubectl get clusterrole <clusterrole-name> -o yaml
Problem indication inside rules::
verbs:verbs:- "*"resources:resources:- "*"apiGroups:apiGroups:- "*"
Any of these (or combinations) mean broad, non–least-privilege permissions that must be assessed manually for necessity and risk.
3. Focus on ClusterRoles actually bound to users/service accounts
List all ClusterRoleBindings and the ClusterRoles they reference:
kubectl get clusterrolebinding -o json \
| jq -r '.items[].roleRef.name' \
| sort -u
Then intersect with the wildcarded ClusterRoles:
# Save wildcarded roles
kubectl get clusterrole -o json \
| jq -r '
.items[]
| select(
.rules[]?
| (
(.verbs[]? == "*")
or (.resources[]? == "*")
or (.apiGroups[]? == "*")
)
)
| .metadata.name
' | sort -u > /tmp/wildcard-clusterroles.txt
# Save bound roles
kubectl get clusterrolebinding -o json \
| jq -r '.items[].roleRef.name' \
| sort -u > /tmp/bound-clusterroles.txt
# Show wildcarded roles that are actually bound
comm -12 /tmp/wildcard-clusterroles.txt /tmp/bound-clusterroles.txt
Problem indication: Any ClusterRole name in the final comm output both uses wildcards and is bound, meaning it actively grants broad permissions and should be prioritized for review.
4. Verify after manual edits
After you edit ClusterRoles with kubectl edit clusterrole <name> to replace * with explicit values, rerun:
kubectl get clusterrole -o json \
| jq -r '
.items[]
| select(
.rules[]?
| (
(.verbs[]? == "*")
or (.resources[]? == "*")
or (.apiGroups[]? == "*")
)
)
| .metadata.name
' | sort -u
If a previously flagged ClusterRole no longer appears, it no longer contains wildcards and passes this manual check (subject to your least-privilege judgment).
Automation
#!/usr/bin/env bash
# Report Roles and ClusterRoles that use wildcards in verbs, resources, or apiGroups.
# Run on: any machine with kubectl access and current-context pointing at the target cluster.
set -euo pipefail
# Ensure we can talk to the cluster
kubectl version --request-timeout=10s >/dev/null
echo "=== Scanning ClusterRoles for wildcard usage ==="
kubectl get clusterroles -o json \
| jq -r '
.items[]
| {
kind,
name: .metadata.name,
rules: (
.rules[]
| select(
(.verbs[]? == "*")
or (.resources[]? == "*")
or (.apiGroups[]? == "*")
)
)
}
| select(.rules != null)
| "KIND: \(.kind)\nNAME: \(.name)\nOFFENDING RULES:\n\(.rules | tojson)\n---"
'
echo
echo "=== Scanning Roles for wildcard usage (all namespaces) ==="
kubectl get roles --all-namespaces -o json \
| jq -r '
.items[]
| {
kind,
namespace: .metadata.namespace,
name: .metadata.name,
rules: (
.rules[]
| select(
(.verbs[]? == "*")
or (.resources[]? == "*")
or (.apiGroups[]? == "*")
)
)
}
| select(.rules != null)
| "KIND: \(.kind)\nNAMESPACE: \(.namespace)\nNAME: \(.name)\nOFFENDING RULES:\n\(.rules | tojson)\n---"
'
How to interpret the output:
- Any block printed under
=== Scanning ClusterRoles for wildcard usage ===or=== Scanning Roles for wildcard usage (all namespaces) ===indicates a potential problem. - In each block:
KIND/NAME/NAMESPACEidentify the Role or ClusterRole.OFFENDING RULESlists the specificrulesentries where at least one of:verbscontains"*",resourcescontains"*",apiGroupscontains"*".
- Each such rule should be manually reviewed and, where feasible, wildcards replaced with explicit
verbs,resources, andapiGroupsusing:kubectl edit clusterrole <name>kubectl edit role -n <namespace> <name>