diff --git a/charts/kyverno/Chart.yaml b/charts/kyverno/Chart.yaml index abdd089bc6..31d1b6c6d1 100644 --- a/charts/kyverno/Chart.yaml +++ b/charts/kyverno/Chart.yaml @@ -35,6 +35,8 @@ annotations: artifacthub.io/changes: | - kind: fixed description: switch to post-delete helm hook to clean up Kyverno configmap + - kind: fixed + description: modify config.webhooks to be as a single object instead of a list of webhooks. dependencies: - name: grafana version: v0.0.0 diff --git a/charts/kyverno/README.md b/charts/kyverno/README.md index e4f7a99458..88e5361cf5 100644 --- a/charts/kyverno/README.md +++ b/charts/kyverno/README.md @@ -296,7 +296,7 @@ The chart values are organised per component. | config.generateSuccessEvents | bool | `false` | Generate success events. | | 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.updateRequestThreshold | int | `1000` | Sets the threshold for the total number of UpdateRequests generated for mutateExisitng and generate policies. | -| config.webhooks | list | `[{"namespaceSelector":{"matchExpressions":[{"key":"kubernetes.io/metadata.name","operator":"NotIn","values":["kube-system"]}]}}]` | 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.webhooks | object | `{"namespaceSelector":{"matchExpressions":[{"key":"kubernetes.io/metadata.name","operator":"NotIn","values":["kube-system"]}]}}` | Defines the `namespaceSelector`/`objectSelector` in the webhook configurations. The Kyverno namespace is excluded if `excludeKyvernoNamespace` is `true` (default) | | config.webhookAnnotations | object | `{"admissions.enforcer/disabled":"true"}` | Defines annotations to set on webhook configurations. | | config.webhookLabels | object | `{}` | Defines labels to set on webhook configurations. | | config.matchConditions | list | `[]` | Defines match conditions to set on webhook configurations (requires Kubernetes 1.27+). | diff --git a/charts/kyverno/templates/config/_helpers.tpl b/charts/kyverno/templates/config/_helpers.tpl index 097aa451bc..2d1d4262b1 100644 --- a/charts/kyverno/templates/config/_helpers.tpl +++ b/charts/kyverno/templates/config/_helpers.tpl @@ -56,13 +56,11 @@ {{- define "kyverno.config.webhooks" -}} {{- $excludeDefault := dict "key" "kubernetes.io/metadata.name" "operator" "NotIn" "values" (list (include "kyverno.namespace" .)) }} -{{- $newWebhook := list }} -{{- range $webhook := .Values.config.webhooks }} - {{- $namespaceSelector := default dict $webhook.namespaceSelector }} - {{- $matchExpressions := default list $namespaceSelector.matchExpressions }} - {{- $newNamespaceSelector := dict "matchLabels" $namespaceSelector.matchLabels "matchExpressions" (append $matchExpressions $excludeDefault) }} - {{- $newWebhook = append $newWebhook (merge (omit $webhook "namespaceSelector") (dict "namespaceSelector" $newNamespaceSelector)) }} -{{- end }} +{{- $webhook := .Values.config.webhooks }} +{{- $namespaceSelector := default dict $webhook.namespaceSelector }} +{{- $matchExpressions := default list $namespaceSelector.matchExpressions }} +{{- $newNamespaceSelector := dict "matchLabels" $namespaceSelector.matchLabels "matchExpressions" (append $matchExpressions $excludeDefault) }} +{{- $newWebhook := merge (omit $webhook "namespaceSelector") (dict "namespaceSelector" $newNamespaceSelector) }} {{- $newWebhook | toJson }} {{- end -}} diff --git a/charts/kyverno/values.yaml b/charts/kyverno/values.yaml index 4d918701f8..b2b3d74725 100644 --- a/charts/kyverno/values.yaml +++ b/charts/kyverno/values.yaml @@ -320,18 +320,16 @@ config: # -- Sets the threshold for the total number of UpdateRequests generated for mutateExisitng and generate policies. updateRequestThreshold: 1000 - # -- 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. + # -- Defines the `namespaceSelector`/`objectSelector` in the webhook configurations. # The Kyverno namespace is excluded if `excludeKyvernoNamespace` is `true` (default) webhooks: # Exclude namespaces - - namespaceSelector: - matchExpressions: - - key: kubernetes.io/metadata.name - operator: NotIn - values: - - kube-system + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: NotIn + values: + - kube-system # Exclude objects # - objectSelector: # matchExpressions: diff --git a/config/install-latest-testing.yaml b/config/install-latest-testing.yaml index eabf7c0b45..1bd3e82d26 100644 --- a/config/install-latest-testing.yaml +++ b/config/install-latest-testing.yaml @@ -175,7 +175,7 @@ data: [Secret,kyverno,kyverno-svc.kyverno.svc.*] [Secret,kyverno,kyverno-cleanup-controller.kyverno.svc.*] updateRequestThreshold: "1000" - webhooks: "[{\"namespaceSelector\":{\"matchExpressions\":[{\"key\":\"kubernetes.io/metadata.name\",\"operator\":\"NotIn\",\"values\":[\"kube-system\"]},{\"key\":\"kubernetes.io/metadata.name\",\"operator\":\"NotIn\",\"values\":[\"kyverno\"]}],\"matchLabels\":null}}]" + webhooks: "{\"namespaceSelector\":{\"matchExpressions\":[{\"key\":\"kubernetes.io/metadata.name\",\"operator\":\"NotIn\",\"values\":[\"kube-system\"]},{\"key\":\"kubernetes.io/metadata.name\",\"operator\":\"NotIn\",\"values\":[\"kyverno\"]}],\"matchLabels\":null}}" webhookAnnotations: "{\"admissions.enforcer/disabled\":\"true\"}" --- apiVersion: v1 diff --git a/pkg/config/config.go b/pkg/config/config.go index 5df2258b25..aba55e979a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -174,8 +174,8 @@ type Configuration interface { ToFilter(kind schema.GroupVersionKind, subresource, namespace, name string) bool // GetGenerateSuccessEvents return if should generate success events GetGenerateSuccessEvents() bool - // GetWebhooks returns the webhook configs - GetWebhooks() []WebhookConfig + // GetWebhook returns the webhook config + GetWebhook() WebhookConfig // GetWebhookAnnotations returns annotations to set on webhook configs GetWebhookAnnotations() map[string]string // GetWebhookLabels returns labels to set on webhook configs @@ -199,7 +199,7 @@ type configuration struct { inclusions match filters []filter generateSuccessEvents bool - webhooks []WebhookConfig + webhook WebhookConfig webhookAnnotations map[string]string webhookLabels map[string]string matchConditions []admissionregistrationv1.MatchCondition @@ -310,10 +310,10 @@ func (cd *configuration) GetGenerateSuccessEvents() bool { return cd.generateSuccessEvents } -func (cd *configuration) GetWebhooks() []WebhookConfig { +func (cd *configuration) GetWebhook() WebhookConfig { cd.mux.RLock() defer cd.mux.RUnlock() - return cd.webhooks + return cd.webhook } func (cd *configuration) GetWebhookAnnotations() map[string]string { @@ -364,7 +364,7 @@ func (cd *configuration) load(cm *corev1.ConfigMap) { cd.inclusions = match{} cd.filters = []filter{} cd.generateSuccessEvents = false - cd.webhooks = nil + cd.webhook = WebhookConfig{} cd.webhookAnnotations = nil cd.webhookLabels = nil cd.matchConditions = nil @@ -451,11 +451,11 @@ func (cd *configuration) load(cm *corev1.ConfigMap) { logger.Info("webhooks not set") } else { logger := logger.WithValues("webhooks", webhooks) - webhooks, err := parseWebhooks(webhooks) + webhook, err := parseWebhooks(webhooks) if err != nil { logger.Error(err, "failed to parse webhooks") } else { - cd.webhooks = webhooks + cd.webhook = *webhook logger.Info("webhooks configured") } } @@ -526,7 +526,7 @@ func (cd *configuration) unload() { cd.inclusions = match{} cd.filters = []filter{} cd.generateSuccessEvents = false - cd.webhooks = nil + cd.webhook = WebhookConfig{} cd.webhookAnnotations = nil cd.webhookLabels = nil logger.Info("configuration unloaded") diff --git a/pkg/config/types.go b/pkg/config/types.go index c290902df9..1ca4a8842c 100644 --- a/pkg/config/types.go +++ b/pkg/config/types.go @@ -17,12 +17,12 @@ type WebhookConfig struct { ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty"` } -func parseWebhooks(in string) ([]WebhookConfig, error) { - var webhookCfgs []WebhookConfig - if err := json.Unmarshal([]byte(in), &webhookCfgs); err != nil { +func parseWebhooks(in string) (*WebhookConfig, error) { + var webhookCfg WebhookConfig + if err := json.Unmarshal([]byte(in), &webhookCfg); err != nil { return nil, err } - return webhookCfgs, nil + return &webhookCfg, nil } func parseExclusions(in string) (exclusions, inclusions []string) { diff --git a/pkg/controllers/webhook/controller.go b/pkg/controllers/webhook/controller.go index 708127d07e..16739a8644 100644 --- a/pkg/controllers/webhook/controller.go +++ b/pkg/controllers/webhook/controller.go @@ -823,11 +823,7 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Conte Webhooks: []admissionregistrationv1.MutatingWebhook{}, } if c.watchdogCheck() { - webhookCfg := config.WebhookConfig{} - webhookCfgs := cfg.GetWebhooks() - if len(webhookCfgs) > 0 { - webhookCfg = webhookCfgs[0] - } + webhookCfg := cfg.GetWebhook() ignoreWebhook := newWebhook(c.defaultTimeout, ignore, cfg.GetMatchConditions()) failWebhook := newWebhook(c.defaultTimeout, fail, cfg.GetMatchConditions()) policies, err := c.getAllPolicies() @@ -964,12 +960,7 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Con Webhooks: []admissionregistrationv1.ValidatingWebhook{}, } if c.watchdogCheck() { - webhookCfg := config.WebhookConfig{} - webhookCfgs := cfg.GetWebhooks() - if len(webhookCfgs) > 0 { - webhookCfg = webhookCfgs[0] - } - + webhookCfg := cfg.GetWebhook() ignoreWebhook := newWebhook(c.defaultTimeout, ignore, cfg.GetMatchConditions()) failWebhook := newWebhook(c.defaultTimeout, fail, cfg.GetMatchConditions()) policies, err := c.getAllPolicies()