Webhook Selector Grouping#
Overview#
Kyverno's webhook controller groups basic policies into distinct ValidatingWebhookConfiguration (or MutatingWebhookConfiguration) entries by their resolved (namespaceSelector, objectSelector) pair. Without this grouping, policies with differing selectors would share one webhook entry and overwrite each other's filters β causing policies to silently stop firing for their intended namespaces or objects.
This mechanism was introduced in PR #16615 to fix bugs reported in #15344 and #16131.
Basic vs. Fine-Grained Policies#
buildWebhookRules first classifies every policy as fine-grained or basic :
| Classification | Condition |
|---|---|
| Fine-grained | Has matchConditions (CEL expressions), matchConstraints.matchPolicy: Exact, or a custom timeoutSeconds |
| Basic | Everything else |
Fine-grained policies each get their own per-policy webhook entry with a unique URL path. Selector grouping only applies to basic policies.
How Grouping Works#
groupBySelectors#
groupBySelectors iterates all basic policies and bins them into a map[string]*selectorGroup keyed by a stable SHA-256 hash of the resolved (namespaceSelector, objectSelector) pair :
- Resolve each policy's selectors β calls
resolveNamespaceSelectorandmergeLabelSelectorsto compute the final selectors the webhook would carry. - Hash the pair via
selectorKey, which SHA-256-hashes the JSON-serialized[namespaceSelector, objectSelector]and returns the full hex digest. The full digest is used (not truncated) to prevent hash collisions . - Group into a
selectorGroupβ policies sharing the same key land in the same bucket; policies with different selectors go into a separate bucket. - Assign a disambiguating suffix β when more than one group exists, each group's webhook name gets a
-{first8chars}suffix derived from its key hash . When all policies share the same selectors (the common case), no suffix is added and the webhook name is unchanged.
selectorGroup struct#
type selectorGroup struct {
namespaceSelector *metav1.LabelSelector
objectSelector *metav1.LabelSelector
policies []engineapi.GenericPolicy
suffix string // empty when there is only one group
}
Each group gets two webhooks β one for failurePolicy: Fail and one for failurePolicy: Ignore β and a shared URL path built from the sorted names of all policies in the group .
resolveNamespaceSelector#
resolveNamespaceSelector determines the effective namespaceSelector for a policy:
- Namespaced policy (non-empty
.metadata.namespace): The selector is pinned to the policy's own namespace viakubernetes.io/metadata.name In [<ns>], merged with the global config selector. The policy'smatchConstraints.namespaceSelectoris ignored for the webhook . - Cluster-scoped policy: Uses
matchConstraints.namespaceSelectormerged with the global config selector .
Why This Matters#
Before this fix, multiple policies with different namespaceSelector or objectSelector values were collapsed into a single webhook entry. Whichever policy was processed last would overwrite the webhook's selectors, silently breaking all other policies in the group .
Concrete failure mode (pre-fix): Two MutatingPolicy objects with namespaceSelector: {matchLabels: {owner: team-a}} and namespaceSelector: {matchLabels: {owner: team-b}} both shared one webhook; the last-applied policy's selector replaced the other's, so the first policy never fired .
The same race condition affected objectSelector: a policy with objectSelector: {matchLabels: {app: pv-migrate}} could intermittently apply its selector to a shared webhook that included a policy with no objectSelector, causing that other policy to only evaluate matching pods .
Key Source Files#
| File | Purpose |
|---|---|
pkg/controllers/webhook/validating.go | buildWebhookRules, groupBySelectors, selectorKey, resolveNamespaceSelector, mergeLabelSelectors |
Despite the filename, buildWebhookRules in validating.go is also called for mutating, generating, and image-validating policies via the webhook controller in pkg/controllers/webhook/controller.go. The groupBySelectors logic is shared across all policy types.