From c704534f80d7b724472f8b940e23fb8d00c44ef8 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Tue, 27 Aug 2024 23:34:37 +0200 Subject: onlyoffice: migrate app to generic storage and new publish framework --- roles/apps/onlyoffice/defaults/main.yml | 25 ++-- roles/apps/onlyoffice/instance/tasks/main.yml | 88 ++++++++++++ .../onlyoffice/instance/templates/pod-spec.yml.j2 | 75 ++++++++++ roles/apps/onlyoffice/tasks/main.yml | 152 +-------------------- roles/apps/onlyoffice/templates/pod-spec.yml.j2 | 102 -------------- 5 files changed, 174 insertions(+), 268 deletions(-) create mode 100644 roles/apps/onlyoffice/instance/tasks/main.yml create mode 100644 roles/apps/onlyoffice/instance/templates/pod-spec.yml.j2 delete mode 100644 roles/apps/onlyoffice/templates/pod-spec.yml.j2 (limited to 'roles') diff --git a/roles/apps/onlyoffice/defaults/main.yml b/roles/apps/onlyoffice/defaults/main.yml index 1ea4773a..7e33368d 100644 --- a/roles/apps/onlyoffice/defaults/main.yml +++ b/roles/apps/onlyoffice/defaults/main.yml @@ -1,30 +1,21 @@ --- -# onlyoffice_app_uid: "960" -# onlyoffice_app_gid: "960" - -onlyoffice_db_uid: "961" -onlyoffice_db_gid: "961" - -# onlyoffice_amqp_uid: "962" -# onlyoffice_amqp_gid: "962" - -# onlyoffice_base_path: /srv/onlyoffice - -# onlyoffice_zfs: -# pool: storage -# name: onlyoffice -# properties: -# compression: lz4 - # onlyoffice_instances: # example: # version: 6.2.1.24 # port: 8600 # hostname: office.example.com # jwt_secret: very-secure-password +# storage: +# type: ... # database: # version: 9.5.25 # password: secret # amqp: # version: 3.8.14 # password: secret +# publish: +# zone: "{{ apps_publish_zone__foo }}" +# hostnames: +# - office.example.com +# tls: +# certificate_provider: ... diff --git a/roles/apps/onlyoffice/instance/tasks/main.yml b/roles/apps/onlyoffice/instance/tasks/main.yml new file mode 100644 index 00000000..2ca6026d --- /dev/null +++ b/roles/apps/onlyoffice/instance/tasks/main.yml @@ -0,0 +1,88 @@ +--- +- name: prepare storage volume + vars: + storage_volume: "{{ onlyoffice_instances[onlyoffice_instance].storage }}" + include_role: + name: "storage/{{ onlyoffice_instances[onlyoffice_instance].storage.type }}/volume" + +- set_fact: + onlyoffice_instance_basepath: "{{ storage_volume_mountpoint }}" + +- name: create onlyoffice database subdirectory + file: + path: "{{ onlyoffice_instance_basepath }}/postgres" + state: directory + +- name: create onlyoffice tls subdirectory + file: + path: "{{ onlyoffice_instance_basepath }}/tls" + state: directory + mode: 0700 + +- name: generate/install TLS certificates for publishment + vars: + x509_certificate_name: "onlyoffice-{{ onlyoffice_instance }}_publish" + x509_certificate_hostnames: [] + x509_certificate_config: + ca: "{{ onlyoffice_instances[onlyoffice_instance].publish.zone.certificate_ca_config }}" + cert: + common_name: "onlyoffice-{{ onlyoffice_instance }}.{{ inventory_hostname }}" + extended_key_usage: + - serverAuth + extended_key_usage_critical: yes + create_subject_key_identifier: yes + not_after: +100w + x509_certificate_renewal: + install: + - dest: "{{ onlyoffice_instance_basepath }}/tls/onlyoffice.crt" + src: + - cert + mode: "0400" + - dest: "{{ onlyoffice_instance_basepath }}/tls/onlyoffice.key" + src: + - key + mode: "0400" + include_role: + name: "x509/{{ onlyoffice_instances[onlyoffice_instance].publish.zone.certificate_provider }}/cert" + +- name: install pod manifest + vars: + kubernetes_standalone_pod: + name: "onlyoffice-{{ onlyoffice_instance }}" + spec: "{{ lookup('template', 'pod-spec.yml.j2') }}" + mode: "0600" + include_role: + name: kubernetes/standalone/pod + +- name: configure nginx vhost for publishment + vars: + nginx_vhost__yaml: | + name: "onlyoffice-{{ onlyoffice_instance }}.{{ inventory_hostname }}" + template: generic + {% if 'tls' in onlyoffice_instances[onlyoffice_instance].publish %} + tls: + {{ onlyoffice_instances[onlyoffice_instance].publish.tls | to_nice_yaml(indent=2) | indent(2) }} + {% endif %} + hostnames: + {% for hostname in onlyoffice_instances[onlyoffice_instance].publish.hostnames %} + - {{ hostname }} + {% endfor %} + locations: + '/': + {% if onlyoffice_instances[onlyoffice_instance].publish.zone.publisher == inventory_hostname %} + proxy_pass: "https://127.0.0.1:{{ onlyoffice_instances[onlyoffice_instance].port }}" + {% else %} + proxy_pass: "https://{{ ansible_default_ipv4.address }}:{{ onlyoffice_instances[onlyoffice_instance].port }}" + {% endif %} + proxy_ssl: + trusted_certificate: "/etc/ssl/apps-publish-{{ onlyoffice_instances[onlyoffice_instance].publish.zone.name }}/apps-publish-{{ onlyoffice_instances[onlyoffice_instance].publish.zone.name }}-ca-crt.pem" + verify: "on" + name: "onlyoffice-{{ onlyoffice_instance }}.{{ inventory_hostname }}" + protocols: "TLSv1.2 TLSv1.3" + extra_directives: |- + client_max_body_size 0; + nginx_vhost: "{{ nginx_vhost__yaml | from_yaml }}" + include_role: + name: nginx/vhost + apply: + delegate_to: "{{ onlyoffice_instances[onlyoffice_instance].publish.zone.publisher }}" diff --git a/roles/apps/onlyoffice/instance/templates/pod-spec.yml.j2 b/roles/apps/onlyoffice/instance/templates/pod-spec.yml.j2 new file mode 100644 index 00000000..ec70f8c1 --- /dev/null +++ b/roles/apps/onlyoffice/instance/templates/pod-spec.yml.j2 @@ -0,0 +1,75 @@ +terminationGracePeriodSeconds: 120 +containers: +- name: documentserver + image: "onlyoffice/documentserver:{{ onlyoffice_instances[onlyoffice_instance].version }}" + resources: + limits: + memory: "4Gi" + env: + - name: "DB_TYPE" + value: "postgres" + - name: "DB_HOST" + value: "127.0.0.1" + - name: "DB_PORT" + value: "5432" + - name: "DB_NAME" + value: "onlyoffice" + - name: "DB_USER" + value: "onlyoffice" + - name: "DB_PWD" + value: "{{ onlyoffice_instances[onlyoffice_instance].database.password }}" + - name: "AMQP_TYPE" + value: "rabbitmq" + - name: "AMQP_URI" + value: "amqp://onlyoffice:{{ onlyoffice_instances[onlyoffice_instance].amqp.password }}@127.0.0.1:5672" + - name: "JWT_ENABLED" + value: "true" + - name: "JWT_SECRET" + value: "{{ onlyoffice_instances[onlyoffice_instance].jwt_secret }}" + volumeMounts: + - name: tls + mountPath: /var/www/onlyoffice/Data/certs/ + readOnly: true + ports: + - containerPort: 443 + hostPort: {{ onlyoffice_instances[onlyoffice_instance].port }} + hostIP: 127.0.0.1 + +- name: postgresql + image: "postgres:{{ onlyoffice_instances[onlyoffice_instance].database.version }}" + args: + - postgres + - -c + - listen_addresses=127.0.0.1 + env: + - name: "POSTGRES_DB" + value: "onlyoffice" + - name: "POSTGRES_USER" + value: "onlyoffice" + - name: "POSTGRES_PASSWORD" + value: "{{ onlyoffice_instances[onlyoffice_instance].database.password }}" + volumeMounts: + - name: postgres + mountPath: /var/lib/postgresql/data + +- name: rabbitmq + image: "rabbitmq:{{ onlyoffice_instances[onlyoffice_instance].amqp.version }}" + env: + - name: "RABBITMQ_NODENAME" + value: "rabbit@localhost" + - name: "RABBITMQ_NODE_IP_ADDRESS" + value: "127.0.0.1" + - name: "RABBITMQ_DEFAULT_USER" + value: "onlyoffice" + - name: "RABBITMQ_DEFAULT_PASS" + value: "{{ onlyoffice_instances[onlyoffice_instance].amqp.password }}" + +volumes: +- name: tls + hostPath: + path: "{{ onlyoffice_instance_basepath }}/tls" + type: Directory +- name: postgres + hostPath: + path: "{{ onlyoffice_instance_basepath }}/postgres" + type: Directory diff --git a/roles/apps/onlyoffice/tasks/main.yml b/roles/apps/onlyoffice/tasks/main.yml index 960e811b..a42ee589 100644 --- a/roles/apps/onlyoffice/tasks/main.yml +++ b/roles/apps/onlyoffice/tasks/main.yml @@ -1,153 +1,7 @@ --- -- name: create zfs datasets - when: onlyoffice_zfs is defined - block: - - name: create zfs base dataset - zfs: - name: "{{ onlyoffice_zfs.pool }}/{{ onlyoffice_zfs.name }}" - state: present - extra_zfs_properties: "{{ onlyoffice_zfs.properties | dehumanize_zfs_properties | default(omit) }}" - - - name: create zfs volumes for instances - loop: "{{ onlyoffice_instances | dict2items }}" - loop_control: - label: "{{ item.key }} ({{ (item.value.zfs_properties | default({})).items() | map('join', '=') | join(', ') }})" - zfs: - name: "{{ onlyoffice_zfs.pool }}/{{ onlyoffice_zfs.name }}/{{ item.key }}" - state: present - extra_zfs_properties: "{{ item.value.zfs_properties | dehumanize_zfs_properties | default(omit) }}" - - - name: configure onlyoffice base bath - set_fact: - onlyoffice_base_path: "{{ (zfs_pools[onlyoffice_zfs.pool].mountpoint, onlyoffice_zfs.name) | path_join }}" - - -- name: create instance subdirectories - when: onlyoffice_zfs is not defined +- name: instance specific tasks loop: "{{ onlyoffice_instances | list }}" - file: - path: "{{ onlyoffice_base_path }}/{{ item }}" - state: directory - - -# TODO: run documentserver components as non-root -# - name: add group for onlyoffice app -# group: -# name: oo-app -# gid: "{{ onlyoffice_app_gid }}" - -# - name: add user for onlyoffice app -# user: -# name: oo-app -# uid: "{{ onlyoffice_app_uid }}" -# group: oo-app -# password: "!" - -# - name: create onlyoffice app subdirectory -# loop: "{{ onlyoffice_instances | list }}" -# file: -# path: "{{ onlyoffice_base_path }}/{{ item }}/onlyoffice" -# owner: "{{ onlyoffice_app_uid }}" -# group: "{{ onlyoffice_app_gid }}" -# state: directory - - -- name: add group for onlyoffice db - group: - name: oo-db - gid: "{{ onlyoffice_db_gid }}" - -- name: add user for onlyoffice db - user: - name: oo-db - uid: "{{ onlyoffice_db_uid }}" - group: oo-db - password: "!" - -- name: create onlyoffice database subdirectory - loop: "{{ onlyoffice_instances | dict2items}}" loop_control: - label: "{{ item.key }}" - file: - path: "{{ onlyoffice_base_path }}/{{ item.key }}/postgres" - owner: "{{ onlyoffice_db_uid }}" - group: "{{ onlyoffice_db_gid }}" - state: directory - - -# TODO: run documentserver components as non-root -# - name: add group for onlyoffice aqmp -# group: -# name: oo-aqmp -# gid: "{{ onlyoffice_aqmp_gid }}" - -# - name: add user for onlyoffice aqmp -# user: -# name: oo-aqmp -# uid: "{{ onlyoffice_aqmp_uid }}" -# group: oo-aqmp -# password: "!" - -# - name: create onlyoffice aqmp subdirectory -# loop: "{{ onlyoffice_instances | list }}" -# file: -# path: "{{ onlyoffice_base_path }}/{{ item }}/onlyoffice" -# owner: "{{ onlyoffice_aqmp_uid }}" -# group: "{{ onlyoffice_aqmp_gid }}" -# state: directory - -# TODO: AQMP config? -# - name: create onlyoffice rabbitmq subdirectory -# loop: "{{ onlyoffice_instances | dict2items}}" -# loop_control: -# label: "{{ item.key }}" -# file: -# path: "{{ onlyoffice_base_path }}/{{ item.key }}/rabbitmq" -# state: directory - -# - name: install rabbitmq config snipped -# loop: "{{ onlyoffice_instances | dict2items}}" -# loop_control: -# label: "{{ item.key }}" -# copy: -# dest: "{{ onlyoffice_base_path }}/{{ item.key }}/rabbitmq/config" -# content: | -# management.tcp.ip = 127.0.0.1 - - -- name: install pod manifest - loop: "{{ onlyoffice_instances | dict2items }}" - loop_control: - label: "{{ item.key }}" - vars: - kubernetes_standalone_pod: - name: "onlyoffice-{{ item.key }}" - spec: "{{ lookup('template', 'pod-spec.yml.j2') }}" - mode: "0600" -# TODO: AQMP config? -# config_hash_items: -# - path: "{{ onlyoffice_base_path }}/{{ item.key }}/rabbitmq/config" -# properties: -# - checksum - include_role: - name: kubernetes/standalone/pod - -- name: configure nginx vhost - loop: "{{ onlyoffice_instances | dict2items }}" - loop_control: - label: "{{ item.key }}" - vars: - nginx_vhost: - name: "onlyoffice-{{ item.key }}" - template: generic - tls: - certificate_provider: "{{ acme_client }}" - hostnames: - - "{{ item.value.hostname }}" - locations: - '/': - proxy_pass: "http://127.0.0.1:{{ item.value.port }}" - extra_directives: |- - client_max_body_size 0; + loop_var: onlyoffice_instance include_role: - name: nginx/vhost + name: apps/onlyoffice/instance diff --git a/roles/apps/onlyoffice/templates/pod-spec.yml.j2 b/roles/apps/onlyoffice/templates/pod-spec.yml.j2 deleted file mode 100644 index 620e0d18..00000000 --- a/roles/apps/onlyoffice/templates/pod-spec.yml.j2 +++ /dev/null @@ -1,102 +0,0 @@ -{# TODO: -securityContext: - allowPrivilegeEscalation: false -#} -terminationGracePeriodSeconds: 120 -containers: -{# TODO: only listen to localhost #} -- name: documentserver - image: "onlyoffice/documentserver:{{ item.value.version }}" - resources: - limits: - memory: "4Gi" -{# TODO: - securityContext: - allowPrivilegeEscalation: false - runAsUser: {{ onlyoffice_amqp_uid }} - runAsGroup: {{ onlyoffice_amqp_gid }} -#} - env: - - name: "DB_TYPE" - value: "postgres" - - name: "DB_HOST" - value: "127.0.0.1" - - name: "DB_PORT" - value: "5432" - - name: "DB_NAME" - value: "onlyoffice" - - name: "DB_USER" - value: "onlyoffice" - - name: "DB_PWD" - value: "{{ item.value.database.password }}" - - name: "AMQP_TYPE" - value: "rabbitmq" - - name: "AMQP_URI" - value: "amqp://onlyoffice:{{ item.value.amqp.password }}@127.0.0.1:5672" - - name: "JWT_ENABLED" - value: "true" - - name: "JWT_SECRET" - value: "{{ item.value.jwt_secret }}" - ports: - - containerPort: 80 - hostPort: {{ item.value.port }} - hostIP: 127.0.0.1 - -- name: postgresql - image: "postgres:{{ item.value.database.version }}" - args: - - postgres - - -c - - listen_addresses=127.0.0.1 - securityContext: - allowPrivilegeEscalation: false - runAsUser: {{ onlyoffice_db_uid }} - runAsGroup: {{ onlyoffice_db_gid }} - env: - - name: "POSTGRES_DB" - value: "onlyoffice" - - name: "POSTGRES_USER" - value: "onlyoffice" - - name: "POSTGRES_PASSWORD" - value: "{{ item.value.database.password }}" - volumeMounts: - - name: postgres - mountPath: /var/lib/postgresql/data - -{# TODO: only listen to localhost #} -- name: rabbitmq - image: "rabbitmq:{{ item.value.amqp.version }}" -{# TODO: - securityContext: - allowPrivilegeEscalation: false - runAsUser: {{ onlyoffice_amqp_uid }} - runAsGroup: {{ onlyoffice_amqp_gid }} -#} - env: - - name: "RABBITMQ_NODENAME" - value: "rabbit@localhost" - - name: "RABBITMQ_NODE_IP_ADDRESS" - value: "127.0.0.1" - - name: "RABBITMQ_DEFAULT_USER" - value: "onlyoffice" - - name: "RABBITMQ_DEFAULT_PASS" - value: "{{ item.value.amqp.password }}" -{# TODO: AQMP config? - volumeMounts: - - name: rabbitmq - mountPath: /etc/rabbitmq/conf.d/k8s.conf - subPath: config - readOnly: true -#} - -volumes: -- name: postgres - hostPath: - path: "{{ onlyoffice_base_path }}/{{ item.key }}/postgres" - type: Directory -{# TODO: AQMP config? -- name: rabbitmq - hostPath: - path: "{{ onlyoffice_base_path }}/{{ item.key }}/rabbitmq" - type: Directory -#} -- cgit v1.2.3