Minimize Access To Webhook Configuration Objects
More Info:
Access to validating or mutating webhook configuration objects can be abused to intercept or alter admission decisions. Restrict this access to trusted administrators only.
Risk Level
High
Address
Security
Compliance Standards
- CIS EKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List all cluster roles and role bindings that reference webhook configuration resources
- Run on: any machine with kubectl access
kubectl get clusterrole -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5kubectl get role -A -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5kubectl get clusterrolebinding -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5kubectl get rolebinding -A -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5Focus on rules where
.resourcesincludesvalidatingwebhookconfigurationsormutatingwebhookconfigurations. -
Identify subjects (users, groups, service accounts) with access to these resources
- Run on: any machine with kubectl access
For each ClusterRole or Role found in step 1, get the full object and its bindings:
kubectl get clusterrole <CLUSTERROLE_NAME> -o yamlkubectl get clusterrolebinding -o yaml | grep -n "<CLUSTERROLE_NAME>" -A10 -B2kubectl get role -A | grep "<ROLE_NAME>"kubectl get role -n <NAMESPACE> <ROLE_NAME> -o yamlkubectl get rolebinding -n <NAMESPACE> -o yaml | grep -n "<ROLE_NAME>" -A10 -B2List all
.subjectsand categorize them as trusted administrators or non-admin workloads. - Run on: any machine with kubectl access
-
Decide which access is justified and which should be removed or constrained
- Review each subject that has
get,list,watch,create,update,patch, ordeleteon the webhook configuration resources. - Treat write access (
create/update/patch/delete) as highly sensitive; normally, only a very small admin group should retain this. - For non-admin service accounts or broad groups (e.g., system:authenticated), plan to remove webhook configuration permissions or replace them with narrower roles that do not include these resources.
- Review each subject that has
-
Remove or restrict webhook configuration access from inappropriate roles
- Run on: any machine with kubectl access
Edit each role/clusterrole that should no longer grant access:
kubectl edit clusterrole <CLUSTERROLE_NAME>kubectl edit role -n <NAMESPACE> <ROLE_NAME>In the opened editor, either:
- Remove
validatingwebhookconfigurationsandmutatingwebhookconfigurationsfrom theresourceslist, or - Delete the entire rule block that references them, if it is not needed.
Save and exit to apply changes.
- Run on: any machine with kubectl access
-
If necessary, rebind subjects to safer roles
- Run on: any machine with kubectl access
For workloads that legitimately needed some permissions but not webhook configuration access, bind them to an alternative role without those resources:
kubectl create rolebinding <NEW_BINDING_NAME> \--clusterrole=<SAFER_CLUSTERROLE_NAME> \--serviceaccount=<NAMESPACE>:<SERVICEACCOUNT_NAME> \--namespace <NAMESPACE>Then remove the old binding:
kubectl delete clusterrolebinding <OLD_BINDING_NAME># orkubectl delete rolebinding -n <NAMESPACE> <OLD_BINDING_NAME> - Run on: any machine with kubectl access
-
Verify that only trusted administrators retain webhook configuration access
- Run on: any machine with kubectl access
Re-run a focused RBAC review:
kubectl get clusterrole -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5kubectl get role -A -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5For each remaining role, confirm via its bindings that only trusted admin users/groups/service accounts are subjects. If any non-admin subjects remain, repeat steps 4–5.
- Run on: any machine with kubectl access
Using kubectl
# 1) List all ClusterRoles that can access admission webhook configurations
# Run on: any machine with kubectl access
kubectl get clusterroles -o json \
| jq -r '
.items[]
| select(
.rules[]
| select(
(.apiGroups[]? == "admissionregistration.k8s.io")
and
(.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
)
)
| .metadata.name
' | sort -u
Problem indication: Any ClusterRole in this list is capable of accessing/altering webhook configurations. Non-admin or generic roles here (e.g., roles used by apps, CI/CD, or controllers) are suspect.
# 2) Show detailed rules for those ClusterRoles
# Replace <clusterrole-name> with a name from the previous command
kubectl get clusterrole <clusterrole-name> -o yaml
Problem indication:
Rules that include:
apiGroups: ["admissionregistration.k8s.io"]resources: ["validatingwebhookconfigurations"]and/or["mutatingwebhookconfigurations"]verbssuch ascreate,update,patch,delete, orlist/get/watchbeyond what trusted admins require.
# 3) Identify who is bound to those ClusterRoles (ClusterRoleBindings)
# Run once to see all relevant ClusterRoleBindings
kubectl get clusterrolebindings -o json \
| jq -r '
.items[]
| select(.roleRef.kind == "ClusterRole")
| select(
.roleRef.name as $r
| $r | IN(
# Paste the ClusterRole names from step 1 inside this IN() list, e.g.:
"cluster-admin","admission-webhook-admin"
)
)
| .metadata.name + " -> " + .roleRef.name + " :: " +
( .subjects // [] | map(.kind + "/" + .name) | join(",") )
' | sort
Problem indication:
Bindings where the subjects include:
- Broad groups (e.g.,
system:authenticated,system:serviceaccounts,system:serviceaccounts:<namespace>) - Application service accounts or CI/CD identities
- External user groups not intended as cluster administrators
# 4) Inspect specific ClusterRoleBindings in detail
kubectl get clusterrolebinding <binding-name> -o yaml
Problem indication:
subjects that are not clearly limited to a small, trusted admin group or break-glass admin accounts.
# 5) Also check namespace-scoped Roles (less common but possible)
kubectl get roles --all-namespaces -o json \
| jq -r '
.items[]
| select(
.rules[]
| select(
(.apiGroups[]? == "admissionregistration.k8s.io")
and
(.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
)
)
| .metadata.namespace + "/" + .metadata.name
' | sort -u
# 6) For any Role found in step 5, see who is bound to it
kubectl get rolebinding -A -o json \
| jq -r '
.items[]
| .metadata.namespace as $ns
| .roleRef.kind as $k
| .roleRef.name as $r
| select($k == "Role")
| .metadata.name as $rb
| .subjects // [] as $subs
| "\($ns)/\($rb) -> \($k)/\($r) :: " + ($subs | map(.kind + "/" + .name) | join(","))
' | sort
Problem indication:
Any Role/RoleBinding that grants access to webhook configuration resources to non-admin service accounts or broad groups.
# 7) Quick verification after manual adjustments (re-run scoping checks)
# ClusterRoles
kubectl get clusterroles -o json \
| jq -r '
.items[]
| select(
.rules[]
| select(
(.apiGroups[]? == "admissionregistration.k8s.io")
and
(.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
)
)
| .metadata.name
' | sort -u
Desired state:
Only a very small, clearly administrative set of ClusterRoles (and corresponding bindings) remain with access to validatingwebhookconfigurations and mutatingwebhookconfigurations.
Automation
#!/usr/bin/env bash
# Report RBAC subjects with access to validating/mutating webhook configurations.
# Run on: any machine with kubectl access and current context pointing to target cluster.
set -euo pipefail
echo "=== Discovering verbs that grant access to webhook configuration resources ==="
# Collect all ClusterRoles and Roles that reference validating/mutating webhook configs
kubectl get clusterroles,roles -A -o json \
| jq -r '
.items[]
| {
kind: .kind,
namespace: (.metadata.namespace // ""),
name: .metadata.name,
rules: (.rules // [])
}
| . as $obj
| $obj.rules[]
| select(
(.resources // [])
| map(
(split("/") | .[0]) # strip possible apiGroup/resource forms
)
| flatten
| any(. == "validatingwebhookconfigurations" or . == "mutatingwebhookconfigurations")
)
| {
kind: $obj.kind,
namespace: $obj.namespace,
name: $obj.name,
apiGroups: (.apiGroups // []),
resources: (.resources // []),
verbs: (.verbs // [])
}
| @json' | sort | uniq > /tmp/webhook-access-roles.json
if [[ ! -s /tmp/webhook-access-roles.json ]]; then
echo "No Roles or ClusterRoles directly reference validating or mutating webhook configurations."
exit 0
fi
echo
echo "=== Roles/ClusterRoles with webhook configuration access ==="
cat /tmp/webhook-access-roles.json | jq -r '
. as $r
| "\($r.kind)\t\($r.namespace)\t\($r.name)\tverbs=\($r.verbs|join(","))\tresources=\($r.resources|join(","))"
' | column -t
echo
echo "=== Finding RoleBindings/ClusterRoleBindings that grant these roles ==="
# Build a list of role references of interest
role_filters=$(cat /tmp/webhook-access-roles.json | jq -r '
. as $r
| if $r.kind == "ClusterRole" then
"(.kind==\"ClusterRoleBinding\" and .roleRef.kind==\"ClusterRole\" and .roleRef.name==\"\($r.name)\")"
else
"(.kind==\"RoleBinding\" and .roleRef.kind==\"Role\" and .roleRef.name==\"\($r.name)\" and .metadata.namespace==\"\($r.namespace)\")"
end
' | sort -u | paste -sd " or " -)
# If there are no role filters, exit
if [[ -z "${role_filters}" ]]; then
echo "No bindings found for the identified Roles/ClusterRoles."
exit 0
fi
kubectl get clusterrolebindings,rolebindings -A -o json \
| jq -r "
.items[]
| select(${role_filters})
| {
kind: .kind,
namespace: (.metadata.namespace // \"\"),
name: .metadata.name,
roleKind: .roleRef.kind,
roleName: .roleRef.name,
subjects: (.subjects // [])
}
| @json" > /tmp/webhook-access-bindings.json
if [[ ! -s /tmp/webhook-access-bindings.json ]]; then
echo "No RoleBindings or ClusterRoleBindings grant the webhook-accessing roles."
exit 0
fi
echo
echo "=== Bindings granting access to webhook configuration objects ==="
cat /tmp/webhook-access-bindings.json | jq -r '
. as $b
| if ($b.subjects | length) == 0 then
"\($b.kind)\t\($b.namespace)\t\($b.name)\trole=\($b.roleKind)/\($b.roleName)\tsubject=<none>"
else
$b.subjects[]
| "\($b.kind)\t\($b.namespace)\t\($b.name)\trole=\($b.roleKind)/\($b.roleName)\tsubject=\(.kind):\(.namespace // "-"):\(.name)"
end
' | column -t
echo
echo "=== Interpretation guidance ==="
cat <<'EOF'
Any subject listed above (User, Group, or ServiceAccount) has RBAC-granted access
to validatingwebhookconfigurations or mutatingwebhookconfigurations.
Potential problems to investigate:
- Non-admin users or broad groups (e.g., "system:authenticated", "system:serviceaccounts")
- Application-specific service accounts that should not control cluster-wide admission
- Wildcard verbs (*) or powerful verbs (create, update, patch, delete) on these resources
- ClusterRoleBindings that expose admin-like ClusterRoles to many subjects
Review each binding and decide whether the subject truly needs this level of access.
If not, update or remove the corresponding Role/ClusterRole or Binding.
EOF
Problematic output indicators:
- Roles/ClusterRoles with
verbsincludingcreate,update,patch,delete, or*onvalidatingwebhookconfigurationsormutatingwebhookconfigurations. - Bindings where:
subjectsinclude broad groups likesystem:authenticated,system:unauthenticated, or generic team groups.- Application/service-specific
ServiceAccountsubjects appear, rather than trusted admin identities. These require manual review and possible RBAC tightening; they cannot be safely auto-fixed.