From 7170cbb0c2d8a5dfacf0131f66243b553eaf5fd9 Mon Sep 17 00:00:00 2001 From: shuting Date: Sat, 27 Jan 2024 21:00:22 +0800 Subject: [PATCH] feat:Webhook config per policy (#9483) * add spec.webhookConfigurations Signed-off-by: ShutingZhao * update crd Signed-off-by: ShutingZhao * configure webhook Signed-off-by: ShutingZhao * register webhook handler Signed-off-by: ShutingZhao * skip storing finegrained policies in cache Signed-off-by: ShutingZhao * update resource validate handler Signed-off-by: ShutingZhao * updates Signed-off-by: ShutingZhao * enable mutate resource handler for fine-grained policies Signed-off-by: ShutingZhao * fix: tests Signed-off-by: ShutingZhao --------- Signed-off-by: ShutingZhao --- api/kyverno/v1/common_types.go | 8 + api/kyverno/v1/spec_types.go | 18 ++ api/kyverno/v1/zz_generated.deepcopy.go | 27 ++ api/kyverno/v2beta1/common_types.go | 8 + api/kyverno/v2beta1/spec_types.go | 9 + api/kyverno/v2beta1/zz_generated.deepcopy.go | 26 ++ .../kyverno/charts/crds/templates/crds.yaml | 184 +++++++++++++ .../data/crds/kyverno.io_clusterpolicies.yaml | 92 +++++++ .../data/crds/kyverno.io_policies.yaml | 92 +++++++ config/crds/kyverno.io_clusterpolicies.yaml | 92 +++++++ config/crds/kyverno.io_policies.yaml | 92 +++++++ config/install-latest-testing.yaml | 184 +++++++++++++ docs/user/crd/index.html | 158 +++++++++++ .../applyconfigurations/kyverno/v1/spec.go | 9 + .../kyverno/v1/webhookconfiguration.go | 45 ++++ .../kyverno/v2beta1/spec.go | 9 + .../kyverno/v2beta1/webhookconfiguration.go | 45 ++++ pkg/client/applyconfigurations/utils.go | 4 + pkg/config/config.go | 2 + pkg/controllers/policycache/controller.go | 2 +- pkg/controllers/webhook/controller.go | 245 ++++++++++-------- pkg/controllers/webhook/utils.go | 45 +++- pkg/controllers/webhook/utils_test.go | 4 +- pkg/validation/policy/validate.go | 4 + pkg/webhooks/handlers/admission.go | 4 + pkg/webhooks/handlers/types.go | 2 + pkg/webhooks/resource/handlers.go | 85 +++++- pkg/webhooks/server.go | 2 + 28 files changed, 1371 insertions(+), 126 deletions(-) create mode 100644 pkg/client/applyconfigurations/kyverno/v1/webhookconfiguration.go create mode 100644 pkg/client/applyconfigurations/kyverno/v2beta1/webhookconfiguration.go diff --git a/api/kyverno/v1/common_types.go b/api/kyverno/v1/common_types.go index d4ec8deaa5..2dfb8bca38 100644 --- a/api/kyverno/v1/common_types.go +++ b/api/kyverno/v1/common_types.go @@ -7,6 +7,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine/variables/regex" "github.com/sigstore/k8s-manifest-sigstore/pkg/k8smanifest" admissionv1 "k8s.io/api/admission/v1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" "k8s.io/api/admissionregistration/v1alpha1" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -49,6 +50,13 @@ const ( Descending ForeachOrder = "Descending" ) +// WebhookConfiguration specifies the configuration for Kubernetes admission webhookconfiguration. +type WebhookConfiguration struct { + // MatchCondition configures admission webhook matchConditions. + // +optional + MatchConditions []admissionregistrationv1.MatchCondition `json:"matchConditions,omitempty" yaml:"matchConditions,omitempty"` +} + // AnyAllConditions consists of conditions wrapped denoting a logical criteria to be fulfilled. // AnyConditions get fulfilled when at least one of its sub-conditions passes. // AllConditions get fulfilled only when all of its sub-conditions pass. diff --git a/api/kyverno/v1/spec_types.go b/api/kyverno/v1/spec_types.go index 41bbdcf02c..d1d6f75d78 100644 --- a/api/kyverno/v1/spec_types.go +++ b/api/kyverno/v1/spec_types.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/kyverno/kyverno/pkg/toggle" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" @@ -121,6 +122,15 @@ type Spec struct { // Defaults to "false" if not specified. // +optional UseServerSideApply bool `json:"useServerSideApply,omitempty" yaml:"useServerSideApply,omitempty"` + + // WebhookConfiguration specifies the custom configuration for Kubernetes admission webhookconfiguration. + // Requires Kubernetes 1.27 or later. + // +optional + WebhookConfiguration *WebhookConfiguration `json:"webhookConfiguration,omitempty" yaml:"webhookConfiguration,omitempty"` +} + +func (s *Spec) CustomWebhookConfiguration() bool { + return s.WebhookConfiguration != nil } func (s *Spec) SetRules(rules []Rule) { @@ -257,6 +267,14 @@ func (s *Spec) GetFailurePolicy(ctx context.Context) FailurePolicyType { return *s.FailurePolicy } +// GetMatchConditions returns matchConditions in webhookConfiguration +func (s *Spec) GetMatchConditions() []admissionregistrationv1.MatchCondition { + if s.WebhookConfiguration != nil { + return s.WebhookConfiguration.MatchConditions + } + return nil +} + // GetFailurePolicy returns the failure policy to be applied func (s *Spec) GetApplyRules() ApplyRulesType { if s.ApplyRules == nil { diff --git a/api/kyverno/v1/zz_generated.deepcopy.go b/api/kyverno/v1/zz_generated.deepcopy.go index 90f8f017c6..0fee540f54 100755 --- a/api/kyverno/v1/zz_generated.deepcopy.go +++ b/api/kyverno/v1/zz_generated.deepcopy.go @@ -23,6 +23,7 @@ package v1 import ( k8smanifest "github.com/sigstore/k8s-manifest-sigstore/pkg/k8smanifest" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" v1alpha1 "k8s.io/api/admissionregistration/v1alpha1" rbacv1 "k8s.io/api/rbac/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -1408,6 +1409,11 @@ func (in *Spec) DeepCopyInto(out *Spec) { *out = new(bool) **out = **in } + if in.WebhookConfiguration != nil { + in, out := &in.WebhookConfiguration, &out.WebhookConfiguration + *out = new(WebhookConfiguration) + (*in).DeepCopyInto(*out) + } return } @@ -1632,3 +1638,24 @@ func (in *Variable) DeepCopy() *Variable { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) { + *out = *in + if in.MatchConditions != nil { + in, out := &in.MatchConditions, &out.MatchConditions + *out = make([]admissionregistrationv1.MatchCondition, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConfiguration. +func (in *WebhookConfiguration) DeepCopy() *WebhookConfiguration { + if in == nil { + return nil + } + out := new(WebhookConfiguration) + in.DeepCopyInto(out) + return out +} diff --git a/api/kyverno/v2beta1/common_types.go b/api/kyverno/v2beta1/common_types.go index 4dc822afd8..d3ae3b5037 100644 --- a/api/kyverno/v2beta1/common_types.go +++ b/api/kyverno/v2beta1/common_types.go @@ -2,10 +2,18 @@ package v2beta1 import ( kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) +// WebhookConfiguration specifies the configuration for Kubernetes admission webhookconfiguration. +type WebhookConfiguration struct { + // MatchCondition configures admission webhook matchConditions. + // +optional + MatchConditions []admissionregistrationv1.MatchCondition `json:"matchConditions,omitempty" yaml:"matchConditions,omitempty"` +} + // Validation defines checks to be performed on matching resources. type Validation struct { // Message specifies a custom message to be displayed on failure. diff --git a/api/kyverno/v2beta1/spec_types.go b/api/kyverno/v2beta1/spec_types.go index 9b4772a419..05854b02e7 100644 --- a/api/kyverno/v2beta1/spec_types.go +++ b/api/kyverno/v2beta1/spec_types.go @@ -82,6 +82,15 @@ type Spec struct { // Defaults to "false" if not specified. // +optional UseServerSideApply bool `json:"useServerSideApply,omitempty" yaml:"useServerSideApply,omitempty"` + + // WebhookConfiguration specifies the custom configuration for Kubernetes admission webhookconfiguration. + // Requires Kubernetes 1.27 or later. + // +optional + WebhookConfiguration *WebhookConfiguration `json:"webhookConfiguration,omitempty" yaml:"webhookConfiguration,omitempty"` +} + +func (s *Spec) CustomWebhookConfiguration() bool { + return s.WebhookConfiguration != nil } func (s *Spec) SetRules(rules []Rule) { diff --git a/api/kyverno/v2beta1/zz_generated.deepcopy.go b/api/kyverno/v2beta1/zz_generated.deepcopy.go index 1c0ca94bc8..f1ae0b0da5 100755 --- a/api/kyverno/v2beta1/zz_generated.deepcopy.go +++ b/api/kyverno/v2beta1/zz_generated.deepcopy.go @@ -810,6 +810,11 @@ func (in *Spec) DeepCopyInto(out *Spec) { *out = new(bool) **out = **in } + if in.WebhookConfiguration != nil { + in, out := &in.WebhookConfiguration, &out.WebhookConfiguration + *out = new(WebhookConfiguration) + (*in).DeepCopyInto(*out) + } return } @@ -875,3 +880,24 @@ func (in *Validation) DeepCopy() *Validation { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) { + *out = *in + if in.MatchConditions != nil { + in, out := &in.MatchConditions, &out.MatchConditions + *out = make([]admissionregistrationv1.MatchCondition, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookConfiguration. +func (in *WebhookConfiguration) DeepCopy() *WebhookConfiguration { + if in == nil { + return nil + } + out := new(WebhookConfiguration) + in.DeepCopyInto(out) + return out +} diff --git a/charts/kyverno/charts/crds/templates/crds.yaml b/charts/kyverno/charts/crds/templates/crds.yaml index 66946c7aa8..abece2432b 100644 --- a/charts/kyverno/charts/crds/templates/crds.yaml +++ b/charts/kyverno/charts/crds/templates/crds.yaml @@ -14420,6 +14420,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -23196,6 +23242,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -32272,6 +32364,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -41050,6 +41188,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, diff --git a/cmd/cli/kubectl-kyverno/data/crds/kyverno.io_clusterpolicies.yaml b/cmd/cli/kubectl-kyverno/data/crds/kyverno.io_clusterpolicies.yaml index e001acdffd..ccda23a704 100644 --- a/cmd/cli/kubectl-kyverno/data/crds/kyverno.io_clusterpolicies.yaml +++ b/cmd/cli/kubectl-kyverno/data/crds/kyverno.io_clusterpolicies.yaml @@ -4469,6 +4469,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -13245,6 +13291,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, diff --git a/cmd/cli/kubectl-kyverno/data/crds/kyverno.io_policies.yaml b/cmd/cli/kubectl-kyverno/data/crds/kyverno.io_policies.yaml index 47f889a79e..7e7b7527a2 100644 --- a/cmd/cli/kubectl-kyverno/data/crds/kyverno.io_policies.yaml +++ b/cmd/cli/kubectl-kyverno/data/crds/kyverno.io_policies.yaml @@ -4470,6 +4470,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -13248,6 +13294,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, diff --git a/config/crds/kyverno.io_clusterpolicies.yaml b/config/crds/kyverno.io_clusterpolicies.yaml index e001acdffd..ccda23a704 100644 --- a/config/crds/kyverno.io_clusterpolicies.yaml +++ b/config/crds/kyverno.io_clusterpolicies.yaml @@ -4469,6 +4469,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -13245,6 +13291,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, diff --git a/config/crds/kyverno.io_policies.yaml b/config/crds/kyverno.io_policies.yaml index 47f889a79e..7e7b7527a2 100644 --- a/config/crds/kyverno.io_policies.yaml +++ b/config/crds/kyverno.io_policies.yaml @@ -4470,6 +4470,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -13248,6 +13294,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, diff --git a/config/install-latest-testing.yaml b/config/install-latest-testing.yaml index e4e799170b..77a3b18b0a 100644 --- a/config/install-latest-testing.yaml +++ b/config/install-latest-testing.yaml @@ -14639,6 +14639,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -23415,6 +23461,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -32493,6 +32585,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, @@ -41271,6 +41409,52 @@ spec: type: array type: object type: array + webhookConfiguration: + description: WebhookConfiguration specifies the custom configuration + for Kubernetes admission webhookconfiguration. Requires Kubernetes + 1.27 or later. + properties: + matchConditions: + description: MatchCondition configures admission webhook matchConditions. + items: + description: MatchCondition represents a condition which must + by fulfilled for a request to be sent to a webhook. + properties: + expression: + description: "Expression represents the expression which + will be evaluated by CEL. Must evaluate to bool. CEL expressions + have access to the contents of the AdmissionRequest and + Authorizer, organized into CEL variables: \n 'object' + - The object from the incoming request. The value is null + for DELETE requests. 'oldObject' - The existing object. + The value is null for CREATE requests. 'request' - Attributes + of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). + 'authorizer' - A CEL Authorizer. May be used to perform + authorization checks for the principal (user or service + account) of the request. See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz + 'authorizer.requestResource' - A CEL ResourceCheck constructed + from the 'authorizer' and configured with the request + resource. Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/ + \n Required." + type: string + name: + description: "Name is an identifier for this match condition, + used for strategic merging of MatchConditions, as well + as providing an identifier for logging purposes. A good + name should be descriptive of the associated expression. + Name must be a qualified name consisting of alphanumeric + characters, '-', '_' or '.', and must start and end with + an alphanumeric character (e.g. 'MyName', or 'my.name', + \ or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') + with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName') + \n Required." + type: string + required: + - expression + - name + type: object + type: array + type: object webhookTimeoutSeconds: description: WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy. After the configured time expires, diff --git a/docs/user/crd/index.html b/docs/user/crd/index.html index bf36ef2310..c727ca77dd 100644 --- a/docs/user/crd/index.html +++ b/docs/user/crd/index.html @@ -293,6 +293,21 @@ If is set to “true” create & update for generate rules will use Defaults to “false” if not specified.

+ + +webhookConfiguration
+ + +WebhookConfiguration + + + + +(Optional) +

WebhookConfiguration specifies the custom configuration for Kubernetes admission webhookconfiguration. +Requires Kubernetes 1.27 or later.

+ + @@ -556,6 +571,21 @@ If is set to “true” create & update for generate rules will use Defaults to “false” if not specified.

+ + +webhookConfiguration
+ + +WebhookConfiguration + + + + +(Optional) +

WebhookConfiguration specifies the custom configuration for Kubernetes admission webhookconfiguration. +Requires Kubernetes 1.27 or later.

+ + @@ -3910,6 +3940,21 @@ If is set to “true” create & update for generate rules will use Defaults to “false” if not specified.

+ + +webhookConfiguration
+ + +WebhookConfiguration + + + + +(Optional) +

WebhookConfiguration specifies the custom configuration for Kubernetes admission webhookconfiguration. +Requires Kubernetes 1.27 or later.

+ +
@@ -4441,6 +4486,40 @@ expression evaluates to nil


+

WebhookConfiguration +

+

+(Appears on: +Spec) +

+

+

WebhookConfiguration specifies the configuration for Kubernetes admission webhookconfiguration.

+

+ + + + + + + + + + + + + +
FieldDescription
+matchConditions
+ + +[]Kubernetes admissionregistration/v1.MatchCondition + + +
+(Optional) +

MatchCondition configures admission webhook matchConditions.

+
+

kyverno.io/v1alpha2

Package v1alpha2 contains API Schema definitions for the policy v1alpha2 API group

@@ -8288,6 +8367,21 @@ If is set to “true” create & update for generate rules will use Defaults to “false” if not specified.

+ + +webhookConfiguration
+ + +WebhookConfiguration + + + + +(Optional) +

WebhookConfiguration specifies the custom configuration for Kubernetes admission webhookconfiguration. +Requires Kubernetes 1.27 or later.

+ + @@ -8550,6 +8644,21 @@ If is set to “true” create & update for generate rules will use Defaults to “false” if not specified.

+ + +webhookConfiguration
+ + +WebhookConfiguration + + + + +(Optional) +

WebhookConfiguration specifies the custom configuration for Kubernetes admission webhookconfiguration. +Requires Kubernetes 1.27 or later.

+ + @@ -9932,6 +10041,21 @@ If is set to “true” create & update for generate rules will use Defaults to “false” if not specified.

+ + +webhookConfiguration
+ + +WebhookConfiguration + + + + +(Optional) +

WebhookConfiguration specifies the custom configuration for Kubernetes admission webhookconfiguration. +Requires Kubernetes 1.27 or later.

+ +
@@ -10067,6 +10191,40 @@ CEL
+

WebhookConfiguration +

+

+(Appears on: +Spec) +

+

+

WebhookConfiguration specifies the configuration for Kubernetes admission webhookconfiguration.

+

+ + + + + + + + + + + + + +
FieldDescription
+matchConditions
+ + +[]Kubernetes admissionregistration/v1.MatchCondition + + +
+(Optional) +

MatchCondition configures admission webhook matchConditions.

+
+

reports.kyverno.io/v1

diff --git a/pkg/client/applyconfigurations/kyverno/v1/spec.go b/pkg/client/applyconfigurations/kyverno/v1/spec.go index 1ed14a4fc8..cf6746a75f 100644 --- a/pkg/client/applyconfigurations/kyverno/v1/spec.go +++ b/pkg/client/applyconfigurations/kyverno/v1/spec.go @@ -38,6 +38,7 @@ type SpecApplyConfiguration struct { GenerateExistingOnPolicyUpdate *bool `json:"generateExistingOnPolicyUpdate,omitempty"` GenerateExisting *bool `json:"generateExisting,omitempty"` UseServerSideApply *bool `json:"useServerSideApply,omitempty"` + WebhookConfiguration *WebhookConfigurationApplyConfiguration `json:"webhookConfiguration,omitempty"` } // SpecApplyConfiguration constructs an declarative configuration of the Spec type for use with @@ -159,3 +160,11 @@ func (b *SpecApplyConfiguration) WithUseServerSideApply(value bool) *SpecApplyCo b.UseServerSideApply = &value return b } + +// WithWebhookConfiguration sets the WebhookConfiguration field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the WebhookConfiguration field is set to the value of the last call. +func (b *SpecApplyConfiguration) WithWebhookConfiguration(value *WebhookConfigurationApplyConfiguration) *SpecApplyConfiguration { + b.WebhookConfiguration = value + return b +} diff --git a/pkg/client/applyconfigurations/kyverno/v1/webhookconfiguration.go b/pkg/client/applyconfigurations/kyverno/v1/webhookconfiguration.go new file mode 100644 index 0000000000..fdcd61b38e --- /dev/null +++ b/pkg/client/applyconfigurations/kyverno/v1/webhookconfiguration.go @@ -0,0 +1,45 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "k8s.io/api/admissionregistration/v1" +) + +// WebhookConfigurationApplyConfiguration represents an declarative configuration of the WebhookConfiguration type for use +// with apply. +type WebhookConfigurationApplyConfiguration struct { + MatchConditions []v1.MatchCondition `json:"matchConditions,omitempty"` +} + +// WebhookConfigurationApplyConfiguration constructs an declarative configuration of the WebhookConfiguration type for use with +// apply. +func WebhookConfiguration() *WebhookConfigurationApplyConfiguration { + return &WebhookConfigurationApplyConfiguration{} +} + +// WithMatchConditions adds the given value to the MatchConditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the MatchConditions field. +func (b *WebhookConfigurationApplyConfiguration) WithMatchConditions(values ...v1.MatchCondition) *WebhookConfigurationApplyConfiguration { + for i := range values { + b.MatchConditions = append(b.MatchConditions, values[i]) + } + return b +} diff --git a/pkg/client/applyconfigurations/kyverno/v2beta1/spec.go b/pkg/client/applyconfigurations/kyverno/v2beta1/spec.go index 427aeef855..1fad94bcb4 100644 --- a/pkg/client/applyconfigurations/kyverno/v2beta1/spec.go +++ b/pkg/client/applyconfigurations/kyverno/v2beta1/spec.go @@ -39,6 +39,7 @@ type SpecApplyConfiguration struct { GenerateExistingOnPolicyUpdate *bool `json:"generateExistingOnPolicyUpdate,omitempty"` GenerateExisting *bool `json:"generateExisting,omitempty"` UseServerSideApply *bool `json:"useServerSideApply,omitempty"` + WebhookConfiguration *WebhookConfigurationApplyConfiguration `json:"webhookConfiguration,omitempty"` } // SpecApplyConfiguration constructs an declarative configuration of the Spec type for use with @@ -160,3 +161,11 @@ func (b *SpecApplyConfiguration) WithUseServerSideApply(value bool) *SpecApplyCo b.UseServerSideApply = &value return b } + +// WithWebhookConfiguration sets the WebhookConfiguration field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the WebhookConfiguration field is set to the value of the last call. +func (b *SpecApplyConfiguration) WithWebhookConfiguration(value *WebhookConfigurationApplyConfiguration) *SpecApplyConfiguration { + b.WebhookConfiguration = value + return b +} diff --git a/pkg/client/applyconfigurations/kyverno/v2beta1/webhookconfiguration.go b/pkg/client/applyconfigurations/kyverno/v2beta1/webhookconfiguration.go new file mode 100644 index 0000000000..9623f7c470 --- /dev/null +++ b/pkg/client/applyconfigurations/kyverno/v2beta1/webhookconfiguration.go @@ -0,0 +1,45 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v2beta1 + +import ( + v1 "k8s.io/api/admissionregistration/v1" +) + +// WebhookConfigurationApplyConfiguration represents an declarative configuration of the WebhookConfiguration type for use +// with apply. +type WebhookConfigurationApplyConfiguration struct { + MatchConditions []v1.MatchCondition `json:"matchConditions,omitempty"` +} + +// WebhookConfigurationApplyConfiguration constructs an declarative configuration of the WebhookConfiguration type for use with +// apply. +func WebhookConfiguration() *WebhookConfigurationApplyConfiguration { + return &WebhookConfigurationApplyConfiguration{} +} + +// WithMatchConditions adds the given value to the MatchConditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the MatchConditions field. +func (b *WebhookConfigurationApplyConfiguration) WithMatchConditions(values ...v1.MatchCondition) *WebhookConfigurationApplyConfiguration { + for i := range values { + b.MatchConditions = append(b.MatchConditions, values[i]) + } + return b +} diff --git a/pkg/client/applyconfigurations/utils.go b/pkg/client/applyconfigurations/utils.go index 910984a366..0bffaf29ed 100644 --- a/pkg/client/applyconfigurations/utils.go +++ b/pkg/client/applyconfigurations/utils.go @@ -143,6 +143,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &kyvernov1.ValidationFailureActionOverrideApplyConfiguration{} case v1.SchemeGroupVersion.WithKind("Variable"): return &kyvernov1.VariableApplyConfiguration{} + case v1.SchemeGroupVersion.WithKind("WebhookConfiguration"): + return &kyvernov1.WebhookConfigurationApplyConfiguration{} // Group=kyverno.io, Version=v1alpha2 case v1alpha2.SchemeGroupVersion.WithKind("AdmissionReport"): @@ -257,6 +259,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &kyvernov2beta1.SpecApplyConfiguration{} case v2beta1.SchemeGroupVersion.WithKind("Validation"): return &kyvernov2beta1.ValidationApplyConfiguration{} + case v2beta1.SchemeGroupVersion.WithKind("WebhookConfiguration"): + return &kyvernov2beta1.WebhookConfigurationApplyConfiguration{} // Group=reports.kyverno.io, Version=v1 case reportsv1.SchemeGroupVersion.WithKind("AdmissionReport"): diff --git a/pkg/config/config.go b/pkg/config/config.go index 5afa3ddcfa..f9ab57332b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -74,6 +74,8 @@ const ( ReadinessServicePath = "/health/readiness" // MetricsPath is the path for exposing metrics MetricsPath = "/metrics" + // FineGrainedWebhookPath is the sub-path for fine-grained webhook configurationss + FineGrainedWebhookPath = "/finegrained" ) // keys in config map diff --git a/pkg/controllers/policycache/controller.go b/pkg/controllers/policycache/controller.go index 915ee3dc01..519dd95cc5 100644 --- a/pkg/controllers/policycache/controller.go +++ b/pkg/controllers/policycache/controller.go @@ -103,7 +103,7 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, nam } return err } - if policy.AdmissionProcessingEnabled() { + if policy.AdmissionProcessingEnabled() && !policy.GetSpec().CustomWebhookConfiguration() { return c.cache.Set(key, policy, c.client.Discovery()) } else { c.cache.Unset(key) diff --git a/pkg/controllers/webhook/controller.go b/pkg/controllers/webhook/controller.go index 0336da9a79..95e945371b 100644 --- a/pkg/controllers/webhook/controller.go +++ b/pkg/controllers/webhook/controller.go @@ -634,74 +634,85 @@ func (c *controller) buildResourceMutatingWebhookConfiguration(ctx context.Conte Webhooks: []admissionregistrationv1.MutatingWebhook{}, } if c.watchdogCheck() { - ignore := newWebhook(c.defaultTimeout, ignore) - fail := newWebhook(c.defaultTimeout, fail) - policies, err := c.getAllPolicies() - if err != nil { - return nil, err - } - c.recordPolicyState(config.MutatingWebhookConfigurationName, policies...) - for _, p := range policies { - if p.AdmissionProcessingEnabled() { - spec := p.GetSpec() - if spec.HasMutateStandard() || spec.HasVerifyImages() { - if spec.GetFailurePolicy(ctx) == kyvernov1.Ignore { - c.mergeWebhook(ignore, p, false) - } else { - c.mergeWebhook(fail, p, false) - } - } - } - } webhookCfg := config.WebhookConfig{} webhookCfgs := cfg.GetWebhooks() if len(webhookCfgs) > 0 { webhookCfg = webhookCfgs[0] } - if !ignore.isEmpty() { - timeout := capTimeout(ignore.maxWebhookTimeout) - result.Webhooks = append( - result.Webhooks, - admissionregistrationv1.MutatingWebhook{ - Name: config.MutatingWebhookName + "-ignore", - ClientConfig: c.clientConfig(caBundle, config.MutatingWebhookServicePath+"/ignore"), - Rules: ignore.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update), - FailurePolicy: &ignore.failurePolicy, - SideEffects: &noneOnDryRun, - AdmissionReviewVersions: []string{"v1"}, - NamespaceSelector: webhookCfg.NamespaceSelector, - ObjectSelector: webhookCfg.ObjectSelector, - TimeoutSeconds: &timeout, - ReinvocationPolicy: &ifNeeded, - MatchConditions: cfg.GetMatchConditions(), - }, - ) + + ignoreWebhook := newWebhook(c.defaultTimeout, ignore, cfg.GetMatchConditions()) + failWebhook := newWebhook(c.defaultTimeout, fail, cfg.GetMatchConditions()) + policies, err := c.getAllPolicies() + if err != nil { + return nil, err } - if !fail.isEmpty() { - timeout := capTimeout(fail.maxWebhookTimeout) - result.Webhooks = append( - result.Webhooks, - admissionregistrationv1.MutatingWebhook{ - Name: config.MutatingWebhookName + "-fail", - ClientConfig: c.clientConfig(caBundle, config.MutatingWebhookServicePath+"/fail"), - Rules: fail.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update), - FailurePolicy: &fail.failurePolicy, - SideEffects: &noneOnDryRun, - AdmissionReviewVersions: []string{"v1"}, - NamespaceSelector: webhookCfg.NamespaceSelector, - ObjectSelector: webhookCfg.ObjectSelector, - TimeoutSeconds: &timeout, - ReinvocationPolicy: &ifNeeded, - MatchConditions: cfg.GetMatchConditions(), - }, - ) + var fineGrainedIgnoreList, fineGrainedFailList []*webhook + c.recordPolicyState(config.MutatingWebhookConfigurationName, policies...) + for _, p := range policies { + if p.AdmissionProcessingEnabled() { + spec := p.GetSpec() + if spec.HasMutateStandard() || spec.HasVerifyImages() { + if spec.CustomWebhookConfiguration() { + fineGrainedIgnore := newWebhookPerPolicy(c.defaultTimeout, ignore, cfg.GetMatchConditions(), p) + fineGrainedFail := newWebhookPerPolicy(c.defaultTimeout, fail, cfg.GetMatchConditions(), p) + if spec.GetFailurePolicy(ctx) == kyvernov1.Ignore { + c.mergeWebhook(fineGrainedIgnore, p, false) + fineGrainedIgnoreList = append(fineGrainedIgnoreList, fineGrainedIgnore) + } else { + c.mergeWebhook(fineGrainedFail, p, false) + fineGrainedFailList = append(fineGrainedFailList, fineGrainedFail) + } + continue + } + + if spec.GetFailurePolicy(ctx) == kyvernov1.Ignore { + c.mergeWebhook(ignoreWebhook, p, false) + } else { + c.mergeWebhook(failWebhook, p, false) + } + } + } } + + webhooks := []*webhook{ignoreWebhook, failWebhook} + webhooks = append(webhooks, fineGrainedIgnoreList...) + webhooks = append(webhooks, fineGrainedFailList...) + result.Webhooks = c.buildResourceMutatingWebhookRules(caBundle, webhookCfg, &noneOnDryRun, webhooks) } else { c.recordPolicyState(config.MutatingWebhookConfigurationName) } return &result, nil } +func (c *controller) buildResourceMutatingWebhookRules(caBundle []byte, webhookCfg config.WebhookConfig, sideEffects *admissionregistrationv1.SideEffectClass, webhooks []*webhook) []admissionregistrationv1.MutatingWebhook { + var mutatingWebhooks []admissionregistrationv1.MutatingWebhook + for _, webhook := range webhooks { + if webhook.isEmpty() { + continue + } + failurePolicy := webhook.failurePolicy + timeout := capTimeout(webhook.maxWebhookTimeout) + name, path := webhookNameAndPath(*webhook, config.MutatingWebhookName, config.MutatingWebhookServicePath) + mutatingWebhooks = append( + mutatingWebhooks, + admissionregistrationv1.MutatingWebhook{ + Name: name, + ClientConfig: c.clientConfig(caBundle, path), + Rules: webhook.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update), + FailurePolicy: &failurePolicy, + SideEffects: sideEffects, + AdmissionReviewVersions: []string{"v1"}, + NamespaceSelector: webhookCfg.NamespaceSelector, + ObjectSelector: webhookCfg.ObjectSelector, + TimeoutSeconds: &timeout, + ReinvocationPolicy: &ifNeeded, + MatchConditions: webhook.matchConditions, + }, + ) + } + return mutatingWebhooks +} + func (c *controller) buildDefaultResourceValidatingWebhookConfiguration(_ context.Context, cfg config.Configuration, caBundle []byte) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) { sideEffects := &none if c.admissionReports { @@ -760,76 +771,90 @@ func (c *controller) buildResourceValidatingWebhookConfiguration(ctx context.Con Webhooks: []admissionregistrationv1.ValidatingWebhook{}, } if c.watchdogCheck() { - ignore := newWebhook(c.defaultTimeout, ignore) - fail := newWebhook(c.defaultTimeout, fail) - policies, err := c.getAllPolicies() - if err != nil { - return nil, err - } - c.recordPolicyState(config.ValidatingWebhookConfigurationName, policies...) - for _, p := range policies { - if p.AdmissionProcessingEnabled() { - spec := p.GetSpec() - if spec.HasValidate() || spec.HasGenerate() || spec.HasMutateExisting() || spec.HasVerifyImageChecks() || spec.HasVerifyManifests() { - if spec.GetFailurePolicy(ctx) == kyvernov1.Ignore { - c.mergeWebhook(ignore, p, true) - } else { - c.mergeWebhook(fail, p, true) - } - } - } - } webhookCfg := config.WebhookConfig{} webhookCfgs := cfg.GetWebhooks() if len(webhookCfgs) > 0 { webhookCfg = webhookCfgs[0] } + + ignoreWebhook := newWebhook(c.defaultTimeout, ignore, cfg.GetMatchConditions()) + failWebhook := newWebhook(c.defaultTimeout, fail, cfg.GetMatchConditions()) + policies, err := c.getAllPolicies() + if err != nil { + return nil, err + } + + var fineGrainedIgnoreList, fineGrainedFailList []*webhook + c.recordPolicyState(config.ValidatingWebhookConfigurationName, policies...) + for _, p := range policies { + if p.AdmissionProcessingEnabled() { + spec := p.GetSpec() + if spec.HasValidate() || spec.HasGenerate() || spec.HasMutateExisting() || spec.HasVerifyImageChecks() || spec.HasVerifyManifests() { + if spec.CustomWebhookConfiguration() { + fineGrainedIgnore := newWebhookPerPolicy(c.defaultTimeout, ignore, cfg.GetMatchConditions(), p) + fineGrainedFail := newWebhookPerPolicy(c.defaultTimeout, fail, cfg.GetMatchConditions(), p) + if spec.GetFailurePolicy(ctx) == kyvernov1.Ignore { + c.mergeWebhook(fineGrainedIgnore, p, true) + fineGrainedIgnoreList = append(fineGrainedIgnoreList, fineGrainedIgnore) + } else { + c.mergeWebhook(fineGrainedFail, p, true) + fineGrainedFailList = append(fineGrainedFailList, fineGrainedFail) + } + continue + } + + if spec.GetFailurePolicy(ctx) == kyvernov1.Ignore { + c.mergeWebhook(ignoreWebhook, p, true) + } else { + c.mergeWebhook(failWebhook, p, true) + } + } + } + } + sideEffects := &none if c.admissionReports { sideEffects = &noneOnDryRun } - if !ignore.isEmpty() { - timeout := capTimeout(ignore.maxWebhookTimeout) - result.Webhooks = append( - result.Webhooks, - admissionregistrationv1.ValidatingWebhook{ - Name: config.ValidatingWebhookName + "-ignore", - ClientConfig: c.clientConfig(caBundle, config.ValidatingWebhookServicePath+"/ignore"), - Rules: ignore.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect), - FailurePolicy: &ignore.failurePolicy, - SideEffects: sideEffects, - AdmissionReviewVersions: []string{"v1"}, - NamespaceSelector: webhookCfg.NamespaceSelector, - ObjectSelector: webhookCfg.ObjectSelector, - TimeoutSeconds: &timeout, - MatchConditions: cfg.GetMatchConditions(), - }, - ) - } - if !fail.isEmpty() { - timeout := capTimeout(fail.maxWebhookTimeout) - result.Webhooks = append( - result.Webhooks, - admissionregistrationv1.ValidatingWebhook{ - Name: config.ValidatingWebhookName + "-fail", - ClientConfig: c.clientConfig(caBundle, config.ValidatingWebhookServicePath+"/fail"), - Rules: fail.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect), - FailurePolicy: &fail.failurePolicy, - SideEffects: sideEffects, - AdmissionReviewVersions: []string{"v1"}, - NamespaceSelector: webhookCfg.NamespaceSelector, - ObjectSelector: webhookCfg.ObjectSelector, - TimeoutSeconds: &timeout, - MatchConditions: cfg.GetMatchConditions(), - }, - ) - } + + webhooks := []*webhook{ignoreWebhook, failWebhook} + webhooks = append(webhooks, fineGrainedIgnoreList...) + webhooks = append(webhooks, fineGrainedFailList...) + result.Webhooks = c.buildResourceValidatingWebhookRules(caBundle, webhookCfg, sideEffects, webhooks) } else { c.recordPolicyState(config.MutatingWebhookConfigurationName) } return &result, nil } +func (c *controller) buildResourceValidatingWebhookRules(caBundle []byte, webhookCfg config.WebhookConfig, sideEffects *admissionregistrationv1.SideEffectClass, webhooks []*webhook) []admissionregistrationv1.ValidatingWebhook { + var validatingWebhooks []admissionregistrationv1.ValidatingWebhook + for _, webhook := range webhooks { + if webhook.isEmpty() { + continue + } + timeout := capTimeout(webhook.maxWebhookTimeout) + name, path := webhookNameAndPath(*webhook, config.ValidatingWebhookName, config.ValidatingWebhookServicePath) + failurePolicy := webhook.failurePolicy + validatingWebhooks = append( + validatingWebhooks, + admissionregistrationv1.ValidatingWebhook{ + Name: name, + ClientConfig: c.clientConfig(caBundle, path), + Rules: webhook.buildRulesWithOperations(admissionregistrationv1.Create, admissionregistrationv1.Update, admissionregistrationv1.Delete, admissionregistrationv1.Connect), + FailurePolicy: &failurePolicy, + SideEffects: sideEffects, + AdmissionReviewVersions: []string{"v1"}, + NamespaceSelector: webhookCfg.NamespaceSelector, + ObjectSelector: webhookCfg.ObjectSelector, + TimeoutSeconds: &timeout, + MatchConditions: webhook.matchConditions, + }, + ) + } + return validatingWebhooks +} + func (c *controller) getAllPolicies() ([]kyvernov1.PolicyInterface, error) { var policies []kyvernov1.PolicyInterface if cpols, err := c.cpolLister.List(labels.Everything()); err != nil { diff --git a/pkg/controllers/webhook/utils.go b/pkg/controllers/webhook/utils.go index 2f71a717d8..f7c51b390c 100644 --- a/pkg/controllers/webhook/utils.go +++ b/pkg/controllers/webhook/utils.go @@ -7,20 +7,27 @@ import ( "github.com/kyverno/kyverno/api/kyverno" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/config" "golang.org/x/exp/maps" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" + objectmeta "k8s.io/client-go/tools/cache" "k8s.io/utils/ptr" ) // webhook is the instance that aggregates the GVK of existing policies // based on group, kind, scopeType, failurePolicy and webhookTimeout +// a fine-grained webhook is created per policy with a unique path type webhook struct { + // policyMeta is set for fine-grained webhooks + policyMeta objectmeta.ObjectName + maxWebhookTimeout int32 failurePolicy admissionregistrationv1.FailurePolicyType rules map[groupVersionScope]sets.Set[string] + matchConditions []admissionregistrationv1.MatchCondition } // groupVersionScope contains the GV and scopeType of a resource @@ -34,14 +41,27 @@ func (gvs groupVersionScope) String() string { return gvs.GroupVersion.String() + "/" + string(gvs.scopeType) } -func newWebhook(timeout int32, failurePolicy admissionregistrationv1.FailurePolicyType) *webhook { +func newWebhook(timeout int32, failurePolicy admissionregistrationv1.FailurePolicyType, matchConditions []admissionregistrationv1.MatchCondition) *webhook { return &webhook{ maxWebhookTimeout: timeout, failurePolicy: failurePolicy, rules: map[groupVersionScope]sets.Set[string]{}, + matchConditions: matchConditions, } } +func newWebhookPerPolicy(timeout int32, failurePolicy admissionregistrationv1.FailurePolicyType, matchConditions []admissionregistrationv1.MatchCondition, policy kyvernov1.PolicyInterface) *webhook { + webhook := newWebhook(timeout, failurePolicy, matchConditions) + webhook.policyMeta = objectmeta.ObjectName{ + Namespace: policy.GetNamespace(), + Name: policy.GetName(), + } + if policy.GetSpec().CustomWebhookConfiguration() { + webhook.matchConditions = policy.GetSpec().GetMatchConditions() + } + return webhook +} + func (wh *webhook) buildRulesWithOperations(ops ...admissionregistrationv1.OperationType) []admissionregistrationv1.RuleWithOperations { var rules []admissionregistrationv1.RuleWithOperations @@ -122,6 +142,14 @@ func (wh *webhook) isEmpty() bool { return len(wh.rules) == 0 } +func (wh *webhook) key(separator string) string { + p := wh.policyMeta + if p.Namespace != "" { + return p.Namespace + separator + p.Name + } + return p.Name +} + func objectMeta(name string, annotations map[string]string, labels map[string]string, owner ...metav1.OwnerReference) metav1.ObjectMeta { desiredLabels := make(map[string]string) defaultLabels := map[string]string{ @@ -167,3 +195,18 @@ func capTimeout(maxWebhookTimeout int32) int32 { } return maxWebhookTimeout } + +func webhookNameAndPath(wh webhook, baseName, basePath string) (name string, path string) { + if wh.failurePolicy == ignore { + name = baseName + "-ignore" + path = basePath + "/ignore" + } else { + name = baseName + "-fail" + path = basePath + "/fail" + } + if wh.policyMeta.Name != "" { + name = name + "-finegrained-" + wh.key("-") + path = path + config.FineGrainedWebhookPath + "/" + wh.key("/") + } + return name, path +} diff --git a/pkg/controllers/webhook/utils_test.go b/pkg/controllers/webhook/utils_test.go index dc0d8f5edb..7bdc3d85a3 100644 --- a/pkg/controllers/webhook/utils_test.go +++ b/pkg/controllers/webhook/utils_test.go @@ -12,9 +12,9 @@ import ( ) func Test_webhook_isEmpty(t *testing.T) { - empty := newWebhook(DefaultWebhookTimeout, admissionregistrationv1.Ignore) + empty := newWebhook(DefaultWebhookTimeout, admissionregistrationv1.Ignore, []admissionregistrationv1.MatchCondition{}) assert.Equal(t, empty.isEmpty(), true) - notEmpty := newWebhook(DefaultWebhookTimeout, admissionregistrationv1.Ignore) + notEmpty := newWebhook(DefaultWebhookTimeout, admissionregistrationv1.Ignore, []admissionregistrationv1.MatchCondition{}) notEmpty.set(GroupVersionResourceScope{ GroupVersionResource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}, Scope: admissionregistrationv1.NamespacedScope, diff --git a/pkg/validation/policy/validate.go b/pkg/validation/policy/validate.go index d8fac4b237..cfe1a8ac78 100644 --- a/pkg/validation/policy/validate.go +++ b/pkg/validation/policy/validate.go @@ -123,6 +123,10 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf spec := policy.GetSpec() background := spec.BackgroundProcessingEnabled() mutateExistingOnPolicyUpdate := spec.GetMutateExistingOnPolicyUpdate() + if policy.GetSpec().CustomWebhookConfiguration() && + !kubeutils.HigherThanKubernetesVersion(client.GetKubeClient().Discovery(), logging.GlobalLogger(), 1, 27, 0) { + return warnings, fmt.Errorf("custom webhook configurations are only supported in kubernetes version 1.27.0 and above") + } warnings = append(warnings, checkValidationFailureAction(spec)...) var errs field.ErrorList diff --git a/pkg/webhooks/handlers/admission.go b/pkg/webhooks/handlers/admission.go index 1b23812e30..d1a546aee3 100644 --- a/pkg/webhooks/handlers/admission.go +++ b/pkg/webhooks/handlers/admission.go @@ -8,6 +8,7 @@ import ( "time" "github.com/go-logr/logr" + "github.com/julienschmidt/httprouter" admissionv1 "k8s.io/api/admission/v1" ) @@ -47,8 +48,11 @@ func (inner AdmissionHandler) withAdmission(logger logr.Logger) HttpHandler { "uid", admissionReview.Request.UID, "user", admissionReview.Request.UserInfo, ) + + params := httprouter.ParamsFromContext(request.Context()) admissionRequest := AdmissionRequest{ AdmissionRequest: *admissionReview.Request, + URLParams: params.ByName("policy"), } admissionResponse := inner(request.Context(), logger, admissionRequest, startTime) admissionReview.Response = &admissionResponse diff --git a/pkg/webhooks/handlers/types.go b/pkg/webhooks/handlers/types.go index 03a98b7a4b..e023d51698 100644 --- a/pkg/webhooks/handlers/types.go +++ b/pkg/webhooks/handlers/types.go @@ -23,6 +23,8 @@ type AdmissionRequest struct { // GroupVersionKind is the top level GVK. GroupVersionKind schema.GroupVersionKind + + URLParams string } type AdmissionResponse = admissionv1.AdmissionResponse diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go index bfd485d581..5fb701f450 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -3,6 +3,8 @@ package resource import ( "context" "errors" + "fmt" + "strings" "time" "github.com/go-logr/logr" @@ -101,16 +103,13 @@ func NewHandlers( func (h *resourceHandlers) Validate(ctx context.Context, logger logr.Logger, request handlers.AdmissionRequest, failurePolicy string, startTime time.Time) handlers.AdmissionResponse { kind := request.Kind.Kind - logger = logger.WithValues("kind", kind) + logger = logger.WithValues("kind", kind).WithValues("URLParams", request.URLParams) logger.V(4).Info("received an admission request in validating webhook") - // timestamp at which this admission request got triggered - gvr := schema.GroupVersionResource(request.Resource) - policies := filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.ValidateEnforce, gvr, request.SubResource, request.Namespace)...) - mutatePolicies := filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.Mutate, gvr, request.SubResource, request.Namespace)...) - generatePolicies := filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.Generate, gvr, request.SubResource, request.Namespace)...) - imageVerifyValidatePolicies := filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.VerifyImagesValidate, gvr, request.SubResource, request.Namespace)...) - policies = append(policies, imageVerifyValidatePolicies...) + policies, mutatePolicies, generatePolicies, _, err := h.retrieveAndCategorizePolicies(ctx, logger, request, failurePolicy, false) + if err != nil { + return errorResponse(logger, request.UID, err, "failed to fetch policy with key") + } if len(policies) == 0 && len(mutatePolicies) == 0 && len(generatePolicies) == 0 { logger.V(4).Info("no policies matched admission request") @@ -143,11 +142,13 @@ func (h *resourceHandlers) Validate(ctx context.Context, logger logr.Logger, req func (h *resourceHandlers) Mutate(ctx context.Context, logger logr.Logger, request handlers.AdmissionRequest, failurePolicy string, startTime time.Time) handlers.AdmissionResponse { kind := request.Kind.Kind - logger = logger.WithValues("kind", kind) + logger = logger.WithValues("kind", kind).WithValues("URLParams", request.URLParams) logger.V(4).Info("received an admission request in mutating webhook") - gvr := schema.GroupVersionResource(request.Resource) - mutatePolicies := filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.Mutate, gvr, request.SubResource, request.Namespace)...) - verifyImagesPolicies := filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.VerifyImagesMutate, gvr, request.SubResource, request.Namespace)...) + + _, mutatePolicies, _, verifyImagesPolicies, err := h.retrieveAndCategorizePolicies(ctx, logger, request, failurePolicy, true) + if err != nil { + return errorResponse(logger, request.UID, err, "failed to fetch policy with key") + } if len(mutatePolicies) == 0 && len(verifyImagesPolicies) == 0 { logger.V(4).Info("no policies matched mutate admission request") return admissionutils.ResponseSuccess(request.UID) @@ -184,6 +185,66 @@ func (h *resourceHandlers) Mutate(ctx context.Context, logger logr.Logger, reque return admissionutils.MutationResponse(request.UID, patch, warnings...) } +func (h *resourceHandlers) retrieveAndCategorizePolicies( + ctx context.Context, logger logr.Logger, request handlers.AdmissionRequest, failurePolicy string, mutation bool) ( + []kyvernov1.PolicyInterface, []kyvernov1.PolicyInterface, []kyvernov1.PolicyInterface, []kyvernov1.PolicyInterface, error, +) { + var policies, mutatePolicies, generatePolicies, imageVerifyValidatePolicies []kyvernov1.PolicyInterface + if request.URLParams == "" { + gvr := schema.GroupVersionResource(request.Resource) + policies = filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.ValidateEnforce, gvr, request.SubResource, request.Namespace)...) + mutatePolicies = filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.Mutate, gvr, request.SubResource, request.Namespace)...) + generatePolicies = filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.Generate, gvr, request.SubResource, request.Namespace)...) + if mutation { + imageVerifyValidatePolicies = filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.VerifyImagesMutate, gvr, request.SubResource, request.Namespace)...) + } else { + imageVerifyValidatePolicies = filterPolicies(ctx, failurePolicy, h.pCache.GetPolicies(policycache.VerifyImagesValidate, gvr, request.SubResource, request.Namespace)...) + policies = append(policies, imageVerifyValidatePolicies...) + } + } else { + meta := strings.Split(request.URLParams, "/") + polName := meta[1] + polNamespace := "" + + if len(meta) >= 3 { + polNamespace = meta[1] + polName = meta[2] + } + + var policy kyvernov1.PolicyInterface + var err error + if polNamespace == "" { + policy, err = h.cpolLister.Get(polName) + } else { + policy, err = h.polLister.Policies(polNamespace).Get(polName) + } + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("key %s/%s: %v", polNamespace, polName, err) + } + + filteredPolicies := filterPolicies(ctx, failurePolicy, policy) + if len(filteredPolicies) == 0 { + logger.V(4).Info("no policy found with key", "namespace", polNamespace, "name", polName) + return nil, nil, nil, nil, nil + } + policy = filteredPolicies[0] + spec := policy.GetSpec() + if spec.HasValidate() { + policies = append(policies, policy) + } + if spec.HasGenerate() { + generatePolicies = append(generatePolicies, policy) + } + if spec.HasMutate() { + mutatePolicies = append(mutatePolicies, policy) + } + if spec.HasVerifyImages() { + policies = append(policies, policy) + } + } + return policies, mutatePolicies, generatePolicies, imageVerifyValidatePolicies, nil +} + func filterPolicies(ctx context.Context, failurePolicy string, policies ...kyvernov1.PolicyInterface) []kyvernov1.PolicyInterface { var results []kyvernov1.PolicyInterface for _, policy := range policies { diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 42f90efea8..64bcac7f57 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -285,4 +285,6 @@ func registerWebhookHandlers( mux.HandlerFunc("POST", basePath, builder(all).ToHandlerFunc(name)) mux.HandlerFunc("POST", basePath+"/ignore", builder(ignore).ToHandlerFunc(name)) mux.HandlerFunc("POST", basePath+"/fail", builder(fail).ToHandlerFunc(name)) + mux.HandlerFunc("POST", basePath+"/ignore"+config.FineGrainedWebhookPath+"/*policy", builder(ignore).ToHandlerFunc(name)) + mux.HandlerFunc("POST", basePath+"/fail"+config.FineGrainedWebhookPath+"/*policy", builder(fail).ToHandlerFunc(name)) }