Minimize Wildcard Use In Roles And ClusterRoles
More Info:
Wildcards in verbs, resources, or apiGroups grant overly broad permissions that violate least privilege. Replace them with explicitly scoped objects and actions.
Risk Level
Critical
Address
Security
Compliance Standards
- CIS EKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List Roles/ClusterRoles using 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, metadata:{name,namespace}}'kubectl get clusterroles -o json | jq '.items[] | select(.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")) | {kind, metadata:{name}}' -
For each offending Role/ClusterRole, export its definition for review/edit (example for a ClusterRole named
example-role):kubectl get clusterrole example-role -o yaml > /tmp/example-role.yaml# or for a namespaced Rolekubectl get role example-role -n example-namespace -o yaml > /tmp/example-role.yaml -
Manually edit the exported file to replace wildcards with explicit values (run on the machine where the file is saved):
- Replace
verbs: ["*"]orverbs: - "*"with only the needed verbs, e.g.:verbs:- get- list- watch - Replace
resources: ["*"]orresources: - "*"with specific resources, e.g.:resources:- pods- services - Replace
apiGroups: ["*"]orapiGroups: - "*"with required API groups, e.g.:apiGroups:- ""- apps - Remove any rule entries that are not actually needed. Save the file.
- Replace
-
Apply the edited Role/ClusterRole back to the cluster (run on any machine with kubectl access):
kubectl apply -f /tmp/example-role.yaml -
If these objects are managed by GitOps or other IaC, update the source manifests similarly (edit the Role/ClusterRole definitions in your Git/IaC repo to match the changes above and run your usual deployment pipeline) to prevent them from being reverted.
-
Verify no Roles/ClusterRoles still use wildcards (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" -eq 0 ]; thenecho "no_wildcards_present"elseecho "wildcards_present ($total)"fi
Using kubectl
On any machine with kubectl access:
- Identify ClusterRoles using wildcards
kubectl get clusterroles -o json | jq -r '
.items[]
| select(
.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)
| .metadata.name' \
| sort -u
For a single ClusterRole, inspect details:
kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml
- Edit an existing ClusterRole to remove wildcards
For each listed ClusterRole, replace * in verbs, resources, and apiGroups with explicit entries based on actual needs.
Interactive edit:
kubectl edit clusterrole <CLUSTERROLE_NAME>
Example change (illustrative only; adjust to your requirements):
# BEFORE
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
# AFTER
rules:
- apiGroups: [""] # core API group
resources:
- pods
- services
verbs:
- get
- list
- watch
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
Save and exit the editor to apply.
- Apply a declarative manifest (preferred for GitOps/IaC-managed roles)
If your ClusterRole is managed via manifests, update the manifest and re-apply it instead of using kubectl edit.
Example manifest snippet:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: <CLUSTERROLE_NAME>
rules:
- apiGroups: [""]
resources:
- pods
- configmaps
verbs:
- get
- list
- watch
Apply:
kubectl apply -f <path-to-updated-clusterrole-manifest>.yaml
Repeat for each ClusterRole using wildcards.
- Verification
Re-run the wildcard check to confirm there are no remaining wildcards in Roles or ClusterRoles:
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
# Automation for CISEKS 4.1.3 – Minimize wildcard use in ClusterRoles and Roles
# Run on: any machine with kubectl access and jq installed.
# WARNING: This script CANNOT safely auto-resolve wildcards without human input.
# It will:
# - Detect Roles/ClusterRoles using wildcards
# - Export them to YAML files for review
# - (Optionally) apply edited manifests
# - Re-run the audit to verify
# This is safe to re-run; it only overwrites the export directory and reapplies your edited YAML.
set -euo pipefail
EXPORT_DIR="${EXPORT_DIR:-./rbac-wildcards-export}"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
WORK_DIR="${EXPORT_DIR}/${TIMESTAMP}"
mkdir -p "${WORK_DIR}/roles" "${WORK_DIR}/clusterroles"
echo "=== [1/4] Discovering Roles with wildcards ==="
kubectl get roles --all-namespaces -o json | jq -c '
.items[]
| select(
.rules[]? |
(.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)
| {ns: .metadata.namespace, name: .metadata.name}
' | while read -r item; do
ns=$(echo "${item}" | jq -r '.ns')
name=$(echo "${item}" | jq -r '.name')
echo "Exporting Role ${ns}/${name}"
kubectl get role "${name}" -n "${ns}" -o yaml > "${WORK_DIR}/roles/${ns}__${name}.yaml"
done
echo "=== [2/4] Discovering ClusterRoles with wildcards ==="
kubectl get clusterroles -o json | jq -c '
.items[]
| select(
.rules[]? |
(.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*")
)
| {name: .metadata.name}
' | while read -r item; do
name=$(echo "${item}" | jq -r '.name')
echo "Exporting ClusterRole ${name}"
kubectl get clusterrole "${name}" -o yaml > "${WORK_DIR}/clusterroles/${name}.yaml"
done
echo
echo "=== Manual remediation required ==="
echo "Directory with exported RBAC objects: ${WORK_DIR}"
echo "For each YAML file:"
echo " - Edit .rules[*].verbs, .resources, and .apiGroups"
echo " - Replace any \"*\" with the minimal explicit values needed."
echo
echo "Example (conceptual only):"
echo " verbs: [\"*\"] -> verbs: [\"get\", \"list\"]"
echo " resources: [\"*\"] -> resources: [\"pods\", \"pods/log\"]"
echo " apiGroups: [\"*\"] -> apiGroups: [\"\", \"apps\"]"
echo
echo "Do NOT change the metadata.name or metadata.namespace fields."
read -rp "Have you finished editing YAML files under ${WORK_DIR} and want to apply them now? (y/N): " APPLY_ANSWER
APPLY_ANSWER=${APPLY_ANSWER:-n}
if [[ "${APPLY_ANSWER}" =~ ^[Yy]$ ]]; then
echo "=== [3/4] Applying edited Roles ==="
if compgen -G "${WORK_DIR}/roles/*.yaml" > /dev/null; then
for f in "${WORK_DIR}/roles/"*.yaml; do
echo "Applying Role from ${f}"
kubectl apply -f "${f}"
done
else
echo "No Role YAML files found to apply."
fi
echo "=== [3/4] Applying edited ClusterRoles ==="
if compgen -G "${WORK_DIR}/clusterroles/*.yaml" > /dev/null; then
for f in "${WORK_DIR}/clusterroles/"*.yaml; do
echo "Applying ClusterRole from ${f}"
kubectl apply -f "${f}"
done
else
echo "No ClusterRole YAML files found to apply."
fi
else
echo "Skipping kubectl apply. You can manually run:"
echo " kubectl apply -f ${WORK_DIR}/roles/"
echo " kubectl apply -f ${WORK_DIR}/clusterroles/"
fi
echo
echo "=== [4/4] Verification (re-running wildcard audit) ==="
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 "Result: wildcards_present"
echo " Remaining Roles with wildcards : ${wildcards}"
echo " Remaining ClusterRoles with wildcards: ${wildcards_clusterroles}"
echo "Inspect remaining offenders with:"
echo " kubectl get roles --all-namespaces -o json | jq -r '.items[] | select(.rules[]? | (.verbs[]? == \"*\" or .resources[]? == \"*\" or .apiGroups[]? == \"*\")) | \"Role \\(.metadata.namespace)/\\(.metadata.name)\"'"
echo " kubectl get clusterroles -o json | jq -r '.items[] | select(.rules[]? | (.verbs[]? == \"*\" or .resources[]? == \"*\" or .apiGroups[]? == \"*\")) | \"ClusterRole \\(.metadata.name)\"'"
exit 1
else
echo "Result: no RBAC wildcards detected in Roles or ClusterRoles."
fi