Webhook Generation#
Kyverno's webhook controller builds ValidatingWebhookConfiguration entries dynamically from the set of active policies. The core entry point is buildWebhookRules in pkg/controllers/webhook/validating.go, which classifies each policy as either basic or fine-grained and produces the corresponding admissionregistrationv1.ValidatingWebhook slice.
Basic vs. Fine-Grained Classification#
A policy is classified as fine-grained if any of the following is true :
| Condition | Webhook behavior |
|---|---|
Has matchConditions (validated CEL expressions) | Gets its own webhook entry with per-policy ClientConfig path |
matchConstraints.matchPolicy: Exact | Propagated directly to webhook.MatchPolicy |
Has a custom timeoutSeconds | Propagated directly to webhook.TimeoutSeconds |
Fine-grained webhooks are assigned a per-policy URL path via path.Join(queryPath, p.GetName()) (or β¦/p.GetNamespace()/p.GetName() for namespaced policies via generateName). They are sorted deterministically by namespace+name before being emitted .
Policies that do not qualify as fine-grained are basic. Basic policies are grouped by their resolved (namespaceSelector, objectSelector) pair via groupBySelectors: policies resolving to different selectors get separate webhooks so they cannot overwrite each other's filters . The group key is a SHA-256 hash of the serialized selectors, preventing hash collisions between distinct selector pairs . All policies in a group share a single webhook entry with a URL path built from the sorted list of all policy names in the group.
Each category produces two webhooks per group/policy: one with failurePolicy: Fail and one with failurePolicy: Ignore, routing only the policies with the matching failure policy into each .
Namespace Selector Resolution#
resolveNamespaceSelector handles the selector for each policy's webhook:
- Namespaced policies (
NamespacedImageValidatingPolicy,NamespacedValidatingPolicy, etc.) always have their webhooknamespaceSelectorpinned tokubernetes.io/metadata.name = <policy-namespace>, regardless of anymatchConstraints.namespaceSelectorin the spec. This limits the webhook's reach to the policy's own namespace. - Cluster-scoped policies keep their configured
matchConstraints.namespaceSelector, merged with the global webhookNamespaceSelectorfrom config.
The merge is done by mergeLabelSelectors, which normalizes empty MatchLabels and MatchExpressions to nil after merging (to avoid reconciliation loops on platforms that normalize webhook configurations, such as GKE Autopilot).
Autogeneration of Pod Controller Rules#
When building webhook rules, both basic and fine-grained paths invoke autogen to extend a Pod-targeting policy to also cover Pod controller resources (Deployments, Jobs, CronJobs, etc.).
For ImageValidatingPolicy and NamespacedImageValidatingPolicy, the autogen entry point is ivpolautogen.Autogen in pkg/cel/policies/ivpol/autogen/autogen.go. It accepts the ImageValidatingPolicyLike interface β implemented by both ImageValidatingPolicy (cluster-scoped) and NamespacedImageValidatingPolicy β and returns a map of controller configs to autogenerated specs . If spec.autogenConfiguration.podControllers.controllers is set, only those controllers are generated; otherwise all supported controllers are included .
The fine-grained path in buildWebhookRules was previously calling a concrete type assertion p.(*policiesv1beta1.ImageValidatingPolicy), which meant NamespacedImageValidatingPolicy policies with match conditions, Exact matching, or custom timeouts only received the base Pod rule β pod-controller resources were never added. PR #16840 fixed this by switching to the interface accessor policy.AsImageValidatingPolicyLike(), so both types follow the same autogen path .
For the basic path, both types are handled explicitly with separate type assertions for *ImageValidatingPolicy and *NamespacedImageValidatingPolicy, both calling ivpolautogen.Autogen .
GenericPolicy Abstraction#
The GenericPolicy interface in pkg/engine/api/policy.go provides a uniform API over all Kyverno policy types. Relevant methods for webhook generation:
AsImageValidatingPolicyLike()β returns theImageValidatingPolicyLikeinterface, non-nil for bothImageValidatingPolicyandNamespacedImageValidatingPolicy.AsImageValidatingPolicy()/AsNamespacedImageValidatingPolicy()β concrete type accessors.IsNamespaced()β returnstrueforNamespacedImageValidatingPolicy,NamespacedValidatingPolicy,NamespacedMutatingPolicy, andNamespacedGeneratingPolicy.
Constructor helpers β NewImageValidatingPolicy, NewNamespacedImageValidatingPolicy, NewImageValidatingPolicyFromLike β wrap concrete types into the GenericPolicy interface for use in webhook building and engine dispatch.
Key Source Files#
| File | Role |
|---|---|
pkg/controllers/webhook/validating.go | buildWebhookRules β fine-grained/basic classification, autogen, selector resolution |
pkg/engine/api/policy.go | GenericPolicy interface and *Like accessor methods |
pkg/cel/policies/ivpol/autogen/autogen.go | Autogen(ImageValidatingPolicyLike) β pod controller rule generation for ivpol/nivpol |