diff --git a/common/playbooks/repository-nexus.yaml b/common/playbooks/repository-nexus.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d3f40c18f022bad65b298ce7e94065138daa0454 --- /dev/null +++ b/common/playbooks/repository-nexus.yaml @@ -0,0 +1,186 @@ +--- +# Secrets in "/nexus" (user passwords): +# +# * admin +# * binder +# * notebooks-reader +# * notebooks-writer +# +# (the list is dynamic based on templates/nexus/user-*.yaml) +# +- name: Sonatype Nexus Deployment + hosts: master[0] + vars: + nexus_url: "https://{{ nexus_hostname }}/service/rest/v1" + nexus_blobstore_name: default + nexus_blobstore_type: file + nexus_repository_binder_name: binder + nexus_repository_notebooks_name: notebooks + nexus_repository_port: 8082 + secrets: "{{ lookup('community.hashi_vault.hashi_vault', (vault_mount_point, 'nexus') | join('/'), token_validate=false) }}" + become: true + tasks: + - name: Debug Secrets + debug: + msg: "{{ item.key }} = {{ item.value }}" + loop: "{{ secrets | dict2items }}" + - name: Create Nexus configuration file on master + vars: + name: nexus + template: + src: templates/nexus.yaml + dest: /tmp/nexus.yaml + mode: 0600 + - name: Deploy/update Nexus instance + command: kubectl apply -f /tmp/nexus.yaml + environment: + KUBECONFIG: /etc/kubernetes/admin.conf + PATH: /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin + changed_when: true + when: true + - name: Wait for Nexus pod ready + command: kubectl wait pod --all --namespace nexus --for condition=ready --timeout=5m + environment: + KUBECONFIG: /etc/kubernetes/admin.conf + changed_when: false + when: true + # workaround problem with kubectl wait + retries: 1 + - name: Wait for Nexus REST API + uri: + url: "{{ nexus_url }}/status" + status_code: 200 + method: GET + register: _result + until: _result.status == 200 + retries: 120 + delay: 15 + - name: Check the admin password + uri: + url: "{{ nexus_url }}/status" + force_basic_auth: true + method: HEAD + user: 'admin' + password: "{{ secrets['admin'] }}" + status_code: 200, 401 + register: nexus_admin_password_check + - name: Admin password setup + when: + - nexus_admin_password_check.status == 401 + block: + - name: Get initial admin password + shell: 'kubectl exec -it -n nexus $(kubectl get pod -n nexus -l app=sonatype-nexus -o name) -- cat /nexus-data/admin.password' + register: nexus_admin_password_initial + changed_when: false + environment: + KUBECONFIG: /etc/kubernetes/admin.conf + - name: Set the admin password + uri: + url: "{{ nexus_url }}/security/users/admin/change-password" + force_basic_auth: true + headers: + Content-Type: text/plain + method: PUT + user: 'admin' + password: "{{ nexus_admin_password_initial.stdout }}" + body: "{{ secrets['admin'] }}" + body_format: raw + status_code: [200, 204] + rescue: + - name: Admin Password Setup Fail + fail: + msg: "Failed admin password setup" + - name: Check blobstore + uri: + url: "{{ nexus_url }}/blobstores/{{ nexus_blobstore_type }}/{{ nexus_blobstore_name }}" + force_basic_auth: true + user: 'admin' + password: "{{ secrets['admin'] }}" + # XXX: workaround REST API bug for S3 (Nexus 3.33.0-01) + status_code: [200, 400, 404, 500] + register: nexus_blobstore_check + # XXX: REST API bug II - needs to be created manually + - name: Create blobstore + when: &blobstore_changed + - nexus_blobstore_check.status == 404 or nexus_blobstore_check.status == 400 + uri: + url: "{{ nexus_url }}/blobstores/{{ nexus_blobstore_type }}" + force_basic_auth: true + method: POST + user: 'admin' + password: "{{ secrets['admin'] }}" + body: "{{ lookup('template', 'templates/nexus/blobstore.yaml') | from_yaml }}" + body_format: json + status_code: [200, 201] + changed_when: *blobstore_changed + - name: Check repository + uri: + url: "{{ nexus_url }}/repositories/docker/hosted/{{ nexus_repository_binder_name }}" + force_basic_auth: true + user: 'admin' + password: "{{ secrets['admin'] }}" + status_code: [200, 404] + register: nexus_repository_check + - name: Delete original repositories + when: &repositories_deleted + - nexus_repository_check.status == 404 + uri: + url: "{{ nexus_url }}/repositories/{{ item }}" + force_basic_auth: true + method: DELETE + user: 'admin' + password: "{{ secrets['admin'] }}" + status_code: [200, 204, 404] + register: _result + loop: + - maven-central + - maven-public + - maven-releases + - maven-snapshots + - nuget-group + - nuget-hosted + - nuget.org-proxy + changed_when: _result.status == 200 or _result.status == 204 + - name: Create repositories + include_tasks: subtasks/nexus-repository.yaml + loop: + - name: "{{ nexus_repository_binder_name }}" + type: docker/hosted + - name: "{{ nexus_repository_notebooks_name }}" + type: docker/hosted + - name: Create roles + include_tasks: subtasks/nexus-role.yaml + vars: + rolename: "{{ item | basename | splitext | first | regex_replace('^role-', '') }}" + with_fileglob: + - "templates/nexus/role-*.yaml" + - name: Create users + include_tasks: subtasks/nexus-user.yaml + vars: + username: "{{ item | basename | splitext | first | regex_replace('^user-', '') }}" + with_fileglob: + - "templates/nexus/user-*.yaml" + - name: Check security realms + uri: + url: "{{ nexus_url }}/security/realms/active" + force_basic_auth: true + user: 'admin' + password: "{{ secrets['admin'] }}" + return_content: true + register: nexus_realms_check + - name: Update securty realms + when: &realms_changed + - '"DockerToken" not in nexus_realms_check.content' + uri: + url: "{{ nexus_url }}/security/realms/active" + force_basic_auth: true + headers: + accept: application/json + Content-Type: application/json + method: PUT + user: 'admin' + password: "{{ secrets['admin'] }}" + body: "{{ lookup('template', 'templates/nexus/realms.yaml') | from_yaml }}" + body_format: json + status_code: [200, 204] + changed_when: *realms_changed diff --git a/common/playbooks/subtasks/nexus-repository.yaml b/common/playbooks/subtasks/nexus-repository.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4787166250c18baf9785c5860598fa4b870e35af --- /dev/null +++ b/common/playbooks/subtasks/nexus-repository.yaml @@ -0,0 +1,23 @@ +--- +- name: Check repository {{ item.name }} + uri: + url: "{{ nexus_url }}/repositories/{{ item.type }}/{{ item.name }}" + force_basic_auth: true + user: 'admin' + password: "{{ secrets['admin'] }}" + status_code: [200, 404] + register: nexus_repository_check + +- name: Create repository {{ item.name }} + when: &repository_created + - nexus_repository_check.status == 404 + uri: + url: "{{ nexus_url }}/repositories/{{ item.type }}" + force_basic_auth: true + method: POST + user: 'admin' + password: "{{ secrets['admin'] }}" + body: "{{ lookup('template', 'templates/nexus/repository-' + item.name + '.yaml') | from_yaml }}" + body_format: json + status_code: [200, 201] + changed_when: *repository_created diff --git a/common/playbooks/subtasks/nexus-role.yaml b/common/playbooks/subtasks/nexus-role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..498c4989f966355cedb16effcc3e5ff16150d8e6 --- /dev/null +++ b/common/playbooks/subtasks/nexus-role.yaml @@ -0,0 +1,23 @@ +--- +- name: Check role {{ rolename }} + uri: + url: "{{ nexus_url }}/security/roles/{{ rolename }}" + force_basic_auth: true + user: 'admin' + password: "{{ secrets['admin'] }}" + status_code: [200, 404] + register: nexus_role_check + +- name: Create role {{ rolename }} + when: &role_created + - nexus_role_check.status == 404 + uri: + url: "{{ nexus_url }}/security/roles" + force_basic_auth: true + method: POST + user: 'admin' + password: "{{ secrets['admin'] }}" + body: "{{ lookup('template', item) | from_yaml }}" + body_format: json + status_code: [200, 201] + changed_when: *role_created diff --git a/common/playbooks/subtasks/nexus-user.yaml b/common/playbooks/subtasks/nexus-user.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0d3a7a1cab476674c95f7e0672e893087793588b --- /dev/null +++ b/common/playbooks/subtasks/nexus-user.yaml @@ -0,0 +1,27 @@ +--- +- name: Check user {{ username }} + uri: + url: "{{ nexus_url }}/security/users?userId={{ username }}" + force_basic_auth: true + user: 'admin' + password: "{{ secrets['admin'] }}" + return_content: true + status_code: [200, 404] + register: nexus_user_check + +- name: Create user {{ username }} + when: &user_created + - username not in nexus_user_check.content + uri: + url: "{{ nexus_url }}/security/users" + force_basic_auth: true + headers: + accept: application/json + Content-Type: application/json + method: POST + user: 'admin' + password: "{{ secrets['admin'] }}" + body: "{{ lookup('template', item) | from_yaml }}" + body_format: json + status_code: [200, 201] + changed_when: *user_created diff --git a/common/playbooks/templates/nexus.yaml b/common/playbooks/templates/nexus.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6208be20aaffcdae41b717cf1f34867191843510 --- /dev/null +++ b/common/playbooks/templates/nexus.yaml @@ -0,0 +1,149 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{ name }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: nexus-pvc + namespace: {{ name }} + labels: + app: sonatype-nexus +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 500Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nexus + namespace: {{ name }} + labels: + app: sonatype-nexus +spec: + replicas: 1 + selector: + matchLabels: + app: sonatype-nexus + template: + metadata: + labels: + app: sonatype-nexus + spec: + containers: + - image: sonatype/nexus3 + imagePullPolicy: Always + name: nexus + ports: + - containerPort: 8081 + - containerPort: {{ nexus_repository_port }} + - containerPort: {{ nexus_repository_port + 1 }} + resources: + requests: + cpu: 2 + limits: + cpu: 4 + volumeMounts: + - mountPath: /nexus-data + name: nexus-data-volume + volumes: + - name: nexus-data-volume + persistentVolumeClaim: + claimName: nexus-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: nexus + namespace: {{ name }} +spec: + ports: + - port: 80 + targetPort: 8081 + protocol: TCP + name: http + - port: 5000 + targetPort: {{ nexus_repository_port }} + protocol: TCP + name: docker-container-notebooks + - port: 5001 + targetPort: {{ nexus_repository_port + 1 }} + protocol: TCP + name: docker-repository + selector: + app: sonatype-nexus +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: nexus-ingress + namespace: nexus + annotations: + kubernetes.io/ingress.class: "nginx" + kubernetes.io/tls-acme: "true" + ingress.kubernetes.io/proxy-body-size: 100m + nginx.ingress.kubernetes.io/proxy-connect-timeout: "15" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-request-buffering: "on" +spec: + tls: + - hosts: + - {{ nexus_hostname }} + - {{ registry_binder_hostname }} + - {{ registry_notebooks_hostname }} + secretName: acme-tls-{{ name }} + rules: + - host: {{ nexus_hostname }} + http: + paths: + - backend: + service: + name: nexus + port: + number: 80 + path: / + pathType: Prefix + - host: {{ registry_binder_hostname }} + http: + paths: + - backend: + service: + name: nexus + port: + number: 5000 + path: / + pathType: Prefix + - host: {{ registry_notebooks_hostname }} + http: + paths: + - backend: + service: + name: nexus + port: + number: 5001 + path: / + pathType: Prefix +# direct access without nginx layer and SSL (for debugging) +# --- +# apiVersion: v1 +# kind: Service +# metadata: +# name: nexus-repository-direct +# namespace: {{ name }} +# spec: +# type: NodePort +# selector: +# app: sonatype-nexus +# ports: +# - port: 5002 +# targetPort: {{ nexus_repository_port + 1 }} +# protocol: TCP +# nodePort: 31444 +# externalIPs: {{ groups['ingress'] }} diff --git a/common/playbooks/templates/nexus/blobstore.yaml b/common/playbooks/templates/nexus/blobstore.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5c46365df74a4d3c86bf6c5b1949382f332aedb1 --- /dev/null +++ b/common/playbooks/templates/nexus/blobstore.yaml @@ -0,0 +1,4 @@ +--- +name: {{ nexus_blobstore_name }} + +path: default diff --git a/common/playbooks/templates/nexus/realms.yaml b/common/playbooks/templates/nexus/realms.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c49f88fc96a6193fc06a48897e2dbdeb26c3067e --- /dev/null +++ b/common/playbooks/templates/nexus/realms.yaml @@ -0,0 +1,3 @@ +--- +- NexusAuthenticatingRealm +- DockerToken diff --git a/common/playbooks/templates/nexus/repository-binder.yaml b/common/playbooks/templates/nexus/repository-binder.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5cf07bddac8402390dc059a6d24422c1187d1020 --- /dev/null +++ b/common/playbooks/templates/nexus/repository-binder.yaml @@ -0,0 +1,12 @@ +--- +name: {{ nexus_repository_binder_name }} +online: true +storage: + blobStoreName: {{ nexus_blobstore_name }} + strictContentTypeValidation: true + writePolicy: allow +docker: + v1Enabled: false + # basic-auth worked only with binder 0.2.0-n577.h14cc6c7 + jupyterhub 0.11.1 + forceBasicAuth: false + httpPort: {{ nexus_repository_port }} diff --git a/common/playbooks/templates/nexus/repository-notebooks.yaml b/common/playbooks/templates/nexus/repository-notebooks.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7447e5f663e2e514e18615498aeb6bc3bbfd66f8 --- /dev/null +++ b/common/playbooks/templates/nexus/repository-notebooks.yaml @@ -0,0 +1,11 @@ +--- +name: {{ nexus_repository_notebooks_name }} +online: true +storage: + blobStoreName: {{ nexus_blobstore_name }} + strictContentTypeValidation: true + writePolicy: allow +docker: + v1Enabled: false + forceBasicAuth: true + httpPort: {{ nexus_repository_port + 1 }} diff --git a/common/playbooks/templates/nexus/role-anonymous.yaml b/common/playbooks/templates/nexus/role-anonymous.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f88aaf0e1577c9843bc5b6db948c329a95d6045f --- /dev/null +++ b/common/playbooks/templates/nexus/role-anonymous.yaml @@ -0,0 +1,10 @@ +--- +id: anonymous +name: anonymous +description: Anonymous Role for Jupyter Binder repository manager +# only explicit repository read roles to avoid access to the internal repository +privileges: + - nx-healthcheck-read + - nx-repository-view-docker-{{ nexus_repository_binder_name }}-browse + - nx-repository-view-docker-{{ nexus_repository_binder_name }}-read + - nx-search-read diff --git a/common/playbooks/templates/nexus/role-binder.yaml b/common/playbooks/templates/nexus/role-binder.yaml new file mode 100644 index 0000000000000000000000000000000000000000..977d88721bc65bcca1c14fc15b08c5a0ee11a7f4 --- /dev/null +++ b/common/playbooks/templates/nexus/role-binder.yaml @@ -0,0 +1,10 @@ +--- +id: binder +name: binder +description: Jupyter Binder +privileges: + - nx-repository-view-docker-{{ nexus_repository_binder_name }}-add + - nx-repository-view-docker-{{ nexus_repository_binder_name }}-edit + - nx-repository-view-docker-{{ nexus_repository_binder_name }}-read +roles: + - anonymous diff --git a/common/playbooks/templates/nexus/role-notebooks-read.yaml b/common/playbooks/templates/nexus/role-notebooks-read.yaml new file mode 100644 index 0000000000000000000000000000000000000000..13a2e7bcb5a5ea3ef70a6e790e36503309803776 --- /dev/null +++ b/common/playbooks/templates/nexus/role-notebooks-read.yaml @@ -0,0 +1,7 @@ +--- +id: {{ nexus_repository_notebooks_name }}-read +name: {{ nexus_repository_notebooks_name }}-read +description: Jupyter Notebooks internal repository read access +privileges: + - nx-repository-view-docker-{{ nexus_repository_notebooks_name }}-browse + - nx-repository-view-docker-{{ nexus_repository_notebooks_name }}-read diff --git a/common/playbooks/templates/nexus/role-notebooks-write.yaml b/common/playbooks/templates/nexus/role-notebooks-write.yaml new file mode 100644 index 0000000000000000000000000000000000000000..38f5883ca5b9da706212ca57357a234ddd4a7eb8 --- /dev/null +++ b/common/playbooks/templates/nexus/role-notebooks-write.yaml @@ -0,0 +1,10 @@ +--- +id: {{ nexus_repository_notebooks_name }}-write +name: {{ nexus_repository_notebooks_name }}-write +description: Jupyter Notebooks internal repository write access +privileges: + - nx-repository-view-docker-{{ nexus_repository_notebooks_name }}-add + - nx-repository-view-docker-{{ nexus_repository_notebooks_name }}-browse + - nx-repository-view-docker-{{ nexus_repository_notebooks_name }}-delete + - nx-repository-view-docker-{{ nexus_repository_notebooks_name }}-edit + - nx-repository-view-docker-{{ nexus_repository_notebooks_name }}-read diff --git a/common/playbooks/templates/nexus/user-binder.yaml b/common/playbooks/templates/nexus/user-binder.yaml new file mode 100644 index 0000000000000000000000000000000000000000..032c8fd512ade0a168a51f24957dd531f6315b64 --- /dev/null +++ b/common/playbooks/templates/nexus/user-binder.yaml @@ -0,0 +1,9 @@ +--- +userId: binder +firstName: Jupyter Binder +lastName: Writer +emailAddress: binder@{{ registry_binder_hostname }} +password: {{ secrets['binder'] }} +status: active +roles: + - binder diff --git a/common/playbooks/templates/nexus/user-notebooks-reader.yaml b/common/playbooks/templates/nexus/user-notebooks-reader.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0329bc03b0a0178f2146523b0107be53ed3b488d --- /dev/null +++ b/common/playbooks/templates/nexus/user-notebooks-reader.yaml @@ -0,0 +1,9 @@ +--- +userId: notebooks-reader +firstName: Jupyter Notebooks +lastName: Reader +emailAddress: notebooks-reader@{{ registry_notebooks_hostname }} +password: {{ secrets['notebooks-reader'] }} +status: active +roles: + - {{ nexus_repository_notebooks_name }}-read diff --git a/common/playbooks/templates/nexus/user-notebooks-writer.yaml b/common/playbooks/templates/nexus/user-notebooks-writer.yaml new file mode 100644 index 0000000000000000000000000000000000000000..40c3fc0a6b38eeb5c4ad7ce0de104419b954dcb3 --- /dev/null +++ b/common/playbooks/templates/nexus/user-notebooks-writer.yaml @@ -0,0 +1,9 @@ +--- +userId: notebooks-writer +firstName: Jupyter Notebooks +lastName: Writer +emailAddress: notebooks-writer@{{ registry_notebooks_hostname }} +password: {{ secrets['notebooks-writer'] }} +status: active +roles: + - {{ nexus_repository_notebooks_name }}-write