Skip to content
Snippets Groups Projects
Commit fcfb3434 authored by František Dvořák's avatar František Dvořák
Browse files

Sonatype Nexus repository deployment

* use Hashicorp Vault
* document used secrets in playbook
* two repositories: private for notebooks and public for binder
* required 2 vCPU, recommended 4 vCPU
* hack due to kubectl wait problem
* increased timeouts for bigger images
parent d1d9e51f
No related branches found
No related tags found
No related merge requests found
Showing
with 502 additions and 0 deletions
---
# 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
---
- 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
---
- 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
---
- 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
---
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'] }}
---
name: {{ nexus_blobstore_name }}
path: default
---
- NexusAuthenticatingRealm
- DockerToken
---
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 }}
---
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 }}
---
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
---
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
---
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
---
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
---
userId: binder
firstName: Jupyter Binder
lastName: Writer
emailAddress: binder@{{ registry_binder_hostname }}
password: {{ secrets['binder'] }}
status: active
roles:
- binder
---
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
---
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment