From 626246ac4dab5f5a5a6a5046b5a606bd736d128f Mon Sep 17 00:00:00 2001 From: sharmapulkit04 Date: Thu, 17 Jun 2021 01:07:39 +0530 Subject: [PATCH] Added validation webhook,cert-manager,and updated Makefile. - Scaffolded a new validation webhook using operator-sdk - Added manifests for webhook. - Added manifests for self signed issuer and certificates - Added a new spec named ValidateSecurityWarnings to the Jenkins custom resource definition to enable/disable security check. - Updated Makefile to deploy the operator locally. - Updated helm template and default values.yaml --- Makefile | 21 ++ PROJECT | 1 + api/v1alpha2/jenkins_types.go | 4 + api/v1alpha2/jenkins_webhook.go | 61 +++ chart/jenkins-operator/templates/jenkins.yaml | 1 + chart/jenkins-operator/values.yaml | 4 + config/crd/bases/jenkins.io_jenkins.yaml | 4 + config/webhook/manifests.yaml | 29 ++ config/webhook/service.yaml | 12 + main.go | 3 + webhook/all_in_one_v1alpha2.yaml | 357 ++++++++++++++++++ webhook/cert-manager.yaml | 26 ++ webhook/operator.yaml | 64 ++++ webhook/rbac.yaml | 224 +++++++++++ webhook/webhook.yaml | 43 +++ 15 files changed, 854 insertions(+) create mode 100644 api/v1alpha2/jenkins_webhook.go create mode 100644 config/webhook/manifests.yaml create mode 100644 config/webhook/service.yaml create mode 100644 webhook/all_in_one_v1alpha2.yaml create mode 100644 webhook/cert-manager.yaml create mode 100644 webhook/operator.yaml create mode 100644 webhook/rbac.yaml create mode 100644 webhook/webhook.yaml diff --git a/Makefile b/Makefile index a67811a1..e0e15fce 100644 --- a/Makefile +++ b/Makefile @@ -517,3 +517,24 @@ kubebuilder: mkdir -p ${ENVTEST_ASSETS_DIR} test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.0/hack/setup-envtest.sh source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); + +#TODO Integrate with master Makefile. +MANIFESTS := webhook/all_in_one_$(API_VERSION).yaml +all-in-one-build-webhook: ## Re-generate all-in-one yaml + @echo "+ $@" + > $(MANIFESTS) + cat webhook/rbac.yaml >> $(MANIFESTS) + cat webhook/operator.yaml >> $(MANIFESTS) + cat webhook/cert-manager.yaml >> $(MANIFESTS) + cat webhook/webhook.yaml >> $(MANIFESTS) + sed -i "s~{DOCKER_REGISTRY}:{GITCOMMIT}~${DOCKER_REGISTRY}:${GITCOMMIT}~;" ${MANIFESTS} + +# start the cluster locally and set it to use the docker daemon from minikube +install-cert-manager: minikube-start + kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.0/cert-manager.yaml + + +#Launch cert-manager and deploy the operator locally along with webhook +deploy-webhook: install-cert-manager install-crds container-runtime-build all-in-one-build-webhook + @echo "+ $@" + kubectl apply -f ${MANIFESTS} diff --git a/PROJECT b/PROJECT index 7ae7f502..27ed1d17 100644 --- a/PROJECT +++ b/PROJECT @@ -7,6 +7,7 @@ resources: group: jenkins.io kind: Jenkins version: v1alpha2 + webhookVersion: v1 version: 3-alpha plugins: manifests.sdk.operatorframework.io/v2: {} diff --git a/api/v1alpha2/jenkins_types.go b/api/v1alpha2/jenkins_types.go index aad78814..bd4e6607 100644 --- a/api/v1alpha2/jenkins_types.go +++ b/api/v1alpha2/jenkins_types.go @@ -18,6 +18,10 @@ type JenkinsSpec struct { // +optional SeedJobs []SeedJob `json:"seedJobs,omitempty"` + // ValidateSecurityWarnings enables or disables validating potential security warnings in Jenkins plugins via admission webhooks. + //+optional + ValidateSecurityWarnings bool `json:"ValidateSecurityWarnings,omitempty"` + // Notifications defines list of a services which are used to inform about Jenkins status // Can be used to integrate chat services like Slack, Microsoft Teams or Mailgun // +optional diff --git a/api/v1alpha2/jenkins_webhook.go b/api/v1alpha2/jenkins_webhook.go new file mode 100644 index 00000000..92b58457 --- /dev/null +++ b/api/v1alpha2/jenkins_webhook.go @@ -0,0 +1,61 @@ +/* +Copyright 2021. + +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 v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/runtime" + 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 jenkinslog = logf.Log.WithName("jenkins-resource") + +func (in *Jenkins) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(in). + Complete() +} + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +// +kubebuilder:webhook:path=/validate-jenkins-io-jenkins-io-v1alpha2-jenkins,mutating=false,failurePolicy=fail,sideEffects=None,groups=jenkins.io.jenkins.io,resources=jenkins,verbs=create;update,versions=v1alpha2,name=vjenkins.kb.io,admissionReviewVersions={v1,v1beta1} + +var _ webhook.Validator = &Jenkins{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (in *Jenkins) ValidateCreate() error { + jenkinslog.Info("validate create", "name", in.Name) + + // TODO(user): fill in your validation logic upon object creation. + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (in *Jenkins) ValidateUpdate(old runtime.Object) error { + jenkinslog.Info("validate update", "name", in.Name) + + // TODO(user): fill in your validation logic upon object update. + return nil +} + +func (in *Jenkins) ValidateDelete() error { + // TODO(user): fill in your validation logic upon object deletion. + return nil +} diff --git a/chart/jenkins-operator/templates/jenkins.yaml b/chart/jenkins-operator/templates/jenkins.yaml index 3501d2da..9ba138b0 100644 --- a/chart/jenkins-operator/templates/jenkins.yaml +++ b/chart/jenkins-operator/templates/jenkins.yaml @@ -146,6 +146,7 @@ spec: {{- toYaml . | nindent 6 }} {{- end }} {{- with .Values.jenkins.seedJobs }} + ValidateSecurityWarnings: {{ .Values.jenkins.ValidateSecurityWarnings }} seedJobs: {{- toYaml . | nindent 4 }} {{- end }} {{- end }} diff --git a/chart/jenkins-operator/values.yaml b/chart/jenkins-operator/values.yaml index dac2d691..645d8d7b 100644 --- a/chart/jenkins-operator/values.yaml +++ b/chart/jenkins-operator/values.yaml @@ -47,6 +47,10 @@ jenkins: # See https://github.com/jenkinsci/kubernetes-operator/pull/193 for more info disableCSRFProtection: false + + # ValidateSecurityWarnings enables or disables validating potential security warnings in Jenkins plugins via admission webhooks. + ValidateSecurityWarnings: false + # imagePullSecrets is used if you want to pull images from private repository # See https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configuration/#pulling-docker-images-from-private-repositories for more info imagePullSecrets: [] diff --git a/config/crd/bases/jenkins.io_jenkins.yaml b/config/crd/bases/jenkins.io_jenkins.yaml index 9382f383..47d626c6 100644 --- a/config/crd/bases/jenkins.io_jenkins.yaml +++ b/config/crd/bases/jenkins.io_jenkins.yaml @@ -35,6 +35,10 @@ spec: spec: description: Spec defines the desired state of the Jenkins properties: + ValidateSecurityWarnings: + description: ValidateSecurityWarnings enables or disables validating + potential security warnings in Jenkins plugins via admission webhooks. + type: boolean backup: description: 'Backup defines configuration of Jenkins backup More info: https://jenkinsci.github.io/kubernetes-operator/docs/getting-started/latest/configure-backup-and-restore/' diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml new file mode 100644 index 00000000..28d59535 --- /dev/null +++ b/config/webhook/manifests.yaml @@ -0,0 +1,29 @@ + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-jenkins-io-jenkins-io-v1alpha2-jenkins + failurePolicy: Fail + name: vjenkins.kb.io + rules: + - apiGroups: + - jenkins.io.jenkins.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - jenkins + sideEffects: None diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 00000000..31e0f829 --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,12 @@ + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager diff --git a/main.go b/main.go index 82e0cc97..8bc382ea 100644 --- a/main.go +++ b/main.go @@ -169,6 +169,9 @@ func main() { fatal(errors.Wrap(err, "unable to create Jenkins controller"), *debug) } + if err = (&v1alpha2.Jenkins{}).SetupWebhookWithManager(mgr); err != nil { + fatal(errors.Wrap(err, "unable to create Webhook"), *debug) + } // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("health", healthz.Ping); err != nil { diff --git a/webhook/all_in_one_v1alpha2.yaml b/webhook/all_in_one_v1alpha2.yaml new file mode 100644 index 00000000..e53fc659 --- /dev/null +++ b/webhook/all_in_one_v1alpha2.yaml @@ -0,0 +1,357 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: jenkins-operator +--- +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + - coordination.k8s.io + resources: + - configmaps + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: jenkins-operator + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: jenkins-operator +rules: +- apiGroups: + - apps + resources: + - daemonsets + - deployments + - replicasets + - statefulsets + verbs: + - '*' +- apiGroups: + - apps + - jenkins-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - build.openshift.io + resources: + - buildconfigs + - builds + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + - services + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods + - pods/exec + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/portforward + verbs: + - create +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - image.openshift.io + resources: + - imagestreams + verbs: + - get + - list + - watch +- apiGroups: + - jenkins.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - jenkins.io + resources: + - jenkins + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - jenkins.io + resources: + - jenkins/finalizers + verbs: + - update +- apiGroups: + - jenkins.io + resources: + - jenkins/status + verbs: + - get + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - create + - get + - list + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: jenkins-operator +subjects: +- kind: ServiceAccount + name: jenkins-operator +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: jenkins-operator + labels: + control-plane: controller-manager +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + serviceAccountName: jenkins-operator + securityContext: + runAsUser: 65532 + containers: + - command: + - /manager + args: + - --leader-elect + image: jenkins-operator:6f33fe82-dirty + name: jenkins-operator + imagePullPolicy: IfNotPresent + securityContext: + allowPrivilegeEscalation: false + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert + terminationGracePeriodSeconds: 10 +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: webhook-certificate + namespace: default +spec: + duration: 2160h + renewBefore: 360h + secretName: webhook-server-cert + dnsNames: + - webhook-service.default.svc + - webhook-service.default.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned + +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: selfsigned + namespace: default +spec: + selfSigned: {} + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: default/webhook-certificate +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: default + path: /validate-jenkins-io-v1alpha2-jenkins + failurePolicy: Fail + name: vjenkins.kb.io + rules: + - apiGroups: + - jenkins.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - jenkins + sideEffects: None + +--- +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: default +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager +--- diff --git a/webhook/cert-manager.yaml b/webhook/cert-manager.yaml new file mode 100644 index 00000000..8538c70b --- /dev/null +++ b/webhook/cert-manager.yaml @@ -0,0 +1,26 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: webhook-certificate + namespace: default +spec: + duration: 2160h + renewBefore: 360h + secretName: webhook-server-cert + dnsNames: + - webhook-service.default.svc + - webhook-service.default.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned + +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: selfsigned + namespace: default +spec: + selfSigned: {} + +--- diff --git a/webhook/operator.yaml b/webhook/operator.yaml new file mode 100644 index 00000000..81cbdf98 --- /dev/null +++ b/webhook/operator.yaml @@ -0,0 +1,64 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: jenkins-operator + labels: + control-plane: controller-manager +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + serviceAccountName: jenkins-operator + securityContext: + runAsUser: 65532 + containers: + - command: + - /manager + args: + - --leader-elect + image: {DOCKER_REGISTRY}:{GITCOMMIT} + name: jenkins-operator + imagePullPolicy: IfNotPresent + securityContext: + allowPrivilegeEscalation: false + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert + terminationGracePeriodSeconds: 10 +--- diff --git a/webhook/rbac.yaml b/webhook/rbac.yaml new file mode 100644 index 00000000..959b6aa0 --- /dev/null +++ b/webhook/rbac.yaml @@ -0,0 +1,224 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: jenkins-operator +--- +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + - coordination.k8s.io + resources: + - configmaps + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: jenkins-operator + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: jenkins-operator +rules: +- apiGroups: + - apps + resources: + - daemonsets + - deployments + - replicasets + - statefulsets + verbs: + - '*' +- apiGroups: + - apps + - jenkins-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - build.openshift.io + resources: + - buildconfigs + - builds + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - configmaps + - secrets + - services + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods + - pods/exec + verbs: + - '*' +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods/portforward + verbs: + - create +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - image.openshift.io + resources: + - imagestreams + verbs: + - get + - list + - watch +- apiGroups: + - jenkins.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - jenkins.io + resources: + - jenkins + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - jenkins.io + resources: + - jenkins/finalizers + verbs: + - update +- apiGroups: + - jenkins.io + resources: + - jenkins/status + verbs: + - get + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - create + - get + - list + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: jenkins-operator +subjects: +- kind: ServiceAccount + name: jenkins-operator +--- diff --git a/webhook/webhook.yaml b/webhook/webhook.yaml new file mode 100644 index 00000000..3a86ab0b --- /dev/null +++ b/webhook/webhook.yaml @@ -0,0 +1,43 @@ +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: default/webhook-certificate +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: default + path: /validate-jenkins-io-v1alpha2-jenkins + failurePolicy: Fail + name: vjenkins.kb.io + rules: + - apiGroups: + - jenkins.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - jenkins + sideEffects: None + +--- +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: default +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager +---