diff --git a/Makefile b/Makefile index bb2b301c5..085b0f4b0 100644 --- a/Makefile +++ b/Makefile @@ -97,11 +97,11 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) crd paths="./api/..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) crd webhook paths="./api/..." output:crd:artifacts:config=config/crd/bases $(CONTROLLER_GEN) rbac:roleName=manager-role paths="./controllers" output:rbac:artifacts:config=config/rbac # Hub - $(CONTROLLER_GEN) crd paths="./api-hub/..." output:crd:artifacts:config=config/crd-hub/bases + $(CONTROLLER_GEN) crd webhook paths="./api-hub/..." output:crd:artifacts:config=config/crd-hub/bases $(CONTROLLER_GEN) rbac:roleName=manager-role paths="./controllers/hub" output:rbac:artifacts:config=config/rbac-hub .PHONY: generate diff --git a/PROJECT b/PROJECT index e5ae90964..cb4dfc176 100644 --- a/PROJECT +++ b/PROJECT @@ -16,6 +16,9 @@ resources: kind: Module path: github.com/kubernetes-sigs/kernel-module-management/api/v1beta1 version: v1beta1 + webhooks: + validation: true + webhookVersion: v1 - api: crdVersion: v1 namespaced: false diff --git a/api/v1beta1/module_webhook.go b/api/v1beta1/module_webhook.go new file mode 100644 index 000000000..ce262f083 --- /dev/null +++ b/api/v1beta1/module_webhook.go @@ -0,0 +1,111 @@ +/* +Copyright 2022. + +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. +*/ + +package v1beta1 + +import ( + "errors" + "fmt" + "k8s.io/apimachinery/pkg/runtime" + "regexp" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// log is for logging in this package. +var modulelog = logf.Log.WithName("module-resource") + +func (m *Module) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(m). + Complete() +} + +//+kubebuilder:webhook:path=/validate-kmm-sigs-x-k8s-io-v1beta1-module,mutating=false,failurePolicy=fail,sideEffects=None,groups=kmm.sigs.x-k8s.io,resources=modules,verbs=create;update,versions=v1beta1,name=vmodule.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &Module{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (m *Module) ValidateCreate() error { + modulelog.Info("Validating Module creation", "name", m.Name, "namespace", m.Namespace) + + if err := m.validateKernelMapping(); err != nil { + return fmt.Errorf("failed to validate kernel mappings: %v", err) + } + + return m.validateModprobe() +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (m *Module) ValidateUpdate(_ runtime.Object) error { + modulelog.Info("Validating Module update", "name", m.Name, "namespace", m.Namespace) + + if err := m.validateKernelMapping(); err != nil { + return fmt.Errorf("failed to validate kernel mappings: %v", err) + } + + return m.validateModprobe() +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (m *Module) ValidateDelete() error { + return nil +} + +func (m *Module) validateKernelMapping() error { + container := m.Spec.ModuleLoader.Container + + for idx, km := range container.KernelMappings { + if km.Regexp != "" && km.Literal != "" { + return fmt.Errorf("regexp and literal are mutually exclusive properties at kernelMappings[%d]", idx) + } + + if km.Regexp == "" && km.Literal == "" { + return fmt.Errorf("regexp or literal must be set at kernelMappings[%d]", idx) + } + + if _, err := regexp.Compile(km.Regexp); err != nil { + return fmt.Errorf("invalid regexp at index %d: %v", idx, err) + } + + if container.ContainerImage == "" && km.ContainerImage == "" { + return fmt.Errorf("missing spec.moduleLoader.container.kernelMappings[%d].containerImage", idx) + } + } + + return nil +} + +func (m *Module) validateModprobe() error { + modprobe := m.Spec.ModuleLoader.Container.Modprobe + moduleNameDefined := modprobe.ModuleName != "" + rawLoadArgsDefined := modprobe.RawArgs != nil && len(modprobe.RawArgs.Load) > 0 + rawUnloadArgsDefined := modprobe.RawArgs != nil && len(modprobe.RawArgs.Unload) > 0 + + if moduleNameDefined { + if rawLoadArgsDefined || rawUnloadArgsDefined { + return errors.New("rawArgs cannot be set when moduleName is set") + } + return nil + } + + if !rawLoadArgsDefined || !rawUnloadArgsDefined { + return errors.New("load and unload rawArgs must be set when moduleName is unset") + } + + return nil +} diff --git a/api/v1beta1/module_webhook_test.go b/api/v1beta1/module_webhook_test.go new file mode 100644 index 000000000..54ceed2f3 --- /dev/null +++ b/api/v1beta1/module_webhook_test.go @@ -0,0 +1,382 @@ +/* +Copyright 2022. + +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. +*/ + +package v1beta1 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "testing" +) + +func TestV1beta1(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "V1beta1 Suite") +} + +var _ = Describe("validateKernelMapping", func() { + It("should pass when there are not kernel mappings", func() { + e := (&Module{}).validateKernelMapping() + Expect(e).ToNot(HaveOccurred()) + }) + + It("should pass when regexp,literal and containerImage are valid", func() { + mod1 := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + KernelMappings: []KernelMapping{ + {Regexp: "valid-regexp", ContainerImage: "image-url"}, + {Regexp: "^.*$", ContainerImage: "image-url"}, + }, + }, + }, + }, + } + Expect(mod1.validateKernelMapping()).ToNot(HaveOccurred()) + + mod2 := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + ContainerImage: "image-url", + KernelMappings: []KernelMapping{ + {Regexp: "valid-regexp"}, + }, + }, + }, + }, + } + Expect(mod2.validateKernelMapping()).ToNot(HaveOccurred()) + + mod3 := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + ContainerImage: "image-url", + KernelMappings: []KernelMapping{ + {Literal: "any-value"}, + }, + }, + }, + }, + } + Expect(mod3.validateKernelMapping()).ToNot(HaveOccurred()) + }) + + It("should fail when an invalid regex is found", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + KernelMappings: []KernelMapping{ + {Regexp: "*-invalid-regexp"}, + }, + }, + }, + }, + } + + e := mod.validateKernelMapping() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To(ContainSubstring("invalid regexp")) + }) + + It("should fail when literal and regex are set", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + KernelMappings: []KernelMapping{ + {Regexp: "^valid-regexp$", Literal: "any-value"}, + }, + }, + }, + }, + } + + e := mod.validateKernelMapping() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To(ContainSubstring("regexp and literal are mutually exclusive properties at kernelMappings")) + }) + + It("should fail when neither literal and regex are set", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + KernelMappings: []KernelMapping{ + {ContainerImage: "image-url"}, + }, + }, + }, + }, + } + + e := mod.validateKernelMapping() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To(ContainSubstring("regexp or literal must be set at kernelMappings")) + }) + + It("should fail when a kernel-mapping has invalid containerName", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + KernelMappings: []KernelMapping{ + {Regexp: "valid-regexp"}, + }, + }, + }, + }, + } + + e := mod.validateKernelMapping() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To( + ContainSubstring( + "missing spec.moduleLoader.container.kernelMappings", + ), + ) + }) +}) + +var _ = Describe("validateModprobe", func() { + It("should fail when moduleName and rawArgs are missing", func() { + mod := &Module{} + + e := mod.validateModprobe() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To(ContainSubstring("load and unload rawArgs must be set when moduleName is unset")) + }) + + It("should fail when rawArgs.load is empty and moduleName is not set", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + Modprobe: ModprobeSpec{ + RawArgs: &ModprobeArgs{ + Load: []string{}, + Unload: []string{"arg"}, + }, + }, + }, + }, + }, + } + + e := mod.validateModprobe() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To( + ContainSubstring("load and unload rawArgs must be set when moduleName is unset"), + ) + }) + + It("should fail when rawArgs and moduleName are set", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + Modprobe: ModprobeSpec{ + ModuleName: "mod-name", + RawArgs: &ModprobeArgs{ + Load: []string{"arg"}, + Unload: []string{"arg"}, + }, + }, + }, + }, + }, + } + + e := mod.validateModprobe() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To( + ContainSubstring("rawArgs cannot be set when moduleName is set"), + ) + }) + + It("should fail when rawArgs.unload is empty and moduleName is not set", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + Modprobe: ModprobeSpec{ + RawArgs: &ModprobeArgs{ + Load: []string{"arg"}, + Unload: []string{}, + }, + }, + }, + }, + }, + } + + e := mod.validateModprobe() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To( + ContainSubstring( + "load and unload rawArgs must be set when moduleName is unset", + ), + ) + }) + + It("should pass when rawArgs has load and unload values and moduleName is not set", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + Modprobe: ModprobeSpec{ + RawArgs: &ModprobeArgs{ + Load: []string{"arg"}, + Unload: []string{"arg"}, + }, + }, + }, + }, + }, + } + + e := mod.validateModprobe() + Expect(e).ToNot(HaveOccurred()) + }) + + It("should pass when modprobe has moduleName and rawArgs are not set", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + Modprobe: ModprobeSpec{ + ModuleName: "module-name", + }, + }, + }, + }, + } + + e := mod.validateModprobe() + Expect(e).ToNot(HaveOccurred()) + }) +}) + +var _ = Describe("ValidateCreate", func() { + It("should pass when all conditions are met", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + Modprobe: ModprobeSpec{ + ModuleName: "mod-name", + }, + KernelMappings: []KernelMapping{ + {Regexp: "valid-regexp", ContainerImage: "image-url"}, + }, + }, + }, + }, + } + + e := mod.ValidateCreate() + Expect(e).ToNot(HaveOccurred()) + }) + + It("should fail when validating kernel mappings regexps", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + KernelMappings: []KernelMapping{ + {Regexp: "*-invalid-regexp"}, + }, + }, + }, + }, + } + + e := mod.ValidateCreate() + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To(ContainSubstring("failed to validate kernel mappings")) + }) +}) + +var _ = Describe("ValidateUpdate", func() { + It("should pass when all conditions are met", func() { + mod1 := &Module{ + + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + Modprobe: ModprobeSpec{ + ModuleName: "module-name", + }, + ContainerImage: "image-url", + KernelMappings: []KernelMapping{ + {Regexp: "valid-regexp"}, + }, + }, + }, + }, + } + + modprobeRawArgs := ModprobeArgs{ + Load: []string{"arg-1"}, Unload: []string{"arg-1"}, + } + mod2 := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + Modprobe: ModprobeSpec{ + RawArgs: &modprobeRawArgs, + }, + ContainerImage: "image-url", + KernelMappings: []KernelMapping{ + {Regexp: "valid-regexp"}, + }, + }, + }, + }, + } + + Expect(mod1.ValidateUpdate(nil)).ToNot(HaveOccurred()) + Expect(mod2.ValidateUpdate(nil)).ToNot(HaveOccurred()) + }) + + It("should fail when validating kernel mappings regexps", func() { + mod := &Module{ + Spec: ModuleSpec{ + ModuleLoader: ModuleLoaderSpec{ + Container: ModuleLoaderContainerSpec{ + KernelMappings: []KernelMapping{ + {Regexp: "*-invalid-regexp"}, + }, + }, + }, + }, + } + + e := mod.ValidateUpdate(nil) + Expect(e).To(HaveOccurred()) + Expect(e.Error()).To(ContainSubstring("failed to validate kernel mappings")) + }) +}) + +var _ = Describe("ValidateDelete", func() { + It("should do nothing and return always nil", func() { + module := &Module{} + Expect(module.ValidateDelete()).ToNot(HaveOccurred()) + }) +}) diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 04edadc4a..249069e2c 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -23,7 +23,7 @@ package v1beta1 import ( "k8s.io/api/core/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. diff --git a/ci/prow/e2e-incluster-build b/ci/prow/e2e-incluster-build index 64fb4e363..eb10cf1df 100755 --- a/ci/prow/e2e-incluster-build +++ b/ci/prow/e2e-incluster-build @@ -30,6 +30,8 @@ kubectl apply -f ci/kmm-kmod-dockerfile.yaml echo "Add Secrets containing the signing key and certificate..." kubectl apply -f ci/secret_kmm-kmod-signing.yaml +kubectl wait --for=condition=Available deployment/kmm-operator-controller-manager -n kmm-operator-system + echo "Add an kmm-ci Module that contains a valid mapping..." kubectl apply -f ci/module-kmm-ci-build-sign.yaml diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 50b60a410..29225bb87 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -178,6 +178,10 @@ func main() { } } + if err = (&v1beta12.Module{}).SetupWebhookWithManager(mgr); err != nil { + cmd.FatalError(setupLogger, err, "unable to create webhook", "webhook", "Module") + } + //+kubebuilder:scaffold:builder if err = mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/config/certificate/certificate.yaml b/config/certificate/certificate.yaml new file mode 100644 index 000000000..cc94e9cc6 --- /dev/null +++ b/config/certificate/certificate.yaml @@ -0,0 +1,39 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: issuer + app.kubernetes.io/instance: selfsigned-issuer + app.kubernetes.io/component: certificate + app.kubernetes.io/created-by: kernel-module-management + app.kubernetes.io/part-of: kernel-module-management + app.kubernetes.io/managed-by: kustomize + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: certificate + app.kubernetes.io/instance: serving-cert + app.kubernetes.io/component: certificate + app.kubernetes.io/created-by: kernel-module-management + app.kubernetes.io/part-of: kernel-module-management + app.kubernetes.io/managed-by: kustomize + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/config/certificate/kustomization.yaml b/config/certificate/kustomization.yaml new file mode 100644 index 000000000..bebea5a59 --- /dev/null +++ b/config/certificate/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/certificate/kustomizeconfig.yaml b/config/certificate/kustomizeconfig.yaml new file mode 100644 index 000000000..90d7c313c --- /dev/null +++ b/config/certificate/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index ec770f628..41c1f6950 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -16,49 +16,44 @@ bases: - ../crd - ../rbac - ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- ../webhook +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml +- ../webhook # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager +- ../certificate # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- manager_webhook_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml +patchesStrategicMerge: + - manager_webhook_patch.yaml + - webhookcainjection_patch.yaml # the following config is for teaching kustomize how to do var substitution vars: -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service + # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. + - name: CERTIFICATE_NAMESPACE # namespace of the certificate CR + objref: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml + fieldref: + fieldpath: metadata.namespace + - name: CERTIFICATE_NAME + objref: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml + - name: SERVICE_NAMESPACE # namespace of the service + objref: + kind: Service + version: v1 + name: webhook-service + fieldref: + fieldpath: metadata.namespace + - name: SERVICE_NAME + objref: + kind: Service + version: v1 + name: webhook-service \ No newline at end of file diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml new file mode 100644 index 000000000..738de350b --- /dev/null +++ b/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml new file mode 100644 index 000000000..18d99aea1 --- /dev/null +++ b/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,16 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + labels: + app.kubernetes.io/name: validatingwebhookconfiguration + app.kubernetes.io/instance: validating-webhook-configuration + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: kernel-module-management + app.kubernetes.io/part-of: kernel-module-management + app.kubernetes.io/managed-by: kustomize + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml new file mode 100644 index 000000000..7ba3f999b --- /dev/null +++ b/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- service.yaml +- manifests.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml new file mode 100644 index 000000000..e809f7820 --- /dev/null +++ b/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,18 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml new file mode 100644 index 000000000..0a265f3b5 --- /dev/null +++ b/config/webhook/manifests.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-kmm-sigs-x-k8s-io-v1beta1-module + failurePolicy: Fail + name: vmodule.kb.io + rules: + - apiGroups: + - kmm.sigs.x-k8s.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - modules + sideEffects: None diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 000000000..6f9c08ecd --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,20 @@ + +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: service + app.kubernetes.io/instance: webhook-service + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: kernel-module-management + app.kubernetes.io/part-of: kernel-module-management + app.kubernetes.io/managed-by: kustomize + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager