diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b9fb75c96..224a72ebe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Note +- Added match conditions support in kyverno config map. - Deprecated flag `--imageSignatureRepository`. Will be removed in 1.12. Use per rule configuration `verifyImages.Repository` instead. - Added `--aggregateReports` flag for reports controller to enable/disable aggregated reports (default value is `true`). - Added `--policyReports` flag for reports controller to enable/disable policy reports (default value is `true`). diff --git a/charts/kyverno/Chart.yaml b/charts/kyverno/Chart.yaml index aaf1360bef..6ce3b4b477 100644 --- a/charts/kyverno/Chart.yaml +++ b/charts/kyverno/Chart.yaml @@ -64,3 +64,5 @@ annotations: description: Add helper to handle the labels for cleanup jobs, add component label - kind: added description: allow podSecurityContext and securityContext for webhooksCleanup + - kind: added + description: match conditions support in webhooks diff --git a/charts/kyverno/README.md b/charts/kyverno/README.md index 2d94f4c6ea..0f9712dcf4 100644 --- a/charts/kyverno/README.md +++ b/charts/kyverno/README.md @@ -272,6 +272,7 @@ The chart values are organised per component. | config.resourceFilters | list | See [values.yaml](values.yaml) | Resource types to be skipped by the Kyverno policy engine. Make sure to surround each entry in quotes so that it doesn't get parsed as a nested YAML list. These are joined together without spaces, run through `tpl`, and the result is set in the config map. | | config.webhooks | list | `[]` | Defines the `namespaceSelector` in the webhook configurations. Note that it takes a list of `namespaceSelector` and/or `objectSelector` in the JSON format, and only the first element will be forwarded to the webhook configurations. The Kyverno namespace is excluded if `excludeKyvernoNamespace` is `true` (default) | | config.webhookAnnotations | object | `{}` | Defines annotations to set on webhook configurations. | +| config.matchConditions | list | `[]` | Defines match conditions to set on webhook configurations (requires Kubernetes 1.27+). | | config.excludeKyvernoNamespace | bool | `true` | Exclude Kyverno namespace Determines if default Kyverno namespace exclusion is enabled for webhooks and resourceFilters | | config.resourceFiltersExcludeNamespaces | list | `[]` | resourceFilter namespace exclude Namespaces to exclude from the default resourceFilters | diff --git a/charts/kyverno/templates/NOTES.txt b/charts/kyverno/templates/NOTES.txt index 5cb16e1507..c44e55fc51 100644 --- a/charts/kyverno/templates/NOTES.txt +++ b/charts/kyverno/templates/NOTES.txt @@ -32,3 +32,7 @@ The following components have been installed in your cluster: {{- if semverCompare "<1.21.0" .Capabilities.KubeVersion.Version }} ⚠️ WARNING: The minimal Kubernetes version officially supported by Kyverno is 1.21. Earlier versions are untested and Kyverno is not guaranteed to work with Kubernetes {{ .Capabilities.KubeVersion.Version }}. {{- end }} + +{{- with .Values.config.matchConditions }} +⚠️ WARNING: Match conditions require a Kubernetes 1.27+ cluster with `AdmissionWebhookMatchConditions` feature gate enabled. +{{- end }} diff --git a/charts/kyverno/templates/config/configmap.yaml b/charts/kyverno/templates/config/configmap.yaml index 2304c12663..7a0370619a 100644 --- a/charts/kyverno/templates/config/configmap.yaml +++ b/charts/kyverno/templates/config/configmap.yaml @@ -42,4 +42,7 @@ data: {{- with .Values.config.webhookAnnotations }} webhookAnnotations: {{ toJson . | quote }} {{- end }} + {{- with .Values.config.matchConditions }} + matchConditions: {{ toJson . | quote }} + {{- end }} {{- end -}} diff --git a/charts/kyverno/values.yaml b/charts/kyverno/values.yaml index a196b8e4e5..bf8c37a150 100644 --- a/charts/kyverno/values.yaml +++ b/charts/kyverno/values.yaml @@ -210,6 +210,9 @@ config: # Example to disable admission enforcer on AKS: # 'admissions.enforcer/disabled': 'true' + # -- Defines match conditions to set on webhook configurations (requires Kubernetes 1.27+). + matchConditions: [] + # -- Exclude Kyverno namespace # Determines if default Kyverno namespace exclusion is enabled for webhooks and resourceFilters excludeKyvernoNamespace: true diff --git a/pkg/config/config.go b/pkg/config/config.go index 74aaace174..3838e0b19a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -9,6 +9,7 @@ import ( valid "github.com/asaskevich/govalidator" osutils "github.com/kyverno/kyverno/pkg/utils/os" "github.com/kyverno/kyverno/pkg/utils/wildcard" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -87,6 +88,7 @@ const ( generateSuccessEvents = "generateSuccessEvents" webhooks = "webhooks" webhookAnnotations = "webhookAnnotations" + matchConditions = "matchConditions" ) var ( @@ -160,6 +162,8 @@ type Configuration interface { GetWebhooks() []WebhookConfig // GetWebhookAnnotations returns annotations to set on webhook configs GetWebhookAnnotations() map[string]string + // GetMatchConditions returns match conditions to set on webhook configs + GetMatchConditions() []admissionregistrationv1.MatchCondition // Load loads configuration from a configmap Load(*corev1.ConfigMap) // OnChanged adds a callback to be invoked when the configuration is reloaded @@ -177,6 +181,7 @@ type configuration struct { generateSuccessEvents bool webhooks []WebhookConfig webhookAnnotations map[string]string + matchConditions []admissionregistrationv1.MatchCondition mux sync.RWMutex callbacks []func() } @@ -295,6 +300,12 @@ func (cd *configuration) GetWebhookAnnotations() map[string]string { return cd.webhookAnnotations } +func (cd *configuration) GetMatchConditions() []admissionregistrationv1.MatchCondition { + cd.mux.RLock() + defer cd.mux.RUnlock() + return cd.matchConditions +} + func (cd *configuration) Load(cm *corev1.ConfigMap) { if cm != nil { cd.load(cm) @@ -321,6 +332,7 @@ func (cd *configuration) load(cm *corev1.ConfigMap) { cd.generateSuccessEvents = false cd.webhooks = nil cd.webhookAnnotations = nil + cd.matchConditions = nil // load filters cd.filters = parseKinds(data[resourceFilters]) logger.Info("filters configured", "filters", cd.filters) @@ -425,6 +437,20 @@ func (cd *configuration) load(cm *corev1.ConfigMap) { logger.Info("webhookAnnotations configured") } } + // load match conditions + matchConditions, ok := data[matchConditions] + if !ok { + logger.Info("matchConditions not set") + } else { + logger := logger.WithValues("matchConditions", matchConditions) + matchConditions, err := parseMatchConditions(matchConditions) + if err != nil { + logger.Error(err, "failed to parse match conditions") + } else { + cd.matchConditions = matchConditions + logger.Info("matchConditions configured") + } + } } func (cd *configuration) unload() { diff --git a/pkg/config/types.go b/pkg/config/types.go index fe9f81f445..b57d811c1c 100644 --- a/pkg/config/types.go +++ b/pkg/config/types.go @@ -6,6 +6,7 @@ import ( "strings" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -52,6 +53,14 @@ func parseWebhookAnnotations(in string) (map[string]string, error) { return out, nil } +func parseMatchConditions(in string) ([]admissionregistrationv1.MatchCondition, error) { + var out []admissionregistrationv1.MatchCondition + if err := json.Unmarshal([]byte(in), &out); err != nil { + return nil, err + } + return out, nil +} + type namespacesConfig struct { IncludeNamespaces []string `json:"include,omitempty"` ExcludeNamespaces []string `json:"exclude,omitempty"` diff --git a/pkg/controllers/generic/webhook/controller.go b/pkg/controllers/generic/webhook/controller.go index 3f49ec1a57..f95bb53eb6 100644 --- a/pkg/controllers/generic/webhook/controller.go +++ b/pkg/controllers/generic/webhook/controller.go @@ -178,6 +178,7 @@ func (c *controller) build(cfg config.Configuration, caBundle []byte) (*admissio SideEffects: c.sideEffects, AdmissionReviewVersions: []string{"v1"}, ObjectSelector: c.labelSelector, + MatchConditions: cfg.GetMatchConditions(), }}, }, nil diff --git a/pkg/controllers/webhook/controller.go b/pkg/controllers/webhook/controller.go index 38b51551f4..614b1fc37b 100644 --- a/pkg/controllers/webhook/controller.go +++ b/pkg/controllers/webhook/controller.go @@ -659,6 +659,7 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Conte ObjectSelector: webhookCfg.ObjectSelector, TimeoutSeconds: &timeout, ReinvocationPolicy: &ifNeeded, + MatchConditions: cfg.GetMatchConditions(), }, ) } @@ -677,6 +678,7 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Conte ObjectSelector: webhookCfg.ObjectSelector, TimeoutSeconds: &timeout, ReinvocationPolicy: &ifNeeded, + MatchConditions: cfg.GetMatchConditions(), }, ) } @@ -786,6 +788,7 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Con NamespaceSelector: webhookCfg.NamespaceSelector, ObjectSelector: webhookCfg.ObjectSelector, TimeoutSeconds: &timeout, + MatchConditions: cfg.GetMatchConditions(), }, ) } @@ -803,6 +806,7 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Con NamespaceSelector: webhookCfg.NamespaceSelector, ObjectSelector: webhookCfg.ObjectSelector, TimeoutSeconds: &timeout, + MatchConditions: cfg.GetMatchConditions(), }, ) }