diff --git a/Makefile b/Makefile index 201d3679..7779d90d 100644 --- a/Makefile +++ b/Makefile @@ -96,8 +96,8 @@ e2e: deepcopy-gen manifests ## Runs e2e tests, you can use EXTRA_ARGS .PHONY: helm-e2e IMAGE_NAME := $(DOCKER_REGISTRY):$(GITCOMMIT) -#TODO: install cert-manager before running helm charts -helm-e2e: helm container-runtime-build ## Runs helm e2e tests, you can use EXTRA_ARGS + +helm-e2e: helm container-runtime-build ## Runs helm e2e tests, you can use EXTRA_ARGS @echo "+ $@" RUNNING_TESTS=1 go test -parallel=1 "./test/helm/" -ginkgo.v -tags "$(BUILDTAGS) cgo" -v -timeout 60m -run "$(E2E_TEST_SELECTOR)" -image-name=$(IMAGE_NAME) $(E2E_TEST_ARGS) @@ -519,25 +519,14 @@ kubebuilder: 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 v1.5.1 install-cert-manager: minikube-start kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.1/cert-manager.yaml uninstall-cert-manager: minikube-start kubectl delete -f https://github.com/jetstack/cert-manager/releases/download/v1.5.1/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 +# Deploy the operator locally along with webhook using helm charts +deploy-webhook: container-runtime-build @echo "+ $@" - kubectl apply -f ${MANIFESTS} + bin/helm upgrade jenkins chart/jenkins-operator --install --set-string operator.image=${IMAGE_NAME} --set webhook.enabled=true --set jenkins.enabled=false diff --git a/deploy/all-in-one-webhook.yaml b/deploy/all-in-one-webhook.yaml new file mode 100644 index 00000000..a3e6a738 --- /dev/null +++ b/deploy/all-in-one-webhook.yaml @@ -0,0 +1,67 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: jenkins-webhook-certificate + namespace: default +spec: + duration: 2160h + renewBefore: 360h + secretName: jenkins-webhook-certificate + dnsNames: + - jenkins-webhook-service.default.svc + - jenkins-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: + name: jenkins-webhook + annotations: + cert-manager.io/inject-ca-from: default/jenkins-webhook-certificate +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: jenkins-webhook-service + namespace: default + path: /validate-jenkins-io-v1alpha2-jenkins + failurePolicy: Fail + name: vjenkins.kb.io + timeoutSeconds: 30 + rules: + - apiGroups: + - jenkins.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - jenkins + scope: "Namespaced" + sideEffects: None +--- +apiVersion: v1 +kind: Service +metadata: + name: jenkins-webhook-service + namespace: default +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager +--- diff --git a/webhook/cert-manager.yaml b/deploy/cert-manager.yaml similarity index 60% rename from webhook/cert-manager.yaml rename to deploy/cert-manager.yaml index 8538c70b..24383a0e 100644 --- a/webhook/cert-manager.yaml +++ b/deploy/cert-manager.yaml @@ -1,15 +1,15 @@ apiVersion: cert-manager.io/v1 kind: Certificate metadata: - name: webhook-certificate + name: jenkins-webhook-certificate namespace: default spec: duration: 2160h renewBefore: 360h - secretName: webhook-server-cert + secretName: jenkins-webhook-certificate dnsNames: - - webhook-service.default.svc - - webhook-service.default.svc.cluster.local + - jenkins-webhook-service.default.svc + - jenkins-webhook-service.default.svc.cluster.local issuerRef: kind: Issuer name: selfsigned @@ -21,6 +21,4 @@ metadata: name: selfsigned namespace: default spec: - selfSigned: {} - ---- + selfSigned: {} \ No newline at end of file diff --git a/webhook/webhook.yaml b/deploy/webhook.yaml similarity index 74% rename from webhook/webhook.yaml rename to deploy/webhook.yaml index 9cc2a2b7..26f6c3a5 100644 --- a/webhook/webhook.yaml +++ b/deploy/webhook.yaml @@ -1,16 +1,17 @@ +--- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - name: validating-webhook-configuration + name: jenkins-webhook annotations: - cert-manager.io/inject-ca-from: default/webhook-certificate + cert-manager.io/inject-ca-from: default/jenkins-webhook-certificate webhooks: - admissionReviewVersions: - v1 - v1beta1 clientConfig: service: - name: webhook-service + name: jenkins-webhook-service namespace: default path: /validate-jenkins-io-v1alpha2-jenkins failurePolicy: Fail @@ -26,18 +27,19 @@ webhooks: - UPDATE resources: - jenkins + scope: "Namespaced" sideEffects: None - --- apiVersion: v1 kind: Service metadata: - name: webhook-service + name: jenkins-webhook-service namespace: default spec: ports: - port: 443 targetPort: 9443 selector: - control-plane: controller-manager + name: jenkins-operator --- + diff --git a/webhook/all_in_one_v1alpha2.yaml b/webhook/all_in_one_v1alpha2.yaml deleted file mode 100644 index 18afb801..00000000 --- a/webhook/all_in_one_v1alpha2.yaml +++ /dev/null @@ -1,357 +0,0 @@ ---- -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:37d0eac4-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: 200m - memory: 200Mi - requests: - cpu: 100m - memory: 80Mi - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - 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: - 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 - timeoutSeconds: 30 - 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/operator.yaml b/webhook/operator.yaml deleted file mode 100644 index 01093af2..00000000 --- a/webhook/operator.yaml +++ /dev/null @@ -1,65 +0,0 @@ ---- -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 - - --validate-security-warnings - 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: 200m - memory: 200Mi - requests: - cpu: 100m - memory: 80Mi - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert - terminationGracePeriodSeconds: 10 ---- diff --git a/webhook/rbac.yaml b/webhook/rbac.yaml deleted file mode 100644 index 959b6aa0..00000000 --- a/webhook/rbac.yaml +++ /dev/null @@ -1,224 +0,0 @@ ---- -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/website/content/en/docs/Developer Guide/_index.md b/website/content/en/docs/Developer Guide/_index.md index 4929368b..7f13b448 100644 --- a/website/content/en/docs/Developer Guide/_index.md +++ b/website/content/en/docs/Developer Guide/_index.md @@ -241,7 +241,7 @@ kubectl --context remote-k8s --namespace default get po Tests are written using [Ginkgo](https://onsi.github.io/ginkgo/) with [Gomega](https://onsi.github.io/gomega/). -Run unit tests with go fmt, lint, statickcheck, vet: +Run unit tests with go fmt, lint, staticcheck, vet: ```bash make verify @@ -262,6 +262,12 @@ make minikube-start make e2e ``` +Run Helm e2e tests: +```bash +eval $(bin/minikube docker-env) +make helm-e2e +``` + Run the specific e2e test: ```bash @@ -292,8 +298,13 @@ kubectl get secret jenkins-operator-credentials- -o 'jsonpath={.data.us kubectl get secret jenkins-operator-credentials- -o 'jsonpath={.data.password}' | base64 -d ``` - - +### Webhook +To deploy the operator along with webhook, run : +```bash +eval $(minikube docker-env) +make deploy-webhook +``` +It uses [cert-manager](https://cert-manager.io/) as an external dependency. ## Self-learning @@ -304,6 +315,8 @@ kubectl get secret jenkins-operator-credentials- -o 'jsonpath={.data.pa * [Operator SDK Tutorial for Go](https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/) +* [Kubebuilder Validating Webhook Implementation](https://book.kubebuilder.io/cronjob-tutorial/webhook-implementation.html) + [dep_tool]:https://golang.github.io/dep/docs/installation.html [git_tool]:https://git-scm.com/downloads [go_tool]:https://golang.org/dl/ @@ -314,4 +327,3 @@ kubectl get secret jenkins-operator-credentials- -o 'jsonpath={.data.pa [minikube]:https://kubernetes.io/docs/tasks/tools/install-minikube/ [virtualbox]:https://www.virtualbox.org/wiki/Downloads [install_dev_tools]:https://jenkinsci.github.io/kubernetes-operator/docs/developer-guide/tools/ - diff --git a/website/content/en/docs/Getting Started/latest/installing-the-operator.md b/website/content/en/docs/Getting Started/latest/installing-the-operator.md index 5d05da3a..236a3057 100644 --- a/website/content/en/docs/Getting Started/latest/installing-the-operator.md +++ b/website/content/en/docs/Getting Started/latest/installing-the-operator.md @@ -892,4 +892,98 @@ below is the full list of those volumeMounts: * jenkins-home * scripts * init-configuration -* operator-credentials \ No newline at end of file +* operator-credentials + +## Validating Webhook +Validating webhook can be used in order to increase the Operator's capabilities to monitor security issues. It will look for security vulnerabilities in the base and requested plugins. It can be easily installed via Helm charts by setting webhook.enabled in values.yaml. + + +**Note**: The webhook takes some time to get up and running. It's recommended to first deploy the Operator and later Jenkins Custom Resource by using toggles in `values.yaml`. +For the installation with yaml manifests (without using Helm chart), first, install cert-manager: + +```bash +kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.1/cert-manager.yaml +``` + +It takes some time to get cert-manager up and running. +Then, install the webhook and other required resources: + +```bash +kubectl apply -f https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/deploy/all-in-one-webhook.yaml +``` + +Now, download the manifests for the operator and other resources from [here](https://raw.githubusercontent.com/jenkinsci/kubernetes-operator/master/deploy/all-in-one-v1alpha2.yaml) and please provide these additional fields in the operator manifest: + +
+
+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
+        - --validate-security-warnings
+        image: jenkins-operator:54231733-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: 200m
+            memory: 100Mi
+          requests:
+            cpu: 100m
+            memory: 20Mi
+        env:
+          - name: WATCH_NAMESPACE
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.namespace
+        volumeMounts:
+          - mountPath: /tmp/k8s-webhook-server/serving-certs
+            name: webhook-certs
+            readOnly: true       
+      volumes:
+      - name: webhook-certs
+        secret:
+          defaultMode: 420
+          secretName: jenkins-webhook-certificate
+      terminationGracePeriodSeconds: 10
+
+
+ +To enable security validation in the jenkins custom resource,set + +>jenkins.ValidateSecurityWarnings=true + diff --git a/website/content/en/docs/How it works/architecture-and-design.md b/website/content/en/docs/How it works/architecture-and-design.md index c9434c7d..221a69db 100644 --- a/website/content/en/docs/How it works/architecture-and-design.md +++ b/website/content/en/docs/How it works/architecture-and-design.md @@ -35,3 +35,7 @@ The **Jenkins Operator** design incorporates the following concepts: Operator state is kept in the custom resource status section, which is used for storing any configuration events or job statuses managed by the operator. It helps to maintain or recover the desired state even after the operator or Jenkins restarts. + +## Webhook + +It rejects/accepts admission requests based on potential security warnings in plugins present in the Jenkins Custom Resource.