From d5a79538a6779a1bda2b058e8cefba2947b9d66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iikka=20J=C3=A4rvenp=C3=A4=C3=A4?= <41309685+iikkart@users.noreply.github.com> Date: Thu, 17 Mar 2022 20:20:43 +0000 Subject: [PATCH 01/83] Improved documentation about permissions More info: https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1699 --- ...onfiguring-playbook-bridge-mautrix-telegram.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/configuring-playbook-bridge-mautrix-telegram.md b/docs/configuring-playbook-bridge-mautrix-telegram.md index 0ac6c1030..924de8caf 100644 --- a/docs/configuring-playbook-bridge-mautrix-telegram.md +++ b/docs/configuring-playbook-bridge-mautrix-telegram.md @@ -49,4 +49,19 @@ If you want to use the relay-bot feature ([relay bot documentation](https://docs ```yaml matrix_mautrix_telegram_bot_token: YOUR_TELEGRAM_BOT_TOKEN +matrix_mautrix_telegram_configuration_extension_yaml: | + bridge: + permissions: + '*': relaybot ``` + +You might also want to give permissions to administrate the bot: +```yaml +matrix_mautrix_telegram_configuration_extension_yaml: | + bridge: + permissions: + '@user:DOMAIN': admin +``` + +More details about permissions in this example: +https://github.com/mautrix/telegram/blob/master/mautrix_telegram/example-config.yaml#L410 From b3176957c3cf1e0797a8d1f1796d14b91ed3d1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arthur=20Brugi=C3=A8re?= <16764085+RoiArthurB@users.noreply.github.com> Date: Sun, 3 Apr 2022 17:10:46 +0700 Subject: [PATCH 02/83] Add hookshot self build for arm64 and amd64 --- docs/self-building.md | 1 + group_vars/matrix_servers | 2 + .../matrix-bridge-hookshot/defaults/main.yml | 11 ++++- .../tasks/setup_install.yml | 42 +++++++++++++++---- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/docs/self-building.md b/docs/self-building.md index ef851f22c..4ec5bb867 100644 --- a/docs/self-building.md +++ b/docs/self-building.md @@ -22,6 +22,7 @@ List of roles where self-building the Docker image is currently possible: - `matrix-dimension` - `matrix-ma1sd` - `matrix-mailer` +- `matrix-bridge-hookshot` - `matrix-bridge-appservice-irc` - `matrix-bridge-appservice-slack` - `matrix-bridge-appservice-webhooks` diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 32be903a5..8673bb7b0 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -671,6 +671,8 @@ matrix_heisenbridge_systemd_wanted_services_list: | # We don't enable bridges by default. matrix_hookshot_enabled: false +matrix_hookshot_container_image_self_build: "{{ matrix_architecture not in ['arm64', 'amd64'] }}" + matrix_hookshot_appservice_token: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'hookshot.as.tok') | to_uuid }}" matrix_hookshot_homeserver_token: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'hookshot.hs.tok') | to_uuid }}" diff --git a/roles/matrix-bridge-hookshot/defaults/main.yml b/roles/matrix-bridge-hookshot/defaults/main.yml index 01dd43b05..6dab5cd65 100644 --- a/roles/matrix-bridge-hookshot/defaults/main.yml +++ b/roles/matrix-bridge-hookshot/defaults/main.yml @@ -5,12 +5,21 @@ matrix_hookshot_enabled: true + +matrix_hookshot_container_image_self_build: false +matrix_hookshot_container_image_self_build_repo: "https://github.com/matrix-org/matrix-hookshot.git" +matrix_hookshot_container_image_self_build_branch: "{{ 'main' if matrix_hookshot_version == 'latest' else matrix_hookshot_version }}" + matrix_hookshot_version: 1.3.0 -matrix_hookshot_docker_image: "{{ matrix_container_global_registry_prefix }}halfshot/matrix-hookshot:{{ matrix_hookshot_version }}" + +matrix_hookshot_docker_image: "{{ matrix_hookshot_docker_image_name_prefix }}mautrix/whatsapp:{{ matrix_mautrix_whatsapp_version }}" +matrix_hookshot_docker_image_name_prefix: "{{ 'localhost/' if matrix_hookshot_container_image_self_build else matrix_container_global_registry_prefix }}" matrix_hookshot_docker_image_force_pull: "{{ matrix_hookshot_docker_image.endswith(':latest') }}" matrix_hookshot_base_path: "{{ matrix_base_data_path }}/hookshot" +matrix_hookshot_docker_src_files_path: "{{ matrix_hookshot_base_path }}/docker-src" + matrix_hookshot_homeserver_address: "{{ matrix_homeserver_container_url }}" matrix_hookshot_container_url: 'matrix-hookshot' diff --git a/roles/matrix-bridge-hookshot/tasks/setup_install.yml b/roles/matrix-bridge-hookshot/tasks/setup_install.yml index b4e44c9c3..cac9fdca9 100644 --- a/roles/matrix-bridge-hookshot/tasks/setup_install.yml +++ b/roles/matrix-bridge-hookshot/tasks/setup_install.yml @@ -2,26 +2,50 @@ - import_tasks: "{{ role_path }}/../matrix-base/tasks/util/ensure_openssl_installed.yml" +- name: Ensure hookshot paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_hookshot_base_path }}", when: true } + - { path: "{{ matrix_hookshot_docker_src_files_path }}", when: "{{ matrix_hookshot_container_image_self_build }}" } + when: item.when|bool + - name: Ensure hookshot image is pulled docker_image: name: "{{ matrix_hookshot_docker_image }}" source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" force_source: "{{ matrix_hookshot_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_hookshot_docker_image_force_pull }}" + when: not matrix_hookshot_container_image_self_build register: result retries: "{{ matrix_container_retries_count }}" delay: "{{ matrix_container_retries_delay }}" until: result is not failed -- name: Ensure hookshot paths exist - file: - path: "{{ item }}" - state: directory - mode: 0750 - owner: "{{ matrix_user_username }}" - group: "{{ matrix_user_groupname }}" - with_items: - - "{{ matrix_hookshot_base_path }}" +- name: Ensure hookshot repository is present on self-build + git: + repo: "{{ matrix_hookshot_container_image_self_build_repo }}" + dest: "{{ matrix_hookshot_docker_src_files_path }}" + version: "{{ matrix_hookshot_container_image_self_build_branch }}" + force: "yes" + register: matrix_hookshot_git_pull_results + when: "matrix_hookshot_container_image_self_build|bool" + +- name: Ensure hookshot Docker image is built + docker_image: + name: "{{ matrix_hookshot_docker_image }}" + source: build + force_source: "{{ matrix_hookshot_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_hookshot_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_hookshot_docker_src_files_path }}" + pull: yes + when: "matrix_hookshot_container_image_self_build|bool" - name: Check if hookshot passkey exists stat: From cfd8a9c0f8287065dde2d040e55608ac157373ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arthur=20Brugi=C3=A8re?= <16764085+RoiArthurB@users.noreply.github.com> Date: Sun, 3 Apr 2022 17:19:35 +0700 Subject: [PATCH 03/83] [HOOKSHOT] Fix yamllint --- roles/matrix-bridge-hookshot/tasks/setup_install.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/roles/matrix-bridge-hookshot/tasks/setup_install.yml b/roles/matrix-bridge-hookshot/tasks/setup_install.yml index cac9fdca9..38dc62a3b 100644 --- a/roles/matrix-bridge-hookshot/tasks/setup_install.yml +++ b/roles/matrix-bridge-hookshot/tasks/setup_install.yml @@ -9,9 +9,9 @@ mode: 0750 owner: "{{ matrix_user_username }}" group: "{{ matrix_user_groupname }}" - with_items: - - { path: "{{ matrix_hookshot_base_path }}", when: true } - - { path: "{{ matrix_hookshot_docker_src_files_path }}", when: "{{ matrix_hookshot_container_image_self_build }}" } + with_items: + - {path: "{{ matrix_hookshot_base_path }}", when: true} + - {path: "{{ matrix_hookshot_docker_src_files_path }}", when: "{{ matrix_hookshot_container_image_self_build }}"} when: item.when|bool - name: Ensure hookshot image is pulled @@ -44,7 +44,7 @@ build: dockerfile: Dockerfile path: "{{ matrix_hookshot_docker_src_files_path }}" - pull: yes + pull: true when: "matrix_hookshot_container_image_self_build|bool" - name: Check if hookshot passkey exists From e7978dbdca91351e235d36ae25a74a0a7266f155 Mon Sep 17 00:00:00 2001 From: SaltireSoul Date: Tue, 5 Apr 2022 03:40:37 +0100 Subject: [PATCH 04/83] Dendrite 0.7.0 --- group_vars/matrix_servers | 14 +- roles/matrix-dendrite/defaults/main.yml | 11 +- .../matrix-dendrite/tasks/setup_dendrite.yml | 1 + .../templates/dendrite/dendrite.yaml.j2 | 176 ++++++++---------- .../systemd/matrix-dendrite.service.j2 | 1 + 5 files changed, 83 insertions(+), 120 deletions(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 26485d688..de81e2a60 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -1592,15 +1592,11 @@ matrix_postgres_additional_databases: | }] if (matrix_synapse_enabled and matrix_synapse_database_database != matrix_postgres_db_name and matrix_synapse_database_host == 'matrix-postgres') else []) + ([{ - 'name': matrix_dendrite_naffka_database, - 'username': matrix_dendrite_database_user, - 'password': matrix_dendrite_database_password, - },{ 'name': matrix_dendrite_appservice_database, 'username': matrix_dendrite_database_user, 'password': matrix_dendrite_database_password, },{ - 'name': matrix_dendrite_federationsender_database, + 'name': matrix_dendrite_federationapi_database, 'username': matrix_dendrite_database_user, 'password': matrix_dendrite_database_password, },{ @@ -1615,20 +1611,16 @@ matrix_postgres_additional_databases: | 'name': matrix_dendrite_room_database, 'username': matrix_dendrite_database_user, 'password': matrix_dendrite_database_password, - },{ - 'name': matrix_dendrite_singingkeyserver_database, - 'username': matrix_dendrite_database_user, - 'password': matrix_dendrite_database_password, },{ 'name': matrix_dendrite_syncapi_database, 'username': matrix_dendrite_database_user, 'password': matrix_dendrite_database_password, },{ - 'name': matrix_dendrite_account_database, + 'name': matrix_dendrite_userapi_database, 'username': matrix_dendrite_database_user, 'password': matrix_dendrite_database_password, },{ - 'name': matrix_dendrite_device_database, + 'name': matrix_dendrite_pushserver_database, 'username': matrix_dendrite_database_user, 'password': matrix_dendrite_database_password, },{ diff --git a/roles/matrix-dendrite/defaults/main.yml b/roles/matrix-dendrite/defaults/main.yml index ec3937c76..99ceb1a03 100644 --- a/roles/matrix-dendrite/defaults/main.yml +++ b/roles/matrix-dendrite/defaults/main.yml @@ -6,13 +6,14 @@ matrix_dendrite_enabled: true matrix_dendrite_docker_image: "{{ matrix_dendrite_docker_image_name_prefix }}matrixdotorg/dendrite-monolith:{{ matrix_dendrite_docker_image_tag }}" matrix_dendrite_docker_image_name_prefix: "docker.io/" -matrix_dendrite_docker_image_tag: "v0.5.1" +matrix_dendrite_docker_image_tag: "v0.7.0" matrix_dendrite_docker_image_force_pull: "{{ matrix_dendrite_docker_image.endswith(':latest') }}" matrix_dendrite_base_path: "{{ matrix_base_data_path }}/dendrite" matrix_dendrite_config_dir_path: "{{ matrix_dendrite_base_path }}/config" matrix_dendrite_storage_path: "{{ matrix_dendrite_base_path }}/storage" matrix_dendrite_media_store_path: "{{ matrix_dendrite_storage_path }}/media-store" +matrix_dendrite_nats_storage_path: "{{ matrix_dendrite_base_path }}/nats" matrix_dendrite_ext_path: "{{ matrix_dendrite_base_path }}/ext" # By default, we make Dendrite only serve HTTP (not HTTPS). @@ -114,16 +115,14 @@ matrix_dendrite_database_str: "postgresql://{{ matrix_dendrite_database_user }}: matrix_dendrite_database_hostname: "matrix-postgres" matrix_dendrite_database_user: "dendrite" matrix_dendrite_database_password: "itsasecret" -matrix_dendrite_naffka_database: "dendrite_naffka" matrix_dendrite_appservice_database: "dendrite_appservice" -matrix_dendrite_federationsender_database: "dendrite_federationsender" +matrix_dendrite_federationapi_database: "dendrite_federationapi" matrix_dendrite_keyserver_database: "dendrite_keyserver" matrix_dendrite_mediaapi_database: "dendrite_mediaapi" matrix_dendrite_room_database: "dendrite_room" -matrix_dendrite_singingkeyserver_database: "dendrite_sigingkeyserver" matrix_dendrite_syncapi_database: "dendrite_syncapi" -matrix_dendrite_account_database: "dendrite_account" -matrix_dendrite_device_database: "dendrite_device" +matrix_dendrite_userapi_database: "dendrite_userapi" +matrix_dendrite_pushserver_database: "dendrite_pushserver" matrix_dendrite_mscs_database: "dendrite_mscs" matrix_dendrite_turn_uris: [] diff --git a/roles/matrix-dendrite/tasks/setup_dendrite.yml b/roles/matrix-dendrite/tasks/setup_dendrite.yml index cbe0cf843..fc306759d 100644 --- a/roles/matrix-dendrite/tasks/setup_dendrite.yml +++ b/roles/matrix-dendrite/tasks/setup_dendrite.yml @@ -9,6 +9,7 @@ with_items: - {path: "{{ matrix_dendrite_config_dir_path }}", when: true} - {path: "{{ matrix_dendrite_ext_path }}", when: true} + - {path: "{{ matrix_dendrite_nats_storage_path }}", when: true} when: "matrix_dendrite_enabled|bool and item.when" - import_tasks: "{{ role_path }}/tasks/dendrite/setup.yml" diff --git a/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 b/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 index 102dd2f59..01bb72f7b 100644 --- a/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 +++ b/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 @@ -28,7 +28,7 @@ # connection can be idle in seconds - a negative value is unlimited. # The version of the configuration file. -version: 1 +version: 2 # Global Matrix configuration. This configuration applies to all components. global: @@ -66,34 +66,40 @@ global: # to other servers and the federation API will not be exposed. disable_federation: {{ (not matrix_dendrite_federation_enabled)|to_json }} - # Configuration for Kafka/Naffka. - kafka: - # List of Kafka broker addresses to connect to. This is not needed if using - # Naffka in monolith mode. - addresses: [] - - # The prefix to use for Kafka topic names for this homeserver. Change this only if - # you are running more than one Dendrite homeserver on the same Kafka deployment. + # Server notices allows server admins to send messages to all users. + server_notices: + enabled: false + # The server localpart to be used when sending notices, ensure this is not yet taken + local_part: "_server" + # The displayname to be used when sending notices + display_name: "Server alerts" + # The mxid of the avatar to use + avatar_url: "" + # The roomname to be used when creating messages + room_name: "Server Alerts" + + # Configuration for NATS JetStream + jetstream: + # A list of NATS Server addresses to connect to. If none are specified, an + # internal NATS server will be started automatically when running Dendrite + # in monolith mode. It is required to specify the address of at least one + # NATS Server node if running in polylith mode. + addresses: + # - jetstream:4222 + + # Keep all NATS streams in memory, rather than persisting it to the storage + # path below. This option is present primarily for integration testing and + # should not be used on a real world Dendrite deployment. + in_memory: false + + # Persistent directory to store JetStream streams in. This directory + # should be preserved across Dendrite restarts. + storage_path: "/matrix-nats-store" + + # The prefix to use for stream names for this homeserver - really only + # useful if running more than one Dendrite on the same NATS deployment. topic_prefix: Dendrite - # Whether to use Naffka instead of Kafka. This is only available in monolith - # mode, but means that you can run a single-process server without requiring - # Kafka. - use_naffka: true - - # The max size a Kafka message is allowed to use. - # You only need to change this value, if you encounter issues with too large messages. - # Must be less than/equal to "max.message.bytes" configured in Kafka. - # Defaults to 8388608 bytes. - # max_message_bytes: 8388608 - - # Naffka database options. Not required when using Kafka. - naffka_database: - connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_naffka_database }}?sslmode=disable - max_open_conns: 10 - max_idle_conns: 2 - conn_max_lifetime: -1 - # Configuration for Prometheus metric collection. metrics: # Whether or not Prometheus metrics are enabled. @@ -126,11 +132,6 @@ app_service_api: max_idle_conns: 2 conn_max_lifetime: -1 - # Disable the validation of TLS certificates of appservices. This is - # not recommended in production since it may allow appservice traffic - # to be sent to an unverified endpoint. - disable_tls_validation: false - # Appservice configuration files to load into this homeserver. config_files: {{ matrix_dendrite_app_service_config_files|to_json }} @@ -146,6 +147,10 @@ client_api: # using the registration shared secret below. registration_disabled: {{ matrix_dendrite_registration_disabled|to_json }} + # Prevents new guest accounts from being created. Guest registration is also + # disabled implicitly by setting 'registration_disabled' above. + guests_disabled: true + # If set, allows registration by anyone who knows the shared secret, regardless of # whether registration is otherwise disabled. registration_shared_secret: {{ matrix_dendrite_registration_shared_secret|string|to_json }} @@ -175,12 +180,6 @@ client_api: threshold: {{ matrix_dendrite_rate_limiting_threshold|to_json }} cooloff_ms: {{ matrix_dendrite_rate_limiting_cooloff_ms|to_json }} -# Configuration for the EDU server. -edu_server: - internal_api: - listen: http://0.0.0.0:7778 - connect: http://edu_server:7778 - # Configuration for the Federation API. federation_api: internal_api: @@ -188,20 +187,8 @@ federation_api: connect: http://federation_api:7772 external_api: listen: http://0.0.0.0:8072 - - # List of paths to X.509 certificates to be used by the external federation listeners. - # These certificates will be used to calculate the TLS fingerprints and other servers - # will expect the certificate to match these fingerprints. Certificates must be in PEM - # format. - federation_certificates: [] - -# Configuration for the Federation Sender. -federation_sender: - internal_api: - listen: http://0.0.0.0:7775 - connect: http://federation_sender:7775 database: - connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_federationsender_database }}?sslmode=disable + connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_federationapi_database }}?sslmode=disable max_open_conns: 10 max_idle_conns: 2 conn_max_lifetime: -1 @@ -221,6 +208,22 @@ federation_sender: host: localhost port: 8080 + # Perspective keyservers to use as a backup when direct key fetches fail. This may + # be required to satisfy key requests for servers that are no longer online when + # joining some rooms. + key_perspectives: + - server_name: matrix.org + keys: + - key_id: ed25519:auto + public_key: Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw + - key_id: ed25519:a_RXGa + public_key: l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ + + # This option will control whether Dendrite will prefer to look up keys directly + # or whether it should try perspective servers first, using direct fetches as a + # last resort. + prefer_direct_fetch: false + # Configuration for the Key Server (for end-to-end encryption). key_server: internal_api: @@ -261,15 +264,15 @@ media_api: # A list of thumbnail sizes to be generated for media content. thumbnail_sizes: - - width: 32 - height: 32 - method: crop - - width: 96 - height: 96 - method: crop - - width: 640 - height: 480 - method: scale + - width: 32 + height: 32 + method: crop + - width: 96 + height: 96 + method: crop + - width: 640 + height: 480 + method: scale # Configuration for experimental MSC's mscs: @@ -295,40 +298,13 @@ room_server: max_idle_conns: 2 conn_max_lifetime: -1 -# Configuration for the Signing Key Server (for server signing keys). -signing_key_server: - internal_api: - listen: http://0.0.0.0:7780 - connect: http://signing_key_server:7780 - database: - connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_singingkeyserver_database }}?sslmode=disable - max_open_conns: 10 - max_idle_conns: 2 - conn_max_lifetime: -1 - - # Perspective keyservers to use as a backup when direct key fetches fail. This may - # be required to satisfy key requests for servers that are no longer online when - # joining some rooms. - key_perspectives: - - server_name: matrix.org - keys: - - key_id: ed25519:auto - public_key: Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw - - key_id: ed25519:a_RXGa - public_key: l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ - - # This option will control whether Dendrite will prefer to look up keys directly - # or whether it should try perspective servers first, using direct fetches as a - # last resort. - prefer_direct_fetch: false - # Configuration for the Sync API. sync_api: internal_api: listen: http://0.0.0.0:7773 connect: http://sync_api:7773 external_api: - listen: http://0.0.0.0:8073 + listen: http://0.0.0.0:8073 database: connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_syncapi_database }}?sslmode=disable max_open_conns: 10 @@ -343,31 +319,25 @@ sync_api: # Configuration for the User API. user_api: - # The cost when hashing passwords on registration/login. Default: 10. Min: 4, Max: 31 - # See https://pkg.go.dev/golang.org/x/crypto/bcrypt for more information. - # Setting this lower makes registration/login consume less CPU resources at the cost of security - # should the database be compromised. Setting this higher makes registration/login consume more - # CPU resources but makes it harder to brute force password hashes. - # This value can be low if performing tests or on embedded Dendrite instances (e.g WASM builds) - # bcrypt_cost: 10 internal_api: listen: http://0.0.0.0:7781 connect: http://user_api:7781 account_database: - connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_account_database }}?sslmode=disable + connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_userapi_database }}?sslmode=disable max_open_conns: 10 max_idle_conns: 2 conn_max_lifetime: -1 - device_database: - connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_device_database }}?sslmode=disable + +# Configuration for the Push Server API. +push_server: + internal_api: + listen: http://localhost:7782 + connect: http://localhost:7782 + database: + connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_pushserver_database }}?sslmode=disable max_open_conns: 10 max_idle_conns: 2 conn_max_lifetime: -1 - # The length of time that a token issued for a relying party from - # /_matrix/client/r0/user/{userId}/openid/request_token endpoint - # is considered to be valid in milliseconds. - # The default lifetime is 3600000ms (60 minutes). - # openid_token_lifetime_ms: 3600000 # Configuration for Opentracing. # See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on diff --git a/roles/matrix-dendrite/templates/dendrite/systemd/matrix-dendrite.service.j2 b/roles/matrix-dendrite/templates/dendrite/systemd/matrix-dendrite.service.j2 index e14734dd7..e1c42cbcb 100644 --- a/roles/matrix-dendrite/templates/dendrite/systemd/matrix-dendrite.service.j2 +++ b/roles/matrix-dendrite/templates/dendrite/systemd/matrix-dendrite.service.j2 @@ -37,6 +37,7 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-dendrite \ {% endif %} --mount type=bind,src={{ matrix_dendrite_config_dir_path }},dst=/data,ro \ --mount type=bind,src={{ matrix_dendrite_storage_path }},dst=/matrix-media-store-parent,bind-propagation=slave \ + --mount type=bind,src={{ matrix_dendrite_nats_storage_path }},dst=/matrix-nats-store,bind-propagation=slave \ {% for volume in matrix_dendrite_container_additional_volumes %} -v {{ volume.src }}:{{ volume.dst }}:{{ volume.options }} \ {% endfor %} From a5a3769ca9ceff0a8d8616318fb687b35b82bc07 Mon Sep 17 00:00:00 2001 From: Aine <97398200+etkecc@users.noreply.github.com> Date: Tue, 5 Apr 2022 11:37:27 +0000 Subject: [PATCH 05/83] add borg backup (#1727) * add borg backup * lint fix * add exlclude patterns * missed in the #1726 fix for honoroit * feedback * Fix indentation * feedback * feedback * feedback Co-authored-by: Slavi Pantaleev --- README.md | 2 + docs/configuring-playbook-backup-borg.md | 56 +++++++++++ group_vars/matrix_servers | 21 ++++ roles/matrix-backup-borg/defaults/main.yml | 63 ++++++++++++ roles/matrix-backup-borg/tasks/init.yml | 4 + roles/matrix-backup-borg/tasks/main.yml | 23 +++++ .../tasks/setup_install.yml | 97 +++++++++++++++++++ .../tasks/setup_uninstall.yml | 41 ++++++++ .../tasks/validate_config.yml | 10 ++ .../templates/config.yaml.j2 | 32 ++++++ roles/matrix-backup-borg/templates/passwd.j2 | 29 ++++++ roles/matrix-backup-borg/templates/sshkey.j2 | 1 + .../systemd/matrix-backup-borg.service.j2 | 58 +++++++++++ .../systemd/matrix-backup-borg.timer.j2 | 10 ++ .../tasks/setup_install.yml | 2 + setup.yml | 1 + 16 files changed, 450 insertions(+) create mode 100644 docs/configuring-playbook-backup-borg.md create mode 100644 roles/matrix-backup-borg/defaults/main.yml create mode 100644 roles/matrix-backup-borg/tasks/init.yml create mode 100644 roles/matrix-backup-borg/tasks/main.yml create mode 100644 roles/matrix-backup-borg/tasks/setup_install.yml create mode 100644 roles/matrix-backup-borg/tasks/setup_uninstall.yml create mode 100644 roles/matrix-backup-borg/tasks/validate_config.yml create mode 100644 roles/matrix-backup-borg/templates/config.yaml.j2 create mode 100644 roles/matrix-backup-borg/templates/passwd.j2 create mode 100644 roles/matrix-backup-borg/templates/sshkey.j2 create mode 100644 roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.service.j2 create mode 100644 roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.timer.j2 diff --git a/README.md b/README.md index 0e6bc51b6..631dc297d 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,8 @@ Using this playbook, you can get the following services configured on your serve - (optional) the [Cinny](https://github.com/ajbura/cinny) web client - see [docs/configuring-playbook-client-cinny.md](docs/configuring-playbook-client-cinny.md) for setup documentation +- (optional) the [Borg](https://borgbackup.org) backup - see [docs/configuring-playbook-backup-borg.md](docs/configuring-playbook-backup-borg.md) for setup documentation + Basically, this playbook aims to get you up-and-running with all the necessities around Matrix, without you having to do anything else. **Note**: the list above is exhaustive. It includes optional or even some advanced components that you will most likely not need. diff --git a/docs/configuring-playbook-backup-borg.md b/docs/configuring-playbook-backup-borg.md new file mode 100644 index 000000000..7ca962c87 --- /dev/null +++ b/docs/configuring-playbook-backup-borg.md @@ -0,0 +1,56 @@ +# Setting up borg backup (optional) + +The playbook can install and configure [borgbackup](https://www.borgbackup.org/) with [borgmatic](https://torsion.org/borgmatic/) for you. +BorgBackup is a deduplicating backup program with optional compression and encryption. +That means your daily incremental backups can be stored in a fraction of the space and is safe whether you store it at home or on a cloud service. + +The backup will run based on `matrix_backup_borg_schedule` var (systemd timer calendar), default: 4am every day + +## Prerequisites + +1. Create ssh key on any machine: + +```bash +ssh-keygen -t ed25519 -N '' -f matrix-borg-backup -C matrix +``` + +2. Add public part of that ssh key to your borg provider / server: + +```bash +# example to append the new PUBKEY contents, where: +# PUBKEY is path to the public key, +# USER is a ssh user on a provider / server +# HOST is a ssh host of a provider / server +cat PUBKEY | ssh USER@HOST 'dd of=.ssh/authorized_keys oflag=append conv=notrunc' +``` + +## Adjusting the playbook configuration + +Minimal working configuration (`inventory/host_vars/matrix.DOMAIN/vars.yml`) to enable borg backup: + +```yaml +matrix_backup_borg_enabled: true +matrix_backup_borg_location_repositories: + - USER@HOST:REPO +matrix_backup_borg_storage_encryption_passphrase: "PASSPHRASE" +matrix_backup_borg_ssh_key_private: | + PRIVATE KEY +``` + +where: + +* USER - ssh user of a provider / server +* HOST - ssh host of a provider / server +* REPO - borg repository name, it will be initialized on backup start, eg: `matrix` +* PASSPHRASE - super-secret borg passphrase, you may generate it with `pwgen -s 64 1` or use any password manager +* PRIVATE KEY - the content of the public part of the ssh key you created before + +Check the `roles/matrix-backup-borg/defaults/main.yml` for the full list of available options + +## Installing + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 54f0ad380..15032cabf 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -1095,6 +1095,27 @@ matrix_bot_mjolnir_systemd_required_services_list: | # ###################################################################### +###################################################################### +# +# matrix-backup-borg +# +###################################################################### + +matrix_backup_borg_enabled: false +matrix_backup_borg_location_source_directories: + - "{{ matrix_base_data_path }}" +matrix_backup_borg_location_exclude_patterns: | + {{ + { + 'synapse': ["{{ matrix_synapse_media_store_path }}/local_thumbnails", "{{ matrix_synapse_media_store_path }}/remote_thumbnail", "{{ matrix_synapse_media_store_path }}/url_cache", "{{ matrix_synapse_media_store_path }}/url_cache_thumbnails"], + }[matrix_homeserver_implementation] + }} + +###################################################################### +# +# /matrix-backup-borg +# +###################################################################### ###################################################################### # diff --git a/roles/matrix-backup-borg/defaults/main.yml b/roles/matrix-backup-borg/defaults/main.yml new file mode 100644 index 000000000..c8a09f7f4 --- /dev/null +++ b/roles/matrix-backup-borg/defaults/main.yml @@ -0,0 +1,63 @@ +--- +matrix_backup_borg_enabled: true + +matrix_backup_borg_container_image_self_build: false +matrix_backup_borg_docker_repo: "https://github.com/borgmatic-collective/docker-borgmatic" +matrix_backup_borg_docker_src_files_path: "{{ matrix_base_data_path }}/borg/docker-src" + +matrix_backup_borg_version: latest +matrix_backup_borg_docker_image: "{{ matrix_backup_borg_docker_image_name_prefix }}etke.cc/borgmatic:{{ matrix_backup_borg_version }}" +matrix_backup_borg_docker_image_name_prefix: "{{ 'localhost/' if matrix_backup_borg_container_image_self_build else 'registry.gitlab.com/' }}" +matrix_backup_borg_docker_image_force_pull: "{{ matrix_backup_borg_docker_image.endswith(':latest') }}" + +matrix_backup_borg_base_path: "{{ matrix_base_data_path }}/backup-borg" +matrix_backup_borg_config_path: "{{ matrix_backup_borg_base_path }}/config" + +# A list of extra arguments to pass to the container +matrix_backup_borg_container_extra_arguments: [] + +# List of systemd services that matrix-backup-borg.service depends on +matrix_backup_borg_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-backup-borg.service wants +matrix_backup_borg_systemd_wanted_services_list: [] + +# systemd calendar configuration for backup job +matrix_backup_borg_schedule: "*-*-* 04:00:00" + +# what directories should be added to backup +matrix_backup_borg_location_source_directories: [] + +# target repositories +matrix_backup_borg_location_repositories: [] + +# exclude following paths: +matrix_backup_borg_location_exclude_patterns: [] + +# borg encryption mode, only repokey-* is supported +matrix_backup_borg_encryption: repokey-blake2 + +# private ssh key used to connect to the borg repo +matrix_backup_borg_ssh_key_private: "" + +# borg ssh command with ssh key +matrix_backup_borg_storage_ssh_command: ssh -o "StrictHostKeyChecking accept-new" -i /etc/borgmatic.d/sshkey + +# compression algorithm +matrix_backup_borg_storage_compression: lz4 + +# archive name format +matrix_backup_borg_storage_archive_name_format: "matrix-{now:%Y-%m-%d-%H%M%S}" + +# repository passphrase +matrix_backup_borg_storage_encryption_passphrase: "" + +# retention configuration +matrix_backup_borg_retention_keep_hourly: 0 +matrix_backup_borg_retention_keep_daily: 7 +matrix_backup_borg_retention_keep_weekly: 4 +matrix_backup_borg_retention_keep_monthly: 12 +matrix_backup_borg_retention_keep_yearly: 2 + +# retention prefix +matrix_backup_borg_retention_prefix: "matrix-" diff --git a/roles/matrix-backup-borg/tasks/init.yml b/roles/matrix-backup-borg/tasks/init.yml new file mode 100644 index 000000000..0a90a2e88 --- /dev/null +++ b/roles/matrix-backup-borg/tasks/init.yml @@ -0,0 +1,4 @@ +--- +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-backup-borg.service', 'matrix-backup-borg.timer'] }}" + when: matrix_backup_borg_enabled|bool diff --git a/roles/matrix-backup-borg/tasks/main.yml b/roles/matrix-backup-borg/tasks/main.yml new file mode 100644 index 000000000..0dbf54e15 --- /dev/null +++ b/roles/matrix-backup-borg/tasks/main.yml @@ -0,0 +1,23 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_backup_borg_enabled|bool" + tags: + - setup-all + - setup-backup-borg + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_backup_borg_enabled|bool" + tags: + - setup-all + - setup-backup-borg + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_backup_borg_enabled|bool" + tags: + - setup-all + - setup-backup-borg diff --git a/roles/matrix-backup-borg/tasks/setup_install.yml b/roles/matrix-backup-borg/tasks/setup_install.yml new file mode 100644 index 000000000..f2c65a164 --- /dev/null +++ b/roles/matrix-backup-borg/tasks/setup_install.yml @@ -0,0 +1,97 @@ +--- +- name: Ensure borg paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - {path: "{{ matrix_backup_borg_config_path }}", when: true} + - {path: "{{ matrix_backup_borg_docker_src_files_path }}", when: true} + when: "item.when|bool" + +- name: Ensure borg config is created + template: + src: "{{ role_path }}/templates/config.yaml.j2" + dest: "{{ matrix_backup_borg_config_path }}/config.yaml" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: 0640 + +- name: Ensure borg passwd is created + template: + src: "{{ role_path }}/templates/passwd.j2" + dest: "{{ matrix_backup_borg_config_path }}/passwd" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: 0640 + +- name: Ensure borg ssh key is created + template: + src: "{{ role_path }}/templates/sshkey.j2" + dest: "{{ matrix_backup_borg_config_path }}/sshkey" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: 0600 + +- name: Ensure borg image is pulled + docker_image: + name: "{{ matrix_backup_borg_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_backup_borg_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_backup_borg_docker_image_force_pull }}" + when: "not matrix_backup_borg_container_image_self_build|bool" + register: result + retries: "{{ matrix_container_retries_count }}" + delay: "{{ matrix_container_retries_delay }}" + until: result is not failed + +- name: Ensure borg repository is present on self-build + git: + repo: "{{ matrix_backup_borg_docker_repo }}" + dest: "{{ matrix_backup_borg_docker_src_files_path }}" + force: "yes" + register: matrix_backup_borg_git_pull_results + when: "matrix_backup_borg_container_image_self_build|bool" + +- name: Ensure borg image is built + docker_image: + name: "{{ matrix_backup_borg_docker_image }}" + source: build + force_source: "{{ matrix_backup_borg_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mailer_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_backup_borg_docker_src_files_path }}" + pull: true + when: "matrix_backup_borg_container_image_self_build|bool" + +- name: Ensure matrix-backup-borg.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-backup-borg.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-backup-borg.service" + mode: 0644 + register: matrix_backup_borg_systemd_service_result + +- name: Ensure matrix-backup-borg.timer installed + template: + src: "{{ role_path }}/templates/systemd/matrix-backup-borg.timer.j2" + dest: "{{ matrix_systemd_path }}/matrix-backup-borg.timer" + mode: 0644 + register: matrix_backup_borg_systemd_timer_result + +- name: Ensure systemd reloaded after matrix-backup-borg.service installation + service: + daemon_reload: true + when: "matrix_backup_borg_systemd_service_result.changed|bool" + +- name: Ensure matrix-backup-borg.service enabled + service: + enabled: true + name: matrix-backup-borg.service + +- name: Ensure matrix-backup-borg.timer enabled + service: + enabled: true + name: matrix-backup-borg.timer diff --git a/roles/matrix-backup-borg/tasks/setup_uninstall.yml b/roles/matrix-backup-borg/tasks/setup_uninstall.yml new file mode 100644 index 000000000..faad44f72 --- /dev/null +++ b/roles/matrix-backup-borg/tasks/setup_uninstall.yml @@ -0,0 +1,41 @@ +--- +- name: Check existence of matrix-backup-borg service + stat: + path: "{{ matrix_systemd_path }}/matrix-backup-borg.service" + register: matrix_backup_borg_service_stat + +- name: Ensure matrix-backup-borg is stopped + service: + name: matrix-backup-borg + state: stopped + enabled: false + daemon_reload: true + register: stopping_result + when: "matrix_backup_borg_service_stat.stat.exists|bool" + +- name: Ensure matrix-backup-borg.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-backup-borg.service" + state: absent + when: "matrix_backup_borg_service_stat.stat.exists|bool" + +- name: Ensure matrix-backup-borg.timer doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-backup-borg.timer" + state: absent + when: "matrix_backup_borg_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-backup-borg.service removal + service: + daemon_reload: true + when: "matrix_backup_borg_service_stat.stat.exists|bool" + +- name: Ensure Matrix borg paths don't exist + file: + path: "{{ matrix_backup_borg_base_path }}" + state: absent + +- name: Ensure borg Docker image doesn't exist + docker_image: + name: "{{ matrix_backup_borg_docker_image }}" + state: absent diff --git a/roles/matrix-backup-borg/tasks/validate_config.yml b/roles/matrix-backup-borg/tasks/validate_config.yml new file mode 100644 index 000000000..4d3fb1c8a --- /dev/null +++ b/roles/matrix-backup-borg/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_backup_borg_ssh_key_private" + - "matrix_backup_borg_location_repositories" + - "matrix_backup_borg_storage_encryption_passphrase" diff --git a/roles/matrix-backup-borg/templates/config.yaml.j2 b/roles/matrix-backup-borg/templates/config.yaml.j2 new file mode 100644 index 000000000..89b6ab7d4 --- /dev/null +++ b/roles/matrix-backup-borg/templates/config.yaml.j2 @@ -0,0 +1,32 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" + +location: + source_directories: {{ matrix_backup_borg_location_source_directories|to_json }} + repositories: {{ matrix_backup_borg_location_repositories|to_json }} + one_file_system: true + exclude_patterns: {{ matrix_backup_borg_location_exclude_patterns|to_json }} + +storage: + compression: {{ matrix_backup_borg_storage_compression }} + ssh_command: {{ matrix_backup_borg_storage_ssh_command }} + archive_name_format: '{{ matrix_backup_borg_storage_archive_name_format }}' + encryption_passphrase: {{ matrix_backup_borg_storage_encryption_passphrase }} + +retention: + keep_hourly: {{ matrix_backup_borg_retention_keep_hourly }} + keep_daily: {{ matrix_backup_borg_retention_keep_daily }} + keep_weekly: {{ matrix_backup_borg_retention_keep_weekly }} + keep_monthly: {{ matrix_backup_borg_retention_keep_monthly }} + keep_yearly: {{ matrix_backup_borg_retention_keep_yearly }} + prefix: '{{ matrix_backup_borg_retention_prefix }}' + +consistency: + checks: + - repository + - archives + +hooks: + after_backup: + - echo "Backup created." + on_error: + - echo "Error while creating a backup." diff --git a/roles/matrix-backup-borg/templates/passwd.j2 b/roles/matrix-backup-borg/templates/passwd.j2 new file mode 100644 index 000000000..d3665cf4b --- /dev/null +++ b/roles/matrix-backup-borg/templates/passwd.j2 @@ -0,0 +1,29 @@ +{# the passwd file with correct username, UID and GID is mandatory to work with borg over ssh, otherwise ssh connections will fail #} +root:x:0:0:root:/root:/bin/ash +bin:x:1:1:bin:/bin:/sbin/nologin +daemon:x:2:2:daemon:/sbin:/sbin/nologin +adm:x:3:4:adm:/var/adm:/sbin/nologin +lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin +sync:x:5:0:sync:/sbin:/bin/sync +shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown +halt:x:7:0:halt:/sbin:/sbin/halt +mail:x:8:12:mail:/var/mail:/sbin/nologin +news:x:9:13:news:/usr/lib/news:/sbin/nologin +uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin +operator:x:11:0:operator:/root:/sbin/nologin +man:x:13:15:man:/usr/man:/sbin/nologin +postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin +cron:x:16:16:cron:/var/spool/cron:/sbin/nologin +ftp:x:21:21::/var/lib/ftp:/sbin/nologin +sshd:x:22:22:sshd:/dev/null:/sbin/nologin +at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin +squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin +xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin +games:x:35:35:games:/usr/games:/sbin/nologin +cyrus:x:85:12::/usr/cyrus:/sbin/nologin +vpopmail:x:89:89::/var/vpopmail:/sbin/nologin +ntp:x:123:123:NTP:/var/empty:/sbin/nologin +smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin +guest:x:405:100:guest:/dev/null:/sbin/nologin +{{ matrix_user_username }}:x:{{ matrix_user_uid }}:{{ matrix_user_gid }}:Matrix:/tmp:/bin/ash +nobody:x:65534:65534:nobody:/:/sbin/nologin diff --git a/roles/matrix-backup-borg/templates/sshkey.j2 b/roles/matrix-backup-borg/templates/sshkey.j2 new file mode 100644 index 000000000..999cf38d1 --- /dev/null +++ b/roles/matrix-backup-borg/templates/sshkey.j2 @@ -0,0 +1 @@ +{{ matrix_backup_borg_ssh_key_private }} diff --git a/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.service.j2 b/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.service.j2 new file mode 100644 index 000000000..977673ee3 --- /dev/null +++ b/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.service.j2 @@ -0,0 +1,58 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Borg Backup +{% for service in matrix_backup_borg_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_backup_borg_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=oneshot +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-backup-borg 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-backup-borg 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_docker }} run --rm --name matrix-backup-borg \ + --log-driver=none \ + --cap-drop=ALL \ + --read-only \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --network={{ matrix_docker_network }} \ + --tmpfs=/tmp:rw,noexec,nosuid,size=100m \ + --mount type=bind,src={{ matrix_backup_borg_config_path }}/passwd,dst=/etc/passwd,ro \ + --mount type=bind,src={{ matrix_backup_borg_config_path }},dst=/etc/borgmatic.d,ro \ + {% for source in matrix_backup_borg_location_source_directories %} + --mount type=bind,src={{ source }},dst={{ source }},ro \ + {% endfor %} + {% for arg in matrix_backup_borg_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_backup_borg_docker_image }} \ + sh -c "borgmatic --init --encryption {{ matrix_backup_borg_encryption }}" + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-backup-borg \ + --log-driver=none \ + --cap-drop=ALL \ + --read-only \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --network={{ matrix_docker_network }} \ + --tmpfs=/tmp:rw,noexec,nosuid,size=100m \ + --mount type=bind,src={{ matrix_backup_borg_config_path }}/passwd,dst=/etc/passwd,ro \ + --mount type=bind,src={{ matrix_backup_borg_config_path }},dst=/etc/borgmatic.d,ro \ + {% for source in matrix_backup_borg_location_source_directories %} + --mount type=bind,src={{ source }},dst={{ source }},ro \ + {% endfor %} + {% for arg in matrix_backup_borg_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_backup_borg_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-backup-borg 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-backup-borg 2>/dev/null' +SyslogIdentifier=matrix-backup-borg + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.timer.j2 b/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.timer.j2 new file mode 100644 index 000000000..541d00203 --- /dev/null +++ b/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.timer.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Matrix Borg Backup timer + +[Timer] +Unit=matrix-backup-borg.service +OnCalendar={{ matrix_backup_borg_schedule }} +RandomizedDelaySec=2h + +[Install] +WantedBy=timers.target diff --git a/roles/matrix-bot-honoroit/tasks/setup_install.yml b/roles/matrix-bot-honoroit/tasks/setup_install.yml index 303c5f8b5..f3ad9b630 100644 --- a/roles/matrix-bot-honoroit/tasks/setup_install.yml +++ b/roles/matrix-bot-honoroit/tasks/setup_install.yml @@ -43,6 +43,8 @@ template: src: "{{ role_path }}/templates/env.j2" dest: "{{ matrix_bot_honoroit_config_path }}/env" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" mode: 0640 - name: Ensure honoroit image is pulled diff --git a/setup.yml b/setup.yml index 68740b4af..197d313e2 100755 --- a/setup.yml +++ b/setup.yml @@ -14,6 +14,7 @@ - matrix-postgres - matrix-redis - matrix-corporal + - matrix-backup-borg - matrix-bridge-appservice-discord - matrix-bridge-appservice-slack - matrix-bridge-appservice-webhooks From 627333d82bf1d9b902c44e209a7caadc20227553 Mon Sep 17 00:00:00 2001 From: Luke <19363185+mochman@users.noreply.github.com> Date: Tue, 5 Apr 2022 12:42:38 +0000 Subject: [PATCH 06/83] fix container image --- roles/matrix-bridge-hookshot/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-hookshot/defaults/main.yml b/roles/matrix-bridge-hookshot/defaults/main.yml index 6dab5cd65..78eacdb14 100644 --- a/roles/matrix-bridge-hookshot/defaults/main.yml +++ b/roles/matrix-bridge-hookshot/defaults/main.yml @@ -12,7 +12,7 @@ matrix_hookshot_container_image_self_build_branch: "{{ 'main' if matrix_hookshot matrix_hookshot_version: 1.3.0 -matrix_hookshot_docker_image: "{{ matrix_hookshot_docker_image_name_prefix }}mautrix/whatsapp:{{ matrix_mautrix_whatsapp_version }}" +matrix_hookshot_docker_image: "{{ matrix_hookshot_docker_image_name_prefix }}halfshot/matrix-hookshot:{{ matrix_hookshot_version }}" matrix_hookshot_docker_image_name_prefix: "{{ 'localhost/' if matrix_hookshot_container_image_self_build else matrix_container_global_registry_prefix }}" matrix_hookshot_docker_image_force_pull: "{{ matrix_hookshot_docker_image.endswith(':latest') }}" From db4b6efb5da80629987672a55a87c4ef370f8bcf Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 5 Apr 2022 16:08:11 +0300 Subject: [PATCH 07/83] Force self-building of matrix-hookshot on arm64 The `halfshot/matrix-hookshot` container images published to Docker Hub (as of 2022-04-05, at least) are only available for `amd64`, not for `arm64`. Self-building on arm64 is necessary. Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1728 It should be noted that a `roiarthurb/matrix-hookshot` container image is available, which is available for the arm64 platform, but that's non-official and doesn't contain an amd64 build, so it's of limited use. --- group_vars/matrix_servers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 15032cabf..17acd5e15 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -671,7 +671,7 @@ matrix_heisenbridge_systemd_wanted_services_list: | # We don't enable bridges by default. matrix_hookshot_enabled: false -matrix_hookshot_container_image_self_build: "{{ matrix_architecture not in ['arm64', 'amd64'] }}" +matrix_hookshot_container_image_self_build: "{{ matrix_architecture not in ['amd64'] }}" matrix_hookshot_appservice_token: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'hookshot.as.tok') | to_uuid }}" From 7559eb99a77cbb40e82ba566db9dbeb0ae9bc23a Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 5 Apr 2022 20:48:15 +0300 Subject: [PATCH 08/83] Update Synapse 1.55.2 -> 1.56.0 --- roles/matrix-synapse/defaults/main.yml | 4 +- .../templates/synapse/homeserver.yaml.j2 | 254 +++++------------- 2 files changed, 74 insertions(+), 184 deletions(-) diff --git a/roles/matrix-synapse/defaults/main.yml b/roles/matrix-synapse/defaults/main.yml index 4cba18fa7..ec58f2336 100644 --- a/roles/matrix-synapse/defaults/main.yml +++ b/roles/matrix-synapse/defaults/main.yml @@ -9,7 +9,7 @@ matrix_synapse_container_image_self_build_repo: "https://github.com/matrix-org/s matrix_synapse_docker_image: "{{ matrix_synapse_docker_image_name_prefix }}matrixdotorg/synapse:{{ matrix_synapse_docker_image_tag }}" matrix_synapse_docker_image_name_prefix: "{{ 'localhost/' if matrix_synapse_container_image_self_build else matrix_container_global_registry_prefix }}" -matrix_synapse_version: v1.55.2 +matrix_synapse_version: v1.56.0 matrix_synapse_docker_image_tag: "{{ matrix_synapse_version }}" matrix_synapse_docker_image_force_pull: "{{ matrix_synapse_docker_image.endswith(':latest') }}" @@ -202,6 +202,8 @@ matrix_synapse_include_profile_data_on_invite: true # Controls whether people with access to the homeserver can register by themselves. matrix_synapse_enable_registration: false +# Controls whether people with access to the homeserver can register by themselves without verification (email/msisdn/token) +matrix_synapse_enable_registration_without_verification: false # reCAPTCHA API for validating registration attempts matrix_synapse_enable_registration_captcha: false diff --git a/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 b/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 index 63e3b7adb..8a701c4d9 100644 --- a/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 +++ b/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 @@ -35,7 +35,7 @@ modules: {{ matrix_synapse_modules|to_json }} # In most cases you should avoid using a matrix specific subdomain such as # matrix.example.com or synapse.example.com as the server_name for the same # reasons you wouldn't use user@email.example.com as your email address. -# See https://github.com/matrix-org/synapse/blob/master/docs/delegate.md +# See https://matrix-org.github.io/synapse/latest/delegate.html # for information on how to host Synapse on a subdomain while preserving # a clean server_name. # @@ -232,9 +232,9 @@ default_room_version: {{ matrix_synapse_default_room_version|to_json }} # 'all local interfaces'. # # type: the type of listener. Normally 'http', but other valid options are: -# 'manhole' (see docs/manhole.md), -# 'metrics' (see docs/metrics-howto.md), -# 'replication' (see docs/workers.md). +# 'manhole' (see https://matrix-org.github.io/synapse/latest/manhole.html), +# 'metrics' (see https://matrix-org.github.io/synapse/latest/metrics-howto.html), +# 'replication' (see https://matrix-org.github.io/synapse/latest/workers.html). # # tls: set to true to enable TLS for this listener. Will use the TLS # key/cert specified in tls_private_key_path / tls_certificate_path. @@ -259,8 +259,8 @@ default_room_version: {{ matrix_synapse_default_room_version|to_json }} # client: the client-server API (/_matrix/client), and the synapse admin # API (/_synapse/admin). Also implies 'media' and 'static'. # -# consent: user consent forms (/_matrix/consent). See -# docs/consent_tracking.md. +# consent: user consent forms (/_matrix/consent). +# See https://matrix-org.github.io/synapse/latest/consent_tracking.html. # # federation: the server-server API (/_matrix/federation). Also implies # 'media', 'keys', 'openid' @@ -269,12 +269,13 @@ default_room_version: {{ matrix_synapse_default_room_version|to_json }} # # media: the media API (/_matrix/media). # -# metrics: the metrics interface. See docs/metrics-howto.md. +# metrics: the metrics interface. +# See https://matrix-org.github.io/synapse/latest/consent_tracking.html. # # openid: OpenID authentication. # -# replication: the HTTP replication API (/_synapse/replication). See -# docs/workers.md. +# replication: the HTTP replication API (/_synapse/replication). +# See https://matrix-org.github.io/synapse/latest/consent_tracking.html. # # static: static resources under synapse/static (/_matrix/static). (Mostly # useful for 'fallback authentication'.) @@ -431,9 +432,16 @@ manhole_settings: # sign up in a short space of time never to return after their initial # session. # +# 'mau_limit_alerting' is a means of limiting client side alerting +# should the mau limit be reached. This is useful for small instances +# where the admin has 5 mau seats (say) for 5 specific people and no +# interest increasing the mau limit further. Defaults to True, which +# means that alerting is enabled +# #limit_usage_by_mau: False #max_mau_value: 50 #mau_trial_days: 2 +#mau_limit_alerting: false # If enabled, the metrics for the number of monthly active users will # be populated, however no one will be limited. If limit_usage_by_mau @@ -560,6 +568,15 @@ templates: # #custom_template_directory: /path/to/custom/templates/ +# List of rooms to exclude from sync responses. This is useful for server +# administrators wishing to group users into a room without these users being able +# to see it from their client. +# +# By default, no room is excluded. +# +#exclude_rooms_from_sync: +# - !foo:example.com + # Message retention policy at the server level. # @@ -808,6 +825,12 @@ caches: # 'txn_limit' gives the maximum number of transactions to run per connection # before reconnecting. Defaults to 0, which means no limit. # +# 'allow_unsafe_locale' is an option specific to Postgres. Under the default behavior, Synapse will refuse to +# start if the postgres db is set to a non-C locale. You can override this behavior (which is *not* recommended) +# by setting 'allow_unsafe_locale' to true. Note that doing so may corrupt your database. You can find more information +# here: https://matrix-org.github.io/synapse/latest/postgres.html#fixing-incorrect-collate-or-ctype and here: +# https://wiki.postgresql.org/wiki/Locale_data_changes +# # 'args' gives options which are passed through to the database engine, # except for options starting 'cp_', which are used to configure the Twisted # connection pool. For a reference to valid arguments, see: @@ -1020,7 +1043,7 @@ media_store_path: "/matrix-media-store-parent/{{ matrix_synapse_media_store_dire # # If you are using a reverse proxy you may also need to set this value in # your reverse proxy's config. Notably Nginx has a small max body size by default. -# See https://matrix-org.github.io/synapse/develop/reverse_proxy.html. +# See https://matrix-org.github.io/synapse/latest/reverse_proxy.html. # max_upload_size: "{{ matrix_synapse_max_upload_size_mb }}M" @@ -1171,6 +1194,26 @@ max_spider_size: 10M url_preview_accept_language: {{ matrix_url_preview_accept_language|to_json }} +# oEmbed allows for easier embedding content from a website. It can be +# used for generating URLs previews of services which support it. +# +oembed: + # A default list of oEmbed providers is included with Synapse. + # + # Uncomment the following to disable using these default oEmbed URLs. + # Defaults to 'false'. + # + #disable_default_providers: true + # Additional files with oEmbed configuration (each should be in the + # form of providers.json). + # + # By default, this list is empty (so only the default providers.json + # is used). + # + #additional_providers: + # - oembed/my_providers.json + + ## Captcha ## # See docs/CAPTCHA_SETUP.md for full details of configuring this. @@ -1230,10 +1273,18 @@ turn_allow_guests: {{ matrix_synapse_turn_allow_guests|to_json }} # Registration can be rate-limited using the parameters in the "Ratelimiting" # section of this file. -# Enable registration for new users. +# Enable registration for new users. Defaults to 'false'. It is highly recommended that if you enable registration, +# you use either captcha, email, or token-based verification to verify that new users are not bots. In order to enable registration +# without any verification, you must also set `enable_registration_without_verification`, found below. # enable_registration: {{ matrix_synapse_enable_registration|to_json }} +# Enable registration without email or captcha verification. Note: this option is *not* recommended, +# as registration without verification is a known vector for spam and abuse. Defaults to false. Has no effect +# unless `enable_registration` is also enabled. +# +enable_registration_without_verification: {{ matrix_synapse_enable_registration_without_verification|to_json }} + # Time that a user's session remains valid for, after they log in. # # Note that this is not currently compatible with guest logins. @@ -1283,8 +1334,6 @@ enable_registration: {{ matrix_synapse_enable_registration|to_json }} # #nonrefreshable_access_token_lifetime: 24h -# The user must provide all of the below types of 3PID when registering. - # The user must provide all of the below types of 3PID when registering. # #registrations_require_3pid: @@ -1962,7 +2011,7 @@ saml2_config: # # module: The class name of a custom mapping module. Default is # 'synapse.handlers.oidc.JinjaOidcMappingProvider'. -# See https://github.com/matrix-org/synapse/blob/master/docs/sso_mapping_providers.md#openid-mapping-providers +# See https://matrix-org.github.io/synapse/latest/sso_mapping_providers.html#openid-mapping-providers # for information on implementing a custom mapping provider. # # config: Configuration for the mapping provider module. This section will @@ -2019,7 +2068,7 @@ saml2_config: # - attribute: groups # value: "admin" # -# See https://github.com/matrix-org/synapse/blob/master/docs/openid.md +# See https://matrix-org.github.io/synapse/latest/openid.html # for information on how to configure these options. # # For backwards compatibility, it is also possible to configure a single OIDC @@ -2044,6 +2093,7 @@ oidc_providers: # token_endpoint: "https://accounts.example.com/oauth2/token" # userinfo_endpoint: "https://accounts.example.com/userinfo" # jwks_uri: "https://accounts.example.com/.well-known/jwks.json" + # skip_verification: true # user_mapping_provider: # config: # subject_claim: "id" @@ -2121,169 +2171,6 @@ sso: # #update_profile_information: true - # Directory in which Synapse will try to find the template files below. - # If not set, or the files named below are not found within the template - # directory, default templates from within the Synapse package will be used. - # - # Synapse will look for the following templates in this directory: - # - # * HTML page to prompt the user to choose an Identity Provider during - # login: 'sso_login_idp_picker.html'. - # - # This is only used if multiple SSO Identity Providers are configured. - # - # When rendering, this template is given the following variables: - # * redirect_url: the URL that the user will be redirected to after - # login. - # - # * server_name: the homeserver's name. - # - # * providers: a list of available Identity Providers. Each element is - # an object with the following attributes: - # - # * idp_id: unique identifier for the IdP - # * idp_name: user-facing name for the IdP - # * idp_icon: if specified in the IdP config, an MXC URI for an icon - # for the IdP - # * idp_brand: if specified in the IdP config, a textual identifier - # for the brand of the IdP - # - # The rendered HTML page should contain a form which submits its results - # back as a GET request, with the following query parameters: - # - # * redirectUrl: the client redirect URI (ie, the `redirect_url` passed - # to the template) - # - # * idp: the 'idp_id' of the chosen IDP. - # - # * HTML page to prompt new users to enter a userid and confirm other - # details: 'sso_auth_account_details.html'. This is only shown if the - # SSO implementation (with any user_mapping_provider) does not return - # a localpart. - # - # When rendering, this template is given the following variables: - # - # * server_name: the homeserver's name. - # - # * idp: details of the SSO Identity Provider that the user logged in - # with: an object with the following attributes: - # - # * idp_id: unique identifier for the IdP - # * idp_name: user-facing name for the IdP - # * idp_icon: if specified in the IdP config, an MXC URI for an icon - # for the IdP - # * idp_brand: if specified in the IdP config, a textual identifier - # for the brand of the IdP - # - # * user_attributes: an object containing details about the user that - # we received from the IdP. May have the following attributes: - # - # * display_name: the user's display_name - # * emails: a list of email addresses - # - # The template should render a form which submits the following fields: - # - # * username: the localpart of the user's chosen user id - # - # * HTML page allowing the user to consent to the server's terms and - # conditions. This is only shown for new users, and only if - # `user_consent.require_at_registration` is set. - # - # When rendering, this template is given the following variables: - # - # * server_name: the homeserver's name. - # - # * user_id: the user's matrix proposed ID. - # - # * user_profile.display_name: the user's proposed display name, if any. - # - # * consent_version: the version of the terms that the user will be - # shown - # - # * terms_url: a link to the page showing the terms. - # - # The template should render a form which submits the following fields: - # - # * accepted_version: the version of the terms accepted by the user - # (ie, 'consent_version' from the input variables). - # - # * HTML page for a confirmation step before redirecting back to the client - # with the login token: 'sso_redirect_confirm.html'. - # - # When rendering, this template is given the following variables: - # - # * redirect_url: the URL the user is about to be redirected to. - # - # * display_url: the same as `redirect_url`, but with the query - # parameters stripped. The intention is to have a - # human-readable URL to show to users, not to use it as - # the final address to redirect to. - # - # * server_name: the homeserver's name. - # - # * new_user: a boolean indicating whether this is the user's first time - # logging in. - # - # * user_id: the user's matrix ID. - # - # * user_profile.avatar_url: an MXC URI for the user's avatar, if any. - # None if the user has not set an avatar. - # - # * user_profile.display_name: the user's display name. None if the user - # has not set a display name. - # - # * HTML page which notifies the user that they are authenticating to confirm - # an operation on their account during the user interactive authentication - # process: 'sso_auth_confirm.html'. - # - # When rendering, this template is given the following variables: - # * redirect_url: the URL the user is about to be redirected to. - # - # * description: the operation which the user is being asked to confirm - # - # * idp: details of the Identity Provider that we will use to confirm - # the user's identity: an object with the following attributes: - # - # * idp_id: unique identifier for the IdP - # * idp_name: user-facing name for the IdP - # * idp_icon: if specified in the IdP config, an MXC URI for an icon - # for the IdP - # * idp_brand: if specified in the IdP config, a textual identifier - # for the brand of the IdP - # - # * HTML page shown after a successful user interactive authentication session: - # 'sso_auth_success.html'. - # - # Note that this page must include the JavaScript which notifies of a successful authentication - # (see https://matrix.org/docs/spec/client_server/r0.6.0#fallback). - # - # This template has no additional variables. - # - # * HTML page shown after a user-interactive authentication session which - # does not map correctly onto the expected user: 'sso_auth_bad_user.html'. - # - # When rendering, this template is given the following variables: - # * server_name: the homeserver's name. - # * user_id_to_verify: the MXID of the user that we are trying to - # validate. - # - # * HTML page shown during single sign-on if a deactivated user (according to Synapse's database) - # attempts to login: 'sso_account_deactivated.html'. - # - # This template has no additional variables. - # - # * HTML page to display to users if something goes wrong during the - # OpenID Connect authentication process: 'sso_error.html'. - # - # When rendering, this template is given two variables: - # * error: the technical name of the error - # * error_description: a human-readable message for the error - # - # You can see the default templates at: - # https://github.com/matrix-org/synapse/tree/master/synapse/res/templates - # - #template_dir: "res/templates" - # JSON web token integration. The following settings can be used to make # Synapse JSON web tokens for authentication, instead of its internal @@ -2298,7 +2185,7 @@ sso: # Note that this is a non-standard login type and client support is # expected to be non-existent. # -# See https://github.com/matrix-org/synapse/blob/master/docs/jwt.md. +# See https://matrix-org.github.io/synapse/latest/jwt.html. # #jwt_config: # Uncomment the following to enable authorization using JSON web @@ -2477,7 +2364,8 @@ email: #app_name: my_branded_matrix_server app_name: Matrix - # Enable sending emails for messages that the user has missed + # Uncomment the following to enable sending emails for messages that the user + # has missed. Disabled by default. # #enable_notifs: false enable_notifs: true @@ -2754,7 +2642,7 @@ user_directory: # User Consent configuration # # for detailed instructions, see -# https://github.com/matrix-org/synapse/blob/master/docs/consent_tracking.md +# https://matrix-org.github.io/synapse/latest/consent_tracking.html # # Parts of this section are required if enabling the 'consent' resource under # 'listeners', in particular 'template_dir' and 'version'. @@ -2804,7 +2692,7 @@ user_directory: # Settings for local room and user statistics collection. See -# docs/room_and_user_statistics.md. +# https://matrix-org.github.io/synapse/latest/room_and_user_statistics.html. # stats: # Uncomment the following to disable room and user statistics. Note that doing @@ -2919,7 +2807,7 @@ opentracing: #enabled: true # The list of homeservers we wish to send and receive span contexts and span baggage. - # See docs/opentracing.rst. + # See https://matrix-org.github.io/synapse/latest/opentracing.html. # # This is a list of regexes which are matched against the server_name of the # homeserver. From a86757a18d11ff8270ecdbf9dc268fb809187159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Thu, 7 Apr 2022 01:16:54 +0200 Subject: [PATCH 09/83] mautrix-facebook: update to v0.4.0 --- .../defaults/main.yml | 2 +- .../templates/config.yaml.j2 | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/roles/matrix-bridge-mautrix-facebook/defaults/main.yml b/roles/matrix-bridge-mautrix-facebook/defaults/main.yml index c9eaa1489..d14698631 100644 --- a/roles/matrix-bridge-mautrix-facebook/defaults/main.yml +++ b/roles/matrix-bridge-mautrix-facebook/defaults/main.yml @@ -7,7 +7,7 @@ matrix_mautrix_facebook_enabled: true matrix_mautrix_facebook_container_image_self_build: false matrix_mautrix_facebook_container_image_self_build_repo: "https://mau.dev/mautrix/facebook.git" -matrix_mautrix_facebook_version: v0.3.3 +matrix_mautrix_facebook_version: v0.4.0 matrix_mautrix_facebook_docker_image: "{{ matrix_mautrix_facebook_docker_image_name_prefix }}mautrix/facebook:{{ matrix_mautrix_facebook_version }}" matrix_mautrix_facebook_docker_image_name_prefix: "{{ 'localhost/' if matrix_mautrix_facebook_container_image_self_build else 'dock.mau.dev/' }}" matrix_mautrix_facebook_docker_image_force_pull: "{{ matrix_mautrix_facebook_docker_image.endswith(':latest') }}" diff --git a/roles/matrix-bridge-mautrix-facebook/templates/config.yaml.j2 b/roles/matrix-bridge-mautrix-facebook/templates/config.yaml.j2 index 49c49be8f..c3cb1932e 100644 --- a/roles/matrix-bridge-mautrix-facebook/templates/config.yaml.j2 +++ b/roles/matrix-bridge-mautrix-facebook/templates/config.yaml.j2 @@ -12,6 +12,9 @@ homeserver: # such as /_matrix/client/unstable/net.maunium.asmux/dms for atomically # updating m.direct. asmux: false + # Whether asynchronous uploads via MSC2246 should be enabled for media. + # Requires a media repo that supports MSC2246. + async_media: false # Application service host/registration related details # Changing these values requires regeneration of the registration. @@ -38,6 +41,11 @@ appservice: # The base URL where the public-facing endpoints are available. The prefix is not added # implicitly. external: {{ matrix_mautrix_facebook_appservice_public_external|to_json }} + # Allow logging in within Matrix. If false, users can only log in using the web interface. + allow_matrix_login: true + # Segment API key to enable analytics tracking for web server endpoints. Set to null to disable. + # Currently the only events are login start, success and fail. + segment_key: null # The unique ID of this appservice. id: facebook @@ -171,6 +179,8 @@ bridge: # and missed message backfilling when reconnecting. # Set to 0 to always re-sync, or -1 to never re-sync automatically. resync_max_disconnected_time: 5 + # Should the bridge do a resync on startup? + sync_on_startup: true # Whether or not temporary disconnections should send notices to the notice room. # If this is false, disconnections will never send messages and connections will only send # messages if it was disconnected for more than resync_max_disconnected_time seconds. @@ -194,6 +204,34 @@ bridge: permissions: '{{ matrix_mautrix_facebook_homeserver_domain }}': user + relay: + # Whether relay mode should be allowed. If allowed, `!fb set-relay` can be used to turn any + # authenticated user into a relaybot for that chat. + enabled: false + # The formats to use when sending messages to Messenger via a relay user. + # + # Available variables: + # $sender_displayname - The display name of the sender (e.g. Example User) + # $sender_username - The username (Matrix ID localpart) of the sender (e.g. exampleuser) + # $sender_mxid - The Matrix ID of the sender (e.g. @exampleuser:example.com) + # $message - The message content + message_formats: + m.text: '$sender_displayname: $message' + m.notice: '$sender_displayname: $message' + m.emote: '* $sender_displayname $message' + m.file: '$sender_displayname sent a file' + m.image: '$sender_displayname sent an image' + m.audio: '$sender_displayname sent an audio file' + m.video: '$sender_displayname sent a video' + m.location: '$sender_displayname sent a location' + +facebook: + device_seed: generate + default_region_hint: ODN + connection_type: WIFI + carrier: Verizon + hni: 311390 + # Python logging configuration. # # See section 16.7.2 of the Python documentation for more info: From 17f8fd003d674eadcb9d3f3396d21cd3fbe74aff Mon Sep 17 00:00:00 2001 From: slikie <13197246+slikie@users.noreply.github.com> Date: Thu, 7 Apr 2022 20:01:26 +0800 Subject: [PATCH 10/83] bump mautrix instagram version to 0.1.3 --- roles/matrix-bridge-mautrix-instagram/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-mautrix-instagram/defaults/main.yml b/roles/matrix-bridge-mautrix-instagram/defaults/main.yml index c4d90e6b8..4ae2d374d 100644 --- a/roles/matrix-bridge-mautrix-instagram/defaults/main.yml +++ b/roles/matrix-bridge-mautrix-instagram/defaults/main.yml @@ -7,7 +7,7 @@ matrix_mautrix_instagram_enabled: true matrix_mautrix_instagram_container_image_self_build: false matrix_mautrix_instagram_container_image_self_build_repo: "https://github.com/mautrix/instagram.git" -matrix_mautrix_instagram_version: v0.1.2 +matrix_mautrix_instagram_version: v0.1.3 # See: https://mau.dev/tulir/mautrix-instagram/container_registry matrix_mautrix_instagram_docker_image: "{{ matrix_mautrix_instagram_docker_image_name_prefix }}mautrix/instagram:{{ matrix_mautrix_instagram_version }}" matrix_mautrix_instagram_docker_image_name_prefix: "{{ 'localhost/' if matrix_mautrix_instagram_container_image_self_build else 'dock.mau.dev/' }}" From ab3e02c7fd54b1af0c500a77a5ab422d5c1a65a4 Mon Sep 17 00:00:00 2001 From: PC-Admin Date: Sat, 9 Apr 2022 08:48:02 +0800 Subject: [PATCH 11/83] Remove matrix-awx sections --- docs/configuring-awx-system.md | 39 --- group_vars/matrix_servers | 21 -- roles/matrix-awx/defaults/main.yml | 8 - .../scripts/matrix_build_room_list.py | 29 -- .../matrix-awx/surveys/access_export.json.j2 | 42 --- .../matrix-awx/surveys/backup_server.json.j2 | 18 - .../surveys/bridge_discord_appservice.json.j2 | 66 ---- .../surveys/configure_corporal.json.j2 | 88 ----- .../surveys/configure_dimension.json.j2 | 30 -- .../surveys/configure_element.json.j2 | 114 ------- .../configure_element_subdomain.json.j2 | 18 - .../surveys/configure_email_relay.json.j2 | 19 -- .../surveys/configure_jitsi.json.j2 | 31 -- .../surveys/configure_ma1sd.json.j2 | 41 --- .../surveys/configure_mjolnir.json.j2 | 29 -- .../surveys/configure_synapse.json.j2 | 198 ----------- .../surveys/configure_synapse_admin.json.j2 | 18 - .../configure_website_access_export.json.j2 | 54 --- roles/matrix-awx/tasks/backup_server.yml | 101 ------ .../tasks/bridge_discord_appservice.yml | 58 ---- .../tasks/cache_matrix_variables.yml | 13 - .../matrix-awx/tasks/create_session_token.yml | 11 - roles/matrix-awx/tasks/create_user.yml | 41 --- .../tasks/customise_website_access_export.yml | 267 --------------- .../matrix-awx/tasks/delete_session_token.yml | 10 - roles/matrix-awx/tasks/export_server.yml | 43 --- roles/matrix-awx/tasks/import_awx.yml | 7 - .../tasks/load_hosting_and_org_variables.yml | 16 - .../tasks/load_matrix_variables.yml | 16 - roles/matrix-awx/tasks/main.yml | 234 ------------- .../tasks/purge_database_events.yml | 14 - .../matrix-awx/tasks/purge_database_main.yml | 320 ------------------ .../tasks/purge_database_no_local.yml | 14 - .../matrix-awx/tasks/purge_database_users.yml | 14 - roles/matrix-awx/tasks/purge_media_local.yml | 19 -- roles/matrix-awx/tasks/purge_media_main.yml | 111 ------ roles/matrix-awx/tasks/purge_media_remote.yml | 19 -- roles/matrix-awx/tasks/rotate_ssh.yml | 25 -- roles/matrix-awx/tasks/self_check.yml | 108 ------ .../tasks/set_variables_corporal.yml | 243 ------------- .../tasks/set_variables_dimension.yml | 105 ------ .../tasks/set_variables_element.yml | 180 ---------- .../tasks/set_variables_element_subdomain.yml | 43 --- .../matrix-awx/tasks/set_variables_jitsi.yml | 45 --- .../matrix-awx/tasks/set_variables_ma1sd.yml | 102 ------ .../matrix-awx/tasks/set_variables_mailer.yml | 44 --- .../tasks/set_variables_mjolnir.yml | 68 ---- .../tasks/set_variables_synapse.yml | 223 ------------ .../tasks/set_variables_synapse_admin.yml | 44 --- roles/matrix-awx/tasks/update_variables.yml | 32 -- roles/matrix-common-after/tasks/awx_post.yml | 77 ----- roles/matrix-common-after/tasks/main.yml | 5 - setup.yml | 1 - 53 files changed, 3536 deletions(-) delete mode 100644 docs/configuring-awx-system.md delete mode 100755 roles/matrix-awx/defaults/main.yml delete mode 100644 roles/matrix-awx/scripts/matrix_build_room_list.py delete mode 100644 roles/matrix-awx/surveys/access_export.json.j2 delete mode 100644 roles/matrix-awx/surveys/backup_server.json.j2 delete mode 100644 roles/matrix-awx/surveys/bridge_discord_appservice.json.j2 delete mode 100755 roles/matrix-awx/surveys/configure_corporal.json.j2 delete mode 100644 roles/matrix-awx/surveys/configure_dimension.json.j2 delete mode 100755 roles/matrix-awx/surveys/configure_element.json.j2 delete mode 100644 roles/matrix-awx/surveys/configure_element_subdomain.json.j2 delete mode 100644 roles/matrix-awx/surveys/configure_email_relay.json.j2 delete mode 100755 roles/matrix-awx/surveys/configure_jitsi.json.j2 delete mode 100644 roles/matrix-awx/surveys/configure_ma1sd.json.j2 delete mode 100644 roles/matrix-awx/surveys/configure_mjolnir.json.j2 delete mode 100755 roles/matrix-awx/surveys/configure_synapse.json.j2 delete mode 100644 roles/matrix-awx/surveys/configure_synapse_admin.json.j2 delete mode 100755 roles/matrix-awx/surveys/configure_website_access_export.json.j2 delete mode 100644 roles/matrix-awx/tasks/backup_server.yml delete mode 100644 roles/matrix-awx/tasks/bridge_discord_appservice.yml delete mode 100644 roles/matrix-awx/tasks/cache_matrix_variables.yml delete mode 100644 roles/matrix-awx/tasks/create_session_token.yml delete mode 100755 roles/matrix-awx/tasks/create_user.yml delete mode 100755 roles/matrix-awx/tasks/customise_website_access_export.yml delete mode 100644 roles/matrix-awx/tasks/delete_session_token.yml delete mode 100644 roles/matrix-awx/tasks/export_server.yml delete mode 100644 roles/matrix-awx/tasks/import_awx.yml delete mode 100644 roles/matrix-awx/tasks/load_hosting_and_org_variables.yml delete mode 100755 roles/matrix-awx/tasks/load_matrix_variables.yml delete mode 100755 roles/matrix-awx/tasks/main.yml delete mode 100644 roles/matrix-awx/tasks/purge_database_events.yml delete mode 100644 roles/matrix-awx/tasks/purge_database_main.yml delete mode 100644 roles/matrix-awx/tasks/purge_database_no_local.yml delete mode 100644 roles/matrix-awx/tasks/purge_database_users.yml delete mode 100644 roles/matrix-awx/tasks/purge_media_local.yml delete mode 100644 roles/matrix-awx/tasks/purge_media_main.yml delete mode 100644 roles/matrix-awx/tasks/purge_media_remote.yml delete mode 100644 roles/matrix-awx/tasks/rotate_ssh.yml delete mode 100644 roles/matrix-awx/tasks/self_check.yml delete mode 100755 roles/matrix-awx/tasks/set_variables_corporal.yml delete mode 100644 roles/matrix-awx/tasks/set_variables_dimension.yml delete mode 100755 roles/matrix-awx/tasks/set_variables_element.yml delete mode 100644 roles/matrix-awx/tasks/set_variables_element_subdomain.yml delete mode 100755 roles/matrix-awx/tasks/set_variables_jitsi.yml delete mode 100755 roles/matrix-awx/tasks/set_variables_ma1sd.yml delete mode 100644 roles/matrix-awx/tasks/set_variables_mailer.yml delete mode 100755 roles/matrix-awx/tasks/set_variables_mjolnir.yml delete mode 100755 roles/matrix-awx/tasks/set_variables_synapse.yml delete mode 100644 roles/matrix-awx/tasks/set_variables_synapse_admin.yml delete mode 100644 roles/matrix-awx/tasks/update_variables.yml delete mode 100644 roles/matrix-common-after/tasks/awx_post.yml diff --git a/docs/configuring-awx-system.md b/docs/configuring-awx-system.md deleted file mode 100644 index 3819a0d33..000000000 --- a/docs/configuring-awx-system.md +++ /dev/null @@ -1,39 +0,0 @@ -# Configuring AWX System (optional) - -An AWX setup for managing multiple Matrix servers. - -This section is used in an AWX system that can create and manage multiple [Matrix](http://matrix.org/) servers. You can issue members an AWX login to their own 'organisation', which they can use to manage/configure 1 to N servers. - -Members can be assigned a server from Digitalocean, or they can connect their own on-premises server. These playbooks are free to use in a commercial context with the 'MemberPress Plus' plugin. They can also be run in a non-commercial context. - -The AWX system is arranged into 'members' each with their own 'subscriptions'. After creating a subscription the user enters the 'provision stage' where they defined the URLs they will use, the servers location and whether or not there's already a website at the base domain. They then proceed onto the 'deploy stage' where they can configure their Matrix server. - -This system can manage the updates, configuration, import and export, backups and monitoring on its own. It is an extension of the popular deploy script [spantaleev/matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy). - -Warning: This system is about to undergo heavy revision, **we do not recommend using it at this time.** - -## Other Required Playbooks - -The following repositories allow you to copy and use this setup: - -[Create AWX System](https://gitlab.com/GoMatrixHosting/create-awx-system) - Creates and configures the AWX system for you. - -[Ansible Create Delete Subscription Membership](https://gitlab.com/GoMatrixHosting/ansible-create-delete-subscription-membership) - Used by the AWX system to create memberships and subscriptions. Also includes other administrative playbooks for updates, backups and restoring servers. - -[Ansible Provision Server](https://gitlab.com/GoMatrixHosting/ansible-provision-server) - Used by AWX members to perform initial configuration of their DigitalOcean or On-Premises server. - -[GMHosting External Tools](https://gitlab.com/GoMatrixHosting/gmhosting-external-tools) - Extra tools we run outside of AWX, some of which are experimental. - - -## Does I need an AWX setup to use this? How do I configure it? - -Yes, you'll need to configure an AWX instance, the [Create AWX System](https://gitlab.com/GoMatrixHosting/create-awx-system) repository makes it easy to do. Just follow the steps listed in ['/docs/Installation_AWX.md' of that repository](https://gitlab.com/GoMatrixHosting/create-awx-system/-/blob/master/docs/Installation_AWX.md). - -For simpler installation steps you can use to get started with this system, check out our minimal installation guide at ['/doc/Installation_Minimal_AWX.md of that repository'](https://gitlab.com/GoMatrixHosting/create-awx-system/-/blob/master/docs/Installation_Minimal_AWX.md). - - -## Does I need a front-end WordPress site? And a DigitalOcean account? - -You do not need a front-end WordPress site or the MemberPress plugin to use this setup. It can be run on it's own in a non-commercial context. - -You also don't need a DigitalOcean account, although this will limit you to only being able to connect 'On-Premises' servers. diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 17acd5e15..a1cadd12a 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -45,27 +45,6 @@ matrix_integration_manager_ui_url: "{{ matrix_dimension_integrations_ui_url if m ###################################################################### -###################################################################### -# -# matrix-awx -# -###################################################################### - -# We don't enable AWX support by default. -matrix_awx_enabled: false - -matrix_nginx_proxy_data_path: "{{ '/chroot/website' if (matrix_awx_enabled and not matrix_nginx_proxy_base_domain_homepage_enabled) else (matrix_nginx_proxy_base_path + '/data') }}" -matrix_nginx_proxy_data_path_in_container: "{{ '/nginx-data/matrix-domain' if (matrix_awx_enabled and not matrix_nginx_proxy_base_domain_homepage_enabled) else '/nginx-data' }}" -matrix_nginx_proxy_data_path_extension: "{{ '' if (matrix_awx_enabled and not matrix_nginx_proxy_base_domain_homepage_enabled) else '/matrix-domain' }}" -matrix_nginx_proxy_base_domain_create_directory: "{{ not matrix_awx_enabled }}" - -###################################################################### -# -# /matrix-awx -# -###################################################################### - - ###################################################################### # # matrix-bridge-appservice-discord diff --git a/roles/matrix-awx/defaults/main.yml b/roles/matrix-awx/defaults/main.yml deleted file mode 100755 index cb8473251..000000000 --- a/roles/matrix-awx/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- - -matrix_awx_enabled: true - -# Defaults for 'Customise Website + Access Export' template -awx_sftp_auth_method: 'Disabled' -awx_sftp_password: '' -awx_sftp_public_key: '' diff --git a/roles/matrix-awx/scripts/matrix_build_room_list.py b/roles/matrix-awx/scripts/matrix_build_room_list.py deleted file mode 100644 index 94779ca70..000000000 --- a/roles/matrix-awx/scripts/matrix_build_room_list.py +++ /dev/null @@ -1,29 +0,0 @@ - -import sys -import requests -import json - -janitor_token = sys.argv[1] -synapse_container_ip = sys.argv[2] -synapse_container_port = sys.argv[3] - -# collect total amount of rooms - -rooms_raw_url = 'http://' + synapse_container_ip + ':' + synapse_container_port + '/_synapse/admin/v1/rooms' -rooms_raw_header = {'Authorization': 'Bearer ' + janitor_token} -rooms_raw = requests.get(rooms_raw_url, headers=rooms_raw_header) -rooms_raw_python = json.loads(rooms_raw.text) -total_rooms = rooms_raw_python["total_rooms"] - -# build complete room list file - -room_list_file = open("/tmp/room_list_complete.json", "w") - -for i in range(0, total_rooms, 100): - rooms_inc_url = 'http://' + synapse_container_ip + ':' + synapse_container_port + '/_synapse/admin/v1/rooms?from=' + str(i) - rooms_inc = requests.get(rooms_inc_url, headers=rooms_raw_header) - room_list_file.write(rooms_inc.text) - -room_list_file.close() - -print(total_rooms) diff --git a/roles/matrix-awx/surveys/access_export.json.j2 b/roles/matrix-awx/surveys/access_export.json.j2 deleted file mode 100644 index d5e1f9456..000000000 --- a/roles/matrix-awx/surveys/access_export.json.j2 +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "Access Export", - "description": "Access the services export.", - "spec": [ - { - "question_name": "SFTP Authorisation Method", - "question_description": "Set whether you want to disable SFTP, use a password to connect to SFTP or connect with a more secure SSH key.", - "required": true, - "min": null, - "max": null, - "default": "{{ awx_sftp_auth_method | string }}", - "choices": "Disabled\nPassword\nSSH Key", - "new_question": true, - "variable": "awx_sftp_auth_method", - "type": "multiplechoice" - }, - { - "question_name": "SFTP Password", - "question_description": "Sets the password of the 'sftp' account, which allows you to upload a multi-file static website by SFTP, as well as export the latest copy of your Matrix service. Must be defined if 'Password' method is selected. WARNING: You must set a strong and unique password here.", - "required": false, - "min": 0, - "max": 64, - "default": "{{ awx_sftp_password }}", - "choices": "", - "new_question": true, - "variable": "awx_sftp_password", - "type": "password" - }, - { - "question_name": "SFTP Public SSH Key (More Secure)", - "question_description": "Sets the public SSH key used to access the 'sftp' account, which allows you to upload a multi-file static website by SFTP, as well as export the latest copy of your Matrix service. Must be defined if 'SSH Key' method is selected.", - "required": false, - "min": 0, - "max": 16384, - "default": "{{ awx_sftp_public_key }}", - "choices": "", - "new_question": true, - "variable": "awx_sftp_public_key", - "type": "text" - } - ] -} diff --git a/roles/matrix-awx/surveys/backup_server.json.j2 b/roles/matrix-awx/surveys/backup_server.json.j2 deleted file mode 100644 index 559daade4..000000000 --- a/roles/matrix-awx/surveys/backup_server.json.j2 +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Backup Server", - "description": "Performs a backup of the entire service to a remote location.", - "spec": [ - { - "question_name": "Enable Backup", - "question_description": "Set if remote backup is enabled or not. If enabled a daily backup of your server will be sent to the backup server located in {{ backup_server_location }}.", - "required": false, - "min": null, - "max": null, - "default": "{{ awx_backup_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "awx_backup_enabled", - "type": "multiplechoice" - } - ] -} diff --git a/roles/matrix-awx/surveys/bridge_discord_appservice.json.j2 b/roles/matrix-awx/surveys/bridge_discord_appservice.json.j2 deleted file mode 100644 index 85b00a5f5..000000000 --- a/roles/matrix-awx/surveys/bridge_discord_appservice.json.j2 +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "Bridge Discord Appservice", - "description": "Enables a private bridge you can use to connect Matrix rooms to Discord.", - "spec": [ - { - "question_name": "Enable Discord AppService Bridge", - "question_description": "Enables a private bridge you can use to connect Matrix rooms to Discord.", - "required": true, - "min": null, - "max": null, - "default": "{{ matrix_appservice_discord_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_appservice_discord_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Discord OAuth2 Client ID", - "question_description": "The OAuth2 'CLIENT ID' which can be found in the 'OAuth2' tab of your new discord application: https://discord.com/developers/applications", - "required": true, - "min": 0, - "max": 128, - "default": "{{ matrix_appservice_discord_client_id | trim }}", - "choices": "", - "new_question": true, - "variable": "matrix_appservice_discord_client_id", - "type": "text" - }, - { - "question_name": "Discord Bot Token", - "question_description": "The Bot 'TOKEN' which can be found in the 'Bot' tab of your new discord application: https://discord.com/developers/applications", - "required": true, - "min": 0, - "max": 256, - "default": "{{ matrix_appservice_discord_bot_token | trim }}", - "choices": "", - "new_question": true, - "variable": "matrix_appservice_discord_bot_token", - "type": "password" - }, - { - "question_name": "Auto-Admin Matrix User", - "question_description": "The username you would like to be automatically joined and promoted to administrator (PL100) in bridged rooms. Exclude the '@' and server name postfix. So to create @stevo:example.org just enter 'stevo'.", - "required": false, - "min": 0, - "max": 1024, - "default": "", - "choices": "", - "new_question": true, - "variable": "awx_appservice_discord_admin_user", - "type": "text" - }, - { - "question_name": "Auto-Admin Rooms", - "question_description": "A list of rooms you want the user to be automatically joined and promoted to administrator (PL100) in. These should be the internal IDs (for example '!axfBUsKhfAjSMBdjKX:example.org') separated by newlines.", - "required": false, - "min": 0, - "max": 4096, - "default": "", - "choices": "", - "new_question": true, - "variable": "awx_appservice_discord_admin_rooms", - "type": "textarea" - } - ] -} diff --git a/roles/matrix-awx/surveys/configure_corporal.json.j2 b/roles/matrix-awx/surveys/configure_corporal.json.j2 deleted file mode 100755 index dc8cd4fdb..000000000 --- a/roles/matrix-awx/surveys/configure_corporal.json.j2 +++ /dev/null @@ -1,88 +0,0 @@ -{ - "name": "Configure Matrix Corporal", - "description": "Configure Matrix Corporal, a tool that manages your Matrix server according to a configuration policy.", - "spec": [ - { - "question_name": "Enable Corporal", - "question_description": "Controls if Matrix Corporal is enabled at all. If you're unsure if you need Matrix Corporal or not, you most likely don't.", - "required": true, - "min": null, - "max": null, - "default": "{{ matrix_corporal_enabled|string|lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_corporal_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Corporal Policy Provider", - "question_description": "Controls what provider policy is used with Matrix Corporal.", - "required": true, - "min": null, - "max": null, - "default": "{{ awx_corporal_policy_provider_mode }}", - "choices": "Simple Static File\nHTTP Pull Mode (API Enabled)\nHTTP Push Mode (API Enabled)", - "new_question": true, - "variable": "awx_corporal_policy_provider_mode", - "type": "multiplechoice" - }, - { - "question_name": "Simple Static File Configuration", - "question_description": "The configuration file for Matrix Corporal, only needed if 'Simple Static File' provider is selected, any configuration entered here will be saved and applied.", - "required": false, - "min": 0, - "max": 65536, - "default": "", - "new_question": true, - "variable": "awx_corporal_simple_static_config", - "type": "textarea" - }, - { - "question_name": "HTTP Pull Mode URI", - "question_description": "The network address to remotely fetch the configuration from. Only needed if 'HTTP Pull Mode (API Enabled)' provider is selected.", - "required": false, - "min": 0, - "max": 4096, - "default": "{{ awx_corporal_pull_mode_uri }}", - "new_question": true, - "variable": "awx_corporal_pull_mode_uri", - "type": "text" - }, - { - "question_name": "HTTP Pull Mode Authentication Token", - "question_description": "An authentication token for pulling the Corporal configuration from a network location. Only needed if 'HTTP Pull Mode (API Enabled)' provider is selected. WARNING: You must set a strong and unique password here.", - "required": false, - "min": 0, - "max": 256, - "default": "{{ awx_corporal_pull_mode_token }}", - "choices": "", - "new_question": true, - "variable": "awx_corporal_pull_mode_token", - "type": "password" - }, - { - "question_name": "Corporal API Authentication Token", - "question_description": "An authentication token for interfacing with Corporals API. Only needed to be set if 'HTTP Pull Mode (API Enabled)' or 'HTTP Push Mode (API Enabled)' provider is selected. WARNING: You must set a strong and unique password here.", - "required": false, - "min": 0, - "max": 256, - "default": "{{ matrix_corporal_http_api_auth_token }}", - "choices": "", - "new_question": true, - "variable": "matrix_corporal_http_api_auth_token", - "type": "password" - }, - { - "question_name": "Raise Synapse Ratelimits", - "question_description": "For Matrix Corporal to work you will need to temporarily raise the rate limits for logins, please return this value to 'Normal' after you're done using Corporal.", - "required": false, - "min": null, - "max": null, - "default": "{{ awx_corporal_raise_ratelimits }}", - "choices": "Normal\nRaised", - "new_question": true, - "variable": "awx_corporal_raise_ratelimits", - "type": "multiplechoice" - } - ] -} diff --git a/roles/matrix-awx/surveys/configure_dimension.json.j2 b/roles/matrix-awx/surveys/configure_dimension.json.j2 deleted file mode 100644 index 5f79cfd08..000000000 --- a/roles/matrix-awx/surveys/configure_dimension.json.j2 +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "Configure Dimension", - "description": "Configure Dimension, the self-hosted integrations server.", - "spec": [ - { - "question_name": "Enable Dimension", - "question_description": "Enables the Dimension integration server, before doing this you need to create a CNAME record for 'dimension.{{ matrix_domain }}' that points to 'matrix.{{ matrix_domain }}'.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_dimension_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_dimension_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Dimension Users", - "question_description": "Here you can list the user accounts that will be able to configure Dimension. Entries must be seperated with newlines and must be a complete Matrix ID. For example: '@dimension:{{ matrix_domain }}'", - "required": false, - "min": 0, - "max": 65536, - "default": {{ awx_dimension_users_final | to_json }}, - "choices": "", - "new_question": true, - "variable": "awx_dimension_users", - "type": "textarea" - } - ] -} diff --git a/roles/matrix-awx/surveys/configure_element.json.j2 b/roles/matrix-awx/surveys/configure_element.json.j2 deleted file mode 100755 index b40217328..000000000 --- a/roles/matrix-awx/surveys/configure_element.json.j2 +++ /dev/null @@ -1,114 +0,0 @@ -{ - "name": "Configure Element", - "description": "Configure Element web client, Element is the most developed Matrix client software.", - "spec": [ - { - "question_name": "Enable Element-Web", - "question_description": "Set if Element web client is enabled or not.", - "required": true, - "min": null, - "max": null, - "default": "{{ matrix_client_element_enabled }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_client_element_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Set Theme for Web Client", - "question_description": "Sets the default theme for the web client, can be changed later by individual users.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_client_element_default_theme }}", - "choices": "light\ndark", - "new_question": true, - "variable": "matrix_client_element_default_theme", - "type": "multiplechoice" - }, - { - "question_name": "Set Branding for Web Client", - "question_description": "Sets the 'branding' seen in the tab and on the welcome page to a custom value.Leaving this field blank will cause the default branding will be used: 'Element'", - "required": false, - "min": 0, - "max": 256, - "default": "{{ matrix_client_element_brand | trim }}", - "choices": "", - "new_question": true, - "variable": "matrix_client_element_brand", - "type": "text" - }, - { - "question_name": "Set Welcome Page Background", - "question_description": "Sets the background image on the welcome page, you should enter a URL to the image you want to use. Must be a 'https' link, otherwise it won't be set. Leaving this field blank will cause the default background to be used.", - "required": false, - "min": 0, - "max": 1024, - "default": "{{ matrix_client_element_branding_welcomeBackgroundUrl | trim }}", - "choices": "", - "new_question": true, - "variable": "matrix_client_element_branding_welcomeBackgroundUrl", - "type": "text" - }, - { - "question_name": "Set Welcome Page Logo", - "question_description": "Sets the logo found on the welcome and login page, must be a valid https link to your logo, the logo itself should be a square vector image (SVG). Leaving this field blank will cause the default Element logo to be used.", - "required": false, - "min": 0, - "max": 1024, - "default": "{{ matrix_client_element_welcome_logo | trim }}", - "choices": "", - "new_question": true, - "variable": "matrix_client_element_welcome_logo", - "type": "text" - }, - { - "question_name": "Set Welcome Page Logo URL", - "question_description": "Sets the URL link the welcome page logo leads to, must be a valid https link. Leaving this field blank will cause this default link to be used: 'https://element.io'", - "required": false, - "min": 0, - "max": 1024, - "default": "{{ matrix_client_element_welcome_logo_link | trim }}", - "choices": "", - "new_question": true, - "variable": "matrix_client_element_welcome_logo_link", - "type": "text" - }, - { - "question_name": "Set Welcome Page Headline", - "question_description": "Sets the headline seen on the welcome page. Leaving this field blank will cause this default headline to be used: 'Welcome to Element!'", - "required": false, - "min": 0, - "max": 512, - "default": "{{ awx_matrix_client_element_welcome_headline | trim }}", - "choices": "", - "new_question": true, - "variable": "awx_matrix_client_element_welcome_headline", - "type": "text" - }, - { - "question_name": "Set Welcome Page Text", - "question_description": "Sets the text seen on the welcome page. Leaving this field blank will cause this default headline to be used: 'Decentralised, encrypted chat & collaboration powered by [Matrix]'", - "required": false, - "min": 0, - "max": 2048, - "default": "{{ awx_matrix_client_element_welcome_text | trim }}", - "choices": "", - "new_question": true, - "variable": "awx_matrix_client_element_welcome_text", - "type": "text" - }, - { - "question_name": "Show Registration Button", - "question_description": "If you show the registration button on the welcome page.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_client_element_registration_enabled }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_client_element_registration_enabled", - "type": "multiplechoice" - } - ] -} diff --git a/roles/matrix-awx/surveys/configure_element_subdomain.json.j2 b/roles/matrix-awx/surveys/configure_element_subdomain.json.j2 deleted file mode 100644 index 8e6aaf282..000000000 --- a/roles/matrix-awx/surveys/configure_element_subdomain.json.j2 +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Configure Element Subdomain", - "description": "Configure Element clients subdomain location. (Eg: 'element' for element.example.org)", - "spec": [ - { - "question_name": "Set Element Subdomain", - "question_description": "Sets the subdomain of the Element web-client, you should only specify the subdomain, not the base domain you've already set. (Eg: 'element' for element.example.org) Note that if you change this value you'll need to reconfigure your DNS.", - "required": false, - "min": 0, - "max": 2048, - "default": "{{ awx_element_subdomain }}", - "choices": "", - "new_question": true, - "variable": "awx_element_subdomain", - "type": "text" - } - ] -} diff --git a/roles/matrix-awx/surveys/configure_email_relay.json.j2 b/roles/matrix-awx/surveys/configure_email_relay.json.j2 deleted file mode 100644 index 65c21a94b..000000000 --- a/roles/matrix-awx/surveys/configure_email_relay.json.j2 +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "Configure Email Relay", - "description": "Enable MailGun relay to increase verification email reliability.", - "spec": [ - { - "question_name": "Enable Email Relay", - "question_description": "Enables the MailGun email relay server, enabling this will increase the reliability of your email verification.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_mailer_relay_use | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_mailer_relay_use", - "type": "multiplechoice" - } - ] -} - diff --git a/roles/matrix-awx/surveys/configure_jitsi.json.j2 b/roles/matrix-awx/surveys/configure_jitsi.json.j2 deleted file mode 100755 index 9cb3044d1..000000000 --- a/roles/matrix-awx/surveys/configure_jitsi.json.j2 +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "Configure Jitsi", - "description": "Configure Jitsi conferencing settings.", - "spec": [ - { - "question_name": "Enable Jitsi", - "question_description": "Set if Jitsi is enabled or not. If disabled your server will use the https://jitsi.riot.im server. If you're on a smaller server disabling this might increase the performance of your Matrix service.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_jitsi_enabled }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_jitsi_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Set Default Language", - "question_description": "2 digit 639-1 language code to adjust the language of the web client. For a list of possible codes see: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes", - "required": false, - "min": 0, - "max": 2, - "default": "{{ matrix_jitsi_web_config_defaultLanguage }}", - "choices": "", - "new_question": true, - "variable": "matrix_jitsi_web_config_defaultLanguage", - "type": "text" - } - ] -} - diff --git a/roles/matrix-awx/surveys/configure_ma1sd.json.j2 b/roles/matrix-awx/surveys/configure_ma1sd.json.j2 deleted file mode 100644 index 055e817c8..000000000 --- a/roles/matrix-awx/surveys/configure_ma1sd.json.j2 +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "Configure ma1sd", - "description": "Configure ma1sd settings, ma1sd is a self-hosted identity server for Matrix.", - "spec": [ - { - "question_name": "Enable ma1sd", - "question_description": "Set if ma1sd is enabled or not. If disabled your server will loose identity functionality (not recommended).", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_ma1sd_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_ma1sd_enabled", - "type": "multiplechoice" - }, - { - "question_name": "ma1sd Authentication Mode", - "question_description": "Set the source of user account authentication credentials with the ma1sd.", - "required": false, - "min": null, - "max": null, - "default": "{{ awx_matrix_ma1sd_auth_store }}", - "choices": "Synapse Internal\nLDAP/AD", - "new_question": true, - "variable": "awx_matrix_ma1sd_auth_store", - "type": "multiplechoice" - }, - { - "question_name": "LDAP/AD Configuration", - "question_description": "Settings for connecting LDAP/AD to the ma1sd service. (ignored if using Synapse Internal, see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/README.md )", - "required": false, - "min": 0, - "max": 65536, - "default": {{ awx_matrix_ma1sd_configuration_extension_yaml | to_json }}, - "new_question": true, - "variable": "awx_matrix_ma1sd_configuration_extension_yaml", - "type": "textarea" - } - ] -} diff --git a/roles/matrix-awx/surveys/configure_mjolnir.json.j2 b/roles/matrix-awx/surveys/configure_mjolnir.json.j2 deleted file mode 100644 index 5e1d78f4c..000000000 --- a/roles/matrix-awx/surveys/configure_mjolnir.json.j2 +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "Configure Mjolnir", - "description": "Configure Mjolnir settings, Mjolnir is a moderation bot for Matrix.", - "spec": [ - { - "question_name": "Enable Mjolnir", - "question_description": "Set if Mjolnir is enabled or not. Mjolnir is a moderation bot for Matrix.", - "required": true, - "min": null, - "max": null, - "default": "{{ matrix_bot_mjolnir_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_bot_mjolnir_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Mjolnir Management Room", - "question_description": "Sets the internal ID of the management room for Mjolnir. Example: '!wAeZaPCKvaCHcSqxAW:matrix.org'", - "required": true, - "min": null, - "max": null, - "default": "{{ matrix_bot_mjolnir_management_room }}", - "new_question": true, - "variable": "matrix_bot_mjolnir_management_room", - "type": "text" - } - ] -} \ No newline at end of file diff --git a/roles/matrix-awx/surveys/configure_synapse.json.j2 b/roles/matrix-awx/surveys/configure_synapse.json.j2 deleted file mode 100755 index 7a4e711d1..000000000 --- a/roles/matrix-awx/surveys/configure_synapse.json.j2 +++ /dev/null @@ -1,198 +0,0 @@ -{ - "name": "Configure Synapse", - "description": "Configure Synapse settings. Synapse is the homeserver software that powers your Matrix instance.", - "spec": [ - { - "question_name": "Enable Public Registration", - "question_description": "Controls whether people with access to the homeserver can register by themselves.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_synapse_enable_registration | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_synapse_enable_registration", - "type": "multiplechoice" - }, - { - "question_name": "Enable Federation", - "question_description": "Controls whether Synapse will federate at all. Disable this to completely isolate your server from the rest of the Matrix network.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_synapse_federation_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_synapse_federation_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Allow Public Rooms Over Federation", - "question_description": "Controls whether remote servers can fetch this server's public rooms directory via federation. For private servers, you'll most likely want to forbid this.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_synapse_allow_public_rooms_over_federation | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_synapse_allow_public_rooms_over_federation", - "type": "multiplechoice" - }, - { - "question_name": "Enable Community Creation", - "question_description": "Allows regular users (who aren't server admins) to create 'communities', which are basically groups of rooms.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_synapse_enable_group_creation | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_synapse_enable_group_creation", - "type": "multiplechoice" - }, - { - "question_name": "Enable Synapse Presence", - "question_description": "Controls whether presence is enabled. This shows who's online and reading your posts. Disabling it will increase both performance and user privacy.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_synapse_presence_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_synapse_presence_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Enable URL Previews", - "question_description": "Controls whether URL previews should be generated. This will cause a request from Synapse to URLs shared by users.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_synapse_url_preview_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_synapse_url_preview_enabled", - "type": "multiplechoice" - }, - { - "question_name": "Enable Guest Access", - "question_description": "Controls whether 'guest accounts' can access rooms without registering. Guest users do not count towards your servers user limit.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_synapse_allow_guest_access | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_synapse_allow_guest_access", - "type": "multiplechoice" - }, - { - "question_name": "Registration Requires Email", - "question_description": "Controls whether an email address is required to register on the server.", - "required": false, - "min": null, - "max": null, - "default": "{{ awx_registrations_require_3pid | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "awx_registrations_require_3pid", - "type": "multiplechoice" - }, - { - "question_name": "Registration Shared Secret", - "question_description": "A secret that allows registration of standard or admin accounts by anyone who has the shared secret, even if registration is otherwise disabled. WARNING: You must set a strong and unique password here.", - "required": false, - "min": 0, - "max": 256, - "default": "", - "choices": "", - "new_question": true, - "variable": "awx_matrix_synapse_registration_shared_secret", - "type": "password" - }, - { - "question_name": "Synapse Max Upload Size", - "question_description": "Sets the maximum size for uploaded files in MB.", - "required": false, - "min": 0, - "max": 3, - "default": "{{ matrix_synapse_max_upload_size_mb }}", - "choices": "", - "new_question": true, - "variable": "awx_synapse_max_upload_size_mb", - "type": "text" - }, - { - "question_name": "URL Preview Languages", - "question_description": "Sets the languages that URL previews will be generated in. Entries are a 2-3 letter IETF language tag, they must be seperated with newlines. For example: 'fr' https://en.wikipedia.org/wiki/IETF_language_tag", - "required": false, - "min": 0, - "max": 65536, - "default": {{ awx_url_preview_accept_language_default | to_json }}, - "choices": "", - "new_question": true, - "variable": "awx_url_preview_accept_language", - "type": "textarea" - }, - { - "question_name": "Federation Whitelist", - "question_description": "Here you can list the URLs of other Matrix homeservers and Synapse will only federate with those homeservers. Entries must be seperated with newlines and must not have a 'https://' prefix. For example: 'matrix.example.org'", - "required": false, - "min": 0, - "max": 65536, - "default": {{ awx_federation_whitelist | to_json }}, - "choices": "", - "new_question": true, - "variable": "awx_federation_whitelist", - "type": "textarea" - }, - { - "question_name": "Synapse Auto-Join Rooms", - "question_description": "Sets the 'auto-join' rooms, where new users will be automatically invited to, these rooms must already exist. Entries must be room addresses that are separated with newlines. For example: '#announcements:example.org'", - "required": false, - "min": 0, - "max": 65536, - "default": {{ awx_synapse_auto_join_rooms | to_json }}, - "choices": "", - "new_question": true, - "variable": "awx_synapse_auto_join_rooms", - "type": "textarea" - }, - { - "question_name": "Enable ReCaptcha on Registration", - "question_description": "Enables Googles ReCaptcha verification for registering an account, recommended for public servers.", - "required": false, - "min": null, - "max": null, - "default": "{{ awx_enable_registration_captcha | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "awx_enable_registration_captcha", - "type": "multiplechoice" - }, - { - "question_name": "Recaptcha Public Key", - "question_description": "Sets the Google ReCaptcha public key for this website.", - "required": false, - "min": 0, - "max": 40, - "default": "{{ awx_recaptcha_public_key }}", - "choices": "", - "new_question": true, - "variable": "awx_recaptcha_public_key", - "type": "text" - }, - { - "question_name": "Recaptcha Private Key", - "question_description": "Sets the Google ReCaptcha private key for this website.", - "required": false, - "min": 0, - "max": 40, - "default": "{{ awx_recaptcha_private_key }}", - "choices": "", - "new_question": true, - "variable": "awx_recaptcha_private_key", - "type": "text" - } - ] -} diff --git a/roles/matrix-awx/surveys/configure_synapse_admin.json.j2 b/roles/matrix-awx/surveys/configure_synapse_admin.json.j2 deleted file mode 100644 index 8845b83ac..000000000 --- a/roles/matrix-awx/surveys/configure_synapse_admin.json.j2 +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Configure Synapse Admin", - "description": "Configure 'Synapse Admin', a moderation tool to help you manage your server.", - "spec": [ - { - "question_name": "Enable Synapse Admin", - "question_description": "Set if Synapse Admin is enabled or not. If enabled you can access it at https://{{ matrix_server_fqn_matrix }}/synapse-admin.", - "required": false, - "min": null, - "max": null, - "default": "{{ matrix_synapse_admin_enabled | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "matrix_synapse_admin_enabled", - "type": "multiplechoice" - } - ] -} diff --git a/roles/matrix-awx/surveys/configure_website_access_export.json.j2 b/roles/matrix-awx/surveys/configure_website_access_export.json.j2 deleted file mode 100755 index d35fb8399..000000000 --- a/roles/matrix-awx/surveys/configure_website_access_export.json.j2 +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "Configure Website Access Backup", - "description": "Configure base domain website settings and access the services backup.", - "spec": [ - { - "question_name": "Customise Base Domain Website", - "question_description": "Set if you want to adjust the base domain website using SFTP.", - "required": true, - "min": null, - "max": null, - "default": "{{ awx_customise_base_domain_website | string | lower }}", - "choices": "true\nfalse", - "new_question": true, - "variable": "awx_customise_base_domain_website", - "type": "multiplechoice" - }, - { - "question_name": "SFTP Authorisation Method", - "question_description": "Set whether you want to disable SFTP, use a password to connect to SFTP or connect with a more secure SSH key.", - "required": true, - "min": null, - "max": null, - "default": "{{ awx_sftp_auth_method | string }}", - "choices": "Disabled\nPassword\nSSH Key", - "new_question": true, - "variable": "awx_sftp_auth_method", - "type": "multiplechoice" - }, - { - "question_name": "SFTP Password", - "question_description": "Sets the password of the 'sftp' account, which allows you to upload a multi-file static website by SFTP, as well as export the latest copy of your Matrix service. Must be defined if 'Password' method is selected. WARNING: You must set a strong and unique password here.", - "required": false, - "min": 0, - "max": 64, - "default": "{{ awx_sftp_password }}", - "choices": "", - "new_question": true, - "variable": "awx_sftp_password", - "type": "password" - }, - { - "question_name": "SFTP Public SSH Key (More Secure)", - "question_description": "Sets the public SSH key used to access the 'sftp' account, which allows you to upload a multi-file static website by SFTP, as well as export the latest copy of your Matrix service. Must be defined if 'SSH Key' method is selected.", - "required": false, - "min": 0, - "max": 16384, - "default": "{{ awx_sftp_public_key }}", - "choices": "", - "new_question": true, - "variable": "awx_sftp_public_key", - "type": "text" - } - ] -} diff --git a/roles/matrix-awx/tasks/backup_server.yml b/roles/matrix-awx/tasks/backup_server.yml deleted file mode 100644 index 553eb1b94..000000000 --- a/roles/matrix-awx/tasks/backup_server.yml +++ /dev/null @@ -1,101 +0,0 @@ ---- - -- name: Record Backup Server variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# AWX Settings Start' - with_dict: - 'awx_backup_enabled': '{{ awx_backup_enabled }}' - tags: use-survey - -- name: Save new 'Backup Server' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/backup_server.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/backup_server.json' - tags: use-survey - -- name: Copy new 'Backup Server' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/backup_server.json' - dest: '/matrix/awx/backup_server.json' - mode: '0660' - tags: use-survey - -- name: Recreate 'Backup Server' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 0 - Backup Server" - description: "Performs a backup of the entire service to a remote location." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "backup-server,use-survey" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/backup_server.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - tags: use-survey - -- name: Include vars in matrix_vars.yml - include_vars: - file: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - no_log: true - -- name: Copy new 'matrix_vars.yml' to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - dest: '/matrix/awx/matrix_vars.yml' - mode: '0660' - tags: use-survey - -- name: Run initial backup of /matrix/ and snapshot the database simultaneously - command: "{{ item }}" - with_items: - - borgmatic -c /root/.config/borgmatic/config_1.yaml - - /bin/sh /usr/local/bin/awx-export-service.sh 1 0 - register: _create_instances - async: 3600 # Maximum runtime in seconds. - poll: 0 # Fire and continue (never poll) - when: awx_backup_enabled|bool - -- name: Wait for both of these jobs to finish - async_status: - jid: "{{ item.ansible_job_id }}" - register: _jobs - until: _jobs.finished - delay: 5 # Check every 5 seconds. - retries: 720 # Retry for a full hour. - with_items: "{{ _create_instances.results }}" - when: awx_backup_enabled|bool - -- name: Perform borg backup of postgres dump - command: borgmatic -c /root/.config/borgmatic/config_2.yaml - when: awx_backup_enabled|bool - -- name: Delete the AWX session token for executing modules - awx.awx.tower_token: - description: 'AWX Session Token' - scope: "write" - state: absent - existing_token_id: "{{ awx_session_token.ansible_facts.tower_token.id }}" - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - -- name: Set boolean value to exit playbook - set_fact: - awx_end_playbook: true - -- name: End playbook if this task list is called. - meta: end_play - when: awx_end_playbook is defined and awx_end_playbook|bool diff --git a/roles/matrix-awx/tasks/bridge_discord_appservice.yml b/roles/matrix-awx/tasks/bridge_discord_appservice.yml deleted file mode 100644 index 3c124db3a..000000000 --- a/roles/matrix-awx/tasks/bridge_discord_appservice.yml +++ /dev/null @@ -1,58 +0,0 @@ ---- - -- name: Record Bridge Discord AppService variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Bridge Discord AppService Start' - with_dict: - 'matrix_appservice_discord_enabled': '{{ matrix_appservice_discord_enabled }}' - 'matrix_appservice_discord_client_id': '{{ matrix_appservice_discord_client_id }}' - 'matrix_appservice_discord_bot_token': '{{ matrix_appservice_discord_bot_token }}' - -- name: If the raw inputs is not empty start constructing parsed awx_appservice_discord_admin_rooms list - set_fact: - awx_appservice_discord_admin_rooms_array: |- - {{ awx_appservice_discord_admin_rooms.splitlines() | to_json }} - when: awx_appservice_discord_admin_rooms | trim | length > 0 - -- name: Promote user to administer (PL100) of each room - command: | - docker exec -i matrix-appservice-discord /bin/sh -c 'cp /cfg/registration.yaml /tmp/discord-registration.yaml && cd /tmp && node /build/tools/adminme.js -c /cfg/config.yaml -m "{{ item.1 }}" -u "@{{ awx_appservice_discord_admin_user }}:{{ matrix_domain }}" -p 100' - with_indexed_items: - - "{{ awx_appservice_discord_admin_rooms_array }}" - when: ( awx_appservice_discord_admin_rooms | trim | length > 0 ) and ( awx_appservice_discord_admin_user is defined ) - -- name: Save new 'Bridge Discord Appservice' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/bridge_discord_appservice.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}//bridge_discord_appservice.json' - -- name: Copy new 'Bridge Discord Appservice' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/bridge_discord_appservice.json' - dest: '/matrix/awx/bridge_discord_appservice.json' - mode: '0660' - -- name: Recreate 'Bridge Discord Appservice' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 3 - Bridge Discord AppService" - description: "Enables a private bridge you can use to connect Matrix rooms to Discord." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-all,bridge-discord-appservice" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/bridge_discord_appservice.json') }}" - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/cache_matrix_variables.yml b/roles/matrix-awx/tasks/cache_matrix_variables.yml deleted file mode 100644 index ca41880a7..000000000 --- a/roles/matrix-awx/tasks/cache_matrix_variables.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- - -- name: Collect current datetime - set_fact: - awx_datetime: "{{ lookup('pipe', 'date +%Y-%m-%d_%H:%M') }}" - -- name: Create cached matrix_vars.yml file location - set_fact: - awx_cached_matrix_vars: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars_{{ awx_datetime }}.yml' - -- name: Create cached matrix_vars.yml - delegate_to: 127.0.0.1 - shell: "cp /var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml {{ awx_cached_matrix_vars }}" diff --git a/roles/matrix-awx/tasks/create_session_token.yml b/roles/matrix-awx/tasks/create_session_token.yml deleted file mode 100644 index 7d984b3d8..000000000 --- a/roles/matrix-awx/tasks/create_session_token.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- - -- name: Create a AWX session token for executing modules - awx.awx.tower_token: - description: 'AWX Session Token' - scope: "write" - state: present - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_master_token }}" - register: awx_session_token - no_log: true diff --git a/roles/matrix-awx/tasks/create_user.yml b/roles/matrix-awx/tasks/create_user.yml deleted file mode 100755 index 7d203ed0a..000000000 --- a/roles/matrix-awx/tasks/create_user.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -# -# Create user and define if they are admin -# -# /usr/local/bin/matrix-synapse-register-user -# - -- name: Set admin bool to zero - set_fact: - awx_admin_bool: 0 - when: awx_admin_access == 'false' - -- name: Examine if server admin set - set_fact: - awx_admin_bool: 1 - when: awx_admin_access == 'true' - -- name: Create user account - command: | - /usr/local/bin/matrix-synapse-register-user {{ awx_new_username | quote }} {{ awx_new_password | quote }} {{ awx_admin_bool }} - register: awx_cmd_output - -- name: Delete the AWX session token for executing modules - awx.awx.tower_token: - description: 'AWX Session Token' - scope: "write" - state: absent - existing_token_id: "{{ awx_session_token.ansible_facts.tower_token.id }}" - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - -- name: Set boolean value to exit playbook - set_fact: - awx_end_playbook: true - -- name: Result - debug: msg="{{ awx_cmd_output.stdout }}" - -- name: End playbook if this task list is called. - meta: end_play - when: awx_end_playbook is defined and awx_end_playbook|bool diff --git a/roles/matrix-awx/tasks/customise_website_access_export.yml b/roles/matrix-awx/tasks/customise_website_access_export.yml deleted file mode 100755 index 80d6d7956..000000000 --- a/roles/matrix-awx/tasks/customise_website_access_export.yml +++ /dev/null @@ -1,267 +0,0 @@ ---- - -- name: Enable index.html creation if user doesn't wish to customise base domain - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Base Domain Settings Start' - with_dict: - 'matrix_nginx_proxy_base_domain_homepage_enabled': 'true' - when: (awx_customise_base_domain_website is defined) and not awx_customise_base_domain_website|bool - -- name: Disable index.html creation to allow multi-file site if user does wish to customise base domain - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Base Domain Settings Start' - with_dict: - 'matrix_nginx_proxy_base_domain_homepage_enabled': 'false' - when: (awx_customise_base_domain_website is defined) and awx_customise_base_domain_website|bool - -- name: Record custom 'Customise Website + Access Export' variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Custom Settings Start' - with_dict: - 'awx_sftp_auth_method': '"{{ awx_sftp_auth_method }}"' - 'awx_sftp_password': '"{{ awx_sftp_password }}"' - 'awx_sftp_public_key': '"{{ awx_sftp_public_key }}"' - -- name: Record custom 'Customise Website + Access Export' variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Custom Settings Start' - with_dict: - 'awx_customise_base_domain_website': '{{ awx_customise_base_domain_website }}' - when: awx_customise_base_domain_website is defined - -- name: Reload vars in matrix_vars.yml - include_vars: - file: '{{ awx_cached_matrix_vars }}' - no_log: true - -- name: Save new 'Customise Website + Access Export' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: './roles/matrix-awx/surveys/configure_website_access_export.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_website_access_export.json' - when: awx_customise_base_domain_website is defined - -- name: Copy new 'Customise Website + Access Export' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_website_access_export.json' - dest: '/matrix/awx/configure_website_access_export.json' - mode: '0660' - when: awx_customise_base_domain_website is defined - -- name: Save new 'Customise Website + Access Export' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: './roles/matrix-awx/surveys/access_export.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/access_export.json' - when: awx_customise_base_domain_website is undefined - -- name: Copy new 'Customise Website + Access Export' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/access_export.json' - dest: '/matrix/awx/access_export.json' - mode: '0660' - when: awx_customise_base_domain_website is undefined - -- name: Recreate 'Configure Website + Access Export' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Website + Access Export" - description: "Configure base domain website settings and access the servers export." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-nginx-proxy" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_website_access_export.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - when: awx_customise_base_domain_website is defined - -- name: Recreate 'Access Export' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Access Export" - description: "Access the services export." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-nginx-proxy" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/access_export.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - when: awx_customise_base_domain_website is undefined - -- name: If user doesn't define a awx_sftp_password, create a disabled 'sftp' account - user: - name: sftp - comment: SFTP user to set custom web files and access servers export - shell: /bin/false - home: /home/sftp - group: matrix - password: '*' - update_password: always - when: awx_sftp_password|length == 0 - -- name: If user defines awx_sftp_password, enable account and set password on 'stfp' account - user: - name: sftp - comment: SFTP user to set custom web files and access servers export - shell: /bin/false - home: /home/sftp - group: matrix - password: "{{ awx_sftp_password | password_hash('sha512') }}" - update_password: always - when: awx_sftp_password|length > 0 - -- name: Ensure group "sftp" exists - group: - name: sftp - state: present - -- name: adding existing user 'sftp' to group matrix - user: - name: sftp - groups: sftp - append: true - when: awx_customise_base_domain_website is defined - -- name: Create the ro /chroot directory with sticky bit if it doesn't exist. (/chroot/website has matrix:matrix permissions and is mounted to nginx container) - file: - path: /chroot - state: directory - owner: root - group: root - mode: '1755' - -- name: Ensure /chroot/website location exists. - file: - path: /chroot/website - state: directory - owner: matrix - group: matrix - mode: '0770' - when: awx_customise_base_domain_website is defined - -- name: Ensure /chroot/export location exists - file: - path: /chroot/export - state: directory - owner: sftp - group: sftp - mode: '0700' - -- name: Ensure /home/sftp/.ssh location exists - file: - path: /home/sftp/.ssh - state: directory - owner: sftp - group: sftp - mode: '0700' - -- name: Ensure /home/sftp/authorized_keys exists - file: - path: /home/sftp/.ssh/authorized_keys - state: touch - owner: sftp - group: sftp - mode: '0644' - -- name: Clear authorized_keys file - shell: echo "" > /home/sftp/.ssh/authorized_keys - -- name: Insert public SSH key into authorized_keys file - lineinfile: - path: /home/sftp/.ssh/authorized_keys - line: "{{ awx_sftp_public_key }}" - owner: sftp - group: sftp - mode: '0644' - when: (awx_sftp_public_key | length > 0) and (awx_sftp_auth_method == "SSH Key") - -- name: Remove any existing Subsystem lines - lineinfile: - path: /etc/ssh/sshd_config - state: absent - regexp: '^Subsystem' - -- name: Set SSH Subsystem State - lineinfile: - path: /etc/ssh/sshd_config - insertafter: "^# override default of no subsystems" - line: "Subsystem sftp internal-sftp" - -- name: Add SSH Match User section for disabled auth - blockinfile: - path: /etc/ssh/sshd_config - state: absent - block: | - Match User sftp - ChrootDirectory /chroot - PermitTunnel no - X11Forwarding no - AllowTcpForwarding no - PasswordAuthentication yes - AuthorizedKeysFile /home/sftp/.ssh/authorized_keys - when: awx_sftp_auth_method == "Disabled" - -- name: Add SSH Match User section for password auth - blockinfile: - path: /etc/ssh/sshd_config - state: present - block: | - Match User sftp - ChrootDirectory /chroot - PermitTunnel no - X11Forwarding no - AllowTcpForwarding no - PasswordAuthentication yes - when: awx_sftp_auth_method == "Password" - -- name: Add SSH Match User section for publickey auth - blockinfile: - path: /etc/ssh/sshd_config - state: present - block: | - Match User sftp - ChrootDirectory /chroot - PermitTunnel no - X11Forwarding no - AllowTcpForwarding no - AuthorizedKeysFile /home/sftp/.ssh/authorized_keys - when: awx_sftp_auth_method == "SSH Key" - -- name: Restart service ssh.service - service: - name: ssh.service - state: restarted diff --git a/roles/matrix-awx/tasks/delete_session_token.yml b/roles/matrix-awx/tasks/delete_session_token.yml deleted file mode 100644 index a6a52e487..000000000 --- a/roles/matrix-awx/tasks/delete_session_token.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- - -- name: Delete the AWX session token for executing modules - awx.awx.tower_token: - description: 'AWX Session Token' - scope: "write" - state: absent - existing_token_id: "{{ awx_session_token.ansible_facts.tower_token.id }}" - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" diff --git a/roles/matrix-awx/tasks/export_server.yml b/roles/matrix-awx/tasks/export_server.yml deleted file mode 100644 index a2b97e79e..000000000 --- a/roles/matrix-awx/tasks/export_server.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- - -- name: Run export of /matrix/ and snapshot the database simultaneously - command: "{{ item }}" - with_items: - - /bin/sh /usr/local/bin/awx-export-service.sh 1 0 - - /bin/sh /usr/local/bin/awx-export-service.sh 0 1 - register: awx_create_instances - async: 3600 # Maximum runtime in seconds. - poll: 0 # Fire and continue (never poll) - -- name: Wait for both of these jobs to finish - async_status: - jid: "{{ item.ansible_job_id }}" - register: awx_jobs - until: awx_jobs.finished - delay: 5 # Check every 5 seconds. - retries: 720 # Retry for a full hour. - with_items: "{{ awx_create_instances.results }}" - -- name: Schedule deletion of the export in 24 hours - at: - command: rm /chroot/export/matrix* - count: 1 - units: days - unique: true - -- name: Delete the AWX session token for executing modules - awx.awx.tower_token: - description: 'AWX Session Token' - scope: "write" - state: absent - existing_token_id: "{{ awx_session_token.ansible_facts.tower_token.id }}" - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - -- name: Set boolean value to exit playbook - set_fact: - awx_end_playbook: true - -- name: End playbook if this task list is called. - meta: end_play - when: awx_end_playbook is defined and awx_end_playbook|bool diff --git a/roles/matrix-awx/tasks/import_awx.yml b/roles/matrix-awx/tasks/import_awx.yml deleted file mode 100644 index b2154c7a5..000000000 --- a/roles/matrix-awx/tasks/import_awx.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- - -- name: Ensure correct ownership of /matrix/awx - shell: chown -R matrix:matrix /matrix/awx - -- name: Ensure correct ownership of /matrix/synapse - shell: chown -R matrix:matrix /matrix/synapse diff --git a/roles/matrix-awx/tasks/load_hosting_and_org_variables.yml b/roles/matrix-awx/tasks/load_hosting_and_org_variables.yml deleted file mode 100644 index 6e8bb8995..000000000 --- a/roles/matrix-awx/tasks/load_hosting_and_org_variables.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- - -- name: Include vars in organisation.yml - include_vars: - file: '/var/lib/awx/projects/clients/{{ member_id }}/organisation.yml' - no_log: true - -- name: Include vars in hosting_vars.yml - include_vars: - file: '/var/lib/awx/projects/hosting/hosting_vars.yml' - no_log: true - -- name: Include AWX master token from awx_tokens.yml - include_vars: - file: /var/lib/awx/projects/hosting/awx_tokens.yml - no_log: true diff --git a/roles/matrix-awx/tasks/load_matrix_variables.yml b/roles/matrix-awx/tasks/load_matrix_variables.yml deleted file mode 100755 index 7a76f34b8..000000000 --- a/roles/matrix-awx/tasks/load_matrix_variables.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- - -- name: Include new vars in matrix_vars.yml - include_vars: - file: '{{ awx_cached_matrix_vars }}' - no_log: true - -- name: If include_vars succeeds overwrite the old matrix_vars.yml - delegate_to: 127.0.0.1 - shell: "cp {{ awx_cached_matrix_vars }} /var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml && rm {{ awx_cached_matrix_vars }}" - -- name: Copy new 'matrix_vars.yml' to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - dest: '/matrix/awx/matrix_vars.yml' - mode: '0660' diff --git a/roles/matrix-awx/tasks/main.yml b/roles/matrix-awx/tasks/main.yml deleted file mode 100755 index 93128713e..000000000 --- a/roles/matrix-awx/tasks/main.yml +++ /dev/null @@ -1,234 +0,0 @@ ---- -# Load initial hosting and organisation variables from AWX volume -- include_tasks: - file: "load_hosting_and_org_variables.yml" - apply: - tags: always - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - always - -# Renames or updates the vars.yml if needed -- include_tasks: - file: "update_variables.yml" - apply: - tags: always - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - always - -# Create AWX session token -- include_tasks: - file: "create_session_token.yml" - apply: - tags: always - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - always - -# Perform a backup of the server -- include_tasks: - file: "backup_server.yml" - apply: - tags: backup-server - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - backup-server - -# Perform a export of the server -- include_tasks: - file: "export_server.yml" - apply: - tags: export-server - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - export-server - -# Create a user account if called -- include_tasks: - file: "create_user.yml" - apply: - tags: create-user - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - create-user - -# Purge local/remote media if called -- include_tasks: - file: "purge_media_main.yml" - apply: - tags: purge-media - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - purge-media - -# Purge Synapse database if called -- include_tasks: - file: "purge_database_main.yml" - apply: - tags: purge-database - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - purge-database - -# Rotate SSH key if called -- include_tasks: - file: "rotate_ssh.yml" - apply: - tags: rotate-ssh - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - rotate-ssh - -# Import configs, media repo from /chroot/backup import -- include_tasks: - file: "import_awx.yml" - apply: - tags: import-awx - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - import-awx - -# Perform extra self-check functions -- include_tasks: - file: "self_check.yml" - apply: - tags: self-check - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - self-check - -# Create cached matrix_vars.yml file -- include_tasks: - file: "cache_matrix_variables.yml" - apply: - tags: always - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - always - -# Configure SFTP so user can upload a static website or access the servers export -- include_tasks: - file: "customise_website_access_export.yml" - apply: - tags: setup-nginx-proxy - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-nginx-proxy - -# Additional playbook to set the variable file during Element configuration -- include_tasks: - file: "set_variables_element.yml" - apply: - tags: setup-client-element - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-client-element - -# Additional playbook to set the variable file during Mailer configuration -- include_tasks: - file: "set_variables_mailer.yml" - apply: - tags: setup-mailer - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-mailer - -# Additional playbook to set the variable file during Element configuration -- include_tasks: - file: "set_variables_element_subdomain.yml" - apply: - tags: setup-client-element-subdomain - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-client-element-subdomain - -# Additional playbook to set the variable file during Synapse configuration -- include_tasks: - file: "set_variables_synapse.yml" - apply: - tags: setup-synapse - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-synapse - -# Additional playbook to set the variable file during Jitsi configuration -- include_tasks: - file: "set_variables_jitsi.yml" - apply: - tags: setup-jitsi - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-jitsi - -# Additional playbook to set the variable file during Ma1sd configuration -- include_tasks: - file: "set_variables_ma1sd.yml" - apply: - tags: setup-ma1sd - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-ma1sd - -# Additional playbook to set the variable file during Mjolnir Bot configuration -- include_tasks: - file: "set_variables_mjolnir.yml" - apply: - tags: setup-bot-mjolnir - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-bot-mjolnir - -# Additional playbook to set the variable file during Corporal configuration -- include_tasks: - file: "set_variables_corporal.yml" - apply: - tags: setup-corporal - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-corporal - -# Additional playbook to set the variable file during Dimension configuration -- include_tasks: - file: "set_variables_dimension.yml" - apply: - tags: setup-dimension - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-dimension - -# Additional playbook to set the variable file during Synapse Admin configuration -- include_tasks: - file: "set_variables_synapse_admin.yml" - apply: - tags: setup-synapse-admin - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - setup-synapse-admin - -# Additional playbook to set the variable file during Discord Appservice Bridge configuration -- include_tasks: - file: "bridge_discord_appservice.yml" - apply: - tags: bridge-discord-appservice - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - bridge-discord-appservice - -# Delete AWX session token -- include_tasks: - file: "delete_session_token.yml" - apply: - tags: always - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - always - -# Load newly formed matrix variables from AWX volume -- include_tasks: - file: "load_matrix_variables.yml" - apply: - tags: always - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - always diff --git a/roles/matrix-awx/tasks/purge_database_events.yml b/roles/matrix-awx/tasks/purge_database_events.yml deleted file mode 100644 index 586bc17c9..000000000 --- a/roles/matrix-awx/tasks/purge_database_events.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- - -- name: Purge all rooms with more then N events - shell: | - curl --header "Authorization: Bearer {{ awx_janitors_token.stdout[1:-1] }}" -X POST -H "Content-Type: application/json" -d '{ "delete_local_events": false, "purge_up_to_ts": {{ awx_purge_epoche_time.stdout }}000 }' "{{ awx_synapse_container_ip.stdout }}:{{ matrix_synapse_container_client_api_port }}/_synapse/admin/v1/purge_history/{{ item[1:-1] }}" - register: awx_purge_command - -- name: Print output of purge command - debug: - msg: "{{ awx_purge_command.stdout }}" - -- name: Pause for 5 seconds to let Synapse breathe - pause: - seconds: 5 diff --git a/roles/matrix-awx/tasks/purge_database_main.yml b/roles/matrix-awx/tasks/purge_database_main.yml deleted file mode 100644 index 9882f1951..000000000 --- a/roles/matrix-awx/tasks/purge_database_main.yml +++ /dev/null @@ -1,320 +0,0 @@ ---- - -- name: Ensure dateutils and curl is installed in AWX - delegate_to: 127.0.0.1 - yum: - name: dateutils - state: latest - -- name: Include vars in matrix_vars.yml - include_vars: - file: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - no_log: true - -- name: Ensure curl and jq intalled on target machine - apt: - pkg: - - curl - - jq - state: present - -- name: Collect before shrink size of Synapse database - shell: du -sh /matrix/postgres/data - register: awx_db_size_before_stat - when: (awx_purge_mode.find("Perform final shrink") != -1) - no_log: true - -- name: Collect the internal IP of the matrix-synapse container - shell: "/usr/bin/docker inspect --format '{''{range.NetworkSettings.Networks}''}{''{.IPAddress}''}{''{end}''}' matrix-synapse" - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - register: awx_synapse_container_ip - -- name: Collect access token for @admin-janitor user - shell: | - curl -X POST -d '{"type":"m.login.password", "user":"admin-janitor", "password":"{{ awx_janitor_user_password }}"}' "{{ awx_synapse_container_ip.stdout }}:{{ matrix_synapse_container_client_api_port }}/_matrix/client/r0/login" | jq '.access_token' - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - register: awx_janitors_token - no_log: true - -- name: Copy build_room_list.py script to target machine - copy: - src: ./roles/matrix-awx/scripts/matrix_build_room_list.py - dest: /usr/local/bin/matrix_build_room_list.py - owner: matrix - group: matrix - mode: '0755' - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Run build_room_list.py script - shell: | - runuser -u matrix -- python3 /usr/local/bin/matrix_build_room_list.py {{ awx_janitors_token.stdout[1:-1] }} {{ awx_synapse_container_ip.stdout }} {{ matrix_synapse_container_client_api_port.stdout }} - register: awx_rooms_total - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Fetch complete room list from target machine - fetch: - src: /tmp/room_list_complete.json - dest: "/tmp/{{ subscription_id }}_room_list_complete.json" - flat: true - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Remove complete room list from target machine - file: - path: /tmp/room_list_complete.json - state: absent - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Generate list of rooms with no local users - delegate_to: 127.0.0.1 - shell: | - jq 'try .rooms[] | select(.joined_local_members == 0) | .room_id' < /tmp/{{ subscription_id }}_room_list_complete.json > /tmp/{{ subscription_id }}_room_list_no_local_users.txt - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Count number of rooms with no local users - delegate_to: 127.0.0.1 - shell: | - wc -l /tmp/{{ subscription_id }}_room_list_no_local_users.txt | awk '{ print $1 }' - register: awx_rooms_no_local_total - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Setting host fact awx_room_list_no_local_users - set_fact: - awx_room_list_no_local_users: "{{ lookup('file', '/tmp/{{ subscription_id }}_room_list_no_local_users.txt') }}" - no_log: true - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Purge all rooms with no local users - include_tasks: purge_database_no_local.yml - loop: "{{ awx_room_list_no_local_users.splitlines() | flatten(levels=1) }}" - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Collect epoche time from date - delegate_to: 127.0.0.1 - shell: | - date -d '{{ awx_purge_date }}' +"%s" - when: (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - register: awx_purge_epoche_time - -- name: Generate list of rooms with more then N users - delegate_to: 127.0.0.1 - shell: | - jq 'try .rooms[] | select(.joined_members > {{ awx_purge_metric_value }}) | .room_id' < /tmp/{{ subscription_id }}_room_list_complete.json > /tmp/{{ subscription_id }}_room_list_joined_members.txt - when: awx_purge_mode.find("Number of users [slower]") != -1 - -- name: Count number of rooms with more then N users - delegate_to: 127.0.0.1 - shell: | - wc -l /tmp/{{ subscription_id }}_room_list_joined_members.txt | awk '{ print $1 }' - register: awx_rooms_join_members_total - when: awx_purge_mode.find("Number of users [slower]") != -1 - -- name: Setting host fact awx_room_list_joined_members - delegate_to: 127.0.0.1 - set_fact: - awx_room_list_joined_members: "{{ lookup('file', '/tmp/{{ subscription_id }}_room_list_joined_members.txt') }}" - when: awx_purge_mode.find("Number of users [slower]") != -1 - no_log: true - -- name: Purge all rooms with more then N users - include_tasks: purge_database_users.yml - loop: "{{ awx_room_list_joined_members.splitlines() | flatten(levels=1) }}" - when: awx_purge_mode.find("Number of users [slower]") != -1 - -- name: Generate list of rooms with more then N events - delegate_to: 127.0.0.1 - shell: | - jq 'try .rooms[] | select(.state_events > {{ awx_purge_metric_value }}) | .room_id' < /tmp/{{ subscription_id }}_room_list_complete.json > /tmp/{{ subscription_id }}_room_list_state_events.txt - when: awx_purge_mode.find("Number of events [slower]") != -1 - -- name: Count number of rooms with more then N events - delegate_to: 127.0.0.1 - shell: | - wc -l /tmp/{{ subscription_id }}_room_list_state_events.txt | awk '{ print $1 }' - register: awx_rooms_state_events_total - when: awx_purge_mode.find("Number of events [slower]") != -1 - -- name: Setting host fact awx_room_list_state_events - delegate_to: 127.0.0.1 - set_fact: - awx_room_list_state_events: "{{ lookup('file', '/tmp/{{ subscription_id }}_room_list_state_events.txt') }}" - when: awx_purge_mode.find("Number of events [slower]") != -1 - no_log: true - -- name: Purge all rooms with more then N events - include_tasks: purge_database_events.yml - loop: "{{ awx_room_list_state_events.splitlines() | flatten(levels=1) }}" - when: awx_purge_mode.find("Number of events [slower]") != -1 - -- name: Adjust 'Deploy/Update a Server' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 0 - Deploy/Update a Server" - description: "Creates a new matrix service with Spantaleev's playbooks" - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "rust-synapse-compress-state" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) or (awx_purge_mode.find("Skip purging rooms [faster]") != -1) - -- name: Execute rust-synapse-compress-state job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_launch: - job_template: "{{ matrix_domain }} - 0 - Deploy/Update a Server" - wait: true - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) or (awx_purge_mode.find("Skip purging rooms [faster]") != -1) - -- name: Revert 'Deploy/Update a Server' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 0 - Deploy/Update a Server" - description: "Creates a new matrix service with Spantaleev's playbooks" - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "setup-all,start" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) or (awx_purge_mode.find("Skip purging rooms [faster]") != -1) - -- name: Ensure matrix-synapse is stopped - service: - name: matrix-synapse - state: stopped - daemon_reload: true - when: (awx_purge_mode.find("Perform final shrink") != -1) - -- name: Re-index Synapse database - shell: docker exec -i matrix-postgres psql "host=127.0.0.1 port=5432 dbname=synapse user=synapse password={{ matrix_synapse_connection_password }}" -c 'REINDEX (VERBOSE) DATABASE synapse' - when: (awx_purge_mode.find("Perform final shrink") != -1) - -- name: Ensure matrix-synapse is started - service: - name: matrix-synapse - state: started - daemon_reload: true - when: (awx_purge_mode.find("Perform final shrink") != -1) - -- name: Adjust 'Deploy/Update a Server' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 0 - Deploy/Update a Server" - description: "Creates a new matrix service with Spantaleev's playbooks" - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "run-postgres-vacuum,start" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - when: (awx_purge_mode.find("Perform final shrink") != -1) - -- name: Execute run-postgres-vacuum job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_launch: - job_template: "{{ matrix_domain }} - 0 - Deploy/Update a Server" - wait: true - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - when: (awx_purge_mode.find("Perform final shrink") != -1) - -- name: Revert 'Deploy/Update a Server' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 0 - Deploy/Update a Server" - description: "Creates a new matrix service with Spantaleev's playbooks" - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "setup-all,start" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true - when: (awx_purge_mode.find("Perform final shrink") != -1) - -- name: Cleanup room_list files - delegate_to: 127.0.0.1 - shell: | - rm /tmp/{{ subscription_id }}_room_list* - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - ignore_errors: true - -- name: Collect after shrink size of Synapse database - shell: du -sh /matrix/postgres/data - register: awx_db_size_after_stat - when: (awx_purge_mode.find("Perform final shrink") != -1) - no_log: true - -- name: Print total number of rooms processed - debug: - msg: '{{ awx_rooms_total.stdout }}' - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Print the number of rooms purged with no local users - debug: - msg: '{{ awx_rooms_no_local_total.stdout }}' - when: (awx_purge_mode.find("No local users [recommended]") != -1) or (awx_purge_mode.find("Number of users [slower]") != -1) or (awx_purge_mode.find("Number of events [slower]") != -1) - -- name: Print the number of rooms purged with more then N users - debug: - msg: '{{ awx_rooms_join_members_total.stdout }}' - when: awx_purge_mode.find("Number of users") != -1 - -- name: Print the number of rooms purged with more then N events - debug: - msg: '{{ awx_rooms_state_events_total.stdout }}' - when: awx_purge_mode.find("Number of events") != -1 - -- name: Print before purge size of Synapse database - debug: - msg: "{{ awx_db_size_before_stat.stdout.split('\n') }}" - when: ( awx_db_size_before_stat is defined ) and ( awx_purge_mode.find("Perform final shrink" ) != -1 ) - -- name: Print after purge size of Synapse database - debug: - msg: "{{ awx_db_size_after_stat.stdout.split('\n') }}" - when: (awx_db_size_after_stat is defined) and (awx_purge_mode.find("Perform final shrink") != -1) - -- name: Delete the AWX session token for executing modules - awx.awx.tower_token: - description: 'AWX Session Token' - scope: "write" - state: absent - existing_token_id: "{{ awx_session_token.ansible_facts.tower_token.id }}" - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - -- name: Set boolean value to exit playbook - set_fact: - awx_end_playbook: true - -- name: End playbook early if this task is called. - meta: end_play - when: awx_end_playbook is defined and awx_end_playbook|bool diff --git a/roles/matrix-awx/tasks/purge_database_no_local.yml b/roles/matrix-awx/tasks/purge_database_no_local.yml deleted file mode 100644 index e464f56d1..000000000 --- a/roles/matrix-awx/tasks/purge_database_no_local.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- - -- name: Purge all rooms with no local users - shell: | - curl --header "Authorization: Bearer {{ awx_janitors_token.stdout[1:-1] }}" -X POST -H "Content-Type: application/json" -d '{ "room_id": {{ item }} }' '{{ awx_synapse_container_ip.stdout }}:{{ matrix_synapse_container_client_api_port }}/_synapse/admin/v1/purge_room' - register: awx_purge_command - -- name: Print output of purge command - debug: - msg: "{{ awx_purge_command.stdout }}" - -- name: Pause for 5 seconds to let Synapse breathe - pause: - seconds: 5 diff --git a/roles/matrix-awx/tasks/purge_database_users.yml b/roles/matrix-awx/tasks/purge_database_users.yml deleted file mode 100644 index d315a9ef1..000000000 --- a/roles/matrix-awx/tasks/purge_database_users.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- - -- name: Purge all rooms with more then N users - shell: | - curl --header "Authorization: Bearer {{ awx_janitors_token.stdout[1:-1] }}" -X POST -H "Content-Type: application/json" -d '{ "delete_local_events": false, "purge_up_to_ts": {{ awx_purge_epoche_time.stdout }}000 }' "{{ awx_synapse_container_ip.stdout }}:{{ matrix_synapse_container_client_api_port }}/_synapse/admin/v1/purge_history/{{ item[1:-1] }}" - register: awx_purge_command - -- name: Print output of purge command - debug: - msg: "{{ awx_purge_command.stdout }}" - -- name: Pause for 5 seconds to let Synapse breathe - pause: - seconds: 5 diff --git a/roles/matrix-awx/tasks/purge_media_local.yml b/roles/matrix-awx/tasks/purge_media_local.yml deleted file mode 100644 index 7ef79eca3..000000000 --- a/roles/matrix-awx/tasks/purge_media_local.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- - -- name: Collect epoche time from date - shell: | - date -d '{{ item }}' +"%s" - register: awx_epoche_time - -- name: Purge local media to specific date - shell: | - curl -X POST --header "Authorization: Bearer {{ awx_janitors_token.stdout[1:-1] }}" '{{ awx_synapse_container_ip.stdout }}:{{ matrix_synapse_container_client_api_port }}/_synapse/admin/v1/media/matrix.{{ matrix_domain }}/delete?before_ts={{ awx_epoche_time.stdout }}000' - register: awx_purge_command - -- name: Print output of purge command - debug: - msg: "{{ awx_purge_command.stdout }}" - -- name: Pause for 5 seconds to let Synapse breathe - pause: - seconds: 5 diff --git a/roles/matrix-awx/tasks/purge_media_main.yml b/roles/matrix-awx/tasks/purge_media_main.yml deleted file mode 100644 index bd7e7d1c4..000000000 --- a/roles/matrix-awx/tasks/purge_media_main.yml +++ /dev/null @@ -1,111 +0,0 @@ ---- - -- name: Ensure dateutils is installed in AWX - delegate_to: 127.0.0.1 - yum: - name: dateutils - state: latest - -- name: Include vars in matrix_vars.yml - include_vars: - file: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - no_log: true - -- name: Ensure curl and jq intalled on target machine - apt: - pkg: - - curl - - jq - state: present - -- name: Collect the internal IP of the matrix-synapse container - shell: "/usr/bin/docker inspect --format '{''{range.NetworkSettings.Networks}''}{''{.IPAddress}''}{''{end}''}' matrix-synapse" - register: awx_synapse_container_ip - -- name: Collect access token for @admin-janitor user - shell: | - curl -XPOST -d '{"type":"m.login.password", "user":"admin-janitor", "password":"{{ awx_janitor_user_password }}"}' "{{ awx_synapse_container_ip.stdout }}:{{ matrix_synapse_container_client_api_port }}/_matrix/client/r0/login" | jq '.access_token' - register: awx_janitors_token - no_log: true - -- name: Generate list of dates to purge to - delegate_to: 127.0.0.1 - shell: "dateseq {{ awx_purge_from_date }} {{ awx_purge_to_date }}" - register: awx_purge_dates - -- name: Calculate initial size of local media repository - shell: du -sh /matrix/synapse/storage/media-store/local* - register: awx_local_media_size_before - when: awx_purge_media_type == "Local Media" - async: 600 - ignore_errors: true - no_log: true - -- name: Calculate initial size of remote media repository - shell: du -sh /matrix/synapse/storage/media-store/remote* - register: awx_remote_media_size_before - when: awx_purge_media_type == "Remote Media" - async: 600 - ignore_errors: true - no_log: true - -- name: Purge local media with loop - include_tasks: purge_media_local.yml - loop: "{{ awx_purge_dates.stdout_lines | flatten(levels=1) }}" - when: awx_purge_media_type == "Local Media" - -- name: Purge remote media with loop - include_tasks: purge_media_remote.yml - loop: "{{ awx_purge_dates.stdout_lines | flatten(levels=1) }}" - when: awx_purge_media_type == "Remote Media" - -- name: Calculate final size of local media repository - shell: du -sh /matrix/synapse/storage/media-store/local* - register: awx_local_media_size_after - when: awx_purge_media_type == "Local Media" - ignore_errors: true - no_log: true - -- name: Calculate final size of remote media repository - shell: du -sh /matrix/synapse/storage/media-store/remote* - register: awx_remote_media_size_after - when: awx_purge_media_type == "Remote Media" - ignore_errors: true - no_log: true - -- name: Print size of local media repository before purge - debug: - msg: "{{ awx_local_media_size_before.stdout.split('\n') }}" - when: awx_purge_media_type == "Local Media" - -- name: Print size of local media repository after purge - debug: - msg: "{{ awx_local_media_size_after.stdout.split('\n') }}" - when: awx_purge_media_type == "Local Media" - -- name: Print size of remote media repository before purge - debug: - msg: "{{ awx_remote_media_size_before.stdout.split('\n') }}" - when: awx_purge_media_type == "Remote Media" - -- name: Print size of remote media repository after purge - debug: - msg: "{{ awx_remote_media_size_after.stdout.split('\n') }}" - when: awx_purge_media_type == "Remote Media" - -- name: Delete the AWX session token for executing modules - awx.awx.tower_token: - description: 'AWX Session Token' - scope: "write" - state: absent - existing_token_id: "{{ awx_session_token.ansible_facts.tower_token.id }}" - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - -- name: Set boolean value to exit playbook - set_fact: - awx_end_playbook: true - -- name: End playbook early if this task is called. - meta: end_play - when: awx_end_playbook is defined and awx_end_playbook|bool diff --git a/roles/matrix-awx/tasks/purge_media_remote.yml b/roles/matrix-awx/tasks/purge_media_remote.yml deleted file mode 100644 index 5bb71918f..000000000 --- a/roles/matrix-awx/tasks/purge_media_remote.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- - -- name: Collect epoche time from date - shell: | - date -d '{{ item }}' +"%s" - register: awx_epoche_time - -- name: Purge remote media to specific date - shell: | - curl -X POST --header "Authorization: Bearer {{ awx_janitors_token.stdout[1:-1] }}" '{{ awx_synapse_container_ip.stdout }}:{{ matrix_synapse_container_client_api_port }}/_synapse/admin/v1/purge_media_cache?before_ts={{ awx_epoche_time.stdout }}000' - register: awx_purge_command - -- name: Print output of purge command - debug: - msg: "{{ awx_purge_command.stdout }}" - -- name: Pause for 5 seconds to let Synapse breathe - pause: - seconds: 5 diff --git a/roles/matrix-awx/tasks/rotate_ssh.yml b/roles/matrix-awx/tasks/rotate_ssh.yml deleted file mode 100644 index bd59cbc13..000000000 --- a/roles/matrix-awx/tasks/rotate_ssh.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- - -- name: Set the new authorized key taken from file - authorized_key: - user: root - state: present - exclusive: true - key: "{{ lookup('file', '/var/lib/awx/projects/hosting/client_public.key') }}" - -- name: Delete the AWX session token for executing modules - awx.awx.tower_token: - description: 'AWX Session Token' - scope: "write" - state: absent - existing_token_id: "{{ awx_session_token.ansible_facts.tower_token.id }}" - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - -- name: Set boolean value to exit playbook - set_fact: - end_playbook: true - -- name: End playbook if this task list is called. - meta: end_play - when: end_playbook is defined and end_playbook|bool diff --git a/roles/matrix-awx/tasks/self_check.yml b/roles/matrix-awx/tasks/self_check.yml deleted file mode 100644 index 68e833a47..000000000 --- a/roles/matrix-awx/tasks/self_check.yml +++ /dev/null @@ -1,108 +0,0 @@ ---- - -- name: Install prerequisite apt packages on target - apt: - name: - - sysstat - - curl - state: present - -- name: Install prerequisite yum packages on AWX - delegate_to: 127.0.0.1 - yum: - name: - - bind-utils - state: present - -- name: Install prerequisite pip packages on AWX - delegate_to: 127.0.0.1 - pip: - name: - - dnspython - state: present - -- name: Calculate MAU value - shell: | - curl -s localhost:9000 | grep "^synapse_admin_mau_current " - register: awx_mau_stat - no_log: true - -- name: Calculate CPU usage statistics - shell: iostat -c - register: awx_cpu_usage_stat - no_log: true - -- name: Calculate RAM usage statistics - shell: free -mh - register: awx_ram_usage_stat - no_log: true - -- name: Calculate free disk space - shell: df -h - register: awx_disk_space_stat - no_log: true - -- name: Calculate size of Synapse database - shell: du -sh /matrix/postgres/data - register: awx_db_size_stat - no_log: true - -- name: Calculate size of local media repository - shell: du -sh /matrix/synapse/storage/media-store/local* - register: awx_local_media_size_stat - async: 600 - ignore_errors: true - no_log: true - -- name: Calculate size of remote media repository - shell: du -sh /matrix/synapse/storage/media-store/remote* - register: awx_remote_media_size_stat - async: 600 - ignore_errors: true - no_log: true - -- name: Calculate docker container statistics - shell: docker stats --all --no-stream - register: awx_docker_stats - ignore_errors: true - no_log: true - -- name: Print size of remote media repository - debug: - msg: "{{ awx_remote_media_size_stat.stdout.split('\n') }}" - when: awx_remote_media_size_stat is defined - -- name: Print size of local media repository - debug: - msg: "{{ awx_local_media_size_stat.stdout.split('\n') }}" - when: awx_local_media_size_stat is defined - -- name: Print size of Synapse database - debug: - msg: "{{ awx_db_size_stat.stdout.split('\n') }}" - when: awx_db_size_stat is defined - -- name: Print free disk space - debug: - msg: "{{ awx_disk_space_stat.stdout.split('\n') }}" - when: awx_disk_space_stat is defined - -- name: Print RAM usage statistics - debug: - msg: "{{ awx_ram_usage_stat.stdout.split('\n') }}" - when: awx_ram_usage_stat is defined - -- name: Print CPU usage statistics - debug: - msg: "{{ awx_cpu_usage_stat.stdout.split('\n') }}" - when: awx_cpu_usage_stat is defined - -- name: Print MAU value - debug: - msg: "{{ awx_mau_stat.stdout.split('\n') }}" - when: awx_mau_stat is defined - -- name: Print docker container statistics - debug: - msg: "{{ awx_docker_stats.stdout.split('\n') }}" - when: awx_docker_stats is defined diff --git a/roles/matrix-awx/tasks/set_variables_corporal.yml b/roles/matrix-awx/tasks/set_variables_corporal.yml deleted file mode 100755 index 007ae59ff..000000000 --- a/roles/matrix-awx/tasks/set_variables_corporal.yml +++ /dev/null @@ -1,243 +0,0 @@ ---- - -- name: Record Corporal Enabled/Disabled variable - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Corporal Settings Start' - with_dict: - 'matrix_corporal_enabled': '{{ matrix_corporal_enabled }}' - -- name: Enable Shared Secret Auth if Corporal enabled - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Shared Secret Auth Settings Start' - with_dict: - 'matrix_synapse_ext_password_provider_shared_secret_auth_enabled': 'true' - when: matrix_corporal_enabled|bool - -- name: Disable Shared Secret Auth if Corporal disabled - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Shared Secret Auth Settings Start' - with_dict: - 'matrix_synapse_ext_password_provider_shared_secret_auth_enabled': 'false' - when: not matrix_corporal_enabled|bool - -- name: Enable Rest Auth Endpoint if Corporal enabled - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Synapse Extension Start' - with_dict: - 'matrix_synapse_ext_password_provider_rest_auth_enabled': 'true' - when: matrix_corporal_enabled|bool - -- name: Disable Rest Auth Endpoint if Corporal disabled - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Synapse Extension Start' - with_dict: - 'matrix_synapse_ext_password_provider_rest_auth_enabled': 'false' - when: not matrix_corporal_enabled|bool - -- name: Disable Corporal API if Simple Static File mode selected - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Corporal Settings Start' - with_dict: - 'matrix_corporal_http_api_enabled': 'false' - when: (awx_corporal_policy_provider_mode == "Simple Static File") or (not matrix_corporal_enabled|bool) - -- name: Enable Corporal API if Push/Pull mode delected - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Corporal Settings Start' - with_dict: - 'matrix_corporal_http_api_enabled': 'true' - when: (awx_corporal_policy_provider_mode != "Simple Static File") and (matrix_corporal_enabled|bool) - -- name: Record Corporal API Access Token if it's defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Corporal Settings Start' - with_dict: - 'matrix_corporal_http_api_auth_token': '{{ matrix_corporal_http_api_auth_token }}' - when: ( matrix_corporal_http_api_auth_token|length > 0 ) and ( awx_corporal_policy_provider_mode != "Simple Static File" ) - -- name: Record 'Simple Static File' configuration variables in matrix_vars.yml - delegate_to: 127.0.0.1 - blockinfile: - path: '{{ awx_cached_matrix_vars }}' - insertbefore: "# Corporal Policy Provider Settings End" - marker_begin: "Corporal" - marker_end: "Corporal" - block: | - matrix_corporal_policy_provider_config: | - { - "Type": "static_file", - "Path": "/etc/matrix-corporal/corporal-policy.json" - } - when: awx_corporal_policy_provider_mode == "Simple Static File" - -- name: Touch the /matrix/corporal/ directory - file: - path: "/matrix/corporal/" - state: directory - owner: matrix - group: matrix - mode: '750' - -- name: Touch the /matrix/corporal/config/ directory - file: - path: "/matrix/corporal/config/" - state: directory - owner: matrix - group: matrix - mode: '750' - -- name: Touch the /matrix/corporal/cache/ directory - file: - path: "/matrix/corporal/cache/" - state: directory - owner: matrix - group: matrix - mode: '750' - -- name: Touch the corporal-policy.json file to ensure it exists - file: - path: "/matrix/corporal/config/corporal-policy.json" - state: touch - owner: matrix - group: matrix - mode: '660' - -- name: Touch the last-policy.json file to ensure it exists - file: - path: "/matrix/corporal/config/last-policy.json" - state: touch - owner: matrix - group: matrix - mode: '660' - -- name: Record 'Simple Static File' configuration content in corporal-policy.json - copy: - content: "{{ awx_corporal_simple_static_config | string }}" - dest: "/matrix/corporal/config/corporal-policy.json" - owner: matrix - group: matrix - mode: '660' - when: (awx_corporal_policy_provider_mode == "Simple Static File") and (awx_corporal_simple_static_config|length > 0) - -- name: Record 'HTTP Pull Mode' configuration variables in matrix_vars.yml - delegate_to: 127.0.0.1 - blockinfile: - path: '{{ awx_cached_matrix_vars }}' - insertafter: "# Corporal Policy Provider Settings Start" - block: | - matrix_corporal_policy_provider_config: | - { - "Type": "http", - "Uri": "{{ awx_corporal_pull_mode_uri }}", - "AuthorizationBearerToken": "{{ awx_corporal_pull_mode_token }}", - "CachePath": "/var/cache/matrix-corporal/last-policy.json", - "ReloadIntervalSeconds": 1800, - "TimeoutMilliseconds": 30000 - } - when: (awx_corporal_policy_provider_mode == "HTTP Pull Mode (API Enabled)") and (matrix_corporal_pull_mode_uri|length > 0) and (awx_corporal_pull_mode_token|length > 0) - -- name: Record 'HTTP Push Mode' configuration variables in matrix_vars.yml - delegate_to: 127.0.0.1 - blockinfile: - path: '{{ awx_cached_matrix_vars }}' - insertafter: "# Corporal Policy Provider Settings Start" - block: | - matrix_corporal_policy_provider_config: | - { - "Type": "last_seen_store_policy", - "CachePath": "/var/cache/matrix-corporal/last-policy.json" - } - when: (awx_corporal_policy_provider_mode == "HTTP Push Mode (API Enabled)") - -- name: Lower RateLimit if set to 'Normal' - delegate_to: 127.0.0.1 - replace: - path: '{{ awx_cached_matrix_vars }}' - regexp: ' address:\n per_second: 50\n burst_count: 300\n account:\n per_second: 0.17\n burst_count: 300' - replace: ' address:\n per_second: 0.17\n burst_count: 3\n account:\n per_second: 0.17\n burst_count: 3' - when: awx_corporal_raise_ratelimits == "Normal" - -- name: Raise RateLimit if set to 'Raised' - delegate_to: 127.0.0.1 - replace: - path: '{{ awx_cached_matrix_vars }}' - regexp: ' address:\n per_second: 0.17\n burst_count: 3\n account:\n per_second: 0.17\n burst_count: 3' - replace: ' address:\n per_second: 50\n burst_count: 300\n account:\n per_second: 0.17\n burst_count: 300' - when: awx_corporal_raise_ratelimits == "Raised" - -- name: Save new 'Configure Corporal' survey.json to the AWX tower - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_corporal.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_corporal.json' - -- name: Copy new 'Configure Corporal' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_corporal.json' - dest: '/matrix/awx/configure_corporal.json' - mode: '0660' - -- debug: - msg: "matrix_corporal_matrix_homeserver_api_endpoint: {{ matrix_corporal_matrix_homeserver_api_endpoint }}" - -- debug: - msg: "matrix_corporal_matrix_auth_shared_secret: {{ matrix_corporal_matrix_auth_shared_secret }}" - -- debug: - msg: "matrix_corporal_http_gateway_internal_rest_auth_enabled: {{ matrix_corporal_http_gateway_internal_rest_auth_enabled }}" - -- debug: - msg: "matrix_corporal_matrix_registration_shared_secret: {{ matrix_corporal_matrix_registration_shared_secret }}" - -- name: Recreate 'Configure Corporal (Advanced)' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Corporal (Advanced)" - description: "Configure Matrix Corporal, a tool that manages your Matrix server according to a configuration policy." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-corporal" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_corporal.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_dimension.yml b/roles/matrix-awx/tasks/set_variables_dimension.yml deleted file mode 100644 index 8d8f9c444..000000000 --- a/roles/matrix-awx/tasks/set_variables_dimension.yml +++ /dev/null @@ -1,105 +0,0 @@ ---- - -- name: Include vars in matrix_vars.yml - include_vars: - file: '{{ awx_cached_matrix_vars }}' - no_log: true - -- name: Install jq and curl on remote machine - apt: - name: - - jq - - curl - state: present - -- name: Collect access token of @admin-dimension user - shell: | - curl -X POST --header 'Content-Type: application/json' -d '{"identifier": {"type": "m.id.user","user": "admin-dimension"}, "password": "{{ awx_dimension_user_password }}", "type": "m.login.password"}' 'https://matrix.{{ matrix_domain }}/_matrix/client/r0/login' | jq '.access_token' - register: awx_dimension_user_access_token - -- name: Record Synapse variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Dimension Settings Start' - with_dict: - 'matrix_dimension_enabled': '{{ matrix_dimension_enabled }}' - 'matrix_dimension_access_token': '"{{ awx_dimension_user_access_token.stdout[1:-1] }}"' - -- name: Set final users list if users are defined - set_fact: - awx_dimension_users_final: "{{ awx_dimension_users }}" - when: awx_dimension_users | length > 0 - -- name: Set final users list if no users are defined - set_fact: - awx_dimension_users_final: '@dimension:{{ matrix_domain }}' - when: awx_dimension_users | length == 0 - -- name: Remove Dimension Users - delegate_to: 127.0.0.1 - replace: - path: '{{ awx_cached_matrix_vars }}' - regexp: '^ - .*\n' - after: 'matrix_dimension_admins:' - before: '# Dimension Settings End' - -- name: Set Dimension Users Header - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - insertbefore: '# Dimension Settings End' - line: "matrix_dimension_admins:" - -- name: Set Dimension Users - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - insertafter: '^matrix_dimension_admins:' - line: ' - "{{ item }}"' - with_items: "{{ awx_dimension_users_final.splitlines() }}" - -- name: Record Dimension Custom variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertbefore: '# Dimension Settings End' - with_dict: - 'awx_dimension_users': '{{ awx_dimension_users.splitlines() | to_json }}' - -- name: Save new 'Configure Dimension' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_dimension.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}//configure_dimension.json' - -- name: Copy new 'Configure Dimension' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_dimension.json' - dest: '/matrix/awx/configure_dimension.json' - mode: '0660' - -- name: Recreate 'Configure Dimension' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Dimension" - description: "Configure Dimension, the self-hosted integrations server." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-all,setup-dimension" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_dimension.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_element.yml b/roles/matrix-awx/tasks/set_variables_element.yml deleted file mode 100755 index 4b2ce8590..000000000 --- a/roles/matrix-awx/tasks/set_variables_element.yml +++ /dev/null @@ -1,180 +0,0 @@ ---- - -- name: Record Element-Web variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Element Settings Start' - with_dict: - 'matrix_client_element_enabled': '{{ matrix_client_element_enabled }}' - 'matrix_client_element_jitsi_preferredDomain': 'jitsi.{{ matrix_domain }}' - 'matrix_client_element_default_theme': '{{ matrix_client_element_default_theme }}' - 'matrix_client_element_registration_enabled': '{{ matrix_client_element_registration_enabled }}' - 'matrix_client_element_brand': '{{ matrix_client_element_brand | trim }}' - 'matrix_client_element_branding_welcomeBackgroundUrl': '{{ matrix_client_element_branding_welcomeBackgroundUrl | trim }}' - 'matrix_client_element_welcome_logo': '{{ matrix_client_element_welcome_logo | trim }}' - 'matrix_client_element_welcome_logo_link': '{{ matrix_client_element_welcome_logo_link | trim }}' - -- name: Record Element-Web custom variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: '{{ item.value }}'" - insertbefore: '# Element Settings End' - with_dict: - 'awx_matrix_client_element_welcome_headline': '{{ awx_matrix_client_element_welcome_headline | trim }}' - 'awx_matrix_client_element_welcome_text': '{{ awx_matrix_client_element_welcome_text | trim }}' - -- name: Set Element-Web custom branding locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: '{{ item.value }}'" - insertafter: '# Element Settings Start' - with_dict: - 'matrix_client_element_brand': "{{ matrix_client_element_brand }}" - when: matrix_client_element_brand | trim | length > 0 - -- name: Remove Element-Web custom branding locally on AWX if not defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^matrix_client_element_brand: " - state: absent - when: matrix_client_element_brand | trim | length == 0 - -- name: Set fact for 'https' string - set_fact: - awx_https_string: "https" - -- name: Set Element-Web custom logo locally on AWX if defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: '{{ item.value }}'" - insertafter: '# Element Settings Start' - with_dict: - 'matrix_client_element_welcome_logo': '{{ matrix_client_element_welcome_logo }}' - when: ( awx_https_string in matrix_client_element_welcome_logo ) and ( matrix_client_element_welcome_logo | trim | length > 0 ) - -- name: Remove Element-Web custom logo locally on AWX if not defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^matrix_client_element_welcome_logo: " - state: absent - when: matrix_client_element_welcome_logo | trim | length == 0 - -- name: Set Element-Web custom logo link locally on AWX if defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: '{{ item.value }}'" - insertafter: '# Element Settings Start' - with_dict: - 'matrix_client_element_welcome_logo_link': '{{ matrix_client_element_welcome_logo_link }}' - when: ( awx_https_string in matrix_client_element_welcome_logo_link ) and ( matrix_client_element_welcome_logo_link | trim | length > 0 ) - -- name: Remove Element-Web custom logo link locally on AWX if not defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^matrix_client_element_welcome_logo_link: " - state: absent - when: matrix_client_element_welcome_logo_link | trim | length == 0 - -- name: Set Element-Web custom headline locally on AWX if defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: '{{ item.value }}'" - insertafter: '# Element Settings Start' - with_dict: - 'matrix_client_element_welcome_headline': '{{ awx_matrix_client_element_welcome_headline }}' - when: awx_matrix_client_element_welcome_headline | trim | length > 0 - -- name: Remove Element-Web custom headline locally on AWX if not defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^matrix_client_element_welcome_headline: " - state: absent - when: awx_matrix_client_element_welcome_headline | trim | length == 0 - -- name: Set Element-Web custom text locally on AWX if defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: '{{ item.value }}'" - insertafter: '# Element Settings Start' - with_dict: - 'matrix_client_element_welcome_text': '{{ awx_matrix_client_element_welcome_text }}' - when: awx_matrix_client_element_welcome_text | trim | length > 0 - -- name: Remove Element-Web custom text locally on AWX if not defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^matrix_client_element_welcome_text: " - state: absent - when: awx_matrix_client_element_welcome_text | trim | length == 0 - -- name: Set Element-Web background locally on AWX if defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: '{{ item.value }}'" - insertafter: '# Element Settings Start' - with_dict: - 'matrix_client_element_branding_welcomeBackgroundUrl': '{{ matrix_client_element_branding_welcomeBackgroundUrl }}' - when: matrix_client_element_branding_welcomeBackgroundUrl | trim | length > 0 - -- name: Remove Element-Web background locally on AWX if not defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^matrix_client_element_branding_welcomeBackgroundUrl: " - state: absent - when: matrix_client_element_branding_welcomeBackgroundUrl | trim | length == 0 - -- name: Save new 'Configure Element' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_element.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element.json' - -- name: Copy new 'Configure Element' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element.json' - dest: '/matrix/awx/configure_element.json' - mode: '0660' - -- name: Recreate 'Configure Element' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Element" - description: "Configure Element client via survey." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-client-element" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_element_subdomain.yml b/roles/matrix-awx/tasks/set_variables_element_subdomain.yml deleted file mode 100644 index 1c78b9e0e..000000000 --- a/roles/matrix-awx/tasks/set_variables_element_subdomain.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- - -- name: Record Element-Web variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Element Settings Start' - with_dict: - 'matrix_server_fqn_element': "{{ awx_element_subdomain | trim }}.{{ matrix_domain }}" - -- name: Save new 'Configure Element Subdomain' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_element_subdomain.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element_subdomain.json' - -- name: Copy new 'Configure Element Subdomain' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element_subdomain.json' - dest: '/matrix/awx/configure_element_subdomain.json' - mode: '0660' - -- name: Recreate 'Configure Element Subdomain' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Element Subdomain" - description: "Configure Element clients subdomain location. (Eg: 'element' for element.example.org)" - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-all,setup-client-element-subdomain" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element_subdomain.json') }}" - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_jitsi.yml b/roles/matrix-awx/tasks/set_variables_jitsi.yml deleted file mode 100755 index b12391bf3..000000000 --- a/roles/matrix-awx/tasks/set_variables_jitsi.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- - -- name: Record Jitsi variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Jitsi Settings Start' - with_dict: - 'matrix_jitsi_enabled': '{{ matrix_jitsi_enabled }}' - 'matrix_jitsi_web_config_defaultLanguage': '{{ matrix_jitsi_web_config_defaultLanguage | trim }}' - -- name: Save new 'Configure Jitsi' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_jitsi.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_jitsi.json' - -- name: Copy new 'Configure Jitsi' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_jitsi.json' - dest: '/matrix/awx/configure_jitsi.json' - mode: '0660' - -- name: Recreate 'Configure Jitsi' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Jitsi" - description: "Configure Jitsi conferencing settings." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-jitsi" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_jitsi.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_ma1sd.yml b/roles/matrix-awx/tasks/set_variables_ma1sd.yml deleted file mode 100755 index d46d797fd..000000000 --- a/roles/matrix-awx/tasks/set_variables_ma1sd.yml +++ /dev/null @@ -1,102 +0,0 @@ ---- - -- name: Record ma1sd variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# ma1sd Settings Start' - with_dict: - 'matrix_ma1sd_enabled': '{{ matrix_ma1sd_enabled }}' - -- name: Disable REST auth (matrix-corporal/ma1sd) if using internal auth - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Synapse Extension Start' - with_dict: - 'matrix_synapse_awx_password_provider_rest_auth_enabled': 'false' - when: awx_matrix_ma1sd_auth_store == 'Synapse Internal' - -- name: Enable REST auth if using external LDAP/AD with ma1sd - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Synapse Extension Start' - with_dict: - 'matrix_synapse_awx_password_provider_rest_auth_enabled': 'true' - 'matrix_synapse_awx_password_provider_rest_auth_endpoint': '"http://matrix-ma1sd:{{ matrix_ma1sd_container_port }}"' - when: awx_matrix_ma1sd_auth_store == 'LDAP/AD' - -- name: Remove entire ma1sd configuration extension - delegate_to: 127.0.0.1 - replace: - path: '{{ awx_cached_matrix_vars }}' - regexp: '^.*\n' - after: '# ma1sd Extension Start' - before: '# ma1sd Extension End' - -- name: Replace conjoined ma1sd configuration extension limiters - delegate_to: 127.0.0.1 - replace: - path: '{{ awx_cached_matrix_vars }}' - regexp: '^# ma1sd Extension Start# ma1sd Extension End' - replace: '# ma1sd Extension Start\n# ma1sd Extension End' - -- name: Insert/Update ma1sd configuration extension variables - delegate_to: 127.0.0.1 - blockinfile: - path: '{{ awx_cached_matrix_vars }}' - marker: "# {mark} ma1sd ANSIBLE MANAGED BLOCK" - insertafter: '# ma1sd Extension Start' - block: '{{ awx_matrix_ma1sd_configuration_extension_yaml }}' - -- name: Record ma1sd Custom variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertbefore: '# ma1sd Settings End' - with_dict: - 'awx_matrix_ma1sd_auth_store': '{{ awx_matrix_ma1sd_auth_store }}' - 'awx_matrix_ma1sd_configuration_extension_yaml': '{{ awx_matrix_ma1sd_configuration_extension_yaml.splitlines() | to_json }}' - no_log: true - -- name: Save new 'Configure ma1sd' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_ma1sd.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_ma1sd.json' - -- name: Copy new 'Configure ma1sd' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_ma1sd.json' - dest: '/matrix/awx/configure_ma1sd.json' - mode: '0660' - -- name: Recreate 'Configure ma1sd (Advanced)' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure ma1sd (Advanced)" - description: "Configure Jitsi conferencing settings." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-ma1sd" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_ma1sd.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_mailer.yml b/roles/matrix-awx/tasks/set_variables_mailer.yml deleted file mode 100644 index 6581223d2..000000000 --- a/roles/matrix-awx/tasks/set_variables_mailer.yml +++ /dev/null @@ -1,44 +0,0 @@ ---- - -- name: Record Mailer variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Email Settings Start' - with_dict: - 'matrix_mailer_relay_use': '{{ matrix_mailer_relay_use }}' - -- name: Save new 'Configure Email Relay' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_email_relay.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_email_relay.json' - -- name: Copy new 'Configure Email Relay' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_email_relay.json' - dest: '/matrix/awx/configure_email_relay.json' - mode: '0660' - -- name: Recreate 'Configure Email Relay' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Email Relay" - description: "Enable MailGun relay to increase verification email reliability." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-mailer" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_email_relay.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_mjolnir.yml b/roles/matrix-awx/tasks/set_variables_mjolnir.yml deleted file mode 100755 index 6e3bb1534..000000000 --- a/roles/matrix-awx/tasks/set_variables_mjolnir.yml +++ /dev/null @@ -1,68 +0,0 @@ ---- - -- name: Include vars in matrix_vars.yml - include_vars: - file: '{{ awx_cached_matrix_vars }}' - no_log: true - -- name: Collect the internal IP of the matrix-synapse container - shell: | - /usr/bin/docker inspect --format '{''{range.NetworkSettings.Networks}''}{''{.IPAddress}''}{''{end}''}' matrix-synapse - register: matrix_synapse_ip - -- name: Collect access token of @admin-mjolnir user - shell: | - curl -X POST --header 'Content-Type: application/json' -d '{"identifier": {"type": "m.id.user","user": "admin-mjolnir"}, "password": "{{ awx_mjolnir_user_password }}", "type": "m.login.password"}' 'http://{{ matrix_synapse_ip.stdout }}:8008/_matrix/client/r0/login' | jq '.access_token' - register: awx_mjolnir_user_access_token - no_log: true - -- name: Record Mjolnir Bot variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Mjolnir Settings Start' - with_dict: - 'matrix_bot_mjolnir_enabled': '{{ matrix_bot_mjolnir_enabled }}' - 'matrix_bot_mjolnir_access_token': '{{ awx_mjolnir_user_access_token.stdout[1:-1] }}' - 'matrix_bot_mjolnir_management_room': '"{{ matrix_bot_mjolnir_management_room }}"' - no_log: true - -- name: Remove Synapse rate-limiting for admin-mjolnir user - shell: | - /usr/local/bin/matrix-postgres-cli-non-interactive --dbname=synapse --command="INSERT INTO ratelimit_override VALUES ('@admin-mjolnir:{{ matrix_domain }}', 0, 0);" - ignore_errors: true - -- name: Save new 'Configure Mjolnir' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_mjolnir.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_mjolnir.json' - -- name: Copy new 'Configure Mjolnir' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_mjolnir.json' - dest: '/matrix/awx/configure_mjolnir.json' - mode: '0660' - -- name: Recreate 'Configure Mjolnir Bot' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Mjolnir Bot" - description: "Configure Mjolnir settings, Mjolnir is a moderation bot for Matrix." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-bot-mjolnir" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_mjolnir.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_synapse.yml b/roles/matrix-awx/tasks/set_variables_synapse.yml deleted file mode 100755 index f749f03f6..000000000 --- a/roles/matrix-awx/tasks/set_variables_synapse.yml +++ /dev/null @@ -1,223 +0,0 @@ ---- - -- name: Limit max upload size to 200MB part 1 - set_fact: - matrix_synapse_max_upload_size_mb: "200" - when: awx_synapse_max_upload_size_mb | int >= 200 - -- name: Limit max upload size to 200MB part 2 - set_fact: - matrix_synapse_max_upload_size_mb: "{{ awx_synapse_max_upload_size_mb }}" - when: awx_synapse_max_upload_size_mb | int < 200 - -- name: Record Synapse variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Synapse Settings Start' - with_dict: - 'matrix_synapse_allow_public_rooms_over_federation': '{{ matrix_synapse_allow_public_rooms_over_federation }}' - 'matrix_synapse_enable_registration': '{{ matrix_synapse_enable_registration }}' - 'matrix_synapse_federation_enabled': '{{ matrix_synapse_federation_enabled }}' - 'matrix_synapse_enable_group_creation': '{{ matrix_synapse_enable_group_creation }}' - 'matrix_synapse_presence_enabled': '{{ matrix_synapse_presence_enabled }}' - 'matrix_synapse_max_upload_size_mb': '{{ matrix_synapse_max_upload_size_mb }}' - 'matrix_synapse_url_preview_enabled': '{{ matrix_synapse_url_preview_enabled }}' - 'matrix_synapse_allow_guest_access': '{{ matrix_synapse_allow_guest_access }}' - -- name: Empty Synapse variable 'matrix_synapse_auto_join_rooms' locally on AWX, if raw inputs empty - delegate_to: 127.0.0.1 - replace: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^matrix_synapse_auto_join_rooms: .*$" - replace: "matrix_synapse_auto_join_rooms: []" - when: awx_synapse_auto_join_rooms | length == 0 - -- name: If the raw inputs is not empty start constructing parsed auto_join_rooms list - set_fact: - awx_synapse_auto_join_rooms_array: |- - {{ awx_synapse_auto_join_rooms.splitlines() | to_json }} - when: awx_synapse_auto_join_rooms | length > 0 - -- name: Record Synapse variable 'matrix_synapse_auto_join_rooms' locally on AWX, if it's not blank - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Synapse Settings Start' - with_dict: - "matrix_synapse_auto_join_rooms": "{{ awx_synapse_auto_join_rooms_array }}" - when: awx_synapse_auto_join_rooms | length > 0 - -- name: Record Synapse Shared Secret if it's defined - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Synapse Settings Start' - with_dict: - 'matrix_synapse_registration_shared_secret': '{{ awx_matrix_synapse_registration_shared_secret }}' - when: awx_matrix_synapse_registration_shared_secret | length > 0 - -- name: Record registations_require_3pid extra variable if true - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "{{ item }}" - line: "{{ item }}" - insertbefore: '# Synapse Extension End' - with_items: - - " registrations_require_3pid:" - - " - email" - when: awx_registrations_require_3pid | bool - -- name: Remove registrations_require_3pid extra variable if false - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "{{ item }}" - line: "{{ item }}" - insertbefore: '# Synapse Extension End' - state: absent - with_items: - - " registrations_require_3pid:" - - " - email" - when: not awx_registrations_require_3pid | bool - -- name: Remove URL Languages - delegate_to: 127.0.0.1 - replace: - path: '{{ awx_cached_matrix_vars }}' - regexp: '^(?!.*\bemail\b) - [a-zA-Z\-]{2,5}\n' - after: ' url_preview_accept_language:' - before: '# Synapse Extension End' - -- name: Set URL languages default if raw inputs empty - set_fact: - awx_url_preview_accept_language_default: 'en' - when: awx_url_preview_accept_language | length == 0 - -- name: Set URL languages default if raw inputs not empty - set_fact: - awx_url_preview_accept_language_default: "{{ awx_url_preview_accept_language }}" - when: awx_url_preview_accept_language|length > 0 - -- name: Set URL languages if raw inputs empty - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - insertafter: '^ url_preview_accept_language:' - line: " - {{ awx_url_preview_accept_language_default }}" - when: awx_url_preview_accept_language|length == 0 - -- name: Set URL languages if raw inputs not empty - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - insertafter: '^ url_preview_accept_language:' - line: " - {{ item }}" - with_items: "{{ awx_url_preview_accept_language.splitlines() }}" - when: awx_url_preview_accept_language | length > 0 - -- name: Remove Federation Whitelisting 1 - delegate_to: 127.0.0.1 - replace: - path: '{{ awx_cached_matrix_vars }}' - regexp: '^ - [a-z0-9]+\.[a-z0-9.]+\n' - after: ' federation_domain_whitelist:' - before: '# Synapse Extension End' - -- name: Remove Federation Whitelisting 2 - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - line: " federation_domain_whitelist:" - state: absent - -- name: Set Federation Whitelisting 1 - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - insertafter: '^matrix_synapse_configuration_extension_yaml: \|' - line: " federation_domain_whitelist:" - when: awx_federation_whitelist | length > 0 - -- name: Set Federation Whitelisting 2 - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - insertafter: '^ federation_domain_whitelist:' - line: " - {{ item }}" - with_items: "{{ awx_federation_whitelist.splitlines() }}" - when: awx_federation_whitelist | length > 0 - -- name: Set awx_recaptcha_public_key to a 'public-key' if undefined - set_fact: awx_recaptcha_public_key="public-key" - when: (awx_recaptcha_public_key is not defined) or (awx_recaptcha_public_key|length == 0) - -- name: Set awx_recaptcha_private_key to a 'private-key' if undefined - set_fact: awx_recaptcha_private_key="private-key" - when: (awx_recaptcha_private_key is not defined) or (awx_recaptcha_private_key|length == 0) - -- name: Record Synapse Extension variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertbefore: '# Synapse Extension End' - with_dict: - ' enable_registration_captcha': '{{ awx_enable_registration_captcha }}' - ' recaptcha_public_key': '{{ awx_recaptcha_public_key }}' - ' recaptcha_private_key': '{{ awx_recaptcha_private_key }}' - -- name: Record Synapse Custom variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertbefore: '# Synapse Settings End' - with_dict: - 'awx_federation_whitelist': '{{ awx_federation_whitelist.splitlines() | to_json }}' - 'awx_url_preview_accept_language_default': '{{ awx_url_preview_accept_language_default.splitlines() | to_json }}' - 'awx_enable_registration_captcha': '{{ awx_enable_registration_captcha }}' - 'awx_recaptcha_public_key': '"{{ awx_recaptcha_public_key }}"' - 'awx_recaptcha_private_key': '"{{ awx_recaptcha_private_key }}"' - -- name: Save new 'Configure Synapse' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_synapse.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}//configure_synapse.json' - -- name: Copy new 'Configure Synapse' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse.json' - dest: '/matrix/awx/configure_synapse.json' - mode: '0660' - -- name: Recreate 'Configure Synapse' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Synapse" - description: "Configure Synapse (homeserver) settings." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-synapse" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/set_variables_synapse_admin.yml b/roles/matrix-awx/tasks/set_variables_synapse_admin.yml deleted file mode 100644 index 1e63fb71a..000000000 --- a/roles/matrix-awx/tasks/set_variables_synapse_admin.yml +++ /dev/null @@ -1,44 +0,0 @@ ---- - -- name: Record Synapse Admin variables locally on AWX - delegate_to: 127.0.0.1 - lineinfile: - path: '{{ awx_cached_matrix_vars }}' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: '# Synapse Admin Settings Start' - with_dict: - 'matrix_synapse_admin_enabled': '{{ matrix_synapse_admin_enabled }}' - -- name: Save new 'Configure Synapse Admin' survey.json to the AWX tower, template - delegate_to: 127.0.0.1 - template: - src: 'roles/matrix-awx/surveys/configure_synapse_admin.json.j2' - dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse_admin.json' - -- name: Copy new 'Configure Synapse Admin' survey.json to target machine - copy: - src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse_admin.json' - dest: '/matrix/awx/configure_synapse_admin.json' - mode: '0660' - -- name: Recreate 'Configure Synapse Admin' job template - delegate_to: 127.0.0.1 - awx.awx.tower_job_template: - name: "{{ matrix_domain }} - 1 - Configure Synapse Admin" - description: "Configure 'Synapse Admin', a moderation tool to help you manage your server." - extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" - job_type: run - job_tags: "start,setup-all" - inventory: "{{ member_id }}" - project: "{{ member_id }} - Matrix Docker Ansible Deploy" - playbook: setup.yml - credential: "{{ member_id }} - AWX SSH Key" - survey_enabled: true - survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse_admin.json') }}" - become_enabled: true - state: present - verbosity: 1 - tower_host: "https://{{ awx_host }}" - tower_oauthtoken: "{{ awx_session_token.ansible_facts.tower_token.token }}" - validate_certs: true diff --git a/roles/matrix-awx/tasks/update_variables.yml b/roles/matrix-awx/tasks/update_variables.yml deleted file mode 100644 index b281a8c5b..000000000 --- a/roles/matrix-awx/tasks/update_variables.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- - -- name: Rename synapse presence variable - delegate_to: 127.0.0.1 - replace: - path: "/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml" - regexp: 'matrix_synapse_use_presence' - replace: 'matrix_synapse_presence_enabled' - -- name: Search for matrix_homeserver_generic_secret_key variable in matrix_vars.yml - delegate_to: 127.0.0.1 - register: presence - shell: "grep -i 'matrix_homeserver_generic_secret_key' /var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml" - no_log: true - -- name: Generate matrix_homeserver_generic_secret_key variable if not present - delegate_to: 127.0.0.1 - command: | - openssl rand -hex 16 - register: generic_secret - no_log: true - when: presence is not changed - -- name: Add new matrix_homeserver_generic_secret_key variable if not present - delegate_to: 127.0.0.1 - lineinfile: - path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - line: "matrix_homeserver_generic_secret_key: {{ generic_secret.stdout }}" - insertbefore: '# Basic Settings End' - mode: '0600' - state: present - when: presence is not changed diff --git a/roles/matrix-common-after/tasks/awx_post.yml b/roles/matrix-common-after/tasks/awx_post.yml deleted file mode 100644 index ad0a0ee8a..000000000 --- a/roles/matrix-common-after/tasks/awx_post.yml +++ /dev/null @@ -1,77 +0,0 @@ ---- - -- name: Create user account @admin-janitor - command: | - /usr/local/bin/matrix-synapse-register-user admin-janitor {{ awx_janitor_user_password | quote }} 1 - register: cmd - when: not awx_janitor_user_created|bool - no_log: false - -- name: Update AWX janitor user created variable - delegate_to: 127.0.0.1 - lineinfile: - path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: 'AWX Settings' - with_dict: - 'awx_janitor_user_created': 'true' - when: not awx_janitor_user_created|bool - -- name: Create user account @admin-dimension - command: | - /usr/local/bin/matrix-synapse-register-user admin-dimension {{ awx_dimension_user_password | quote }} 0 - register: cmd - when: not awx_dimension_user_created|bool - no_log: false - -- name: Update AWX dimension user created variable - delegate_to: 127.0.0.1 - lineinfile: - path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: 'AWX Settings' - with_dict: - 'awx_dimension_user_created': 'true' - when: not awx_dimension_user_created|bool - -- name: Create user account @admin-mjolnir - command: | - /usr/local/bin/matrix-synapse-register-user admin-mjolnir {{ awx_mjolnir_user_password | quote }} 0 - register: cmd - when: not awx_mjolnir_user_created|bool - no_log: false - -- name: Update AWX dimension user created variable - delegate_to: 127.0.0.1 - lineinfile: - path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' - regexp: "^#? *{{ item.key | regex_escape() }}:" - line: "{{ item.key }}: {{ item.value }}" - insertafter: 'AWX Settings' - with_dict: - 'awx_mjolnir_user_created': 'true' - when: not awx_mjolnir_user_created|bool - -- name: Ensure /chroot/website location has correct permissions - file: - path: /chroot/website - state: directory - owner: matrix - group: matrix - mode: '0770' - when: awx_customise_base_domain_website is defined - -- name: Collect Discord AppService bot invite link if file exists - command: - cat /matrix/appservice-discord/config/invite_link - register: awx_discord_appservice_link - when: awx_appservice_discord_admin_user is defined - args: - removes: /matrix/appservice-discord/config/invite_link - -- name: Print Discord AppService bot link for user - debug: - msg: "{{ awx_discord_appservice_link.stdout }}" - when: awx_discord_appservice_link.stdout is defined diff --git a/roles/matrix-common-after/tasks/main.yml b/roles/matrix-common-after/tasks/main.yml index 75dee15d5..f3ccf3a52 100644 --- a/roles/matrix-common-after/tasks/main.yml +++ b/roles/matrix-common-after/tasks/main.yml @@ -14,11 +14,6 @@ tags: - always -- import_tasks: "{{ role_path }}/tasks/awx_post.yml" - when: run_setup|bool and matrix_awx_enabled|bool - tags: - - always - - import_tasks: "{{ role_path }}/tasks/run_docker_prune.yml" tags: - run-docker-prune diff --git a/setup.yml b/setup.yml index 197d313e2..52079e32c 100755 --- a/setup.yml +++ b/setup.yml @@ -7,7 +7,6 @@ - roles/matrix-synapse/vars/workers.yml roles: - - matrix-awx - matrix-base - matrix-dynamic-dns - matrix-mailer From 6925e26960246d54efa03d798b7363c12b01ac46 Mon Sep 17 00:00:00 2001 From: SaltireSoul Date: Sat, 9 Apr 2022 02:55:48 +0100 Subject: [PATCH 12/83] Dendrite 0.8.1 --- roles/matrix-dendrite/defaults/main.yml | 2 +- .../templates/dendrite/dendrite.yaml.j2 | 54 ++++++++++++++----- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/roles/matrix-dendrite/defaults/main.yml b/roles/matrix-dendrite/defaults/main.yml index 99ceb1a03..7f2e629a8 100644 --- a/roles/matrix-dendrite/defaults/main.yml +++ b/roles/matrix-dendrite/defaults/main.yml @@ -6,7 +6,7 @@ matrix_dendrite_enabled: true matrix_dendrite_docker_image: "{{ matrix_dendrite_docker_image_name_prefix }}matrixdotorg/dendrite-monolith:{{ matrix_dendrite_docker_image_tag }}" matrix_dendrite_docker_image_name_prefix: "docker.io/" -matrix_dendrite_docker_image_tag: "v0.7.0" +matrix_dendrite_docker_image_tag: "v0.8.1" matrix_dendrite_docker_image_force_pull: "{{ matrix_dendrite_docker_image.endswith(':latest') }}" matrix_dendrite_base_path: "{{ matrix_base_data_path }}/dendrite" diff --git a/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 b/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 index 01bb72f7b..308ee3f32 100644 --- a/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 +++ b/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 @@ -66,6 +66,13 @@ global: # to other servers and the federation API will not be exposed. disable_federation: {{ (not matrix_dendrite_federation_enabled)|to_json }} + # Configures the handling of presence events. + presence: + # Whether inbound presence events are allowed, e.g. receiving presence events from other servers + enable_inbound: false + # Whether outbound presence events are allowed, e.g. sending presence events to other servers + enable_outbound: false + # Server notices allows server admins to send messages to all users. server_notices: enabled: false @@ -132,6 +139,11 @@ app_service_api: max_idle_conns: 2 conn_max_lifetime: -1 + # Disable the validation of TLS certificates of appservices. This is + # not recommended in production since it may allow appservice traffic + # to be sent to an unverified endpoint. + disable_tls_validation: {{ matrix_dendrite_disable_tls_validation|to_json }} + # Appservice configuration files to load into this homeserver. config_files: {{ matrix_dendrite_app_service_config_files|to_json }} @@ -201,12 +213,13 @@ federation_api: # enable this option in production as it presents a security risk! disable_tls_validation: {{ matrix_dendrite_disable_tls_validation|to_json }} + # Not in dendrite-config.yaml, but is in build/docker/config/dendrite.yaml # Use the following proxy server for outbound federation traffic. - proxy_outbound: - enabled: false - protocol: http - host: localhost - port: 8080 + #proxy_outbound: + # enabled: false + # protocol: http + # host: localhost + # port: 8080 # Perspective keyservers to use as a backup when direct key fetches fail. This may # be required to satisfy key requests for servers that are no longer online when @@ -319,6 +332,13 @@ sync_api: # Configuration for the User API. user_api: + # The cost when hashing passwords on registration/login. Default: 10. Min: 4, Max: 31 + # See https://pkg.go.dev/golang.org/x/crypto/bcrypt for more information. + # Setting this lower makes registration/login consume less CPU resources at the cost of security + # should the database be compromised. Setting this higher makes registration/login consume more + # CPU resources but makes it harder to brute force password hashes. + # This value can be low if performing tests or on embedded Dendrite instances (e.g WASM builds) + # bcrypt_cost: 10 internal_api: listen: http://0.0.0.0:7781 connect: http://user_api:7781 @@ -327,17 +347,23 @@ user_api: max_open_conns: 10 max_idle_conns: 2 conn_max_lifetime: -1 + # The length of time that a token issued for a relying party from + # /_matrix/client/r0/user/{userId}/openid/request_token endpoint + # is considered to be valid in milliseconds. + # The default lifetime is 3600000ms (60 minutes). + # openid_token_lifetime_ms: 3600000 +# Not in dendrite-config.yaml, but is in build/docker/config/dendrite.yaml (DB is created just in case) # Configuration for the Push Server API. -push_server: - internal_api: - listen: http://localhost:7782 - connect: http://localhost:7782 - database: - connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_pushserver_database }}?sslmode=disable - max_open_conns: 10 - max_idle_conns: 2 - conn_max_lifetime: -1 +#push_server: +# internal_api: +# listen: http://localhost:7782 +# connect: http://localhost:7782 +# database: +# connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_pushserver_database }}?sslmode=disable +# max_open_conns: 10 +# max_idle_conns: 2 +# conn_max_lifetime: -1 # Configuration for Opentracing. # See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on From b982733a8a518e5db82182e6851993e9c7332709 Mon Sep 17 00:00:00 2001 From: Yan Minagawa Date: Sat, 9 Apr 2022 19:41:48 +0700 Subject: [PATCH 13/83] fix typo in document path for the proxy --- roles/matrix-nginx-proxy/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-nginx-proxy/defaults/main.yml b/roles/matrix-nginx-proxy/defaults/main.yml index 4b1810eae..de1a31461 100644 --- a/roles/matrix-nginx-proxy/defaults/main.yml +++ b/roles/matrix-nginx-proxy/defaults/main.yml @@ -11,7 +11,7 @@ matrix_nginx_proxy_docker_image_force_pull: "{{ matrix_nginx_proxy_docker_image. matrix_nginx_proxy_base_path: "{{ matrix_base_data_path }}/nginx-proxy" matrix_nginx_proxy_data_path: "{{ matrix_nginx_proxy_base_path }}/data" matrix_nginx_proxy_data_path_in_container: "/nginx-data" -matrix_nginx_proxy_data_path_extension: "/matrix_domain" +matrix_nginx_proxy_data_path_extension: "/matrix-domain" matrix_nginx_proxy_confd_path: "{{ matrix_nginx_proxy_base_path }}/conf.d" # List of systemd services that matrix-nginx-proxy.service depends on From 515792790ae0b6dd821a786c49518496f94c4bdc Mon Sep 17 00:00:00 2001 From: SaltireSoul Date: Sat, 9 Apr 2022 21:41:35 +0100 Subject: [PATCH 14/83] uncomment push_server config --- .../templates/dendrite/dendrite.yaml.j2 | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 b/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 index 308ee3f32..fcede4055 100644 --- a/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 +++ b/roles/matrix-dendrite/templates/dendrite/dendrite.yaml.j2 @@ -353,17 +353,17 @@ user_api: # The default lifetime is 3600000ms (60 minutes). # openid_token_lifetime_ms: 3600000 -# Not in dendrite-config.yaml, but is in build/docker/config/dendrite.yaml (DB is created just in case) +# Not in dendrite-config.yaml, but is in build/docker/config/dendrite.yaml # Configuration for the Push Server API. -#push_server: -# internal_api: -# listen: http://localhost:7782 -# connect: http://localhost:7782 -# database: -# connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_pushserver_database }}?sslmode=disable -# max_open_conns: 10 -# max_idle_conns: 2 -# conn_max_lifetime: -1 +push_server: + internal_api: + listen: http://localhost:7782 + connect: http://localhost:7782 + database: + connection_string: {{ matrix_dendrite_database_str }}/{{ matrix_dendrite_pushserver_database }}?sslmode=disable + max_open_conns: 10 + max_idle_conns: 2 + conn_max_lifetime: -1 # Configuration for Opentracing. # See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on From 29847627f1aee22c4810699d92a61d1f4b63aea8 Mon Sep 17 00:00:00 2001 From: heftyzauk <80178101+heftyzauk@users.noreply.github.com> Date: Sun, 10 Apr 2022 21:51:03 +0100 Subject: [PATCH 15/83] Multi-IP coturn Add support for multiple external turn IP addresses, this allows for better comptability with dualstack ipv4/ipv6 hosts, and is supported as per the documentation (point 6 here: https://matrix-org.github.io/synapse/latest/turn-howto.html#configuration) --- group_vars/matrix_servers | 2 +- roles/matrix-coturn/defaults/main.yml | 2 +- roles/matrix-coturn/templates/turnserver.conf.j2 | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index a1cadd12a..92df1bd47 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -1144,7 +1144,7 @@ matrix_coturn_enabled: true matrix_coturn_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" -matrix_coturn_turn_external_ip_address: "{{ ansible_host }}" +matrix_coturn_turn_external_ip_address: ["{{ ansible_host }}"] matrix_coturn_turn_static_auth_secret: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'coturn.sas') | to_uuid }}" diff --git a/roles/matrix-coturn/defaults/main.yml b/roles/matrix-coturn/defaults/main.yml index f12746574..c47430895 100644 --- a/roles/matrix-coturn/defaults/main.yml +++ b/roles/matrix-coturn/defaults/main.yml @@ -64,7 +64,7 @@ matrix_coturn_turn_udp_max_port: 49172 matrix_coturn_turn_static_auth_secret: "" # The external IP address of the machine where Coturn is. -matrix_coturn_turn_external_ip_address: '' +matrix_coturn_turn_external_ip_address: [] matrix_coturn_allowed_peer_ips: [] matrix_coturn_denied_peer_ips: [] diff --git a/roles/matrix-coturn/templates/turnserver.conf.j2 b/roles/matrix-coturn/templates/turnserver.conf.j2 index ba662587d..dfa9f4cc7 100644 --- a/roles/matrix-coturn/templates/turnserver.conf.j2 +++ b/roles/matrix-coturn/templates/turnserver.conf.j2 @@ -5,7 +5,9 @@ realm=turn.{{ matrix_server_fqn_matrix }} min-port={{ matrix_coturn_turn_udp_min_port }} max-port={{ matrix_coturn_turn_udp_max_port }} -external-ip={{ matrix_coturn_turn_external_ip_address }} +{% for ip in matrix_coturn_turn_external_ip_address %} +external-ip={{ ip }} +{% endfor %} log-file=stdout pidfile=/var/tmp/turnserver.pid From 0364c6c6341ba69f9fa0bd5d94b6339c27ab9b35 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Mon, 11 Apr 2022 09:05:33 +0300 Subject: [PATCH 16/83] Suppress old container cleanup (kill/rm) failures People often report and ask about these "failures". More-so previously, when the `docker kill/rm` output was collected, but it still happens now when people do `systemctl status matrix-something` and notice that it says "FAILURE". Suppressing to avoid further time being wasted on saying "this is expected". --- .../templates/systemd/matrix-backup-borg.service.j2 | 8 ++++---- .../templates/systemd/matrix-bot-go-neb.service.j2 | 8 ++++---- .../templates/systemd/matrix-bot-honoroit.service.j2 | 8 ++++---- .../systemd/matrix-bot-matrix-reminder-bot.service.j2 | 8 ++++---- .../templates/systemd/matrix-bot-mjolnir.service.j2 | 8 ++++---- .../systemd/matrix-appservice-discord.service.j2 | 8 ++++---- .../templates/systemd/matrix-appservice-irc.service.j2 | 8 ++++---- .../templates/systemd/matrix-appservice-slack.service.j2 | 8 ++++---- .../systemd/matrix-appservice-webhooks.service.j2 | 8 ++++---- .../templates/systemd/matrix-beeper-linkedin.service.j2 | 8 ++++---- .../templates/systemd/matrix-mautrix-facebook.service.j2 | 8 ++++---- .../systemd/matrix-mautrix-googlechat.service.j2 | 4 ++-- .../templates/systemd/matrix-mautrix-hangouts.service.j2 | 8 ++++---- .../templates/systemd/matrix-mautrix-instagram.service.j2 | 8 ++++---- .../systemd/matrix-mautrix-signal-daemon.service.j2 | 8 ++++---- .../templates/systemd/matrix-mautrix-signal.service.j2 | 8 ++++---- .../templates/systemd/matrix-mautrix-telegram.service.j2 | 8 ++++---- .../templates/systemd/matrix-mautrix-twitter.service.j2 | 8 ++++---- .../templates/systemd/matrix-mautrix-whatsapp.service.j2 | 8 ++++---- .../templates/systemd/matrix-mx-puppet-discord.service.j2 | 8 ++++---- .../templates/systemd/matrix-mx-puppet-groupme.service.j2 | 8 ++++---- .../systemd/matrix-mx-puppet-instagram.service.j2 | 8 ++++---- .../templates/systemd/matrix-mx-puppet-skype.service.j2 | 8 ++++---- .../templates/systemd/matrix-mx-puppet-slack.service.j2 | 8 ++++---- .../templates/systemd/matrix-mx-puppet-steam.service.j2 | 8 ++++---- .../templates/systemd/matrix-mx-puppet-twitter.service.j2 | 8 ++++---- .../templates/systemd/matrix-client-cinny.service.j2 | 8 ++++---- .../templates/systemd/matrix-client-element.service.j2 | 8 ++++---- .../templates/systemd/matrix-client-hydrogen.service.j2 | 8 ++++---- .../templates/systemd/matrix-corporal.service.j2 | 8 ++++---- .../templates/systemd/matrix-coturn.service.j2 | 8 ++++---- .../templates/dendrite/systemd/matrix-dendrite.service.j2 | 8 ++++---- .../templates/systemd/matrix-dimension.service.j2 | 8 ++++---- .../templates/systemd/matrix-dynamic-dns.service.j2 | 8 ++++---- .../templates/systemd/matrix-email2matrix.service.j2 | 8 ++++---- .../templates/systemd/matrix-grafana.service.j2 | 8 ++++---- .../templates/jicofo/matrix-jitsi-jicofo.service.j2 | 8 ++++---- .../templates/jvb/matrix-jitsi-jvb.service.j2 | 8 ++++---- .../templates/prosody/matrix-jitsi-prosody.service.j2 | 8 ++++---- .../templates/web/matrix-jitsi-web.service.j2 | 8 ++++---- .../templates/systemd/matrix-ma1sd.service.j2 | 8 ++++---- .../templates/systemd/matrix-mailer.service.j2 | 8 ++++---- .../templates/systemd/matrix-nginx-proxy.service.j2 | 8 ++++---- .../templates/systemd/matrix-postgres-backup.service.j2 | 4 ++-- .../templates/systemd/matrix-postgres.service.j2 | 8 ++++---- .../systemd/matrix-prometheus-node-exporter.service.j2 | 8 ++++---- .../matrix-prometheus-postgres-exporter.service.j2 | 8 ++++---- .../templates/systemd/matrix-prometheus.service.j2 | 8 ++++---- .../templates/systemd/matrix-registration.service.j2 | 8 ++++---- .../templates/systemd/matrix-sygnal.service.j2 | 8 ++++---- .../templates/systemd/matrix-synapse-admin.service.j2 | 8 ++++---- .../templates/synapse/systemd/matrix-synapse.service.j2 | 8 ++++---- 52 files changed, 204 insertions(+), 204 deletions(-) diff --git a/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.service.j2 b/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.service.j2 index 977673ee3..76217250e 100644 --- a/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.service.j2 +++ b/roles/matrix-backup-borg/templates/systemd/matrix-backup-borg.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=oneshot Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-backup-borg 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-backup-borg 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-backup-borg 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-backup-borg 2>/dev/null || true' ExecStartPre=-{{ matrix_host_command_docker }} run --rm --name matrix-backup-borg \ --log-driver=none \ --cap-drop=ALL \ @@ -50,8 +50,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-backup-borg \ {% endfor %} {{ matrix_backup_borg_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-backup-borg 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-backup-borg 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-backup-borg 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-backup-borg 2>/dev/null || true' SyslogIdentifier=matrix-backup-borg [Install] diff --git a/roles/matrix-bot-go-neb/templates/systemd/matrix-bot-go-neb.service.j2 b/roles/matrix-bot-go-neb/templates/systemd/matrix-bot-go-neb.service.j2 index eabf11372..83eb3c7df 100644 --- a/roles/matrix-bot-go-neb/templates/systemd/matrix-bot-go-neb.service.j2 +++ b/roles/matrix-bot-go-neb/templates/systemd/matrix-bot-go-neb.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-go-neb 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-go-neb 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-go-neb 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-go-neb 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-go-neb \ --log-driver=none \ @@ -39,8 +39,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-go-neb \ {{ matrix_bot_go_neb_docker_image }} \ -c "go-neb /config/config.yaml" -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-go-neb 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-go-neb 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-go-neb 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-go-neb 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-bot-go-neb diff --git a/roles/matrix-bot-honoroit/templates/systemd/matrix-bot-honoroit.service.j2 b/roles/matrix-bot-honoroit/templates/systemd/matrix-bot-honoroit.service.j2 index a2ba1a984..2bb141099 100644 --- a/roles/matrix-bot-honoroit/templates/systemd/matrix-bot-honoroit.service.j2 +++ b/roles/matrix-bot-honoroit/templates/systemd/matrix-bot-honoroit.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-honoroit 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-honoroit 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-honoroit 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-honoroit 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-honoroit \ --log-driver=none \ @@ -29,8 +29,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-honoroit \ {% endfor %} {{ matrix_bot_honoroit_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-honoroit 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-honoroit 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-honoroit 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-honoroit 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-bot-honoroit diff --git a/roles/matrix-bot-matrix-reminder-bot/templates/systemd/matrix-bot-matrix-reminder-bot.service.j2 b/roles/matrix-bot-matrix-reminder-bot/templates/systemd/matrix-bot-matrix-reminder-bot.service.j2 index b1fe3c325..a9cf8bb84 100644 --- a/roles/matrix-bot-matrix-reminder-bot/templates/systemd/matrix-bot-matrix-reminder-bot.service.j2 +++ b/roles/matrix-bot-matrix-reminder-bot/templates/systemd/matrix-bot-matrix-reminder-bot.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-reminder-bot 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-matrix-reminder-bot 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-reminder-bot 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-matrix-reminder-bot 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-matrix-reminder-bot \ --log-driver=none \ @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-matrix-rem {{ matrix_bot_matrix_reminder_bot_docker_image }} \ -c "matrix-reminder-bot /config/config.yaml" -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-reminder-bot 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-matrix-reminder-bot 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-reminder-bot 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-matrix-reminder-bot 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-bot-matrix-reminder-bot diff --git a/roles/matrix-bot-mjolnir/templates/systemd/matrix-bot-mjolnir.service.j2 b/roles/matrix-bot-mjolnir/templates/systemd/matrix-bot-mjolnir.service.j2 index 0b018f25b..7ea6be378 100644 --- a/roles/matrix-bot-mjolnir/templates/systemd/matrix-bot-mjolnir.service.j2 +++ b/roles/matrix-bot-mjolnir/templates/systemd/matrix-bot-mjolnir.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-mjolnir 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-mjolnir 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-mjolnir 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-mjolnir 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-mjolnir \ {% endfor %} {{ matrix_bot_mjolnir_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-mjolnir 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-mjolnir 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-mjolnir 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-mjolnir 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-bot-mjolnir diff --git a/roles/matrix-bridge-appservice-discord/templates/systemd/matrix-appservice-discord.service.j2 b/roles/matrix-bridge-appservice-discord/templates/systemd/matrix-appservice-discord.service.j2 index 84dee8015..0a527c0cd 100644 --- a/roles/matrix-bridge-appservice-discord/templates/systemd/matrix-appservice-discord.service.j2 +++ b/roles/matrix-bridge-appservice-discord/templates/systemd/matrix-appservice-discord.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-discord 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-discord 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-discord 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-discord 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -35,8 +35,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-appservice-dis {{ matrix_appservice_discord_docker_image }} \ node /build/src/discordas.js -p 9005 -c /cfg/config.yaml -f /cfg/registration.yaml -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-discord 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-discord 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-discord 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-discord 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-appservice-discord diff --git a/roles/matrix-bridge-appservice-irc/templates/systemd/matrix-appservice-irc.service.j2 b/roles/matrix-bridge-appservice-irc/templates/systemd/matrix-appservice-irc.service.j2 index 8650bd8db..4bbda18eb 100644 --- a/roles/matrix-bridge-appservice-irc/templates/systemd/matrix-appservice-irc.service.j2 +++ b/roles/matrix-bridge-appservice-irc/templates/systemd/matrix-appservice-irc.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-irc 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-irc 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-irc 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-irc 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -36,8 +36,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-appservice-irc {{ matrix_appservice_irc_docker_image }} \ -c 'node app.js -c /config/config.yaml -f /config/registration.yaml -p 9999' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-irc 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-irc 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-irc 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-irc 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-appservice-irc diff --git a/roles/matrix-bridge-appservice-slack/templates/systemd/matrix-appservice-slack.service.j2 b/roles/matrix-bridge-appservice-slack/templates/systemd/matrix-appservice-slack.service.j2 index 21ba27ef5..017f352f3 100644 --- a/roles/matrix-bridge-appservice-slack/templates/systemd/matrix-appservice-slack.service.j2 +++ b/roles/matrix-bridge-appservice-slack/templates/systemd/matrix-appservice-slack.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-slack 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-slack 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-slack 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-slack 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -35,8 +35,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-appservice-sla {{ matrix_appservice_slack_docker_image }} \ node app.js -p {{matrix_appservice_slack_matrix_port}} -c /config/config.yaml -f /config/slack-registration.yaml -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-slack 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-slack 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-slack 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-slack 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-appservice-slack diff --git a/roles/matrix-bridge-appservice-webhooks/templates/systemd/matrix-appservice-webhooks.service.j2 b/roles/matrix-bridge-appservice-webhooks/templates/systemd/matrix-appservice-webhooks.service.j2 index f27111b3c..556467b4b 100644 --- a/roles/matrix-bridge-appservice-webhooks/templates/systemd/matrix-appservice-webhooks.service.j2 +++ b/roles/matrix-bridge-appservice-webhooks/templates/systemd/matrix-appservice-webhooks.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-webhooks 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-webhooks 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-webhooks 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-webhooks 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -35,8 +35,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-appservice-web {{ matrix_appservice_webhooks_docker_image }} \ node index.js -p {{ matrix_appservice_webhooks_matrix_port }} -c /config/config.yaml -f /config/webhooks-registration.yaml -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-webhooks 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-webhooks 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-webhooks 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-webhooks 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-appservice-webhooks diff --git a/roles/matrix-bridge-beeper-linkedin/templates/systemd/matrix-beeper-linkedin.service.j2 b/roles/matrix-bridge-beeper-linkedin/templates/systemd/matrix-beeper-linkedin.service.j2 index 4498b4f02..37b4f67db 100644 --- a/roles/matrix-bridge-beeper-linkedin/templates/systemd/matrix-beeper-linkedin.service.j2 +++ b/roles/matrix-bridge-beeper-linkedin/templates/systemd/matrix-beeper-linkedin.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-beeper-linkedin 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-beeper-linkedin 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-beeper-linkedin 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-beeper-linkedin 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-beeper-linkedi {{ matrix_beeper_linkedin_docker_image }} \ python3 -m linkedin_matrix -c /data/config.yaml -r /data/registration.yaml -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-beeper-linkedin 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-beeper-linkedin 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-beeper-linkedin 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-beeper-linkedin 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-beeper-linkedin diff --git a/roles/matrix-bridge-mautrix-facebook/templates/systemd/matrix-mautrix-facebook.service.j2 b/roles/matrix-bridge-mautrix-facebook/templates/systemd/matrix-mautrix-facebook.service.j2 index 2899dd0d6..2103dd052 100644 --- a/roles/matrix-bridge-mautrix-facebook/templates/systemd/matrix-mautrix-facebook.service.j2 +++ b/roles/matrix-bridge-mautrix-facebook/templates/systemd/matrix-mautrix-facebook.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-facebook 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-facebook 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-facebook 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-facebook 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -35,8 +35,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-facebo {{ matrix_mautrix_facebook_docker_image }} \ python3 -m mautrix_facebook -c /config/config.yaml --no-update -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-facebook 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-facebook 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-facebook 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-facebook 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mautrix-facebook diff --git a/roles/matrix-bridge-mautrix-googlechat/templates/systemd/matrix-mautrix-googlechat.service.j2 b/roles/matrix-bridge-mautrix-googlechat/templates/systemd/matrix-mautrix-googlechat.service.j2 index c56473bed..930b58c2a 100644 --- a/roles/matrix-bridge-mautrix-googlechat/templates/systemd/matrix-mautrix-googlechat.service.j2 +++ b/roles/matrix-bridge-mautrix-googlechat/templates/systemd/matrix-mautrix-googlechat.service.j2 @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-google {{ matrix_mautrix_googlechat_docker_image }} \ python3 -m mautrix_googlechat -c /config/config.yaml --no-update -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-googlechat 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-googlechat 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-googlechat 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-googlechat 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mautrix-googlechat diff --git a/roles/matrix-bridge-mautrix-hangouts/templates/systemd/matrix-mautrix-hangouts.service.j2 b/roles/matrix-bridge-mautrix-hangouts/templates/systemd/matrix-mautrix-hangouts.service.j2 index 60f0e055f..10402a517 100644 --- a/roles/matrix-bridge-mautrix-hangouts/templates/systemd/matrix-mautrix-hangouts.service.j2 +++ b/roles/matrix-bridge-mautrix-hangouts/templates/systemd/matrix-mautrix-hangouts.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-hangouts matrix-mautrix-hangouts-db 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-hangouts matrix-mautrix-hangouts-db 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-hangouts matrix-mautrix-hangouts-db 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-hangouts matrix-mautrix-hangouts-db 2>/dev/null || true' ExecStartPre={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-hangouts-db \ --log-driver=none \ --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ @@ -44,8 +44,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-hangou {{ matrix_mautrix_hangouts_docker_image }} \ python3 -m mautrix_hangouts -c /config/config.yaml --no-update -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-hangouts 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-hangouts 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-hangouts 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-hangouts 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mautrix-hangouts diff --git a/roles/matrix-bridge-mautrix-instagram/templates/systemd/matrix-mautrix-instagram.service.j2 b/roles/matrix-bridge-mautrix-instagram/templates/systemd/matrix-mautrix-instagram.service.j2 index 33a5bab3b..d2a6aece8 100644 --- a/roles/matrix-bridge-mautrix-instagram/templates/systemd/matrix-mautrix-instagram.service.j2 +++ b/roles/matrix-bridge-mautrix-instagram/templates/systemd/matrix-mautrix-instagram.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-instagram 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-instagram 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-instagram 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-instagram 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-instag {{ matrix_mautrix_instagram_docker_image }} \ python3 -m mautrix_instagram -c /config/config.yaml --no-update -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-instagram 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-instagram 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-instagram 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-instagram 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mautrix-instagram diff --git a/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal-daemon.service.j2 b/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal-daemon.service.j2 index 6f128da39..0ee05d7d7 100644 --- a/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal-daemon.service.j2 +++ b/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal-daemon.service.j2 @@ -15,8 +15,8 @@ Wants={{ service }} Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal-daemon 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal-daemon 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal-daemon 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal-daemon 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -30,8 +30,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-signal -v {{ matrix_mautrix_signal_daemon_path }}:/signald:z \ {{ matrix_mautrix_signal_daemon_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal-daemon 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal-daemon 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal-daemon 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal-daemon 2>/dev/null || true' Restart=always RestartSec=30 diff --git a/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal.service.j2 b/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal.service.j2 index a65895edf..d1ef85f33 100644 --- a/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal.service.j2 +++ b/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal.service.j2 @@ -14,8 +14,8 @@ Wants={{ service }} [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -38,8 +38,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-signal {{ matrix_mautrix_signal_docker_image }} \ python3 -m mautrix_signal -c /config/config.yaml --no-update -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal 2>/dev/null || true' Restart=always RestartSec=30 diff --git a/roles/matrix-bridge-mautrix-telegram/templates/systemd/matrix-mautrix-telegram.service.j2 b/roles/matrix-bridge-mautrix-telegram/templates/systemd/matrix-mautrix-telegram.service.j2 index 459a0fec5..8b21ee2b4 100644 --- a/roles/matrix-bridge-mautrix-telegram/templates/systemd/matrix-mautrix-telegram.service.j2 +++ b/roles/matrix-bridge-mautrix-telegram/templates/systemd/matrix-mautrix-telegram.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-telegram 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-telegram 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-telegram 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-telegram 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -35,8 +35,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-telegr {{ matrix_mautrix_telegram_docker_image }} \ python3 -m mautrix_telegram -c /config/config.yaml --no-update -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-telegram 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-telegram 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-telegram 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-telegram 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mautrix-telegram diff --git a/roles/matrix-bridge-mautrix-twitter/templates/systemd/matrix-mautrix-twitter.service.j2 b/roles/matrix-bridge-mautrix-twitter/templates/systemd/matrix-mautrix-twitter.service.j2 index 73bdbc866..0ce9a1239 100644 --- a/roles/matrix-bridge-mautrix-twitter/templates/systemd/matrix-mautrix-twitter.service.j2 +++ b/roles/matrix-bridge-mautrix-twitter/templates/systemd/matrix-mautrix-twitter.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-twitter 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-twitter 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-twitter 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-twitter 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-twitte {{ matrix_mautrix_twitter_docker_image }} \ python3 -m mautrix_twitter -c /config/config.yaml --no-update -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-twitter 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-twitter 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-twitter 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-twitter 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mautrix-twitter diff --git a/roles/matrix-bridge-mautrix-whatsapp/templates/systemd/matrix-mautrix-whatsapp.service.j2 b/roles/matrix-bridge-mautrix-whatsapp/templates/systemd/matrix-mautrix-whatsapp.service.j2 index 4a492492b..ae44d3420 100644 --- a/roles/matrix-bridge-mautrix-whatsapp/templates/systemd/matrix-mautrix-whatsapp.service.j2 +++ b/roles/matrix-bridge-mautrix-whatsapp/templates/systemd/matrix-mautrix-whatsapp.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-whatsapp 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-whatsapp 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-whatsapp 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-whatsapp 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-whatsa {{ matrix_mautrix_whatsapp_docker_image }} \ /usr/bin/mautrix-whatsapp -c /config/config.yaml -r /config/registration.yaml -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-whatsapp 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-whatsapp 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-whatsapp 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-whatsapp 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mautrix-whatsapp diff --git a/roles/matrix-bridge-mx-puppet-discord/templates/systemd/matrix-mx-puppet-discord.service.j2 b/roles/matrix-bridge-mx-puppet-discord/templates/systemd/matrix-mx-puppet-discord.service.j2 index 6ffb87cd3..7a4c4a383 100644 --- a/roles/matrix-bridge-mx-puppet-discord/templates/systemd/matrix-mx-puppet-discord.service.j2 +++ b/roles/matrix-bridge-mx-puppet-discord/templates/systemd/matrix-mx-puppet-discord.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-discord 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-discord 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-discord 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-discord 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-disc {% endfor %} {{ matrix_mx_puppet_discord_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-discord 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-discord 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-discord 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-discord 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mx-puppet-discord diff --git a/roles/matrix-bridge-mx-puppet-groupme/templates/systemd/matrix-mx-puppet-groupme.service.j2 b/roles/matrix-bridge-mx-puppet-groupme/templates/systemd/matrix-mx-puppet-groupme.service.j2 index dabafd180..afb46ecb7 100644 --- a/roles/matrix-bridge-mx-puppet-groupme/templates/systemd/matrix-mx-puppet-groupme.service.j2 +++ b/roles/matrix-bridge-mx-puppet-groupme/templates/systemd/matrix-mx-puppet-groupme.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-groupme 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-groupme 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-groupme 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-groupme 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-grou {% endfor %} {{ matrix_mx_puppet_groupme_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-groupme 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-groupme 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-groupme 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-groupme 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mx-puppet-groupme diff --git a/roles/matrix-bridge-mx-puppet-instagram/templates/systemd/matrix-mx-puppet-instagram.service.j2 b/roles/matrix-bridge-mx-puppet-instagram/templates/systemd/matrix-mx-puppet-instagram.service.j2 index 965bb41c2..262518fc2 100644 --- a/roles/matrix-bridge-mx-puppet-instagram/templates/systemd/matrix-mx-puppet-instagram.service.j2 +++ b/roles/matrix-bridge-mx-puppet-instagram/templates/systemd/matrix-mx-puppet-instagram.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-instagram 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-instagram 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-instagram 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-instagram 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-inst {% endfor %} {{ matrix_mx_puppet_instagram_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-instagram 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-instagram 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-instagram 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-instagram 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mx-puppet-instagram diff --git a/roles/matrix-bridge-mx-puppet-skype/templates/systemd/matrix-mx-puppet-skype.service.j2 b/roles/matrix-bridge-mx-puppet-skype/templates/systemd/matrix-mx-puppet-skype.service.j2 index 9a7986e4d..ec06485a8 100644 --- a/roles/matrix-bridge-mx-puppet-skype/templates/systemd/matrix-mx-puppet-skype.service.j2 +++ b/roles/matrix-bridge-mx-puppet-skype/templates/systemd/matrix-mx-puppet-skype.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-skype 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-skype 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-skype 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-skype 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-skyp {% endfor %} {{ matrix_mx_puppet_skype_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-skype 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-skype 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-skype 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-skype 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mx-puppet-skype diff --git a/roles/matrix-bridge-mx-puppet-slack/templates/systemd/matrix-mx-puppet-slack.service.j2 b/roles/matrix-bridge-mx-puppet-slack/templates/systemd/matrix-mx-puppet-slack.service.j2 index 973771b3e..118d03696 100644 --- a/roles/matrix-bridge-mx-puppet-slack/templates/systemd/matrix-mx-puppet-slack.service.j2 +++ b/roles/matrix-bridge-mx-puppet-slack/templates/systemd/matrix-mx-puppet-slack.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-slack 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-slack 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-slack 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-slack 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -36,8 +36,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-slac {% endfor %} {{ matrix_mx_puppet_slack_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-slack 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-slack 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-slack 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-slack 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mx-puppet-slack diff --git a/roles/matrix-bridge-mx-puppet-steam/templates/systemd/matrix-mx-puppet-steam.service.j2 b/roles/matrix-bridge-mx-puppet-steam/templates/systemd/matrix-mx-puppet-steam.service.j2 index 0772872b1..f1079e3f7 100644 --- a/roles/matrix-bridge-mx-puppet-steam/templates/systemd/matrix-mx-puppet-steam.service.j2 +++ b/roles/matrix-bridge-mx-puppet-steam/templates/systemd/matrix-mx-puppet-steam.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-steam 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-steam 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-steam 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-steam 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-stea {% endfor %} {{ matrix_mx_puppet_steam_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-steam 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-steam 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-steam 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-steam 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mx-puppet-steam diff --git a/roles/matrix-bridge-mx-puppet-twitter/templates/systemd/matrix-mx-puppet-twitter.service.j2 b/roles/matrix-bridge-mx-puppet-twitter/templates/systemd/matrix-mx-puppet-twitter.service.j2 index 7e1b1c327..5d7cfca61 100644 --- a/roles/matrix-bridge-mx-puppet-twitter/templates/systemd/matrix-mx-puppet-twitter.service.j2 +++ b/roles/matrix-bridge-mx-puppet-twitter/templates/systemd/matrix-mx-puppet-twitter.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-twitter 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-twitter 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-twitter 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-twitter 2>/dev/null || true' # Intentional delay, so that the homeserver (we likely depend on) can manage to start. ExecStartPre={{ matrix_host_command_sleep }} 5 @@ -36,8 +36,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-twit {% endfor %} {{ matrix_mx_puppet_twitter_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-twitter 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-twitter 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-twitter 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-twitter 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mx-puppet-twitter diff --git a/roles/matrix-client-cinny/templates/systemd/matrix-client-cinny.service.j2 b/roles/matrix-client-cinny/templates/systemd/matrix-client-cinny.service.j2 index f4ebd6a04..3f15ac195 100644 --- a/roles/matrix-client-cinny/templates/systemd/matrix-client-cinny.service.j2 +++ b/roles/matrix-client-cinny/templates/systemd/matrix-client-cinny.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-cinny 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-cinny 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-cinny 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-cinny 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-client-cinny \ --log-driver=none \ @@ -30,8 +30,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-client-cinny \ {% endfor %} {{ matrix_client_cinny_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-cinny 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-cinny 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-cinny 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-cinny 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-client-cinny diff --git a/roles/matrix-client-element/templates/systemd/matrix-client-element.service.j2 b/roles/matrix-client-element/templates/systemd/matrix-client-element.service.j2 index fe2a3a865..8d3dec570 100644 --- a/roles/matrix-client-element/templates/systemd/matrix-client-element.service.j2 +++ b/roles/matrix-client-element/templates/systemd/matrix-client-element.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-element 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-element 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-element 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-element 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-client-element \ --log-driver=none \ @@ -35,8 +35,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-client-element {% endfor %} {{ matrix_client_element_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-element 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-element 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-element 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-element 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-client-element diff --git a/roles/matrix-client-hydrogen/templates/systemd/matrix-client-hydrogen.service.j2 b/roles/matrix-client-hydrogen/templates/systemd/matrix-client-hydrogen.service.j2 index c85aeb978..0196d35b4 100644 --- a/roles/matrix-client-hydrogen/templates/systemd/matrix-client-hydrogen.service.j2 +++ b/roles/matrix-client-hydrogen/templates/systemd/matrix-client-hydrogen.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-hydrogen 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-hydrogen 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-hydrogen 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-hydrogen 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-client-hydrogen \ --log-driver=none \ @@ -29,8 +29,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-client-hydroge {% endfor %} {{ matrix_client_hydrogen_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-hydrogen 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-hydrogen 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-hydrogen 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-hydrogen 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-client-hydrogen diff --git a/roles/matrix-corporal/templates/systemd/matrix-corporal.service.j2 b/roles/matrix-corporal/templates/systemd/matrix-corporal.service.j2 index 262e2e77c..d5661b5ab 100644 --- a/roles/matrix-corporal/templates/systemd/matrix-corporal.service.j2 +++ b/roles/matrix-corporal/templates/systemd/matrix-corporal.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-corporal 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-corporal 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-corporal 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-corporal 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-corporal \ --log-driver=none \ @@ -34,8 +34,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-corporal \ {{ matrix_corporal_docker_image }} \ /matrix-corporal -config=/etc/matrix-corporal/config.json -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-corporal 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-corporal 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-corporal 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-corporal 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-corporal diff --git a/roles/matrix-coturn/templates/systemd/matrix-coturn.service.j2 b/roles/matrix-coturn/templates/systemd/matrix-coturn.service.j2 index a39030af1..54bd015e7 100644 --- a/roles/matrix-coturn/templates/systemd/matrix-coturn.service.j2 +++ b/roles/matrix-coturn/templates/systemd/matrix-coturn.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-coturn 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-coturn 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-coturn 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-coturn 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-coturn \ --log-driver=none \ @@ -43,8 +43,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-coturn \ {{ matrix_coturn_docker_image }} \ -c /turnserver.conf -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-coturn 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-coturn 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-coturn 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-coturn 2>/dev/null || true' # This only reloads certificates (not other configuration). # See: https://github.com/coturn/coturn/pull/236 diff --git a/roles/matrix-dendrite/templates/dendrite/systemd/matrix-dendrite.service.j2 b/roles/matrix-dendrite/templates/dendrite/systemd/matrix-dendrite.service.j2 index e1c42cbcb..0457917a9 100644 --- a/roles/matrix-dendrite/templates/dendrite/systemd/matrix-dendrite.service.j2 +++ b/roles/matrix-dendrite/templates/dendrite/systemd/matrix-dendrite.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dendrite 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dendrite 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dendrite 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dendrite 2>/dev/null || true' {% if 'matrix-postgres.service' in matrix_dendrite_systemd_required_services_list %} # Dendrite is too quick to start in relation to its matrix-postgres dependency. @@ -54,8 +54,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-dendrite \ {% endif %} {{ matrix_dendrite_process_extra_arguments|join(' ') }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dendrite 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dendrite 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dendrite 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dendrite 2>/dev/null || true' ExecReload={{ matrix_host_command_docker }} exec matrix-dendrite /bin/sh -c 'kill -HUP 1' Restart=always RestartSec=30 diff --git a/roles/matrix-dimension/templates/systemd/matrix-dimension.service.j2 b/roles/matrix-dimension/templates/systemd/matrix-dimension.service.j2 index e27a55587..e514a74a1 100644 --- a/roles/matrix-dimension/templates/systemd/matrix-dimension.service.j2 +++ b/roles/matrix-dimension/templates/systemd/matrix-dimension.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dimension 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dimension 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dimension 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dimension 2>/dev/null || true' # Fixup database ownership if it got changed somehow (during a server migration, etc.) {% if matrix_dimension_database_engine == 'sqlite' %} @@ -38,8 +38,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-dimension \ {% endfor %} {{ matrix_dimension_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dimension 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dimension 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dimension 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dimension 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-dimension diff --git a/roles/matrix-dynamic-dns/templates/systemd/matrix-dynamic-dns.service.j2 b/roles/matrix-dynamic-dns/templates/systemd/matrix-dynamic-dns.service.j2 index dfdd2f72c..6f2ff1011 100644 --- a/roles/matrix-dynamic-dns/templates/systemd/matrix-dynamic-dns.service.j2 +++ b/roles/matrix-dynamic-dns/templates/systemd/matrix-dynamic-dns.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dynamic-dns 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dynamic-dns 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dynamic-dns 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dynamic-dns 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-dynamic-dns \ --log-driver=none \ --network={{ matrix_docker_network }} \ @@ -26,8 +26,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-dynamic-dns \ {% endfor %} {{ matrix_dynamic_dns_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dynamic-dns 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dynamic-dns 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dynamic-dns 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dynamic-dns 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-dynamic-dns diff --git a/roles/matrix-email2matrix/templates/systemd/matrix-email2matrix.service.j2 b/roles/matrix-email2matrix/templates/systemd/matrix-email2matrix.service.j2 index c92267682..47c151172 100644 --- a/roles/matrix-email2matrix/templates/systemd/matrix-email2matrix.service.j2 +++ b/roles/matrix-email2matrix/templates/systemd/matrix-email2matrix.service.j2 @@ -8,8 +8,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-email2matrix 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-email2matrix 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-email2matrix 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-email2matrix 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-email2matrix \ --log-driver=none \ @@ -24,8 +24,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-email2matrix \ {% endfor %} {{ matrix_email2matrix_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-email2matrix 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-email2matrix 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-email2matrix 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-email2matrix 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-email2matrix diff --git a/roles/matrix-grafana/templates/systemd/matrix-grafana.service.j2 b/roles/matrix-grafana/templates/systemd/matrix-grafana.service.j2 index a4f81e357..e0f580765 100644 --- a/roles/matrix-grafana/templates/systemd/matrix-grafana.service.j2 +++ b/roles/matrix-grafana/templates/systemd/matrix-grafana.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-grafana 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-grafana 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-grafana 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-grafana 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-grafana \ @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-grafana \ {% endfor %} {{ matrix_grafana_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-grafana 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-grafana 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-grafana 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-grafana 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-grafana diff --git a/roles/matrix-jitsi/templates/jicofo/matrix-jitsi-jicofo.service.j2 b/roles/matrix-jitsi/templates/jicofo/matrix-jitsi-jicofo.service.j2 index 6ecafaa03..694fdc7f2 100644 --- a/roles/matrix-jitsi/templates/jicofo/matrix-jitsi-jicofo.service.j2 +++ b/roles/matrix-jitsi/templates/jicofo/matrix-jitsi-jicofo.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jicofo 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jicofo 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jicofo 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jicofo 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-jicofo \ --log-driver=none \ @@ -23,8 +23,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-jicofo \ {% endfor %} {{ matrix_jitsi_jicofo_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jicofo 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jicofo 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jicofo 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jicofo 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-jitsi-jicofo diff --git a/roles/matrix-jitsi/templates/jvb/matrix-jitsi-jvb.service.j2 b/roles/matrix-jitsi/templates/jvb/matrix-jitsi-jvb.service.j2 index 2785795d7..f0b141fc0 100644 --- a/roles/matrix-jitsi/templates/jvb/matrix-jitsi-jvb.service.j2 +++ b/roles/matrix-jitsi/templates/jvb/matrix-jitsi-jvb.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jvb 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jvb 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jvb 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jvb 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-jvb \ --log-driver=none \ @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-jvb \ {% endfor %} {{ matrix_jitsi_jvb_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jvb 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jvb 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jvb 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jvb 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-jitsi-jvb diff --git a/roles/matrix-jitsi/templates/prosody/matrix-jitsi-prosody.service.j2 b/roles/matrix-jitsi/templates/prosody/matrix-jitsi-prosody.service.j2 index 5a4a81e5d..0c3a3932d 100644 --- a/roles/matrix-jitsi/templates/prosody/matrix-jitsi-prosody.service.j2 +++ b/roles/matrix-jitsi/templates/prosody/matrix-jitsi-prosody.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-prosody 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-prosody 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-prosody 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-prosody 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-prosody \ --log-driver=none \ @@ -28,8 +28,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-prosody {% endfor %} {{ matrix_jitsi_prosody_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-prosody 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-prosody 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-prosody 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-prosody 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-jitsi-prosody diff --git a/roles/matrix-jitsi/templates/web/matrix-jitsi-web.service.j2 b/roles/matrix-jitsi/templates/web/matrix-jitsi-web.service.j2 index 35bfca676..8f29bfa82 100644 --- a/roles/matrix-jitsi/templates/web/matrix-jitsi-web.service.j2 +++ b/roles/matrix-jitsi/templates/web/matrix-jitsi-web.service.j2 @@ -10,8 +10,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-web 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-web 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-web 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-web 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-web \ --log-driver=none \ @@ -29,8 +29,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-web \ {% endfor %} {{ matrix_jitsi_web_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-web 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-web 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-web 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-web 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-jitsi-web diff --git a/roles/matrix-ma1sd/templates/systemd/matrix-ma1sd.service.j2 b/roles/matrix-ma1sd/templates/systemd/matrix-ma1sd.service.j2 index 8e5cc6dd1..427f6c9ff 100644 --- a/roles/matrix-ma1sd/templates/systemd/matrix-ma1sd.service.j2 +++ b/roles/matrix-ma1sd/templates/systemd/matrix-ma1sd.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-ma1sd 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-ma1sd 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-ma1sd 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-ma1sd 2>/dev/null || true' # ma1sd writes an SQLite shared library (libsqlitejdbc.so) to /tmp and executes it from there, # so /tmp needs to be mounted with an exec option. @@ -38,8 +38,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-ma1sd \ {% endfor %} {{ matrix_ma1sd_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-ma1sd 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-ma1sd 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-ma1sd 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-ma1sd 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-ma1sd diff --git a/roles/matrix-mailer/templates/systemd/matrix-mailer.service.j2 b/roles/matrix-mailer/templates/systemd/matrix-mailer.service.j2 index bf5a2e42a..83cd298ef 100644 --- a/roles/matrix-mailer/templates/systemd/matrix-mailer.service.j2 +++ b/roles/matrix-mailer/templates/systemd/matrix-mailer.service.j2 @@ -8,8 +8,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mailer 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mailer 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mailer 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mailer 2>/dev/null || true' # --hostname gives us a friendlier hostname than the default. # The real hostname is passed via a `HOSTNAME` environment variable though. @@ -27,8 +27,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mailer \ {% endfor %} {{ matrix_mailer_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mailer 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mailer 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mailer 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mailer 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-mailer diff --git a/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 b/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 index 1143efd4c..172a83bc9 100755 --- a/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 +++ b/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-nginx-proxy 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-nginx-proxy 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-nginx-proxy 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-nginx-proxy 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-nginx-proxy \ --log-driver=none \ @@ -51,8 +51,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-nginx-proxy \ ExecStartPost={{ matrix_host_command_sh }} -c 'attempt=0; while [ $attempt -le 29 ]; do attempt=$(( $attempt + 1 )); if [ "`docker inspect -f {{ '{{.State.Running}}' }} matrix-nginx-proxy 2> /dev/null`" = "true" ]; then break; fi; sleep 1; done; {{ matrix_host_command_docker }} network connect {{ network }} matrix-nginx-proxy' {% endfor %} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-nginx-proxy 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-nginx-proxy 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-nginx-proxy 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-nginx-proxy 2>/dev/null || true' ExecReload={{ matrix_host_command_docker }} exec matrix-nginx-proxy /usr/sbin/nginx -s reload Restart=always RestartSec=30 diff --git a/roles/matrix-postgres-backup/templates/systemd/matrix-postgres-backup.service.j2 b/roles/matrix-postgres-backup/templates/systemd/matrix-postgres-backup.service.j2 index 97c9ae7f9..4ecf3745e 100644 --- a/roles/matrix-postgres-backup/templates/systemd/matrix-postgres-backup.service.j2 +++ b/roles/matrix-postgres-backup/templates/systemd/matrix-postgres-backup.service.j2 @@ -9,7 +9,7 @@ DefaultDependencies=no Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" ExecStartPre=-{{ matrix_host_command_docker }} stop matrix-postgres-backup -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres-backup 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres-backup 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-postgres-backup \ --log-driver=none \ @@ -22,7 +22,7 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-postgres-backu {{ matrix_postgres_backup_docker_image_to_use }} ExecStop=-{{ matrix_host_command_docker }} stop matrix-postgres-backup -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres-backup 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres-backup 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-postgres-backup diff --git a/roles/matrix-postgres/templates/systemd/matrix-postgres.service.j2 b/roles/matrix-postgres/templates/systemd/matrix-postgres.service.j2 index d62a689a9..b30c5ef21 100644 --- a/roles/matrix-postgres/templates/systemd/matrix-postgres.service.j2 +++ b/roles/matrix-postgres/templates/systemd/matrix-postgres.service.j2 @@ -8,8 +8,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-postgres 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-postgres 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres 2>/dev/null || true' # We need /dev/shm to be larger than the default to allow VACUUM to work. # See: @@ -36,8 +36,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-postgres \ {{ matrix_postgres_docker_image_to_use }} \ postgres {{ matrix_postgres_process_extra_arguments|join(' ') }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-postgres 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-postgres 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-postgres diff --git a/roles/matrix-prometheus-node-exporter/templates/systemd/matrix-prometheus-node-exporter.service.j2 b/roles/matrix-prometheus-node-exporter/templates/systemd/matrix-prometheus-node-exporter.service.j2 index 210a0d97a..e38b42e31 100644 --- a/roles/matrix-prometheus-node-exporter/templates/systemd/matrix-prometheus-node-exporter.service.j2 +++ b/roles/matrix-prometheus-node-exporter/templates/systemd/matrix-prometheus-node-exporter.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-node-exporter 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-node-exporter 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-node-exporter 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-node-exporter 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus-node-exporter \ @@ -34,8 +34,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus-nod {{ matrix_prometheus_node_exporter_docker_image }} \ --path.rootfs=/host -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-node-exporter 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-node-exporter 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-node-exporter 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-node-exporter 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-prometheus-node-exporter diff --git a/roles/matrix-prometheus-postgres-exporter/templates/systemd/matrix-prometheus-postgres-exporter.service.j2 b/roles/matrix-prometheus-postgres-exporter/templates/systemd/matrix-prometheus-postgres-exporter.service.j2 index 993ebac49..ff8c2ce45 100644 --- a/roles/matrix-prometheus-postgres-exporter/templates/systemd/matrix-prometheus-postgres-exporter.service.j2 +++ b/roles/matrix-prometheus-postgres-exporter/templates/systemd/matrix-prometheus-postgres-exporter.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-postgres-exporter 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-postgres-exporter 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-postgres-exporter 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-postgres-exporter 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus-postgres-exporter \ @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus-pos --pid=host \ {{ matrix_prometheus_postgres_exporter_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-postgres-exporter 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-postgres-exporter 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-postgres-exporter 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-postgres-exporter 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-prometheus-postgres-exporter diff --git a/roles/matrix-prometheus/templates/systemd/matrix-prometheus.service.j2 b/roles/matrix-prometheus/templates/systemd/matrix-prometheus.service.j2 index ad75d664a..8de57201c 100644 --- a/roles/matrix-prometheus/templates/systemd/matrix-prometheus.service.j2 +++ b/roles/matrix-prometheus/templates/systemd/matrix-prometheus.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus \ @@ -33,8 +33,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus \ {% endfor %} {{ matrix_prometheus_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-prometheus diff --git a/roles/matrix-registration/templates/systemd/matrix-registration.service.j2 b/roles/matrix-registration/templates/systemd/matrix-registration.service.j2 index e73e3e5fc..8acbd3a57 100644 --- a/roles/matrix-registration/templates/systemd/matrix-registration.service.j2 +++ b/roles/matrix-registration/templates/systemd/matrix-registration.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-registration 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-registration 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-registration 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-registration 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-registration \ --log-driver=none \ @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-registration \ {{ matrix_registration_docker_image }} \ serve -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-registration 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-registration 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-registration 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-registration 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-registration diff --git a/roles/matrix-sygnal/templates/systemd/matrix-sygnal.service.j2 b/roles/matrix-sygnal/templates/systemd/matrix-sygnal.service.j2 index 019ab40c0..ae7e889db 100644 --- a/roles/matrix-sygnal/templates/systemd/matrix-sygnal.service.j2 +++ b/roles/matrix-sygnal/templates/systemd/matrix-sygnal.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-sygnal 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-sygnal 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-sygnal 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-sygnal 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-sygnal \ --log-driver=none \ @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-sygnal \ {% endfor %} {{ matrix_sygnal_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-sygnal 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-sygnal 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-sygnal 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-sygnal 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-sygnal diff --git a/roles/matrix-synapse-admin/templates/systemd/matrix-synapse-admin.service.j2 b/roles/matrix-synapse-admin/templates/systemd/matrix-synapse-admin.service.j2 index 4823d89c3..6ed9eaae0 100644 --- a/roles/matrix-synapse-admin/templates/systemd/matrix-synapse-admin.service.j2 +++ b/roles/matrix-synapse-admin/templates/systemd/matrix-synapse-admin.service.j2 @@ -13,8 +13,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse-admin 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse-admin 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse-admin 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse-admin 2>/dev/null || true' ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-synapse-admin \ --log-driver=none \ @@ -32,8 +32,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-synapse-admin {% endfor %} {{ matrix_synapse_admin_docker_image }} -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse-admin 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse-admin 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse-admin 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse-admin 2>/dev/null || true' Restart=always RestartSec=30 SyslogIdentifier=matrix-synapse-admin diff --git a/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse.service.j2 b/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse.service.j2 index e69ffa619..2b59748fd 100644 --- a/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse.service.j2 +++ b/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse.service.j2 @@ -21,8 +21,8 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME={{ matrix_systemd_unit_home_path }}" -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse 2>/dev/null' -ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse 2>/dev/null || true' {% if matrix_s3_media_store_enabled %} # Allow for some time before starting, so that media store can mount. # Mounting can happen later too, but if we start writing, @@ -63,8 +63,8 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-synapse \ {{ matrix_synapse_docker_image }} \ run -m synapse.app.homeserver -c /data/homeserver.yaml -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse 2>/dev/null' -ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse 2>/dev/null || true' ExecReload={{ matrix_host_command_docker }} exec matrix-synapse /bin/sh -c 'kill -HUP 1' Restart=always RestartSec=30 From 268b079374a74b418a87f6fd05ea90d56511cab9 Mon Sep 17 00:00:00 2001 From: heftyzauk <80178101+heftyzauk@users.noreply.github.com> Date: Mon, 11 Apr 2022 11:50:41 +0100 Subject: [PATCH 17/83] Revert Coturn Address Change, add new Addresses var (#2) --- group_vars/matrix_servers | 3 ++- roles/matrix-coturn/defaults/main.yml | 3 ++- roles/matrix-coturn/templates/turnserver.conf.j2 | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 92df1bd47..49f1f0918 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -1144,7 +1144,8 @@ matrix_coturn_enabled: true matrix_coturn_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" -matrix_coturn_turn_external_ip_address: ["{{ ansible_host }}"] +matrix_coturn_turn_external_ip_address: "{{ ansible_host }}" +matrix_coturn_turn_external_ip_addresses: [ "{{ matrix_coturn_turn_external_ip_address }}" ] matrix_coturn_turn_static_auth_secret: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'coturn.sas') | to_uuid }}" diff --git a/roles/matrix-coturn/defaults/main.yml b/roles/matrix-coturn/defaults/main.yml index c47430895..39790bacc 100644 --- a/roles/matrix-coturn/defaults/main.yml +++ b/roles/matrix-coturn/defaults/main.yml @@ -64,7 +64,8 @@ matrix_coturn_turn_udp_max_port: 49172 matrix_coturn_turn_static_auth_secret: "" # The external IP address of the machine where Coturn is. -matrix_coturn_turn_external_ip_address: [] +matrix_coturn_turn_external_ip_address: '' +matrix_coturn_turn_external_ip_addresses: [] matrix_coturn_allowed_peer_ips: [] matrix_coturn_denied_peer_ips: [] diff --git a/roles/matrix-coturn/templates/turnserver.conf.j2 b/roles/matrix-coturn/templates/turnserver.conf.j2 index dfa9f4cc7..2b1ee4f6e 100644 --- a/roles/matrix-coturn/templates/turnserver.conf.j2 +++ b/roles/matrix-coturn/templates/turnserver.conf.j2 @@ -5,7 +5,7 @@ realm=turn.{{ matrix_server_fqn_matrix }} min-port={{ matrix_coturn_turn_udp_min_port }} max-port={{ matrix_coturn_turn_udp_max_port }} -{% for ip in matrix_coturn_turn_external_ip_address %} +{% for ip in matrix_coturn_turn_external_ip_addresses %} external-ip={{ ip }} {% endfor %} From 03d2dcc996d6c197819348fc844a9b0fe07bac7b Mon Sep 17 00:00:00 2001 From: Hefty Zauk Date: Mon, 11 Apr 2022 11:20:09 +0000 Subject: [PATCH 18/83] Move into coturn defaults --- group_vars/matrix_servers | 1 - roles/matrix-coturn/defaults/main.yml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 49f1f0918..a1cadd12a 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -1145,7 +1145,6 @@ matrix_coturn_enabled: true matrix_coturn_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" matrix_coturn_turn_external_ip_address: "{{ ansible_host }}" -matrix_coturn_turn_external_ip_addresses: [ "{{ matrix_coturn_turn_external_ip_address }}" ] matrix_coturn_turn_static_auth_secret: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'coturn.sas') | to_uuid }}" diff --git a/roles/matrix-coturn/defaults/main.yml b/roles/matrix-coturn/defaults/main.yml index 39790bacc..df9e53fec 100644 --- a/roles/matrix-coturn/defaults/main.yml +++ b/roles/matrix-coturn/defaults/main.yml @@ -65,7 +65,7 @@ matrix_coturn_turn_static_auth_secret: "" # The external IP address of the machine where Coturn is. matrix_coturn_turn_external_ip_address: '' -matrix_coturn_turn_external_ip_addresses: [] +matrix_coturn_turn_external_ip_addresses: [ "{{ matrix_coturn_turn_external_ip_address }}" ] matrix_coturn_allowed_peer_ips: [] matrix_coturn_denied_peer_ips: [] From f4ba995d9b85b527205bcb32c266f5e98f4ef9d3 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Mon, 11 Apr 2022 15:38:35 +0300 Subject: [PATCH 19/83] Fix validation and prevent empty "external-ip=" lines in Coturn config We no longer validate that there's an IP address defined. Seems like Coturn can start without one as well, so there's no need to require it. If people populate `matrix_coturn_turn_external_ip_addresses` directly to specify multiple addresses, they can leave `matrix_coturn_turn_external_ip_address` empty. We use the "select not equal to empty string" thing in the for loop to avoid `matrix_coturn_turn_external_ip_address` leading to `matrix_coturn_turn_external_ip_addresses: ['']` leading to `external-ip=` in the Coturn configuration. Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1741 --- roles/matrix-coturn/defaults/main.yml | 2 +- roles/matrix-coturn/tasks/validate_config.yml | 1 - roles/matrix-coturn/templates/turnserver.conf.j2 | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/roles/matrix-coturn/defaults/main.yml b/roles/matrix-coturn/defaults/main.yml index df9e53fec..8ea7d3a50 100644 --- a/roles/matrix-coturn/defaults/main.yml +++ b/roles/matrix-coturn/defaults/main.yml @@ -65,7 +65,7 @@ matrix_coturn_turn_static_auth_secret: "" # The external IP address of the machine where Coturn is. matrix_coturn_turn_external_ip_address: '' -matrix_coturn_turn_external_ip_addresses: [ "{{ matrix_coturn_turn_external_ip_address }}" ] +matrix_coturn_turn_external_ip_addresses: ["{{ matrix_coturn_turn_external_ip_address }}"] matrix_coturn_allowed_peer_ips: [] matrix_coturn_denied_peer_ips: [] diff --git a/roles/matrix-coturn/tasks/validate_config.yml b/roles/matrix-coturn/tasks/validate_config.yml index d8276d3a0..637f720db 100644 --- a/roles/matrix-coturn/tasks/validate_config.yml +++ b/roles/matrix-coturn/tasks/validate_config.yml @@ -6,5 +6,4 @@ You need to define a required configuration setting (`{{ item }}`) for using Coturn. when: "vars[item] == ''" with_items: - - "matrix_coturn_turn_external_ip_address" - "matrix_coturn_turn_static_auth_secret" diff --git a/roles/matrix-coturn/templates/turnserver.conf.j2 b/roles/matrix-coturn/templates/turnserver.conf.j2 index 2b1ee4f6e..1bdf310a2 100644 --- a/roles/matrix-coturn/templates/turnserver.conf.j2 +++ b/roles/matrix-coturn/templates/turnserver.conf.j2 @@ -5,7 +5,7 @@ realm=turn.{{ matrix_server_fqn_matrix }} min-port={{ matrix_coturn_turn_udp_min_port }} max-port={{ matrix_coturn_turn_udp_max_port }} -{% for ip in matrix_coturn_turn_external_ip_addresses %} +{% for ip in matrix_coturn_turn_external_ip_addresses|select('ne', '') %} external-ip={{ ip }} {% endfor %} From ceb2f54970611e40e2916ebfbe31222420ef0fbc Mon Sep 17 00:00:00 2001 From: HarHarLinks Date: Mon, 11 Apr 2022 16:45:33 +0200 Subject: [PATCH 20/83] Make hookshot hardcoded public URLs dynamic --- group_vars/matrix_servers | 6 ++-- .../matrix-bridge-hookshot/defaults/main.yml | 33 ++++++++++--------- roles/matrix-bridge-hookshot/tasks/init.yml | 4 +-- .../templates/config.yml.j2 | 14 ++++---- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index a1cadd12a..a4e630284 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -676,9 +676,9 @@ matrix_hookshot_provisioning_enabled: "{{ matrix_hookshot_provisioning_secret an matrix_hookshot_proxy_metrics: "{{ matrix_nginx_proxy_proxy_synapse_metrics }}" matrix_hookshot_proxy_metrics_basic_auth_enabled: "{{ matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled }}" -matrix_hookshot_generic_urlprefix_port_enabled: "{{ matrix_nginx_proxy_container_https_host_bind_port == 443 if matrix_nginx_proxy_https_enabled else matrix_nginx_proxy_container_https_host_bind_port == 80 }}" -matrix_hookshot_generic_urlprefix_port: ":{{ matrix_nginx_proxy_container_https_host_bind_port if matrix_nginx_proxy_https_enabled else matrix_nginx_proxy_container_http_host_bind_port }}" -matrix_hookshot_generic_urlprefix: "http{{ 's' if matrix_nginx_proxy_https_enabled else '' }}://{{ matrix_server_fqn_matrix }}{{ matrix_hookshot_generic_urlprefix_port if matrix_hookshot_generic_urlprefix_port_enabled else '' }}{{ matrix_hookshot_generic_endpoint }}" +matrix_hookshot_urlprefix_port_enabled: "{{ matrix_nginx_proxy_container_https_host_bind_port == 443 if matrix_nginx_proxy_https_enabled else matrix_nginx_proxy_container_https_host_bind_port == 80 }}" +matrix_hookshot_urlprefix_port: ":{{ matrix_nginx_proxy_container_https_host_bind_port if matrix_nginx_proxy_https_enabled else matrix_nginx_proxy_container_http_host_bind_port }}" +matrix_hookshot_urlprefix: "http{{ 's' if matrix_nginx_proxy_https_enabled else '' }}://{{ matrix_server_fqn_matrix }}{{ matrix_hookshot_urlprefix_port if matrix_hookshot_urlprefix_port_enabled else '' }}{{ matrix_hookshot_generic_endpoint }}" ###################################################################### # diff --git a/roles/matrix-bridge-hookshot/defaults/main.yml b/roles/matrix-bridge-hookshot/defaults/main.yml index 78eacdb14..9a0f5456a 100644 --- a/roles/matrix-bridge-hookshot/defaults/main.yml +++ b/roles/matrix-bridge-hookshot/defaults/main.yml @@ -61,7 +61,7 @@ matrix_hookshot_github_oauth_id: '' # "Client ID" on the GitHub App page matrix_hookshot_github_oauth_secret: '' # "Client Secret" on the GitHub App page # Default value of matrix_hookshot_github_oauth_endpoint: "/hookshot/webhooks/oauth" matrix_hookshot_github_oauth_endpoint: "{{ matrix_hookshot_webhook_endpoint }}/oauth" -matrix_hookshot_github_oauth_uri: "https://{{ matrix_server_fqn_matrix }}{{ matrix_hookshot_github_oauth_endpoint }}" +matrix_hookshot_github_oauth_uri: "{{ matrix_hookshot_urlprefix }}{{ matrix_hookshot_github_oauth_endpoint }}" # These are the default settings mentioned here and don't need to be modified: https://matrix-org.github.io/matrix-hookshot/usage/room_configuration/github_repo.html#configuration matrix_hookshot_github_ignore_hooks: "{}" matrix_hookshot_github_command_prefix: '!gh' @@ -86,6 +86,18 @@ matrix_hookshot_gitlab_instances: matrix_hookshot_gitlab_secret: '' +matrix_hookshot_figma_enabled: false +# Default value of matrix_hookshot_figma_endpoint: "/hookshot/webhooks/figma/webhook" +matrix_hookshot_figma_endpoint: "{{ matrix_hookshot_webhook_endpoint }}/figma/webhook" +matrix_hookshot_figma_publicUrl: "{{ matrix_hookshot_urlprefix }}{{ matrix_hookshot_figma_endpoint }}" +# To bridge figma webhooks, you need to configure one of multiple instances like this: +# matrix_hookshot_figma_instances: +# your-instance: +# teamId: your-team-id +# accessToken: your-personal-access-token +# passcode: your-webhook-passcode + + matrix_hookshot_jira_enabled: false # Get the these values from https://matrix-org.github.io/matrix-hookshot/setup/jira.html#jira-oauth matrix_hookshot_jira_secret: '' @@ -94,7 +106,7 @@ matrix_hookshot_jira_oauth_id: '' matrix_hookshot_jira_oauth_secret: '' # Default value of matrix_hookshot_jira_oauth_endpoint: "/hookshot/webhooks/jira/oauth" matrix_hookshot_jira_oauth_endpoint: "{{ matrix_hookshot_webhook_endpoint }}/jira/oauth" -matrix_hookshot_jira_oauth_uri: "{{ matrix_server_fqn_matrix }}{{ matrix_hookshot_jira_oauth_endpoint }}" +matrix_hookshot_jira_oauth_uri: "{{ matrix_hookshot_urlprefix }}{{ matrix_hookshot_jira_oauth_endpoint }}" # No need to change these @@ -102,30 +114,19 @@ matrix_hookshot_generic_enabled: true # Default value of matrix_hookshot_generic_endpoint: "/hookshot/webhooks" matrix_hookshot_generic_endpoint: "{{ matrix_hookshot_webhook_endpoint }}" # urlprefix gets updated with protocol & port in group_vars/matrix_servers -matrix_hookshot_generic_urlprefix: "{{ matrix_server_fqn_matrix }}{{ matrix_hookshot_generic_endpoint }}" +matrix_hookshot_generic_urlprefix: "{{ matrix_hookshot_urlprefix }}{{ matrix_hookshot_generic_endpoint }}" matrix_hookshot_generic_allow_js_transformation_functions: false # If you're also using matrix-appservice-webhooks, take care that these prefixes don't overlap matrix_hookshot_generic_user_id_prefix: '_webhooks_' -matrix_hookshot_figma_enabled: false -# Default value of matrix_hookshot_figma_endpoint: "/hookshot/webhooks/figma/webhook" -matrix_hookshot_figma_endpoint: "{{ matrix_hookshot_webhook_endpoint }}/figma/webhook" -matrix_hookshot_figma_publicUrl: "{{ matrix_server_fqn_matrix }}{{ matrix_hookshot_figma_endpoint }}" -# To bridge figma webhooks, you need to configure one of multiple instances like this: -# matrix_hookshot_figma_instances: -# your-instance: -# teamId: your-team-id -# accessToken: your-personal-access-token -# passcode: your-webhook-passcode - - # There is no need to edit ports. use matrix_hookshot_container_http_host_bind_ports below to expose ports instead. matrix_hookshot_provisioning_port: 9002 matrix_hookshot_provisioning_secret: '' # Provisioning will be automatically enabled if dimension is enabled and you have provided a provisioning secret, unless you override it matrix_hookshot_provisioning_enabled: false -matrix_hookshot_provisioning_endpoint: "{{ matrix_hookshot_public_endpoint }}/v1" +matrix_hookshot_provisioning_internal: "/v1" +matrix_hookshot_provisioning_endpoint: "{{ matrix_hookshot_public_endpoint }}{{ matrix_hookshot_provisioning_internal }}" # You can configure access to the bridge as documented here https://matrix-org.github.io/matrix-hookshot/setup.html#permissions # When empty, the default permissions are applied. diff --git a/roles/matrix-bridge-hookshot/tasks/init.yml b/roles/matrix-bridge-hookshot/tasks/init.yml index a0f9df978..55dde6ef2 100644 --- a/roles/matrix-bridge-hookshot/tasks/init.yml +++ b/roles/matrix-bridge-hookshot/tasks/init.yml @@ -55,10 +55,10 @@ {# Use the embedded DNS resolver in Docker containers to discover the service #} resolver 127.0.0.11 valid=5s; set $backend "{{ matrix_hookshot_container_url }}:{{ matrix_hookshot_provisioning_port }}"; - proxy_pass http://$backend/v1/$1$is_args$args; + proxy_pass http://$backend{{ matrix_hookshot_provisioning_internal }}/$1$is_args$args; {% else %} {# Generic configuration for use outside of our container setup #} - proxy_pass http://127.0.0.1:{{ matrix_hookshot_provisioning_port }}/v1/$1$is_args$args; + proxy_pass http://127.0.0.1:{{ matrix_hookshot_provisioning_port }}{{ matrix_hookshot_provisioning_internal }}/$1$is_args$args; {% endif %} proxy_set_header Host $host; } diff --git a/roles/matrix-bridge-hookshot/templates/config.yml.j2 b/roles/matrix-bridge-hookshot/templates/config.yml.j2 index fc04c7559..bb05f42ce 100644 --- a/roles/matrix-bridge-hookshot/templates/config.yml.j2 +++ b/roles/matrix-bridge-hookshot/templates/config.yml.j2 @@ -47,6 +47,13 @@ gitlab: webhook: secret: {{ matrix_hookshot_gitlab_secret|to_json }} {% endif %} +{% if matrix_hookshot_figma_enabled %} +figma: + # (Optional) Configure this to enable Figma support + # + publicUrl: {{ matrix_hookshot_figma_publicUrl }} + instances: {{ matrix_hookshot_figma_instances }} +{% endif %} {% if matrix_hookshot_jira_enabled %} jira: # (Optional) Configure this to enable Jira support @@ -69,13 +76,6 @@ generic: allowJsTransformationFunctions: {{ matrix_hookshot_generic_allow_js_transformation_functions }} userIdPrefix: {{ matrix_hookshot_generic_user_id_prefix|to_json }} {% endif %} -{% if matrix_hookshot_figma_enabled %} -figma: - # (Optional) Configure this to enable Figma support - # - publicUrl: {{ matrix_hookshot_figma_publicUrl }} - instances: {{ matrix_hookshot_figma_instances }} -{% endif %} {% if matrix_hookshot_provisioning_enabled %} provisioning: # (Optional) Provisioning API for integration managers From a9e6538ef8ac01fa81885487f69eb15e2579ce4c Mon Sep 17 00:00:00 2001 From: HarHarLinks Date: Mon, 11 Apr 2022 16:48:50 +0200 Subject: [PATCH 21/83] Upgrade Hookshot (1.3.0 -> 1.4.0) https://github.com/matrix-org/matrix-hookshot/releases/tag/1.4.0 --- docs/configuring-playbook-bridge-hookshot.md | 10 ++--- group_vars/matrix_servers | 2 +- .../matrix-bridge-hookshot/defaults/main.yml | 40 ++++++++++++++++++- roles/matrix-bridge-hookshot/tasks/init.yml | 14 +++++++ .../templates/config.yml.j2 | 26 +++++++++++- 5 files changed, 83 insertions(+), 9 deletions(-) diff --git a/docs/configuring-playbook-bridge-hookshot.md b/docs/configuring-playbook-bridge-hookshot.md index 5639f159a..38e13a8a9 100644 --- a/docs/configuring-playbook-bridge-hookshot.md +++ b/docs/configuring-playbook-bridge-hookshot.md @@ -22,18 +22,18 @@ Other configuration options are available via the `matrix_hookshot_configuration ### URLs for bridges setup -All of the following endpoints are reachable on your `matrix.` subdomain (if the feature is enabled). +Unless indicated otherwise, the following endpoints are reachable on your `matrix.` subdomain (if the feature is enabled). -| Listener | default path | variable | used as | +| listener | default path | variable | used as | |---|---|---|---| -| webhooks | `/hookshot/webhooks/` | `matrix_hookshot_webhook_endpoint` | GitHub "Webhook URL" | +| webhooks | `/hookshot/webhooks/` | `matrix_hookshot_webhook_endpoint` | generics, GitHub "Webhook URL", etc. | | github oauth | `/hookshot/webhooks/oauth` | `matrix_hookshot_github_oauth_endpoint` | GitHub "Callback URL" | | jira oauth | `/hookshot/webhooks/jira/oauth` | `matrix_hookshot_jira_oauth_endpoint` | JIRA OAuth | | figma endpoint | `/hookshot/webhooks/figma/webhook` | `matrix_hookshot_figma_endpoint` | Figma | | provisioning | `/hookshot/v1/` | `matrix_hookshot_provisioning_endpoint` | Dimension [provisioning](#provisioning-api) | | appservice | `/hookshot/_matrix/app/` | `matrix_hookshot_appservice_endpoint` | Matrix server | -| metrics | `/hookshot/metrics/` | `matrix_hookshot_metrics_endpoint` | Prometheus | -| widgets | | | not supported | +| widgets | `/hookshot/widgetapi/` | `/matrix_hookshot_widgets_endpoint` | Widgets | +| metrics | `/hookshot/metrics/` (on `stats.` subdomain) | `matrix_hookshot_metrics_endpoint` | Prometheus | See also `matrix_hookshot_matrix_nginx_proxy_configuration` in [init.yml](/roles/matrix-bridge-hookshot/tasks/init.yml). diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index a4e630284..85b8a7013 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -678,7 +678,7 @@ matrix_hookshot_proxy_metrics_basic_auth_enabled: "{{ matrix_nginx_proxy_proxy_s matrix_hookshot_urlprefix_port_enabled: "{{ matrix_nginx_proxy_container_https_host_bind_port == 443 if matrix_nginx_proxy_https_enabled else matrix_nginx_proxy_container_https_host_bind_port == 80 }}" matrix_hookshot_urlprefix_port: ":{{ matrix_nginx_proxy_container_https_host_bind_port if matrix_nginx_proxy_https_enabled else matrix_nginx_proxy_container_http_host_bind_port }}" -matrix_hookshot_urlprefix: "http{{ 's' if matrix_nginx_proxy_https_enabled else '' }}://{{ matrix_server_fqn_matrix }}{{ matrix_hookshot_urlprefix_port if matrix_hookshot_urlprefix_port_enabled else '' }}{{ matrix_hookshot_generic_endpoint }}" +matrix_hookshot_urlprefix: "http{{ 's' if matrix_nginx_proxy_https_enabled else '' }}://{{ matrix_server_fqn_matrix }}{{ matrix_hookshot_urlprefix_port if matrix_hookshot_urlprefix_port_enabled else '' }}" ###################################################################### # diff --git a/roles/matrix-bridge-hookshot/defaults/main.yml b/roles/matrix-bridge-hookshot/defaults/main.yml index 9a0f5456a..a55b995d1 100644 --- a/roles/matrix-bridge-hookshot/defaults/main.yml +++ b/roles/matrix-bridge-hookshot/defaults/main.yml @@ -10,7 +10,7 @@ matrix_hookshot_container_image_self_build: false matrix_hookshot_container_image_self_build_repo: "https://github.com/matrix-org/matrix-hookshot.git" matrix_hookshot_container_image_self_build_branch: "{{ 'main' if matrix_hookshot_version == 'latest' else matrix_hookshot_version }}" -matrix_hookshot_version: 1.3.0 +matrix_hookshot_version: 1.4.0 matrix_hookshot_docker_image: "{{ matrix_hookshot_docker_image_name_prefix }}halfshot/matrix-hookshot:{{ matrix_hookshot_version }}" matrix_hookshot_docker_image_name_prefix: "{{ 'localhost/' if matrix_hookshot_container_image_self_build else matrix_container_global_registry_prefix }}" @@ -65,10 +65,11 @@ matrix_hookshot_github_oauth_uri: "{{ matrix_hookshot_urlprefix }}{{ matrix_hook # These are the default settings mentioned here and don't need to be modified: https://matrix-org.github.io/matrix-hookshot/usage/room_configuration/github_repo.html#configuration matrix_hookshot_github_ignore_hooks: "{}" matrix_hookshot_github_command_prefix: '!gh' -matrix_hookshot_github_show_issue_room_link: false +matrix_hookshot_github_showIssueRoomLink: false matrix_hookshot_github_pr_diff: "{enabled: false, maxLines: 5}" matrix_hookshot_github_including_labels: '' matrix_hookshot_github_excluding_labels: '' +matrix_hookshot_github_hotlink_prefix: "#" matrix_hookshot_gitlab_enabled: true @@ -128,6 +129,41 @@ matrix_hookshot_provisioning_enabled: false matrix_hookshot_provisioning_internal: "/v1" matrix_hookshot_provisioning_endpoint: "{{ matrix_hookshot_public_endpoint }}{{ matrix_hookshot_provisioning_internal }}" + +matrix_hookshot_widgets_enabled: true +matrix_hookshot_widgets_port: 9003 +matrix_hookshot_widgets_addToAdminRooms: false # default off as it is a beta feature +matrix_hookshot_widgets_roomSetupWidget_enabled: true +matrix_hookshot_widgets_roomSetupWidget_addOnInvite: false +# `disallowedIpRanges` describes which IP ranges should be disallowed when resolving homeserver IP addresses (for security reasons). Unless you know what you are doing, it is recommended to not include this key. The following IPs are blocked by default, unless you supply another list. +# matrix_hookshot_widgets_disallowedIpRanges: +# - 127.0.0.0/8 +# - 10.0.0.0/8 +# - 172.16.0.0/12 +# - 192.168.0.0/16 +# - 100.64.0.0/10 +# - 192.0.0.0/24 +# - 169.254.0.0/16 +# - 192.88.99.0/24 +# - 198.18.0.0/15 +# - 192.0.2.0/24 +# - 198.51.100.0/24 +# - 203.0.113.0/24 +# - 224.0.0.0/4 +# - ::1/128 +# - fe80::/10 +# - fc00::/7 +# - 2001:db8::/32 +# - ff00::/8 +# - fec0::/10 +matrix_hookshot_widgets_disallowedIpRanges: '' +matrix_hookshot_widgets_internal: "/widgetapi" +# Default value of matrix_hookshot_widgets_endpoint: "/hookshot/widgetapi" +matrix_hookshot_widgets_endpoint: "{{ matrix_hookshot_public_endpoint }}{{ matrix_hookshot_widgets_internal }}" +matrix_hookshot_widgets_publicUrl: "{{ matrix_hookshot_urlprefix }}{{ matrix_hookshot_widgets_endpoint }}/v1/static" +matrix_hookshot_widgets_branding_widgetTitle: "Hookshot Configuration" + + # You can configure access to the bridge as documented here https://matrix-org.github.io/matrix-hookshot/setup.html#permissions # When empty, the default permissions are applied. # Example: diff --git a/roles/matrix-bridge-hookshot/tasks/init.yml b/roles/matrix-bridge-hookshot/tasks/init.yml index 55dde6ef2..384f6d3be 100644 --- a/roles/matrix-bridge-hookshot/tasks/init.yml +++ b/roles/matrix-bridge-hookshot/tasks/init.yml @@ -63,6 +63,20 @@ proxy_set_header Host $host; } {% endif %} + {% if matrix_hookshot_widgets_enabled %} + location ~ ^{{ matrix_hookshot_widgets_endpoint }}/(.*)$ { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_hookshot_container_url }}:{{ matrix_hookshot_widgets_port }}"; + proxy_pass http://$backend{{ matrix_hookshot_widgets_internal }}/$1$is_args$args; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_hookshot_widgets_port }}{{ matrix_hookshot_widgets_internal }}/$1$is_args$args; + {% endif %} + proxy_set_header Host $host; + } + {% endif %} location ~ ^{{ matrix_hookshot_webhook_endpoint }}/(.*)$ { {% if matrix_nginx_proxy_enabled|default(False) %} {# Use the embedded DNS resolver in Docker containers to discover the service #} diff --git a/roles/matrix-bridge-hookshot/templates/config.yml.j2 b/roles/matrix-bridge-hookshot/templates/config.yml.j2 index bb05f42ce..c17715097 100644 --- a/roles/matrix-bridge-hookshot/templates/config.yml.j2 +++ b/roles/matrix-bridge-hookshot/templates/config.yml.j2 @@ -33,10 +33,12 @@ github: # ignoreHooks: {{ matrix_hookshot_github_ignore_hooks }} commandPrefix: "{{ matrix_hookshot_github_command_prefix }}" - showIssueRoomLink: {{ matrix_hookshot_github_show_issue_room_link }} + showIssueRoomLink: {{ matrix_hookshot_github_showIssueRoomLink }} prDiff: {{ matrix_hookshot_github_pr_diff }} includingLabels:{{ matrix_hookshot_github_including_labels }} excludingLabels: {{ matrix_hookshot_github_excluding_labels }} + hotlinkIssues: + prefix: "{{ matrix_hookshot_github_hotlink_prefix }}" {% endif %} {% if matrix_hookshot_gitlab_enabled %} gitlab: @@ -100,6 +102,22 @@ logging: # (Optional) Logging settings. You can have a severity debug,info,warn,error # level: info +{% if matrix_hookshot_widgets_enabled %} +widgets: + # (Optional) EXPERIMENTAL support for complimentary widgets + # + addToAdminRooms: {{ matrix_hookshot_widgets_addToAdminRooms }} +{% if matrix_hookshot_widgets_roomSetupWidget_enabled %} + roomSetupWidget: + addOnInvite: {{ matrix_hookshot_widgets_roomSetupWidget_addOnInvite }} +{% endif %} +{% if not matrix_hookshot_widgets_disallowedIpRanges is in [None, ''] %} + disallowedIpRanges: {{ matrix_hookshot_widgets_disallowedIpRanges }} +{% endif %} + publicUrl: {{ matrix_hookshot_widgets_publicUrl }} + branding: + widgetTitle: {{ matrix_hookshot_widgets_branding_widgetTitle }} +{% endif %} {% if matrix_hookshot_permissions %} permissions: {{ matrix_hookshot_permissions }} {% endif %} @@ -125,3 +143,9 @@ listeners: resources: - provisioning {% endif %} +{% if matrix_hookshot_widgets_enabled %} + - port: {{ matrix_hookshot_widgets_port }} + bindAddress: 0.0.0.0 + resources: + - widgets +{% endif %} From f6cb59116b93bc826fdb2a7b8999046eaa08e0d6 Mon Sep 17 00:00:00 2001 From: Yan Minagawa Date: Tue, 12 Apr 2022 14:31:49 +0700 Subject: [PATCH 22/83] This adds a variable for requiring MSC3231 token for registration --- roles/matrix-synapse/defaults/main.yml | 5 +++++ roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/roles/matrix-synapse/defaults/main.yml b/roles/matrix-synapse/defaults/main.yml index ec58f2336..9e3326695 100644 --- a/roles/matrix-synapse/defaults/main.yml +++ b/roles/matrix-synapse/defaults/main.yml @@ -210,6 +210,11 @@ matrix_synapse_enable_registration_captcha: false matrix_synapse_recaptcha_public_key: '' matrix_synapse_recaptcha_private_key: '' +# Requires an MSC3231 token for registration. Note that `matrix_synapse_enable_registration` must be set to `true`. +# Tokens can be created via the API or through synapse-admin. +# Disabling this option will not delete any tokens previously generated. +matrix_synapse_registration_requires_token: false + # Allows non-server-admin users to create groups on this server matrix_synapse_enable_group_creation: false diff --git a/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 b/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 index 8a701c4d9..37cad10fe 100644 --- a/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 +++ b/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 @@ -1373,7 +1373,9 @@ allowed_local_3pids: {{ matrix_synapse_allowed_local_3pids|to_json }} # Disabling this option will not delete any tokens previously generated. # Defaults to false. Uncomment the following to require tokens: # -#registration_requires_token: true +registration_requires_token: {{ matrix_synapse_registration_requires_token|to_json }} + + # If set, allows registration of standard or admin accounts by anyone who # has the shared secret, even if registration is otherwise disabled. From 10c6c24c160e4b956c455161a14708c0a4b6ff47 Mon Sep 17 00:00:00 2001 From: Aine <97398200+etkecc@users.noreply.github.com> Date: Tue, 12 Apr 2022 11:15:11 +0000 Subject: [PATCH 23/83] Update element 1.10.8 -> 1.10.9 --- roles/matrix-client-element/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-client-element/defaults/main.yml b/roles/matrix-client-element/defaults/main.yml index c395efb72..34cf22a74 100644 --- a/roles/matrix-client-element/defaults/main.yml +++ b/roles/matrix-client-element/defaults/main.yml @@ -9,7 +9,7 @@ matrix_client_element_container_image_self_build_repo: "https://github.com/vecto # - https://github.com/vector-im/element-web/issues/19544 matrix_client_element_container_image_self_build_low_memory_system_patch_enabled: "{{ ansible_memtotal_mb < 4096 }}" -matrix_client_element_version: v1.10.8 +matrix_client_element_version: v1.10.9 matrix_client_element_docker_image: "{{ matrix_client_element_docker_image_name_prefix }}vectorim/element-web:{{ matrix_client_element_version }}" matrix_client_element_docker_image_name_prefix: "{{ 'localhost/' if matrix_client_element_container_image_self_build else matrix_container_global_registry_prefix }}" matrix_client_element_docker_image_force_pull: "{{ matrix_client_element_docker_image.endswith(':latest') }}" From ac1bd494940e1d476b7f63625968f7ee7bee1e33 Mon Sep 17 00:00:00 2001 From: Aine <97398200+etkecc@users.noreply.github.com> Date: Tue, 12 Apr 2022 11:16:36 +0000 Subject: [PATCH 24/83] Update coturn 4.5.2-r8 -> 4.5.2-r11 --- roles/matrix-coturn/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-coturn/defaults/main.yml b/roles/matrix-coturn/defaults/main.yml index 8ea7d3a50..46492e21d 100644 --- a/roles/matrix-coturn/defaults/main.yml +++ b/roles/matrix-coturn/defaults/main.yml @@ -7,7 +7,7 @@ matrix_coturn_container_image_self_build_repo: "https://github.com/coturn/coturn matrix_coturn_container_image_self_build_repo_version: "docker/{{ matrix_coturn_version }}" matrix_coturn_container_image_self_build_repo_dockerfile_path: "docker/coturn/alpine/Dockerfile" -matrix_coturn_version: 4.5.2-r8 +matrix_coturn_version: 4.5.2-r11 matrix_coturn_docker_image: "{{ matrix_coturn_docker_image_name_prefix }}coturn/coturn:{{ matrix_coturn_version }}-alpine" matrix_coturn_docker_image_name_prefix: "{{ 'localhost/' if matrix_coturn_container_image_self_build else matrix_container_global_registry_prefix }}" matrix_coturn_docker_image_force_pull: "{{ matrix_coturn_docker_image.endswith(':latest') }}" From 9c606d1fcb2b5f921ec2a0bd46fbed15b6b0be97 Mon Sep 17 00:00:00 2001 From: Christos Karamolegkos Date: Tue, 12 Apr 2022 17:00:07 +0300 Subject: [PATCH 25/83] Update jitsi to version 7001 Tested, works without any configuration changes. --- roles/matrix-jitsi/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-jitsi/defaults/main.yml b/roles/matrix-jitsi/defaults/main.yml index 5f543d4af..c32682678 100644 --- a/roles/matrix-jitsi/defaults/main.yml +++ b/roles/matrix-jitsi/defaults/main.yml @@ -70,7 +70,7 @@ matrix_jitsi_jibri_recorder_password: '' matrix_jitsi_enable_lobby: false -matrix_jitsi_version: stable-6865 +matrix_jitsi_version: stable-7001 matrix_jitsi_container_image_tag: "{{ matrix_jitsi_version }}" # for backward-compatibility matrix_jitsi_web_docker_image: "{{ matrix_container_global_registry_prefix }}jitsi/web:{{ matrix_jitsi_container_image_tag }}" From b9bf20c761eedb612d51b5fe4654d336142189ae Mon Sep 17 00:00:00 2001 From: Aine <97398200+etkecc@users.noreply.github.com> Date: Tue, 12 Apr 2022 15:52:49 +0000 Subject: [PATCH 26/83] Update honoroit 0.9.5 -> 0.9.6 This update brings stable threads support --- roles/matrix-bot-honoroit/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bot-honoroit/defaults/main.yml b/roles/matrix-bot-honoroit/defaults/main.yml index 8495c6e18..90bfa2696 100644 --- a/roles/matrix-bot-honoroit/defaults/main.yml +++ b/roles/matrix-bot-honoroit/defaults/main.yml @@ -8,7 +8,7 @@ matrix_bot_honoroit_container_image_self_build: false matrix_bot_honoroit_docker_repo: "https://gitlab.com/etke.cc/honoroit.git" matrix_bot_honoroit_docker_src_files_path: "{{ matrix_base_data_path }}/honoroit/docker-src" -matrix_bot_honoroit_version: v0.9.5 +matrix_bot_honoroit_version: v0.9.6 matrix_bot_honoroit_docker_image: "{{ matrix_bot_honoroit_docker_image_name_prefix }}honoroit:{{ matrix_bot_honoroit_version }}" matrix_bot_honoroit_docker_image_name_prefix: "{{ 'localhost/' if matrix_bot_honoroit_container_image_self_build else 'registry.gitlab.com/etke.cc/' }}" matrix_bot_honoroit_docker_image_force_pull: "{{ matrix_bot_honoroit_docker_image.endswith(':latest') }}" From 23d0832e85a3e35f4c6ecc3f90cc6ec4c166ba8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliv=C3=A9r=20Falvai?= Date: Tue, 12 Apr 2022 20:14:12 +0200 Subject: [PATCH 27/83] Improve borg backup instructions --- docs/configuring-playbook-backup-borg.md | 26 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/configuring-playbook-backup-borg.md b/docs/configuring-playbook-backup-borg.md index 7ca962c87..2bcc14cee 100644 --- a/docs/configuring-playbook-backup-borg.md +++ b/docs/configuring-playbook-backup-borg.md @@ -4,17 +4,23 @@ The playbook can install and configure [borgbackup](https://www.borgbackup.org/) BorgBackup is a deduplicating backup program with optional compression and encryption. That means your daily incremental backups can be stored in a fraction of the space and is safe whether you store it at home or on a cloud service. -The backup will run based on `matrix_backup_borg_schedule` var (systemd timer calendar), default: 4am every day +You will need a remote server where borg will store the backups. There are hosted, borg compatible solutions available, such as [BorgBase](https://www.borgbase.com). + +The backup will run based on `matrix_backup_borg_schedule` var (systemd timer calendar), default: 4am every day. ## Prerequisites -1. Create ssh key on any machine: +1. Create a new SSH key: ```bash ssh-keygen -t ed25519 -N '' -f matrix-borg-backup -C matrix ``` -2. Add public part of that ssh key to your borg provider / server: +This can be done on any machine and you don't need to place the key in the `.ssh` folder. It will be added to the Ansible config later. + +2. Add the **public** part of this SSH key (the `matrix-borg-backup.pub` file) to your borg provider/server: + +If you plan to use a hosted solution, follow their instructions. If you have your own server, copy the key over: ```bash # example to append the new PUBKEY contents, where: @@ -35,17 +41,21 @@ matrix_backup_borg_location_repositories: matrix_backup_borg_storage_encryption_passphrase: "PASSPHRASE" matrix_backup_borg_ssh_key_private: | PRIVATE KEY +matrix_backup_borg_location_source_directories: + - "{{ matrix_base_data_path }}" ``` where: -* USER - ssh user of a provider / server -* HOST - ssh host of a provider / server +* USER - SSH user of a provider/server +* HOST - SSH host of a provider/server * REPO - borg repository name, it will be initialized on backup start, eg: `matrix` -* PASSPHRASE - super-secret borg passphrase, you may generate it with `pwgen -s 64 1` or use any password manager -* PRIVATE KEY - the content of the public part of the ssh key you created before +* PASSPHRASE - passphrase used for encrypting backups, you may generate it with `pwgen -s 64 1` or use any password manager +* PRIVATE KEY - the content of the **private** part of the SSH key you created before + +`matrix_backup_borg_location_source_directories` defines the list of directories to back up, `{{ matrix_base_data_path }}` is the base directory for every service's data, such as Synapse, Postgres and the bridges. You might want to exclude certain directories or file patterns from the backup using the `matrix_backup_borg_location_exclude_patterns` variable. -Check the `roles/matrix-backup-borg/defaults/main.yml` for the full list of available options +Check the `roles/matrix-backup-borg/defaults/main.yml` file for the full list of available options. ## Installing From 121f860d634c04efc4ab83e2d93fd9c74a95aa45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliv=C3=A9r=20Falvai?= Date: Wed, 13 Apr 2022 08:58:19 +0200 Subject: [PATCH 28/83] Update configuring-playbook-backup-borg.md --- docs/configuring-playbook-backup-borg.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/configuring-playbook-backup-borg.md b/docs/configuring-playbook-backup-borg.md index 2bcc14cee..70466a6e6 100644 --- a/docs/configuring-playbook-backup-borg.md +++ b/docs/configuring-playbook-backup-borg.md @@ -41,8 +41,6 @@ matrix_backup_borg_location_repositories: matrix_backup_borg_storage_encryption_passphrase: "PASSPHRASE" matrix_backup_borg_ssh_key_private: | PRIVATE KEY -matrix_backup_borg_location_source_directories: - - "{{ matrix_base_data_path }}" ``` where: @@ -53,7 +51,7 @@ where: * PASSPHRASE - passphrase used for encrypting backups, you may generate it with `pwgen -s 64 1` or use any password manager * PRIVATE KEY - the content of the **private** part of the SSH key you created before -`matrix_backup_borg_location_source_directories` defines the list of directories to back up, `{{ matrix_base_data_path }}` is the base directory for every service's data, such as Synapse, Postgres and the bridges. You might want to exclude certain directories or file patterns from the backup using the `matrix_backup_borg_location_exclude_patterns` variable. +`matrix_backup_borg_location_source_directories` defines the list of directories to back up: it's set to `{{ matrix_base_data_path }}` by default, which is the base directory for every service's data, such as Synapse, Postgres and the bridges. You might want to exclude certain directories or file patterns from the backup using the `matrix_backup_borg_location_exclude_patterns` variable. Check the `roles/matrix-backup-borg/defaults/main.yml` file for the full list of available options. From 2df993977a5c821675b3b8224ae75ddb1aae788a Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 14 Apr 2022 08:52:37 +0300 Subject: [PATCH 29/83] Ensure git cloning when self-building is done with the matrix user, not root Fixes https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1749 --- CHANGELOG.md | 13 +++++++++++++ roles/matrix-backup-borg/tasks/setup_install.yml | 2 ++ roles/matrix-bot-honoroit/tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ roles/matrix-bot-mjolnir/tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../matrix-bridge-hookshot/tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 4 ++++ .../tasks/setup_install.yml | 4 ++++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ .../tasks/setup_install.yml | 2 ++ roles/matrix-client-cinny/tasks/setup_install.yml | 2 ++ roles/matrix-client-element/tasks/setup_install.yml | 2 ++ .../matrix-client-hydrogen/tasks/setup_install.yml | 2 ++ roles/matrix-corporal/tasks/setup_corporal.yml | 2 ++ roles/matrix-coturn/tasks/setup_install.yml | 2 ++ roles/matrix-dimension/tasks/setup_install.yml | 2 ++ roles/matrix-dynamic-dns/tasks/install.yml | 2 ++ roles/matrix-email2matrix/tasks/setup_install.yml | 2 ++ roles/matrix-ma1sd/tasks/setup_install.yml | 2 ++ roles/matrix-mailer/tasks/setup_mailer.yml | 2 ++ .../tasks/util/migrate_db_to_postgres.yml | 2 ++ roles/matrix-registration/tasks/setup_install.yml | 2 ++ roles/matrix-synapse-admin/tasks/setup.yml | 2 ++ .../matrix-synapse/tasks/synapse/setup_install.yml | 2 ++ 39 files changed, 93 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0196a1b06..9c68ed63d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 2022-04-14 + +## (Compatibility Break) Changes to `docker-src` permissions necessitating manual action + +Users who build container images from source will need to manually correct file permissions of some directories on the server. + +When self-building, the playbook used to `git clone` repositories (into `/matrix/SERVICE/docker-src`) using the `root` user, but now uses `matrix` instead to work around [the following issue with git 2.35.2](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1749). + +If you're on a non-`amd64` architecture (that is, you're overriding `matrix_architecture` in your `vars.yml` file) or you have enabled self-building for some service (e.g. `matrix_*_self_build: true`), you're certainly building some container images from source and have `docker-src` directories with mixed permissions lying around in various `/matrix/SERVICE` directories. + +The playbook *could* correct these permissions automatically, but that requires additional Ansible tasks in some ~45 different places - something that takes considerable effort. So we ask users observing errors related to `docker-src` directories to correct the problem manually by **running this command on the Matrix server** (which deletes all `/matrix/*/docker-src` directories): `find /matrix -maxdepth 2 -name 'docker-src' | xargs rm -rf` + + # 2022-03-17 ## (Compatibility Break) ma1sd identity server no longer installed by default diff --git a/roles/matrix-backup-borg/tasks/setup_install.yml b/roles/matrix-backup-borg/tasks/setup_install.yml index f2c65a164..90757a803 100644 --- a/roles/matrix-backup-borg/tasks/setup_install.yml +++ b/roles/matrix-backup-borg/tasks/setup_install.yml @@ -52,6 +52,8 @@ repo: "{{ matrix_backup_borg_docker_repo }}" dest: "{{ matrix_backup_borg_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_backup_borg_git_pull_results when: "matrix_backup_borg_container_image_self_build|bool" diff --git a/roles/matrix-bot-honoroit/tasks/setup_install.yml b/roles/matrix-bot-honoroit/tasks/setup_install.yml index f3ad9b630..584df9b71 100644 --- a/roles/matrix-bot-honoroit/tasks/setup_install.yml +++ b/roles/matrix-bot-honoroit/tasks/setup_install.yml @@ -64,6 +64,8 @@ repo: "{{ matrix_bot_honoroit_docker_repo }}" dest: "{{ matrix_bot_honoroit_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_bot_honoroit_git_pull_results when: "matrix_bot_honoroit_container_image_self_build|bool" diff --git a/roles/matrix-bot-matrix-reminder-bot/tasks/setup_install.yml b/roles/matrix-bot-matrix-reminder-bot/tasks/setup_install.yml index ffb38ffc8..d7f4706fb 100644 --- a/roles/matrix-bot-matrix-reminder-bot/tasks/setup_install.yml +++ b/roles/matrix-bot-matrix-reminder-bot/tasks/setup_install.yml @@ -57,6 +57,8 @@ repo: "{{ matrix_bot_matrix_reminder_bot_docker_repo }}" dest: "{{ matrix_bot_matrix_reminder_bot_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_bot_matrix_reminder_bot_git_pull_results when: "matrix_bot_matrix_reminder_bot_container_image_self_build|bool" diff --git a/roles/matrix-bot-mjolnir/tasks/setup_install.yml b/roles/matrix-bot-mjolnir/tasks/setup_install.yml index f3b031fa9..66f2806a8 100644 --- a/roles/matrix-bot-mjolnir/tasks/setup_install.yml +++ b/roles/matrix-bot-mjolnir/tasks/setup_install.yml @@ -35,6 +35,8 @@ dest: "{{ matrix_bot_mjolnir_docker_src_files_path }}" version: "{{ matrix_bot_mjolnir_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_bot_mjolnir_git_pull_results when: "matrix_bot_mjolnir_container_image_self_build|bool" diff --git a/roles/matrix-bridge-appservice-irc/tasks/setup_install.yml b/roles/matrix-bridge-appservice-irc/tasks/setup_install.yml index 1b317464e..1ae0b3fa3 100644 --- a/roles/matrix-bridge-appservice-irc/tasks/setup_install.yml +++ b/roles/matrix-bridge-appservice-irc/tasks/setup_install.yml @@ -74,6 +74,8 @@ repo: "{{ matrix_appservice_irc_docker_repo }}" dest: "{{ matrix_appservice_irc_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_appservice_irc_git_pull_results when: "matrix_appservice_irc_enabled|bool and matrix_appservice_irc_container_image_self_build|bool" diff --git a/roles/matrix-bridge-appservice-slack/tasks/setup_install.yml b/roles/matrix-bridge-appservice-slack/tasks/setup_install.yml index 2dcc23c61..42aa020c5 100644 --- a/roles/matrix-bridge-appservice-slack/tasks/setup_install.yml +++ b/roles/matrix-bridge-appservice-slack/tasks/setup_install.yml @@ -48,6 +48,8 @@ repo: "{{ matrix_appservice_slack_docker_repo }}" dest: "{{ matrix_appservice_slack_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_appservice_slack_git_pull_results when: "matrix_appservice_slack_container_image_self_build|bool" diff --git a/roles/matrix-bridge-appservice-webhooks/tasks/setup_install.yml b/roles/matrix-bridge-appservice-webhooks/tasks/setup_install.yml index 6759bca86..274f54c53 100644 --- a/roles/matrix-bridge-appservice-webhooks/tasks/setup_install.yml +++ b/roles/matrix-bridge-appservice-webhooks/tasks/setup_install.yml @@ -33,6 +33,8 @@ dest: "{{ matrix_appservice_webhooks_docker_src_files_path }}" version: "{{ matrix_appservice_webhooks_container_image_self_build_repo_version }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_appservice_webhooks_git_pull_results - name: Ensure Appservice webhooks Docker image is built diff --git a/roles/matrix-bridge-beeper-linkedin/tasks/setup_install.yml b/roles/matrix-bridge-beeper-linkedin/tasks/setup_install.yml index 575b22c16..74f80314b 100644 --- a/roles/matrix-bridge-beeper-linkedin/tasks/setup_install.yml +++ b/roles/matrix-bridge-beeper-linkedin/tasks/setup_install.yml @@ -41,6 +41,8 @@ dest: "{{ matrix_beeper_linkedin_docker_src_files_path }}" version: "{{ matrix_beeper_linkedin_container_image_self_build_branch }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_beeper_linkedin_git_pull_results # Building the container image (using the default Dockerfile) requires that a docker-requirements.txt file be generated. diff --git a/roles/matrix-bridge-hookshot/tasks/setup_install.yml b/roles/matrix-bridge-hookshot/tasks/setup_install.yml index 38dc62a3b..25f2978cd 100644 --- a/roles/matrix-bridge-hookshot/tasks/setup_install.yml +++ b/roles/matrix-bridge-hookshot/tasks/setup_install.yml @@ -32,6 +32,8 @@ dest: "{{ matrix_hookshot_docker_src_files_path }}" version: "{{ matrix_hookshot_container_image_self_build_branch }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_hookshot_git_pull_results when: "matrix_hookshot_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mautrix-facebook/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-facebook/tasks/setup_install.yml index c37b9e10c..699ed88a0 100644 --- a/roles/matrix-bridge-mautrix-facebook/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-facebook/tasks/setup_install.yml @@ -66,6 +66,8 @@ dest: "{{ matrix_mautrix_facebook_docker_src_files_path }}" version: "{{ matrix_mautrix_facebook_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_facebook_git_pull_results when: "matrix_mautrix_facebook_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mautrix-googlechat/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-googlechat/tasks/setup_install.yml index daab10e31..bf04e834c 100644 --- a/roles/matrix-bridge-mautrix-googlechat/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-googlechat/tasks/setup_install.yml @@ -65,6 +65,8 @@ repo: "{{ matrix_mautrix_googlechat_container_image_self_build_repo }}" dest: "{{ matrix_mautrix_googlechat_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_googlechat_git_pull_results when: "matrix_mautrix_googlechat_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mautrix-hangouts/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-hangouts/tasks/setup_install.yml index d2b7157ec..6a8808159 100644 --- a/roles/matrix-bridge-mautrix-hangouts/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-hangouts/tasks/setup_install.yml @@ -65,6 +65,8 @@ repo: "{{ matrix_mautrix_hangouts_container_image_self_build_repo }}" dest: "{{ matrix_mautrix_hangouts_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_hangouts_git_pull_results when: "matrix_mautrix_hangouts_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mautrix-instagram/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-instagram/tasks/setup_install.yml index 4e531615f..5e30adbe4 100644 --- a/roles/matrix-bridge-mautrix-instagram/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-instagram/tasks/setup_install.yml @@ -38,6 +38,8 @@ repo: "{{ matrix_mautrix_instagram_container_image_self_build_repo }}" dest: "{{ matrix_mautrix_instagram_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_instagram_git_pull_results when: "matrix_mautrix_instagram_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml index 840cbd6e4..c7202f05a 100644 --- a/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml @@ -26,6 +26,8 @@ repo: "{{ matrix_mautrix_signal_docker_repo }}" dest: "{{ matrix_mautrix_signal_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_signal_git_pull_results when: "matrix_mautrix_signal_container_image_self_build|bool" @@ -56,6 +58,8 @@ repo: "{{ matrix_mautrix_signal_daemon_docker_repo }}" dest: "{{ matrix_mautrix_signal_daemon_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_signal_daemon_git_pull_results when: "matrix_mautrix_signal_daemon_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mautrix-telegram/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-telegram/tasks/setup_install.yml index 1960288d6..55e7d016f 100644 --- a/roles/matrix-bridge-mautrix-telegram/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-telegram/tasks/setup_install.yml @@ -65,6 +65,8 @@ repo: "{{ matrix_telegram_lottieconverter_docker_repo }}" dest: "{{ matrix_telegram_lottieconverter_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_telegram_lottieconverter_git_pull_results when: "matrix_telegram_lottieconverter_container_image_self_build|bool and matrix_mautrix_telegram_container_image_self_build|bool" @@ -85,6 +87,8 @@ repo: "{{ matrix_mautrix_telegram_docker_repo }}" dest: "{{ matrix_mautrix_telegram_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_telegram_git_pull_results when: "matrix_mautrix_telegram_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mautrix-twitter/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-twitter/tasks/setup_install.yml index 6e587900d..552c9d525 100644 --- a/roles/matrix-bridge-mautrix-twitter/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-twitter/tasks/setup_install.yml @@ -43,6 +43,8 @@ dest: "{{ matrix_mautrix_twitter_docker_src_files_path }}" # version: "{{ matrix_coturn_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_twitter_git_pull_results when: "matrix_mautrix_twitter_enabled|bool and matrix_mautrix_twitter_container_image_self_build" diff --git a/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_install.yml index 8f27ac2a3..f47675b58 100644 --- a/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_install.yml @@ -68,6 +68,8 @@ dest: "{{ matrix_mautrix_whatsapp_docker_src_files_path }}" version: "{{ matrix_mautrix_whatsapp_container_image_self_build_branch }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mautrix_whatsapp_git_pull_results when: "matrix_mautrix_whatsapp_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mx-puppet-discord/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-discord/tasks/setup_install.yml index 26a7c0c3f..3ddfa39d5 100644 --- a/roles/matrix-bridge-mx-puppet-discord/tasks/setup_install.yml +++ b/roles/matrix-bridge-mx-puppet-discord/tasks/setup_install.yml @@ -83,6 +83,8 @@ dest: "{{ matrix_mx_puppet_discord_docker_src_files_path }}" force: "yes" version: "{{ matrix_mx_puppet_discord_container_image_self_build_version }}" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mx_puppet_discord_git_pull_results when: "matrix_mx_puppet_discord_enabled|bool and matrix_mx_puppet_discord_container_image_self_build" diff --git a/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_install.yml index 0d43a0d02..286c5611c 100644 --- a/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_install.yml +++ b/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_install.yml @@ -83,6 +83,8 @@ repo: "{{ matrix_mx_puppet_groupme_container_image_self_build_repo }}" dest: "{{ matrix_mx_puppet_groupme_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mx_puppet_groupme_git_pull_results when: "matrix_mx_puppet_groupme_enabled|bool and matrix_mx_puppet_groupme_container_image_self_build" diff --git a/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_install.yml index cb613074c..2e74c059e 100644 --- a/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_install.yml +++ b/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_install.yml @@ -66,6 +66,8 @@ repo: "{{ matrix_mx_puppet_instagram_container_image_self_build_repo }}" dest: "{{ matrix_mx_puppet_instagram_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mx_puppet_instagram_git_pull_results when: "matrix_mx_puppet_instagram_enabled|bool and matrix_mx_puppet_instagram_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mx-puppet-skype/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-skype/tasks/setup_install.yml index c3776c708..96ae82e61 100644 --- a/roles/matrix-bridge-mx-puppet-skype/tasks/setup_install.yml +++ b/roles/matrix-bridge-mx-puppet-skype/tasks/setup_install.yml @@ -83,6 +83,8 @@ repo: "{{ matrix_mx_puppet_skype_container_image_self_build_repo }}" dest: "{{ matrix_mx_puppet_skype_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mx_puppet_skype_git_pull_results when: "matrix_mx_puppet_skype_enabled|bool and matrix_mx_puppet_skype_container_image_self_build|bool" diff --git a/roles/matrix-bridge-mx-puppet-slack/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-slack/tasks/setup_install.yml index b064ee838..3a7dfb409 100644 --- a/roles/matrix-bridge-mx-puppet-slack/tasks/setup_install.yml +++ b/roles/matrix-bridge-mx-puppet-slack/tasks/setup_install.yml @@ -80,6 +80,8 @@ dest: "{{ matrix_mx_puppet_slack_docker_src_files_path }}" force: "yes" version: "{{ matrix_mx_puppet_slack_container_image_self_build_version }}" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mx_puppet_slack_git_pull_results when: "matrix_mx_puppet_slack_enabled|bool and matrix_mx_puppet_slack_container_image_self_build" diff --git a/roles/matrix-bridge-mx-puppet-steam/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-steam/tasks/setup_install.yml index b8b3f737d..ac2a2fda9 100644 --- a/roles/matrix-bridge-mx-puppet-steam/tasks/setup_install.yml +++ b/roles/matrix-bridge-mx-puppet-steam/tasks/setup_install.yml @@ -83,6 +83,8 @@ repo: "{{ matrix_mx_puppet_steam_container_image_self_build_repo }}" dest: "{{ matrix_mx_puppet_steam_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mx_puppet_steam_git_pull_results when: "matrix_mx_puppet_steam_enabled|bool and matrix_mx_puppet_steam_container_image_self_build" diff --git a/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_install.yml index 485900a85..6336b0a08 100644 --- a/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_install.yml +++ b/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_install.yml @@ -83,6 +83,8 @@ repo: "{{ matrix_mx_puppet_twitter_container_image_self_build_repo }}" dest: "{{ matrix_mx_puppet_twitter_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mx_puppet_twitter_git_pull_results when: "matrix_mx_puppet_twitter_enabled|bool and matrix_mx_puppet_twitter_container_image_self_build" diff --git a/roles/matrix-client-cinny/tasks/setup_install.yml b/roles/matrix-client-cinny/tasks/setup_install.yml index 48865008f..da979f565 100644 --- a/roles/matrix-client-cinny/tasks/setup_install.yml +++ b/roles/matrix-client-cinny/tasks/setup_install.yml @@ -29,6 +29,8 @@ dest: "{{ matrix_client_cinny_docker_src_files_path }}" version: "{{ matrix_client_cinny_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_client_cinny_git_pull_results when: "matrix_client_cinny_container_image_self_build|bool" diff --git a/roles/matrix-client-element/tasks/setup_install.yml b/roles/matrix-client-element/tasks/setup_install.yml index e9c7096e9..4d0af82d6 100644 --- a/roles/matrix-client-element/tasks/setup_install.yml +++ b/roles/matrix-client-element/tasks/setup_install.yml @@ -30,6 +30,8 @@ dest: "{{ matrix_client_element_docker_src_files_path }}" version: "{{ matrix_client_element_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_client_element_git_pull_results when: "matrix_client_element_container_image_self_build|bool" diff --git a/roles/matrix-client-hydrogen/tasks/setup_install.yml b/roles/matrix-client-hydrogen/tasks/setup_install.yml index 0e4868f6b..db866178e 100644 --- a/roles/matrix-client-hydrogen/tasks/setup_install.yml +++ b/roles/matrix-client-hydrogen/tasks/setup_install.yml @@ -30,6 +30,8 @@ dest: "{{ matrix_client_hydrogen_docker_src_files_path }}" version: "{{ matrix_client_hydrogen_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_client_hydrogen_git_pull_results when: "matrix_client_hydrogen_container_image_self_build|bool" diff --git a/roles/matrix-corporal/tasks/setup_corporal.yml b/roles/matrix-corporal/tasks/setup_corporal.yml index 6c520ee00..a3582592c 100644 --- a/roles/matrix-corporal/tasks/setup_corporal.yml +++ b/roles/matrix-corporal/tasks/setup_corporal.yml @@ -23,6 +23,8 @@ dest: "{{ matrix_corporal_container_src_files_path }}" version: "{{ matrix_corporal_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_corporal_git_pull_results when: "matrix_corporal_enabled|bool and matrix_corporal_container_image_self_build|bool" diff --git a/roles/matrix-coturn/tasks/setup_install.yml b/roles/matrix-coturn/tasks/setup_install.yml index 621177e52..a721f186b 100644 --- a/roles/matrix-coturn/tasks/setup_install.yml +++ b/roles/matrix-coturn/tasks/setup_install.yml @@ -36,6 +36,8 @@ dest: "{{ matrix_coturn_docker_src_files_path }}" version: "{{ matrix_coturn_container_image_self_build_repo_version }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_coturn_git_pull_results - name: Ensure Coturn Docker image is built diff --git a/roles/matrix-dimension/tasks/setup_install.yml b/roles/matrix-dimension/tasks/setup_install.yml index 1ba4f2d4c..b999383e1 100644 --- a/roles/matrix-dimension/tasks/setup_install.yml +++ b/roles/matrix-dimension/tasks/setup_install.yml @@ -102,6 +102,8 @@ dest: "{{ matrix_dimension_docker_src_files_path }}" version: "{{ matrix_dimension_container_image_self_build_branch }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" when: "matrix_dimension_container_image_self_build|bool" register: matrix_dimension_git_pull_results diff --git a/roles/matrix-dynamic-dns/tasks/install.yml b/roles/matrix-dynamic-dns/tasks/install.yml index 4dffe6819..60f079374 100644 --- a/roles/matrix-dynamic-dns/tasks/install.yml +++ b/roles/matrix-dynamic-dns/tasks/install.yml @@ -30,6 +30,8 @@ repo: "{{ matrix_dynamic_dns_container_image_self_build_repo }}" dest: "{{ matrix_dynamic_dns_docker_src_files_path }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_dynamic_dns_git_pull_results when: "matrix_dynamic_dns_enabled|bool and matrix_dynamic_dns_container_image_self_build|bool" diff --git a/roles/matrix-email2matrix/tasks/setup_install.yml b/roles/matrix-email2matrix/tasks/setup_install.yml index 74e7c6764..a2470728e 100644 --- a/roles/matrix-email2matrix/tasks/setup_install.yml +++ b/roles/matrix-email2matrix/tasks/setup_install.yml @@ -39,6 +39,8 @@ dest: "{{ matrix_email2matrix_docker_src_files_path }}" version: "{{ matrix_email2matrix_container_image_self_build_branch }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_email2matrix_git_pull_results when: "matrix_email2matrix_container_image_self_build|bool" diff --git a/roles/matrix-ma1sd/tasks/setup_install.yml b/roles/matrix-ma1sd/tasks/setup_install.yml index c56c81f98..e3347a4db 100644 --- a/roles/matrix-ma1sd/tasks/setup_install.yml +++ b/roles/matrix-ma1sd/tasks/setup_install.yml @@ -85,6 +85,8 @@ dest: "{{ matrix_ma1sd_docker_src_files_path }}" version: "{{ matrix_ma1sd_container_image_self_build_branch }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_ma1sd_git_pull_results - name: Ensure ma1sd Docker image is built diff --git a/roles/matrix-mailer/tasks/setup_mailer.yml b/roles/matrix-mailer/tasks/setup_mailer.yml index 5ad02a577..d2f8f9171 100644 --- a/roles/matrix-mailer/tasks/setup_mailer.yml +++ b/roles/matrix-mailer/tasks/setup_mailer.yml @@ -29,6 +29,8 @@ dest: "{{ matrix_mailer_container_image_self_build_src_files_path }}" version: "{{ matrix_mailer_container_image_self_build_version }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_mailer_git_pull_results when: "matrix_mailer_enabled|bool and matrix_mailer_container_image_self_build|bool" diff --git a/roles/matrix-postgres/tasks/util/migrate_db_to_postgres.yml b/roles/matrix-postgres/tasks/util/migrate_db_to_postgres.yml index 73acb4330..90f73dba8 100644 --- a/roles/matrix-postgres/tasks/util/migrate_db_to_postgres.yml +++ b/roles/matrix-postgres/tasks/util/migrate_db_to_postgres.yml @@ -38,6 +38,8 @@ dest: "{{ matrix_postgres_pgloader_container_image_self_build_src_path }}" version: "{{ matrix_postgres_pgloader_container_image_self_build_repo_branch }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_postgres_pgloader_git_pull_results # If `stable` is used, we hit an error when processing /opt/src/pgloader/build/quicklisp/dists/quicklisp/software/uax-15-20201220-git/data/CompositionExclusions.txt: diff --git a/roles/matrix-registration/tasks/setup_install.yml b/roles/matrix-registration/tasks/setup_install.yml index 2b5beafa0..6ff2de302 100644 --- a/roles/matrix-registration/tasks/setup_install.yml +++ b/roles/matrix-registration/tasks/setup_install.yml @@ -63,6 +63,8 @@ dest: "{{ matrix_registration_docker_src_files_path }}" version: "{{ matrix_registration_container_image_self_build_branch }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_registration_git_pull_results when: "matrix_registration_container_image_self_build|bool" diff --git a/roles/matrix-synapse-admin/tasks/setup.yml b/roles/matrix-synapse-admin/tasks/setup.yml index 2243706be..f83ccdc3f 100644 --- a/roles/matrix-synapse-admin/tasks/setup.yml +++ b/roles/matrix-synapse-admin/tasks/setup.yml @@ -22,6 +22,8 @@ dest: "{{ matrix_synapse_admin_docker_src_files_path }}" version: "{{ matrix_synapse_admin_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_synapse_admin_git_pull_results when: "matrix_synapse_admin_enabled|bool and matrix_synapse_admin_container_image_self_build|bool" diff --git a/roles/matrix-synapse/tasks/synapse/setup_install.yml b/roles/matrix-synapse/tasks/synapse/setup_install.yml index deedd7bde..2302a6f23 100644 --- a/roles/matrix-synapse/tasks/synapse/setup_install.yml +++ b/roles/matrix-synapse/tasks/synapse/setup_install.yml @@ -25,6 +25,8 @@ dest: "{{ matrix_synapse_docker_src_files_path }}" version: "{{ matrix_synapse_docker_image.split(':')[1] }}" force: "yes" + become: true + become_user: "{{ matrix_user_username }}" register: matrix_synapse_git_pull_results - name: Check if Synapse Docker image exists From 7e062328ff420062aa919cb74f14e2dff698e78d Mon Sep 17 00:00:00 2001 From: Kim Brose Date: Thu, 14 Apr 2022 15:01:40 +0200 Subject: [PATCH 30/83] Upgrade Hookshot (1.4.0 -> 1.5.0) --- roles/matrix-bridge-hookshot/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-hookshot/defaults/main.yml b/roles/matrix-bridge-hookshot/defaults/main.yml index a55b995d1..45807ba9c 100644 --- a/roles/matrix-bridge-hookshot/defaults/main.yml +++ b/roles/matrix-bridge-hookshot/defaults/main.yml @@ -10,7 +10,7 @@ matrix_hookshot_container_image_self_build: false matrix_hookshot_container_image_self_build_repo: "https://github.com/matrix-org/matrix-hookshot.git" matrix_hookshot_container_image_self_build_branch: "{{ 'main' if matrix_hookshot_version == 'latest' else matrix_hookshot_version }}" -matrix_hookshot_version: 1.4.0 +matrix_hookshot_version: 1.5.0 matrix_hookshot_docker_image: "{{ matrix_hookshot_docker_image_name_prefix }}halfshot/matrix-hookshot:{{ matrix_hookshot_version }}" matrix_hookshot_docker_image_name_prefix: "{{ 'localhost/' if matrix_hookshot_container_image_self_build else matrix_container_global_registry_prefix }}" From d5f4c17146dbe87d44770dc9dbf215b24eb2375b Mon Sep 17 00:00:00 2001 From: Aine Date: Thu, 14 Apr 2022 18:06:54 +0300 Subject: [PATCH 31/83] matrix-backup-borg: integrate postgres backups, add extended borgmatic configuration --- group_vars/matrix_servers | 26 ++++++++++-- roles/matrix-backup-borg/defaults/main.yml | 42 ++++++++++++++++--- .../tasks/setup_install.yml | 9 ++-- .../util/detect_existing_postgres_version.yml | 42 +++++++++++++++++++ .../templates/config.yaml.j2 | 10 +++++ setup.yml | 2 +- 6 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 roles/matrix-backup-borg/tasks/util/detect_existing_postgres_version.yml diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 85b8a7013..91324025e 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -1081,13 +1081,33 @@ matrix_bot_mjolnir_systemd_required_services_list: | ###################################################################### matrix_backup_borg_enabled: false +matrix_backup_borg_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm32', 'arm64'] }}" +matrix_backup_borg_postgresql_enabled: "{{ matrix_postgres_enabled }}" +matrix_backup_borg_postgresql_databases_hostname: "{{ matrix_postgres_connection_hostname }}" +matrix_backup_borg_postgresql_databases_username: "{{ matrix_postgres_connection_username }}" +matrix_backup_borg_postgresql_databases_password: "{{ matrix_postgres_connection_password }}" +matrix_backup_borg_postgresql_databases_port: "{{ matrix_postgres_connection_port }}" +matrix_backup_borg_postgresql_databases: | + {{ + (([{ + 'name': matrix_synapse_database_database + }] if (matrix_synapse_enabled and matrix_synapse_database_database == matrix_postgres_db_name and matrix_synapse_database_host == 'matrix-postgres') else []) + + + matrix_postgres_additional_databases)|map(attribute='name')|list + }} matrix_backup_borg_location_source_directories: - "{{ matrix_base_data_path }}" matrix_backup_borg_location_exclude_patterns: | {{ - { - 'synapse': ["{{ matrix_synapse_media_store_path }}/local_thumbnails", "{{ matrix_synapse_media_store_path }}/remote_thumbnail", "{{ matrix_synapse_media_store_path }}/url_cache", "{{ matrix_synapse_media_store_path }}/url_cache_thumbnails"], - }[matrix_homeserver_implementation] + ([matrix_synapse_media_store_path + '/local_thumbnails', matrix_synapse_media_store_path + '/remote_thumbnail', matrix_synapse_media_store_path + '/url_cache', matrix_synapse_media_store_path + '/url_cache_thumbnails'] if matrix_homeserver_implementation == 'synapse' else []) + + + ([matrix_postgres_data_path] if matrix_postgres_enabled else []) + }} +matrix_backup_borg_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) }} ###################################################################### diff --git a/roles/matrix-backup-borg/defaults/main.yml b/roles/matrix-backup-borg/defaults/main.yml index c8a09f7f4..374b5befa 100644 --- a/roles/matrix-backup-borg/defaults/main.yml +++ b/roles/matrix-backup-borg/defaults/main.yml @@ -1,17 +1,17 @@ --- matrix_backup_borg_enabled: true +matrix_backup_borg_base_path: "{{ matrix_base_data_path }}/backup-borg" +matrix_backup_borg_config_path: "{{ matrix_backup_borg_base_path }}/config" + matrix_backup_borg_container_image_self_build: false matrix_backup_borg_docker_repo: "https://github.com/borgmatic-collective/docker-borgmatic" -matrix_backup_borg_docker_src_files_path: "{{ matrix_base_data_path }}/borg/docker-src" +matrix_backup_borg_docker_src_files_path: "{{ matrix_backup_borg_base_path }}/docker-src" -matrix_backup_borg_version: latest +matrix_backup_borg_version: "" matrix_backup_borg_docker_image: "{{ matrix_backup_borg_docker_image_name_prefix }}etke.cc/borgmatic:{{ matrix_backup_borg_version }}" matrix_backup_borg_docker_image_name_prefix: "{{ 'localhost/' if matrix_backup_borg_container_image_self_build else 'registry.gitlab.com/' }}" -matrix_backup_borg_docker_image_force_pull: "{{ matrix_backup_borg_docker_image.endswith(':latest') }}" - -matrix_backup_borg_base_path: "{{ matrix_base_data_path }}/backup-borg" -matrix_backup_borg_config_path: "{{ matrix_backup_borg_base_path }}/config" +matrix_backup_borg_docker_image_force_pull: "{{ matrix_backup_borg_docker_image.endswith(':latest') or matrix_backup_borg_version|default('') == '' }}" # A list of extra arguments to pass to the container matrix_backup_borg_container_extra_arguments: [] @@ -28,6 +28,14 @@ matrix_backup_borg_schedule: "*-*-* 04:00:00" # what directories should be added to backup matrix_backup_borg_location_source_directories: [] +# postgres db backup +matrix_backup_borg_postgresql_enabled: true +matrix_backup_borg_postgresql_databases: [] +matrix_backup_borg_postgresql_databases_hostname: "matrix-postgres" +matrix_backup_borg_postgresql_databases_username: "matrix" +matrix_backup_borg_postgresql_databases_password: "" +matrix_backup_borg_postgresql_databases_port: 5432 + # target repositories matrix_backup_borg_location_repositories: [] @@ -61,3 +69,25 @@ matrix_backup_borg_retention_keep_yearly: 2 # retention prefix matrix_backup_borg_retention_prefix: "matrix-" + +# Default borgmatic configuration template which covers the generic use case. +# You can customize it by controlling the various variables inside it. +# +# For a more advanced customization, you can extend the default (see `matrix_backup_borg_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_backup_borg_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_backup_borg_configuration_extension_yaml: | + # Your custom YAML configuration for borgmatic goes here. + # This configuration extends the default starting configuration (`matrix_borg_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_backup_borg_configuration_yaml`. + +matrix_backup_borg_configuration_extension: "{{ matrix_backup_borg_configuration_extension_yaml|from_yaml if matrix_backup_borg_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final borgmatic configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_backup_borg_configuration_yaml`. +matrix_backup_borg_configuration: "{{ matrix_backup_borg_configuration_yaml|from_yaml|combine(matrix_backup_borg_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-backup-borg/tasks/setup_install.yml b/roles/matrix-backup-borg/tasks/setup_install.yml index 90757a803..9c34c53f1 100644 --- a/roles/matrix-backup-borg/tasks/setup_install.yml +++ b/roles/matrix-backup-borg/tasks/setup_install.yml @@ -1,4 +1,7 @@ --- +- import_tasks: "{{ role_path }}/tasks/util/detect_existing_postgres_version.yml" + when: 'matrix_backup_borg_enabled|bool and matrix_backup_borg_postgresql_enabled|bool and matrix_postgres_backup_postgres_data_path != ""' + - name: Ensure borg paths exist file: path: "{{ item.path }}" @@ -11,9 +14,9 @@ - {path: "{{ matrix_backup_borg_docker_src_files_path }}", when: true} when: "item.when|bool" -- name: Ensure borg config is created - template: - src: "{{ role_path }}/templates/config.yaml.j2" +- name: Ensure borgmatic config is created + copy: + content: "{{ matrix_backup_borg_configuration|to_nice_yaml(indent=2, width=999999) }}" dest: "{{ matrix_backup_borg_config_path }}/config.yaml" owner: "{{ matrix_user_username }}" group: "{{ matrix_user_groupname }}" diff --git a/roles/matrix-backup-borg/tasks/util/detect_existing_postgres_version.yml b/roles/matrix-backup-borg/tasks/util/detect_existing_postgres_version.yml new file mode 100644 index 000000000..0d50d93f5 --- /dev/null +++ b/roles/matrix-backup-borg/tasks/util/detect_existing_postgres_version.yml @@ -0,0 +1,42 @@ +--- + +# This utility aims to determine if there is some existing Postgres version in use or not. +# If there is, it also tries to detect the Docker image that corresponds to that version. + +- name: Initialize Postgres version determination variables (default to empty) + set_fact: + matrix_backup_borg_postgresql_detection_pg_version_path: "{{ matrix_postgres_data_path }}/PG_VERSION" + matrix_backup_borg_postgresql_detected_existing: false + matrix_backup_borg_postgresql_detected_version: "" + matrix_backup_borg_version: "" + +- name: Determine existing Postgres version (check PG_VERSION file) + stat: + path: "{{ matrix_backup_borg_postgresql_detection_pg_version_path }}" + register: result_pg_version_stat + +- set_fact: + matrix_backup_borg_postgresql_detected_existing: true + when: "result_pg_version_stat.stat.exists" + +- name: Determine existing Postgres version (read PG_VERSION file) + slurp: + src: "{{ matrix_backup_borg_postgresql_detection_pg_version_path }}" + register: result_pg_version + when: matrix_backup_borg_postgresql_detected_existing|bool + +- name: Determine existing Postgres version (make sense of PG_VERSION file) + set_fact: + matrix_backup_borg_postgresql_detected_version: "{{ result_pg_version['content']|b64decode|replace('\n', '') }}" + when: matrix_backup_borg_postgresql_detected_existing|bool + +- name: Determine corresponding Docker image version to detected version + set_fact: + matrix_backup_borg_version: "{{ matrix_backup_borg_postgresql_detected_version }}" + when: "matrix_backup_borg_postgresql_detected_version == '12' or matrix_backup_borg_postgresql_detected_version.startswith('12.') or matrix_backup_borg_postgresql_detected_version == '13' or matrix_backup_borg_postgresql_detected_version.startswith('13.') or matrix_backup_borg_postgresql_detected_version == '14' or matrix_backup_borg_postgresql_detected_version.startswith('14.')" + +- name: Fail if existing Postgres version is not supported by borgmatic docker image + fail: + msg: >- + Your Postgres v{{ matrix_backup_borg_postgresql_detected_version }} is not supported. + when: "matrix_backup_borg_version == ''" diff --git a/roles/matrix-backup-borg/templates/config.yaml.j2 b/roles/matrix-backup-borg/templates/config.yaml.j2 index 89b6ab7d4..8ac2a8b2f 100644 --- a/roles/matrix-backup-borg/templates/config.yaml.j2 +++ b/roles/matrix-backup-borg/templates/config.yaml.j2 @@ -26,6 +26,16 @@ consistency: - archives hooks: +{% if matrix_backup_borg_postgresql_enabled %} + postgresql_databases: + {% for database in matrix_backup_borg_postgresql_databases %} + - name: {{ database }} + hostname: {{ matrix_backup_borg_postgresql_databases_hostname }} + username: {{ matrix_backup_borg_postgresql_databases_username }} + password: {{ matrix_backup_borg_postgresql_databases_password }} + port: {{ matrix_backup_borg_postgresql_databases_port }} + {% endfor %} +{% endif %} after_backup: - echo "Backup created." on_error: diff --git a/setup.yml b/setup.yml index 52079e32c..de86665b6 100755 --- a/setup.yml +++ b/setup.yml @@ -13,7 +13,6 @@ - matrix-postgres - matrix-redis - matrix-corporal - - matrix-backup-borg - matrix-bridge-appservice-discord - matrix-bridge-appservice-slack - matrix-bridge-appservice-webhooks @@ -62,4 +61,5 @@ - matrix-aux - matrix-postgres-backup - matrix-prometheus-postgres-exporter + - matrix-backup-borg - matrix-common-after From 5611cab71ae06c83527a8c8bb566cdec4e0b6010 Mon Sep 17 00:00:00 2001 From: joecool1029 Date: Thu, 14 Apr 2022 13:38:38 -0400 Subject: [PATCH 32/83] Update element 1.10.9 -> 1.10.10 --- roles/matrix-client-element/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-client-element/defaults/main.yml b/roles/matrix-client-element/defaults/main.yml index 34cf22a74..205f3480a 100644 --- a/roles/matrix-client-element/defaults/main.yml +++ b/roles/matrix-client-element/defaults/main.yml @@ -9,7 +9,7 @@ matrix_client_element_container_image_self_build_repo: "https://github.com/vecto # - https://github.com/vector-im/element-web/issues/19544 matrix_client_element_container_image_self_build_low_memory_system_patch_enabled: "{{ ansible_memtotal_mb < 4096 }}" -matrix_client_element_version: v1.10.9 +matrix_client_element_version: v1.10.10 matrix_client_element_docker_image: "{{ matrix_client_element_docker_image_name_prefix }}vectorim/element-web:{{ matrix_client_element_version }}" matrix_client_element_docker_image_name_prefix: "{{ 'localhost/' if matrix_client_element_container_image_self_build else matrix_container_global_registry_prefix }}" matrix_client_element_docker_image_force_pull: "{{ matrix_client_element_docker_image.endswith(':latest') }}" From a9d0cbc560a380fb9a5147101c817574d9d134ab Mon Sep 17 00:00:00 2001 From: Aine Date: Fri, 15 Apr 2022 19:38:10 +0300 Subject: [PATCH 33/83] feedback --- roles/matrix-backup-borg/defaults/main.yml | 8 ++-- .../tasks/setup_install.yml | 14 ++++++- .../util/detect_existing_postgres_version.yml | 42 ------------------- .../templates/config.yaml.j2 | 32 +++++++------- 4 files changed, 33 insertions(+), 63 deletions(-) delete mode 100644 roles/matrix-backup-borg/tasks/util/detect_existing_postgres_version.yml diff --git a/roles/matrix-backup-borg/defaults/main.yml b/roles/matrix-backup-borg/defaults/main.yml index 374b5befa..189b6042b 100644 --- a/roles/matrix-backup-borg/defaults/main.yml +++ b/roles/matrix-backup-borg/defaults/main.yml @@ -5,9 +5,10 @@ matrix_backup_borg_base_path: "{{ matrix_base_data_path }}/backup-borg" matrix_backup_borg_config_path: "{{ matrix_backup_borg_base_path }}/config" matrix_backup_borg_container_image_self_build: false -matrix_backup_borg_docker_repo: "https://github.com/borgmatic-collective/docker-borgmatic" +matrix_backup_borg_docker_repo: "https://gitlab.com/etke.cc/borgmatic" matrix_backup_borg_docker_src_files_path: "{{ matrix_backup_borg_base_path }}/docker-src" +# version determined automatically, based on postgres server version (if enabled), otherwise latest is used matrix_backup_borg_version: "" matrix_backup_borg_docker_image: "{{ matrix_backup_borg_docker_image_name_prefix }}etke.cc/borgmatic:{{ matrix_backup_borg_version }}" matrix_backup_borg_docker_image_name_prefix: "{{ 'localhost/' if matrix_backup_borg_container_image_self_build else 'registry.gitlab.com/' }}" @@ -30,6 +31,7 @@ matrix_backup_borg_location_source_directories: [] # postgres db backup matrix_backup_borg_postgresql_enabled: true +matrix_backup_borg_supported_postgres_versions: ['12', '13', '14'] matrix_backup_borg_postgresql_databases: [] matrix_backup_borg_postgresql_databases_hostname: "matrix-postgres" matrix_backup_borg_postgresql_databases_username: "matrix" @@ -55,7 +57,7 @@ matrix_backup_borg_storage_ssh_command: ssh -o "StrictHostKeyChecking accept-new matrix_backup_borg_storage_compression: lz4 # archive name format -matrix_backup_borg_storage_archive_name_format: "matrix-{now:%Y-%m-%d-%H%M%S}" +matrix_backup_borg_storage_archive_name_format: matrix-{now:%Y-%m-%d-%H%M%S} # repository passphrase matrix_backup_borg_storage_encryption_passphrase: "" @@ -68,7 +70,7 @@ matrix_backup_borg_retention_keep_monthly: 12 matrix_backup_borg_retention_keep_yearly: 2 # retention prefix -matrix_backup_borg_retention_prefix: "matrix-" +matrix_backup_borg_retention_prefix: matrix- # Default borgmatic configuration template which covers the generic use case. # You can customize it by controlling the various variables inside it. diff --git a/roles/matrix-backup-borg/tasks/setup_install.yml b/roles/matrix-backup-borg/tasks/setup_install.yml index 9c34c53f1..1903d8654 100644 --- a/roles/matrix-backup-borg/tasks/setup_install.yml +++ b/roles/matrix-backup-borg/tasks/setup_install.yml @@ -1,6 +1,16 @@ --- -- import_tasks: "{{ role_path }}/tasks/util/detect_existing_postgres_version.yml" - when: 'matrix_backup_borg_enabled|bool and matrix_backup_borg_postgresql_enabled|bool and matrix_postgres_backup_postgres_data_path != ""' +- block: + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/detect_existing_postgres_version.yml" + + - name: Fail if detected Postgres version is unsupported + fail: + msg: "You cannot use borg backup with such an old version ({{ matrix_postgres_detected_version }}) of Postgres. Consider upgrading - link to docs for upgrading Postgres: docs/maintenance-postgres.md#upgrading-postgresql" + when: "matrix_postgres_detected_version not in matrix_backup_borg_supported_postgres_versions" + + - name: Set the correct borg backup version to use + set_fact: + matrix_backup_borg_version: "{{ matrix_postgres_detected_version }}" + when: matrix_backup_borg_postgresql_enabled|bool and matrix_backup_borg_version == '' - name: Ensure borg paths exist file: diff --git a/roles/matrix-backup-borg/tasks/util/detect_existing_postgres_version.yml b/roles/matrix-backup-borg/tasks/util/detect_existing_postgres_version.yml deleted file mode 100644 index 0d50d93f5..000000000 --- a/roles/matrix-backup-borg/tasks/util/detect_existing_postgres_version.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- - -# This utility aims to determine if there is some existing Postgres version in use or not. -# If there is, it also tries to detect the Docker image that corresponds to that version. - -- name: Initialize Postgres version determination variables (default to empty) - set_fact: - matrix_backup_borg_postgresql_detection_pg_version_path: "{{ matrix_postgres_data_path }}/PG_VERSION" - matrix_backup_borg_postgresql_detected_existing: false - matrix_backup_borg_postgresql_detected_version: "" - matrix_backup_borg_version: "" - -- name: Determine existing Postgres version (check PG_VERSION file) - stat: - path: "{{ matrix_backup_borg_postgresql_detection_pg_version_path }}" - register: result_pg_version_stat - -- set_fact: - matrix_backup_borg_postgresql_detected_existing: true - when: "result_pg_version_stat.stat.exists" - -- name: Determine existing Postgres version (read PG_VERSION file) - slurp: - src: "{{ matrix_backup_borg_postgresql_detection_pg_version_path }}" - register: result_pg_version - when: matrix_backup_borg_postgresql_detected_existing|bool - -- name: Determine existing Postgres version (make sense of PG_VERSION file) - set_fact: - matrix_backup_borg_postgresql_detected_version: "{{ result_pg_version['content']|b64decode|replace('\n', '') }}" - when: matrix_backup_borg_postgresql_detected_existing|bool - -- name: Determine corresponding Docker image version to detected version - set_fact: - matrix_backup_borg_version: "{{ matrix_backup_borg_postgresql_detected_version }}" - when: "matrix_backup_borg_postgresql_detected_version == '12' or matrix_backup_borg_postgresql_detected_version.startswith('12.') or matrix_backup_borg_postgresql_detected_version == '13' or matrix_backup_borg_postgresql_detected_version.startswith('13.') or matrix_backup_borg_postgresql_detected_version == '14' or matrix_backup_borg_postgresql_detected_version.startswith('14.')" - -- name: Fail if existing Postgres version is not supported by borgmatic docker image - fail: - msg: >- - Your Postgres v{{ matrix_backup_borg_postgresql_detected_version }} is not supported. - when: "matrix_backup_borg_version == ''" diff --git a/roles/matrix-backup-borg/templates/config.yaml.j2 b/roles/matrix-backup-borg/templates/config.yaml.j2 index 8ac2a8b2f..2929db8b1 100644 --- a/roles/matrix-backup-borg/templates/config.yaml.j2 +++ b/roles/matrix-backup-borg/templates/config.yaml.j2 @@ -7,18 +7,18 @@ location: exclude_patterns: {{ matrix_backup_borg_location_exclude_patterns|to_json }} storage: - compression: {{ matrix_backup_borg_storage_compression }} - ssh_command: {{ matrix_backup_borg_storage_ssh_command }} - archive_name_format: '{{ matrix_backup_borg_storage_archive_name_format }}' - encryption_passphrase: {{ matrix_backup_borg_storage_encryption_passphrase }} + compression: {{ matrix_backup_borg_storage_compression|to_json }} + ssh_command: {{ matrix_backup_borg_storage_ssh_command|to_json }} + archive_name_format: {{ matrix_backup_borg_storage_archive_name_format|to_json }} + encryption_passphrase: {{ matrix_backup_borg_storage_encryption_passphrase|to_json }} retention: - keep_hourly: {{ matrix_backup_borg_retention_keep_hourly }} - keep_daily: {{ matrix_backup_borg_retention_keep_daily }} - keep_weekly: {{ matrix_backup_borg_retention_keep_weekly }} - keep_monthly: {{ matrix_backup_borg_retention_keep_monthly }} - keep_yearly: {{ matrix_backup_borg_retention_keep_yearly }} - prefix: '{{ matrix_backup_borg_retention_prefix }}' + keep_hourly: {{ matrix_backup_borg_retention_keep_hourly|to_json }} + keep_daily: {{ matrix_backup_borg_retention_keep_daily|to_json }} + keep_weekly: {{ matrix_backup_borg_retention_keep_weekly|to_json }} + keep_monthly: {{ matrix_backup_borg_retention_keep_monthly|to_json }} + keep_yearly: {{ matrix_backup_borg_retention_keep_yearly|to_json }} + prefix: {{ matrix_backup_borg_retention_prefix|to_json }} consistency: checks: @@ -26,14 +26,14 @@ consistency: - archives hooks: -{% if matrix_backup_borg_postgresql_enabled %} +{% if matrix_backup_borg_postgresql_enabled and matrix_backup_borg_postgresql_databases|length > 0 %} postgresql_databases: {% for database in matrix_backup_borg_postgresql_databases %} - - name: {{ database }} - hostname: {{ matrix_backup_borg_postgresql_databases_hostname }} - username: {{ matrix_backup_borg_postgresql_databases_username }} - password: {{ matrix_backup_borg_postgresql_databases_password }} - port: {{ matrix_backup_borg_postgresql_databases_port }} + - name: {{ database|to_json }} + hostname: {{ matrix_backup_borg_postgresql_databases_hostname|to_json }} + username: {{ matrix_backup_borg_postgresql_databases_username|to_json }} + password: {{ matrix_backup_borg_postgresql_databases_password|to_json }} + port: {{ matrix_backup_borg_postgresql_databases_port|to_json }} {% endfor %} {% endif %} after_backup: From c520a758ec78f035a616195a141858ad11e2dbe5 Mon Sep 17 00:00:00 2001 From: Aine Date: Fri, 15 Apr 2022 19:43:45 +0300 Subject: [PATCH 34/83] fix linter --- roles/matrix-backup-borg/tasks/setup_install.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-backup-borg/tasks/setup_install.yml b/roles/matrix-backup-borg/tasks/setup_install.yml index 1903d8654..6ef398631 100644 --- a/roles/matrix-backup-borg/tasks/setup_install.yml +++ b/roles/matrix-backup-borg/tasks/setup_install.yml @@ -9,7 +9,7 @@ - name: Set the correct borg backup version to use set_fact: - matrix_backup_borg_version: "{{ matrix_postgres_detected_version }}" + matrix_backup_borg_version: "{{ matrix_postgres_detected_version }}" when: matrix_backup_borg_postgresql_enabled|bool and matrix_backup_borg_version == '' - name: Ensure borg paths exist From 3fbbd5a52cf32a658fbb02dcc25786d580abfdf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arthur=20Brugi=C3=A8re?= <16764085+RoiArthurB@users.noreply.github.com> Date: Sun, 17 Apr 2022 14:07:50 +0700 Subject: [PATCH 35/83] Update mautrix-whatsapp 0.3.0 -> 0.3.1 --- roles/matrix-bridge-mautrix-whatsapp/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-mautrix-whatsapp/defaults/main.yml b/roles/matrix-bridge-mautrix-whatsapp/defaults/main.yml index 6aae2015e..d920be51c 100644 --- a/roles/matrix-bridge-mautrix-whatsapp/defaults/main.yml +++ b/roles/matrix-bridge-mautrix-whatsapp/defaults/main.yml @@ -8,7 +8,7 @@ matrix_mautrix_whatsapp_container_image_self_build: false matrix_mautrix_whatsapp_container_image_self_build_repo: "https://mau.dev/mautrix/whatsapp.git" matrix_mautrix_whatsapp_container_image_self_build_branch: "{{ 'master' if matrix_mautrix_whatsapp_version == 'latest' else matrix_mautrix_whatsapp_version }}" -matrix_mautrix_whatsapp_version: v0.3.0 +matrix_mautrix_whatsapp_version: v0.3.1 # See: https://mau.dev/mautrix/whatsapp/container_registry matrix_mautrix_whatsapp_docker_image: "{{ matrix_mautrix_whatsapp_docker_image_name_prefix }}mautrix/whatsapp:{{ matrix_mautrix_whatsapp_version }}" matrix_mautrix_whatsapp_docker_image_name_prefix: "{{ 'localhost/' if matrix_mautrix_whatsapp_container_image_self_build else 'dock.mau.dev/' }}" From 471806e7bdb0d510edf9276b38e1dfd9adb72c7b Mon Sep 17 00:00:00 2001 From: Lunar Date: Sun, 17 Apr 2022 20:27:04 -0500 Subject: [PATCH 36/83] Increase default async time for rust-synapse-compress-state Increase the async timeout value defaults, as larger Matrix servers need more time to complete. --- .../tasks/rust-synapse-compress-state/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/matrix-synapse/tasks/rust-synapse-compress-state/main.yml b/roles/matrix-synapse/tasks/rust-synapse-compress-state/main.yml index 1aaf3a812..219f1c986 100644 --- a/roles/matrix-synapse/tasks/rust-synapse-compress-state/main.yml +++ b/roles/matrix-synapse/tasks/rust-synapse-compress-state/main.yml @@ -11,17 +11,17 @@ - name: Set matrix_synapse_rust_synapse_compress_state_find_rooms_command_wait_time, if not provided set_fact: - matrix_synapse_rust_synapse_compress_state_find_rooms_command_wait_time: 300 + matrix_synapse_rust_synapse_compress_state_find_rooms_command_wait_time: 1800 when: "matrix_synapse_rust_synapse_compress_state_find_rooms_command_wait_time|default('') == ''" - name: Set matrix_synapse_rust_synapse_compress_state_compress_room_time, if not provided set_fact: - matrix_synapse_rust_synapse_compress_state_compress_room_time: 1800 + matrix_synapse_rust_synapse_compress_state_compress_room_time: 3600 when: "matrix_synapse_rust_synapse_compress_state_compress_room_time|default('') == ''" - name: Set matrix_synapse_rust_synapse_compress_state_psql_import_time, if not provided set_fact: - matrix_synapse_rust_synapse_compress_state_psql_import_time: 1800 + matrix_synapse_rust_synapse_compress_state_psql_import_time: 3600 when: "matrix_synapse_rust_synapse_compress_state_psql_import_time|default('') == ''" - name: Set matrix_synapse_rust_synapse_compress_state_min_state_groups_required, if not provided From 4be425c267b41ca19dd260f5e0cb2e0f456878ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliv=C3=A9r=20Falvai?= Date: Mon, 18 Apr 2022 13:10:39 +0200 Subject: [PATCH 37/83] Upgrade Telegrame bridge to 0.11.3 --- roles/matrix-bridge-mautrix-telegram/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-mautrix-telegram/defaults/main.yml b/roles/matrix-bridge-mautrix-telegram/defaults/main.yml index eb70d3fa4..65a446e00 100644 --- a/roles/matrix-bridge-mautrix-telegram/defaults/main.yml +++ b/roles/matrix-bridge-mautrix-telegram/defaults/main.yml @@ -14,7 +14,7 @@ matrix_mautrix_telegram_container_image_self_build: false matrix_mautrix_telegram_docker_repo: "https://mau.dev/mautrix/telegram.git" matrix_mautrix_telegram_docker_src_files_path: "{{ matrix_base_data_path }}/mautrix-telegram/docker-src" -matrix_mautrix_telegram_version: v0.11.2 +matrix_mautrix_telegram_version: v0.11.3 # See: https://mau.dev/mautrix/telegram/container_registry matrix_mautrix_telegram_docker_image: "dock.mau.dev/mautrix/telegram:{{ matrix_mautrix_telegram_version }}" matrix_mautrix_telegram_docker_image_force_pull: "{{ matrix_mautrix_telegram_docker_image.endswith(':latest') }}" From 949fdd0135c1f8b98ee59271d904d0d8f6ca2201 Mon Sep 17 00:00:00 2001 From: Adriel Sand <61815862+thebiblelover7@users.noreply.github.com> Date: Mon, 18 Apr 2022 14:48:37 +0300 Subject: [PATCH 38/83] matrix-backup-borg: add ability to backup to unencrypted repositories (#1754) * matrix-backup-borg: added option for unencrypted repo access * matrix-backup-borg: fixed requiring password for unencrypted repos; changed variable name * matrix-backup-borg: add unknown_unencrypted_repo_access_is_ok to config.yaml.j2 * matrix-backup-borg: cleanup comments * matrix-backup-borg: add documentation regarding unencrypted repos * matrix-backup-borg: add readability and ease of use to code * matrix-backup-borg: fix wording in defaults/main.yml comment * matrix-backup-borg: add quotes to docs * Indicate the variable to use Co-authored-by: Slavi Pantaleev --- docs/configuring-playbook-backup-borg.md | 2 ++ roles/matrix-backup-borg/defaults/main.yml | 5 ++++- roles/matrix-backup-borg/tasks/validate_config.yml | 7 ++++++- roles/matrix-backup-borg/templates/config.yaml.j2 | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/configuring-playbook-backup-borg.md b/docs/configuring-playbook-backup-borg.md index 70466a6e6..4177c561e 100644 --- a/docs/configuring-playbook-backup-borg.md +++ b/docs/configuring-playbook-backup-borg.md @@ -51,6 +51,8 @@ where: * PASSPHRASE - passphrase used for encrypting backups, you may generate it with `pwgen -s 64 1` or use any password manager * PRIVATE KEY - the content of the **private** part of the SSH key you created before +To backup without encryption, add `matrix_backup_borg_encryption: 'none'` to your vars. This will also enable the `matrix_backup_borg_unknown_unencrypted_repo_access_is_ok` variable. + `matrix_backup_borg_location_source_directories` defines the list of directories to back up: it's set to `{{ matrix_base_data_path }}` by default, which is the base directory for every service's data, such as Synapse, Postgres and the bridges. You might want to exclude certain directories or file patterns from the backup using the `matrix_backup_borg_location_exclude_patterns` variable. Check the `roles/matrix-backup-borg/defaults/main.yml` file for the full list of available options. diff --git a/roles/matrix-backup-borg/defaults/main.yml b/roles/matrix-backup-borg/defaults/main.yml index 189b6042b..906522c24 100644 --- a/roles/matrix-backup-borg/defaults/main.yml +++ b/roles/matrix-backup-borg/defaults/main.yml @@ -44,12 +44,15 @@ matrix_backup_borg_location_repositories: [] # exclude following paths: matrix_backup_borg_location_exclude_patterns: [] -# borg encryption mode, only repokey-* is supported +# borg encryption mode, only "repokey-*" and "none" are supported matrix_backup_borg_encryption: repokey-blake2 # private ssh key used to connect to the borg repo matrix_backup_borg_ssh_key_private: "" +# allow unencrypted repo access +matrix_backup_borg_unknown_unencrypted_repo_access_is_ok: "{{ matrix_backup_borg_encryption == 'none' }}" + # borg ssh command with ssh key matrix_backup_borg_storage_ssh_command: ssh -o "StrictHostKeyChecking accept-new" -i /etc/borgmatic.d/sshkey diff --git a/roles/matrix-backup-borg/tasks/validate_config.yml b/roles/matrix-backup-borg/tasks/validate_config.yml index 4d3fb1c8a..84b78d1ea 100644 --- a/roles/matrix-backup-borg/tasks/validate_config.yml +++ b/roles/matrix-backup-borg/tasks/validate_config.yml @@ -7,4 +7,9 @@ with_items: - "matrix_backup_borg_ssh_key_private" - "matrix_backup_borg_location_repositories" - - "matrix_backup_borg_storage_encryption_passphrase" + +- name: Fail if encryption passphrase is undefined unless repository is unencrypted + fail: + msg: >- + You need to define a required passphrase using the `matrix_backup_borg_storage_encryption_passphrase` variable. + when: "matrix_backup_borg_storage_encryption_passphrase == '' and matrix_backup_borg_encryption != 'none'" diff --git a/roles/matrix-backup-borg/templates/config.yaml.j2 b/roles/matrix-backup-borg/templates/config.yaml.j2 index 2929db8b1..210b7a65a 100644 --- a/roles/matrix-backup-borg/templates/config.yaml.j2 +++ b/roles/matrix-backup-borg/templates/config.yaml.j2 @@ -11,6 +11,7 @@ storage: ssh_command: {{ matrix_backup_borg_storage_ssh_command|to_json }} archive_name_format: {{ matrix_backup_borg_storage_archive_name_format|to_json }} encryption_passphrase: {{ matrix_backup_borg_storage_encryption_passphrase|to_json }} + unknown_unencrypted_repo_access_is_ok: {{ matrix_backup_borg_unknown_unencrypted_repo_access_is_ok|to_json }} retention: keep_hourly: {{ matrix_backup_borg_retention_keep_hourly|to_json }} From 15ce32a30cea46627ae753f28c7bd103250e6eb7 Mon Sep 17 00:00:00 2001 From: Aine Date: Mon, 18 Apr 2022 19:37:14 +0300 Subject: [PATCH 39/83] update honoroit 0.9.6 -> 0.9.7 --- roles/matrix-bot-honoroit/defaults/main.yml | 11 ++++++++++- roles/matrix-bot-honoroit/templates/env.j2 | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/roles/matrix-bot-honoroit/defaults/main.yml b/roles/matrix-bot-honoroit/defaults/main.yml index 90bfa2696..9e7cf2b5c 100644 --- a/roles/matrix-bot-honoroit/defaults/main.yml +++ b/roles/matrix-bot-honoroit/defaults/main.yml @@ -8,7 +8,7 @@ matrix_bot_honoroit_container_image_self_build: false matrix_bot_honoroit_docker_repo: "https://gitlab.com/etke.cc/honoroit.git" matrix_bot_honoroit_docker_src_files_path: "{{ matrix_base_data_path }}/honoroit/docker-src" -matrix_bot_honoroit_version: v0.9.6 +matrix_bot_honoroit_version: v0.9.7 matrix_bot_honoroit_docker_image: "{{ matrix_bot_honoroit_docker_image_name_prefix }}honoroit:{{ matrix_bot_honoroit_version }}" matrix_bot_honoroit_docker_image_name_prefix: "{{ 'localhost/' if matrix_bot_honoroit_container_image_self_build else 'registry.gitlab.com/etke.cc/' }}" matrix_bot_honoroit_docker_image_force_pull: "{{ matrix_bot_honoroit_docker_image.endswith(':latest') }}" @@ -96,6 +96,15 @@ matrix_bot_honoroit_text_prefix_done: '' # Text: greetings matrix_bot_honoroit_text_greetings: '' +# Text: invite +matrix_bot_honoroit_text_invite: '' + +# Text: join +matrix_bot_honoroit_text_join: '' + +# Text: leave +matrix_bot_honoroit_text_leave: '' + # Text: error matrix_bot_honoroit_text_error: '' diff --git a/roles/matrix-bot-honoroit/templates/env.j2 b/roles/matrix-bot-honoroit/templates/env.j2 index 37719d032..7f1eef5b8 100644 --- a/roles/matrix-bot-honoroit/templates/env.j2 +++ b/roles/matrix-bot-honoroit/templates/env.j2 @@ -11,6 +11,9 @@ HONOROIT_CACHESIZE={{ matrix_bot_honoroit_cachesize }} HONOROIT_TEXT_PREFIX_OPEN={{ matrix_bot_honoroit_text_prefix_open }} HONOROIT_TEXT_PREFIX_DONE={{ matrix_bot_honoroit_text_prefix_done }} HONOROIT_TEXT_GREETINGS={{ matrix_bot_honoroit_text_greetings }} +HONOROIT_TEXT_INVITE={{ matrix_bot_honoroit_text_invite }} +HONOROIT_TEXT_JOIN={{ matrix_bot_honoroit_text_join }} +HONOROIT_TEXT_LEAVE={{ matrix_bot_honoroit_text_leave }} HONOROIT_TEXT_ERROR={{ matrix_bot_honoroit_text_error }} HONOROIT_TEXT_EMPTYROOM={{ matrix_bot_honoroit_text_emptyroom }} HONOROIT_TEXT_DONE={{ matrix_bot_honoroit_text_done }} From 949228eaf8e1bc5697d60c6574503e4d8141cbe2 Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 19 Apr 2022 14:41:31 +0300 Subject: [PATCH 40/83] update synapse 1.56.0 -> 1.57.0 --- roles/matrix-synapse/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-synapse/defaults/main.yml b/roles/matrix-synapse/defaults/main.yml index 9e3326695..1fb247a04 100644 --- a/roles/matrix-synapse/defaults/main.yml +++ b/roles/matrix-synapse/defaults/main.yml @@ -9,7 +9,7 @@ matrix_synapse_container_image_self_build_repo: "https://github.com/matrix-org/s matrix_synapse_docker_image: "{{ matrix_synapse_docker_image_name_prefix }}matrixdotorg/synapse:{{ matrix_synapse_docker_image_tag }}" matrix_synapse_docker_image_name_prefix: "{{ 'localhost/' if matrix_synapse_container_image_self_build else matrix_container_global_registry_prefix }}" -matrix_synapse_version: v1.56.0 +matrix_synapse_version: v1.57.0 matrix_synapse_docker_image_tag: "{{ matrix_synapse_version }}" matrix_synapse_docker_image_force_pull: "{{ matrix_synapse_docker_image.endswith(':latest') }}" From f0842d7226f239d434d7e40911e5bbd2cd78ad2b Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 19 Apr 2022 17:28:43 +0300 Subject: [PATCH 41/83] Document that upgrading to Synapse v1.57 may be dangerous in some instances Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1766 --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c68ed63d..d8de53ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# 2022-04-19 + +## (Compatibility Break) Upgrading to Synapse v1.57 on setups using workers may require manual action + +If you're running a worker setup for Synapse (`matrix_synapse_workers_enabled: true`), the [Synapse v1.57 upgrade notes](https://github.com/matrix-org/synapse/blob/v1.57.0rc1/docs/upgrade.md#changes-to-database-schema-for-application-services) say that you may need to take special care when upgrading: + +> Synapse v1.57.0 includes a change to the way transaction IDs are managed for application services. If your deployment uses a dedicated worker for application service traffic, **it must be stopped** when the database is upgraded (which normally happens when the main process is upgraded), to ensure the change is made safely without any risk of reusing transaction IDs. + +If you're not running an `appservice` worker (`matrix_synapse_workers_preset: little-federation-helper` or `matrix_synapse_workers_appservice_workers_count: 0`), you are probably safe to upgrade as per normal, without taking any special care. + +If you are running a setup with an `appservice` worker, or otherwise want to be on the safe side, we recommend the following upgrade path: + +0. Pull the latest playbook changes +1. Stop all services (`ansible-playbook -i inventory/hosts setup.yml --tags=stop`) +2. Re-run the playbook (`ansible-playbook -i inventory/hosts setup.yml --tags=setup-all`) +3. Start Postgres (`systemctl start matrix-postgres` on the server) +4. Start the main Synapse process (`systemctl start matrix-synapse` on the server) +5. Wait a while so that Synapse can start and complete the database migrations. You can use `journalctl -fu matrix-synapse` on the server to get a clue. Waiting a few minutes should also be enough. +6. It should now be safe to start all other services. `ansible-playbook -i inventory/hosts setup.yml --tags=start` will do it for you + + # 2022-04-14 ## (Compatibility Break) Changes to `docker-src` permissions necessitating manual action From 295ef29fe0ecd03e48f80fc7b60924d8b253c561 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 19 Apr 2022 19:29:41 +0300 Subject: [PATCH 42/83] Announce borg backup support Related to: - https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1727 - https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1754 - https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1755 - https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/467 --- CHANGELOG.md | 7 +++++++ docs/configuring-playbook-backup-borg.md | 3 +++ docs/configuring-playbook-postgres-backup.md | 3 +++ docs/configuring-playbook.md | 7 +++++++ 4 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8de53ad4..b7800da08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 2022-04-19 +## Borg backup support + +Thanks to [Aine](https://gitlab.com/etke.cc) of [etke.cc](https://etke.cc/), the playbook can now set up [Borg](https://www.borgbackup.org/) backups with [borgmatic](https://torsion.org/borgmatic/) of your Matrix server. + +See our [Setting up borg backup](docs/configuring-playbook-backup-borg.md) documentation to get started. + + ## (Compatibility Break) Upgrading to Synapse v1.57 on setups using workers may require manual action If you're running a worker setup for Synapse (`matrix_synapse_workers_enabled: true`), the [Synapse v1.57 upgrade notes](https://github.com/matrix-org/synapse/blob/v1.57.0rc1/docs/upgrade.md#changes-to-database-schema-for-application-services) say that you may need to take special care when upgrading: diff --git a/docs/configuring-playbook-backup-borg.md b/docs/configuring-playbook-backup-borg.md index 4177c561e..44c970af9 100644 --- a/docs/configuring-playbook-backup-borg.md +++ b/docs/configuring-playbook-backup-borg.md @@ -8,6 +8,9 @@ You will need a remote server where borg will store the backups. There are hoste The backup will run based on `matrix_backup_borg_schedule` var (systemd timer calendar), default: 4am every day. +By default, if you're using the integrated Postgres database server (as opposed to [an external Postgres server](configuring-playbook-external-postgres.md)), Borg backups will also include dumps of your Postgres database. An alternative solution for backing up the Postgres database is [postgres backup](configuring-playbook-postgres-backup.md). If you decide to go with another solution, you can disable Postgres-backup support for Borg using the `matrix_backup_borg_postgresql_enabled` variable. + + ## Prerequisites 1. Create a new SSH key: diff --git a/docs/configuring-playbook-postgres-backup.md b/docs/configuring-playbook-postgres-backup.md index 2d878c11d..75b599c88 100644 --- a/docs/configuring-playbook-postgres-backup.md +++ b/docs/configuring-playbook-postgres-backup.md @@ -2,6 +2,9 @@ The playbook can install and configure [docker-postgres-backup-local](https://github.com/prodrigestivill/docker-postgres-backup-local) for you. +For a more complete backup solution (one that includes not only Postgres, but also other configuration/data files), you may wish to look into [borg backup](configuring-playbook-backup-borg.md) instead. + + ## Adjusting the playbook configuration Minimal working configuration (`inventory/host_vars/matrix.DOMAIN/vars.yml`) to enable Postgres backup: diff --git a/docs/configuring-playbook.md b/docs/configuring-playbook.md index 3d5e6c2c7..c842a8703 100644 --- a/docs/configuring-playbook.md +++ b/docs/configuring-playbook.md @@ -152,6 +152,13 @@ When you're done with all the configuration you'd like to do, continue with [Ins - [Setting up Mjolnir](configuring-playbook-bot-mjolnir.md) - a moderation tool/bot (optional) +### Backups + +- [Setting up borg backup](configuring-playbook-backup-borg.md) - a full Matrix server backup solution, including the Postgres database (optional) + +- [Setting up postgres backup](configuring-playbook-postgres-backup.md) - a Postgres-database backup solution (note: does not include other files) (optional) + + ### Other specialized services - [Setting up the Sygnal push gateway](configuring-playbook-sygnal.md) (optional) From 80c9551ef95f87b167bbb2f0aaf40a0e457bcc8a Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 19 Apr 2022 19:42:13 +0300 Subject: [PATCH 43/83] matrix-backup-borg - cleanup the .service from the systemd list --- roles/matrix-backup-borg/tasks/init.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-backup-borg/tasks/init.yml b/roles/matrix-backup-borg/tasks/init.yml index 0a90a2e88..a82fb7b8e 100644 --- a/roles/matrix-backup-borg/tasks/init.yml +++ b/roles/matrix-backup-borg/tasks/init.yml @@ -1,4 +1,4 @@ --- - set_fact: - matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-backup-borg.service', 'matrix-backup-borg.timer'] }}" + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-backup-borg.timer'] }}" when: matrix_backup_borg_enabled|bool From 502ea21fba3afe318f7c811d60edbe061b09765e Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 19 Apr 2022 22:01:14 +0300 Subject: [PATCH 44/83] add retires to all get_url actions --- roles/matrix-base/defaults/main.yml | 4 ++++ roles/matrix-grafana/tasks/setup.yml | 4 ++++ roles/matrix-prometheus/tasks/setup_install.yml | 4 ++++ .../tasks/ext/encryption-disabler/setup_install.yml | 4 ++++ roles/matrix-synapse/tasks/ext/rest-auth/setup_install.yml | 4 ++++ .../tasks/ext/shared-secret-auth/setup_install.yml | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/roles/matrix-base/defaults/main.yml b/roles/matrix-base/defaults/main.yml index ae39d00a5..498a6c321 100644 --- a/roles/matrix-base/defaults/main.yml +++ b/roles/matrix-base/defaults/main.yml @@ -71,6 +71,10 @@ matrix_container_global_registry_prefix: "docker.io/" matrix_container_retries_count: 10 matrix_container_retries_delay: 10 +# Each get_url will retry on failed attempt 10 times with delay of 10 seconds between each attempt. +matrix_geturl_retries_count: 10 +matrix_geturl_retries_delay: 10 + matrix_user_username: "matrix" matrix_user_groupname: "matrix" diff --git a/roles/matrix-grafana/tasks/setup.yml b/roles/matrix-grafana/tasks/setup.yml index 95a0ba53f..16b9fa659 100644 --- a/roles/matrix-grafana/tasks/setup.yml +++ b/roles/matrix-grafana/tasks/setup.yml @@ -70,6 +70,10 @@ group: "{{ matrix_user_groupname }}" with_items: "{{ matrix_grafana_dashboard_download_urls_all }}" when: matrix_grafana_enabled|bool + register: result + retries: "{{ matrix_geturl_retries_count }}" + delay: "{{ matrix_geturl_retries_delay }}" + until: result is not failed - name: Ensure matrix-grafana.service installed template: diff --git a/roles/matrix-prometheus/tasks/setup_install.yml b/roles/matrix-prometheus/tasks/setup_install.yml index e0fe8cf62..06989e7ef 100644 --- a/roles/matrix-prometheus/tasks/setup_install.yml +++ b/roles/matrix-prometheus/tasks/setup_install.yml @@ -32,6 +32,10 @@ owner: "{{ matrix_user_username }}" group: "{{ matrix_user_groupname }}" when: "matrix_prometheus_scraper_synapse_rules_enabled|bool" + register: result + retries: "{{ matrix_geturl_retries_count }}" + delay: "{{ matrix_geturl_retries_delay }}" + until: result is not failed - name: Ensure prometheus.yml installed copy: diff --git a/roles/matrix-synapse/tasks/ext/encryption-disabler/setup_install.yml b/roles/matrix-synapse/tasks/ext/encryption-disabler/setup_install.yml index dfc15a207..90411a34b 100644 --- a/roles/matrix-synapse/tasks/ext/encryption-disabler/setup_install.yml +++ b/roles/matrix-synapse/tasks/ext/encryption-disabler/setup_install.yml @@ -8,6 +8,10 @@ mode: 0440 owner: "{{ matrix_user_username }}" group: "{{ matrix_user_groupname }}" + register: result + retries: "{{ matrix_geturl_retries_count }}" + delay: "{{ matrix_geturl_retries_delay }}" + until: result is not failed - set_fact: matrix_synapse_modules: | diff --git a/roles/matrix-synapse/tasks/ext/rest-auth/setup_install.yml b/roles/matrix-synapse/tasks/ext/rest-auth/setup_install.yml index 634b1ca5e..13a64c58c 100644 --- a/roles/matrix-synapse/tasks/ext/rest-auth/setup_install.yml +++ b/roles/matrix-synapse/tasks/ext/rest-auth/setup_install.yml @@ -13,6 +13,10 @@ mode: 0440 owner: "{{ matrix_user_username }}" group: "{{ matrix_user_groupname }}" + register: result + retries: "{{ matrix_geturl_retries_count }}" + delay: "{{ matrix_geturl_retries_delay }}" + until: result is not failed - set_fact: matrix_synapse_password_providers_enabled: true diff --git a/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml b/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml index f408e2f9f..843f03703 100644 --- a/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml +++ b/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml @@ -18,6 +18,10 @@ mode: 0440 owner: "{{ matrix_user_username }}" group: "{{ matrix_user_groupname }}" + register: result + retries: "{{ matrix_geturl_retries_count }}" + delay: "{{ matrix_geturl_retries_delay }}" + until: result is not failed - set_fact: matrix_synapse_modules: | From d04767a9d65158eb4670e90c7052d0dee1a07c9d Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Wed, 20 Apr 2022 18:46:10 +0300 Subject: [PATCH 45/83] Upgrade Synapse (1.57.0 -> 1.57.1) --- roles/matrix-synapse/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-synapse/defaults/main.yml b/roles/matrix-synapse/defaults/main.yml index 1fb247a04..44b82e954 100644 --- a/roles/matrix-synapse/defaults/main.yml +++ b/roles/matrix-synapse/defaults/main.yml @@ -9,7 +9,7 @@ matrix_synapse_container_image_self_build_repo: "https://github.com/matrix-org/s matrix_synapse_docker_image: "{{ matrix_synapse_docker_image_name_prefix }}matrixdotorg/synapse:{{ matrix_synapse_docker_image_tag }}" matrix_synapse_docker_image_name_prefix: "{{ 'localhost/' if matrix_synapse_container_image_self_build else matrix_container_global_registry_prefix }}" -matrix_synapse_version: v1.57.0 +matrix_synapse_version: v1.57.1 matrix_synapse_docker_image_tag: "{{ matrix_synapse_version }}" matrix_synapse_docker_image_force_pull: "{{ matrix_synapse_docker_image.endswith(':latest') }}" From 018da4fb2517fae2d21ce583b42bf08085239743 Mon Sep 17 00:00:00 2001 From: Didier 'OdyX' Raboud Date: Wed, 20 Apr 2022 19:03:54 +0200 Subject: [PATCH 46/83] Slack appservice: Enable RTM by default It is very confusing to debug why messages only go from Matrix to Slack but not from Slack to Matrix. RTM should be enabled by default, as that's the recommended way to make this work. --- roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 b/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 index 96e689675..732b0b640 100644 --- a/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 +++ b/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 @@ -9,6 +9,9 @@ homeserver: url: "{{ matrix_appservice_slack_homeserver_url }}" media_url: "{{ matrix_appservice_slack_homeserver_media_url }}" +rtm: + enable: true + {% if matrix_appservice_slack_database_engine == 'nedb' %} dbdir: "/data" {% else %} From b2105f35ecd6f4ad0dec612d92727b809e9330b2 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 21 Apr 2022 09:58:30 +0300 Subject: [PATCH 47/83] Add comments around rtm Comments taken from https://github.com/matrix-org/matrix-appservice-slack/blob/develop/config/config.sample.yaml We should probably reconcile our configuration with that one and include comments for other fields as well. --- .../templates/config.yaml.j2 | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 b/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 index 732b0b640..911dd75e4 100644 --- a/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 +++ b/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 @@ -9,8 +9,20 @@ homeserver: url: "{{ matrix_appservice_slack_homeserver_url }}" media_url: "{{ matrix_appservice_slack_homeserver_media_url }}" +# Real Time Messaging API (RTM) +# Optional if slack_hook_port and inbound_uri_prefix are defined, required otherwise. +# rtm: - enable: true + # Use the RTM API to listen for requests, which does not require + # the bridge to listen on the hook port. + # You should leave this enabled, unless you plan to use the + # bridge exclusively for webhooks. + # + enable: true + + # Logging level specific to RTM traffic. + # + log_level: "silent" {% if matrix_appservice_slack_database_engine == 'nedb' %} dbdir: "/data" From acaebfbf6729c4a313cbea0f26180f01aab99926 Mon Sep 17 00:00:00 2001 From: borisrunakov <93043305+borisrunakov@users.noreply.github.com> Date: Thu, 21 Apr 2022 10:31:26 +0300 Subject: [PATCH 48/83] optional media cache with range requests support (#1759) --- roles/matrix-nginx-proxy/defaults/main.yml | 12 +++++++++++- .../tasks/setup_nginx_proxy.yml | 10 ++++++---- .../nginx/conf.d/matrix-synapse.conf.j2 | 19 +++++++++++++++++++ .../systemd/matrix-nginx-proxy.service.j2 | 3 +++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/roles/matrix-nginx-proxy/defaults/main.yml b/roles/matrix-nginx-proxy/defaults/main.yml index de1a31461..0aaa53edc 100644 --- a/roles/matrix-nginx-proxy/defaults/main.yml +++ b/roles/matrix-nginx-proxy/defaults/main.yml @@ -275,7 +275,7 @@ matrix_nginx_proxy_proxy_matrix_federation_api_ssl_trusted_certificate: "{{ matr # The tmpfs at /tmp needs to be large enough to handle multiple concurrent file uploads. matrix_nginx_proxy_tmp_directory_size_mb: "{{ (matrix_nginx_proxy_proxy_matrix_federation_api_client_max_body_size_mb | int) * 50 }}" - +matrix_nginx_proxy_tmp_cache_directory_size_mb: "{{ (matrix_nginx_proxy_synapse_cache_max_size_mb | int) * 2 }}" # A list of strings containing additional configuration blocks to add to the nginx server configuration (nginx.conf). # for big matrixservers to enlarge the number of open files to prevent timeouts # matrix_nginx_proxy_proxy_additional_configuration_blocks: @@ -557,6 +557,16 @@ matrix_nginx_proxy_synapse_media_repository_locations: [] matrix_nginx_proxy_synapse_user_dir_locations: [] matrix_nginx_proxy_synapse_frontend_proxy_locations: [] +# synapse content caching +matrix_nginx_proxy_synapse_cache_enabled: false +matrix_nginx_proxy_synapse_cache_path: "{{ '/tmp/synapse-cache' if matrix_nginx_proxy_enabled else matrix_nginx_proxy_data_path+'/synapse-cache' }}" +matrix_nginx_proxy_synapse_cache_keys_zone_name: "STATIC" +matrix_nginx_proxy_synapse_cache_keys_zone_size: "10m" +matrix_nginx_proxy_synapse_cache_inactive_time: "48h" +matrix_nginx_proxy_synapse_cache_max_size_mb: 1024 +matrix_nginx_proxy_synapse_cache_proxy_cache_valid_time: "24h" + + # The amount of worker processes and connections # Consider increasing these when you are expecting high amounts of traffic # http://nginx.org/en/docs/ngx_core_module.html#worker_connections diff --git a/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml b/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml index 92454e96f..30001dd29 100644 --- a/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml +++ b/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml @@ -12,15 +12,17 @@ # - name: Ensure Matrix nginx-proxy paths exist file: - path: "{{ item }}" + path: "{{ item.path }}" state: directory mode: 0750 owner: "{{ matrix_user_username }}" group: "{{ matrix_user_groupname }}" with_items: - - "{{ matrix_nginx_proxy_base_path }}" - - "{{ matrix_nginx_proxy_data_path }}" - - "{{ matrix_nginx_proxy_confd_path }}" + - {path: "{{ matrix_nginx_proxy_base_path }}", when: true} + - {path: "{{ matrix_nginx_proxy_data_path }}", when: true} + - {path: "{{ matrix_nginx_proxy_confd_path }}", when: true} + - {path: "{{ matrix_nginx_proxy_synapse_cache_path }}", when: "{{ matrix_nginx_proxy_synapse_cache_enabled and not matrix_nginx_proxy_enabled }}"} + when: item.when|bool - name: Ensure Matrix nginx-proxy configured (main config override) template: diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 index 720b50867..b15546fe6 100644 --- a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 @@ -5,6 +5,9 @@ {% set user_dir_workers = matrix_nginx_proxy_synapse_workers_list|selectattr('type', 'equalto', 'user_dir')|list %} {% set frontend_proxy_workers = matrix_nginx_proxy_synapse_workers_list|selectattr('type', 'equalto', 'frontend_proxy')|list %} {% if matrix_nginx_proxy_synapse_workers_enabled %} + {% if matrix_nginx_proxy_synapse_cache_enabled %} + proxy_cache_path {{ matrix_nginx_proxy_synapse_cache_path }} levels=1:2 keys_zone={{ matrix_nginx_proxy_synapse_cache_keys_zone_name }}:{{ matrix_nginx_proxy_synapse_cache_keys_zone_size }} inactive={{ matrix_nginx_proxy_synapse_cache_inactive_time }} max_size={{ matrix_nginx_proxy_synapse_cache_max_size_mb }}m; + {% endif %} # Round Robin "upstream" pools for workers {% if generic_workers %} @@ -95,6 +98,14 @@ server { client_body_buffer_size 25M; client_max_body_size {{ matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size_mb }}M; proxy_max_temp_file_size 0; + + {% if matrix_nginx_proxy_synapse_cache_enabled %} + proxy_buffering on; + proxy_cache {{ matrix_nginx_proxy_synapse_cache_keys_zone_name }}; + proxy_cache_valid any {{ matrix_nginx_proxy_synapse_cache_proxy_cache_valid_time }}; + proxy_force_ranges on; + add_header X-Cache-Status $upstream_cache_status; + {% endif %} } {% endfor %} {% endif %} @@ -227,6 +238,14 @@ server { client_body_buffer_size 25M; client_max_body_size {{ matrix_nginx_proxy_proxy_matrix_federation_api_client_max_body_size_mb }}M; proxy_max_temp_file_size 0; + + {% if matrix_nginx_proxy_synapse_cache_enabled %} + proxy_buffering on; + proxy_cache {{ matrix_nginx_proxy_synapse_cache_keys_zone_name }}; + proxy_cache_valid any {{ matrix_nginx_proxy_synapse_cache_proxy_cache_valid_time }}; + proxy_force_ranges on; + add_header X-Cache-Status $upstream_cache_status; + {% endif %} } {% endfor %} {% endif %} diff --git a/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 b/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 index 172a83bc9..74356ea93 100755 --- a/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 +++ b/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 @@ -22,6 +22,9 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-nginx-proxy \ --cap-drop=ALL \ --read-only \ --tmpfs=/tmp:rw,noexec,nosuid,size={{ matrix_nginx_proxy_tmp_directory_size_mb }}m \ + {% if matrix_nginx_proxy_synapse_cache_enabled %} + --tmpfs=/tmp/synapse-cache:rw,noexec,nosuid,size={{ matrix_nginx_proxy_tmp_cache_directory_size_mb }}m\ + {% endif %} --network={{ matrix_docker_network }} \ {% if matrix_nginx_proxy_container_http_host_bind_port %} -p {{ matrix_nginx_proxy_container_http_host_bind_port }}:8080 \ From 90a142439aa983c7e59c1c9598bfbed2473789ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian-Samuel=20Geb=C3=BChr?= Date: Thu, 21 Apr 2022 10:07:47 +0200 Subject: [PATCH 49/83] Add matrix-registration-bot (#1771) * Add matrix-registration-bot This adds an install and uninstall task plus helpers. The bot is disabled by default. This commit does not include documentation, yet. In short, the bot can be enabled by adding matrix_bot_matrix_registration_bot_enabled: true matrix_bot_matrix_registration_bot_matrix_user_password: "verysecret" matrix_bot_matrix_registration_bot_matrix_admin_token: "supersecret" to the host_vars * Change bot username to bot.matrix-registration-bot following convention * Address smaller remarks, fix local docker build * Switch to an env file * Add environment variables extension for additional config * Add documentation for the matrix-registration-bot * Add screenshot on how to obtain admin access token * Use bot as admin to only have one access token (bot and admin api) * Use cleaner setting of matrix_synapse_registration_requires_token * Use config file for cleaner more secure usage * Delete unneeded env * Rename vars to make usage clear * Fix typos/wording and add notice about logging out * Convert configuration to use |to_json * Reorder role includes Nothing should be after `matrix-common-after`. `matrix-bot-matrix-registration-bot` can probably be anywhere, but it makes sense to put it next to the other `matrix-bot-*` roles. * Minor group_vars/matrix_servers touchups Co-authored-by: Slavi Pantaleev --- .../obtain_admin_access_token_element.png | Bin 0 -> 210079 bytes ...ng-playbook-bot-matrix-registration-bot.md | 72 +++++++++++++++++ docs/configuring-playbook.md | 1 + group_vars/matrix_servers | 29 +++++++ .../defaults/main.yml | 49 ++++++++++++ .../tasks/init.yml | 5 ++ .../tasks/main.yml | 23 ++++++ .../tasks/setup_install.yml | 73 ++++++++++++++++++ .../tasks/setup_uninstall.yml | 36 +++++++++ .../tasks/validate_config.yml | 10 +++ .../templates/config/config.yml.j2 | 12 +++ ...rix-bot-matrix-registration-bot.service.j2 | 38 +++++++++ setup.yml | 1 + 13 files changed, 349 insertions(+) create mode 100644 docs/assets/obtain_admin_access_token_element.png create mode 100644 docs/configuring-playbook-bot-matrix-registration-bot.md create mode 100644 roles/matrix-bot-matrix-registration-bot/defaults/main.yml create mode 100644 roles/matrix-bot-matrix-registration-bot/tasks/init.yml create mode 100644 roles/matrix-bot-matrix-registration-bot/tasks/main.yml create mode 100644 roles/matrix-bot-matrix-registration-bot/tasks/setup_install.yml create mode 100644 roles/matrix-bot-matrix-registration-bot/tasks/setup_uninstall.yml create mode 100644 roles/matrix-bot-matrix-registration-bot/tasks/validate_config.yml create mode 100644 roles/matrix-bot-matrix-registration-bot/templates/config/config.yml.j2 create mode 100644 roles/matrix-bot-matrix-registration-bot/templates/systemd/matrix-bot-matrix-registration-bot.service.j2 diff --git a/docs/assets/obtain_admin_access_token_element.png b/docs/assets/obtain_admin_access_token_element.png new file mode 100644 index 0000000000000000000000000000000000000000..2204ff39b036f0aa3f5b20c470f0a55df85c4f9d GIT binary patch literal 210079 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 z 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 z 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

G!!vHWW@mF5W1&J;Rl-yFxgS|dqYP0@%D)vK zKEBHnO^$1jV1?uAv>7$x#-+>FV%$159#9n+#e zkaDEcM06Hf4;RM zogQA3@iNih=eL2;KUj3!wZ~kAqpNbGchJ(@3G=H_S|yHp)Rm8P&tkGoy*r|HIMJhA zlA=&6NZQ!@O|$-9@#M$5OT}3GqMtu=I`|phyx(o}&R&%q= z>s?D5?wmmaO|D*E>kCW?MUqXn^;`T$l%MCd#J<{UBoU2CP8^XdX`gfIPK6H7&i5&A zm}Ke_btHPGw8TWTr4j`-p{OS?PzXp_AD{S%qc^hc1f=}}Pmq^2*whUcTGB^bVhzWJ zlQC7{`37K7oY&Nep5Uy&5j_WY6 z?llbg)mF-fz!+nGPRl^~2h*rps)K$83K8QSg^4!WjF%VAUeL~_~$er(e3yN_Of@GCz@ ztyq~1^g2y#WYa3*8Po}0MB9w;i2BUWq0QrjT}-Ur)#HWkJx9>B|DZIeXI0E{Uk6>rW!uHvFzK`Y4uPE}uh_aik|jS0`mm;+N%j zB=BujtGvND7S1Af1E;oTvsWgQD614bOPA=pW24PyaiwUc$q9w|tK`_}9l5!iGMu6G zlZrn~v;zC-ajI>UMc@mMY-i8?(K57rcvB)Rn@< zbiy#h(c2o=M#IXFZZ(~gaFnum-})A&qfkm`b*h#=e&@LjMwW}nDpn**{~@J zsf-6n@S0dya-Af%167n|i>3#o)>7CsLnFU8y{U%po;2pI^!^`V)yFtB15P?MN;T4{ z+D$*5^u(!!Jn*y9$gR3WhG(zLY^M1Ss%M$-OdcQT-&C1N^*9alZD1<8G{~k~BQegK zoZD(L<~Ov1BIm(aakmg(&gG_J`Mc@ek6R7znXIcF=klw3jg3&$lUnGCE0XzU^DCLC zlxCgruEqrn_+<;euJd@-?^nK{+mzPqcOy%;?$MZm%4}XWbyj}zbW@2cR!zql=D&r@ zeBN`==?$^X-IwLc%p_VNnG3Egc(Ip3Zk;fsIB`MZ*I)ZArFEswrFWrMhWfcBY55An z>D-i`DLw^mlu4t-$o->XzTgl=XYzG~y4@?`xcNgd{WF5efP=Zbx@6d)3KIvfhCjTn z-nS$m7n9-l#JFfFr};dcS4~nr_t;wF6xHh~ER*sTVws&P|8IiK{Mb3{r51i3CzHq4 zIEu%k#uV4azQaGv2*MVn>8 zq}3B>&B>yKEiAY4m>`j=Wo(uH#9g3UGg3)yGme^-f{Fo z|KDI(X^L#Ccj){?zBM@RKjw0KX8OTrLgIU?=9Z*z)pPg1ywu?LtTfJyFwVrannR7V z=PFBTHW;(HIdoq&xFB!-TDR!a;c{H^_aEM&r#n;ERz+8^h)g^4cWLs=t^{GsDIJ`7 z4R2EnU#ow2n?lGZ%B1b_EypryAwE(S-2CAO{LVD_(>WV$pH{A;YxzyHT=5Af!NpXw z!-bu3@1)+31x)(K{rW3zUJUbHKiSt~s^sSnyDVctK3R3L`RXKgaxJ8W$H_bFbIKvF z?uH%SRllR>cVt^xepp_9m}-4W2w_uhET;2yLzm9&rlL9N^?Uzr2`fHiteCJKAsj zPGrD`*G2ff=3e75;|@1^!HK!~o{a2mUa`mA6PUdDrZu#P#8gPeCik}+mPi!9e2uWH#Iq&C( zhT+DRp0t+pHx>}VZSn+Rl!2Y%EM`kyXJ{wAk1jp;OtLN=P?H!A~RQMbZJt-KA ze{yzX)q%7CP5o|*&#a+25A~Fp7Pk#r^>4@VHf8=jO@3@m9Sw8OD~Z(At5ScK_RW8m zcJpEY#7|oO3oM-?&+DfI66bvxRtYR};NeAAITv+B){K2mla%iPw>$r24x3s;`S1OT z*Xy&B{PxUm=6;qvaAvYR89aRKNyZ*$z+lGyvfUtRE2`8r)(9J`#LTJ;{LKG|yPpKt{@?Eune$D} z^}fXXOG96bGGQ)lUEZ52BWG7`(Rj1SBi1TQnKoZcge%s+`^+xK-%pNi5>G5smJA=U z*Pu%77~wa{MS6De7Z7lf{^h@U%`Mq+{wSg6zbHaT z-hPbCYv_{r;r}|y$U^JUbz;+B-}67a?wYVzB3Nru(5;0mX=*mHc=@V!i!V#M(?nVjI3P^`)2~Jan$GDaBTKDoe>lJVt<28yCp9cvey~A zuAW3oF)Xjw|0D=aD;K2zTZUD+wSB*ofOJSVD4^0Pozg8Jos*RA?rsnfNkNfr zM7nd*D2;S?cg{UnuKRiJcOU!v{_?Ja^<}LY*SxMV&T;1dCu-;$jT6=Tv(t;fD8lTz z;#(Z^@bZJhWQdL*jBg&EI|N}tzg{lrc978L@%{LI{fBuo7I2w(E;?!jFQf_x!@HI6 zgM}1t)a46>?wRUn+J756$T>VQI{6-a$BLtnu8aC2$=9!1K#s>5M<=DjW*W6u+qz2n zl>=DSV_9E^kxlbgD}3Ya4Poug;eU<1<5@~|?6@5p2WbA9cd*4%oe7<(h#K2#k@Pso z#L=Hchq!F@Sw$taXK&kUEXW+M8)W~uPzn#lZYg0N{`VtHLEtJ$0DANfXeM}gTqtTm z*R75h(Na&mJ2AA165&mw+hQMuyB@cgX{`P3(@z_pS^joC#TSVKKfA{(y!YpasdLq* zTzqTt zu!voTY$LX^HLg9b#Ia{q*{tJ-WJWdBc(6o`U!H@d@aqAO4TXG|en z*YBqkaQdY%cv$F_5pD6J@oO?9;koif-YymH%b2Ivo{h6Yhkq=M5CAQD3w!)2OZYc_ zTah*03WguG>;>59h0JVj>pEm_S(M8!Q>je1HrN`Q4w(Mlql3SGx4mHc`q@)1;oun; z;8v0B5IyX+bic^MWlGdOxiZh0OzKW%7SHtd$F&kf13(WkyfGDqW|Hu?J<^NWuXvD-mdn{zTdh%an>%>UX% z54V37anQEq#7 zSOn4iF<%7N;U?GnpGC=Oi^Z zKg(R%^Zh@+HE^{4aT!>%_3?|l_`%<$D%P~X;gIJ-H=o)dV|~j897u=*X?x#@P9vT5 zYo%A782sG9gK?~acPkMHSp_FZL>D-NScbDt>1U8_@!Y!9f%W~pc|_&)=0MHkKVJp? z$9)f+^m(c1`e>;K_ttnJxCh00_C*?5`?{p`+#P?iGa`Nb!)biXJQ}E4gQs$K6a#$3 zUFvE9Cks{qW9pPH;2(^=>bf}!A+Yw1X5_x^-s`r-t1(67p;Y#L0*>wIAa>-N?F`>; zco5swlB$jp*IZr`SUY@tZ;RUg!mhFATg>FUleZ~R!cL~27YDHI`CTE~{40X^L9(+# zdJ7>SU~;#xeqtmyI#nkM4(J0`_ea9lU+|~CMuP`9i1p?P@S7kU%;I(I)(~&Vuz13N z5ovuJ@vgOS^~aW7nSb|N)x*QI%6N?hEJ3jW?XDkg*7owrIPKEZ<3g$C$S-8bAokDQ zKAM;dtGV}0Uw82j`a;$!e1{5b)70&I@@Nl>Spxn;E}tiEYgqCeLCgyHr-DlM+qhW; z=qGI}c(w?tV5d(QAfVrGNuffOR?fXq?anCP8Cf1_r;nUQXCPYJG*!2ng>ra-MYo<8 zQAAp^wEH~?xIb82oqBaaoh2;@-OqR~kgBy?Ewq)cn-^j_IuQ-p*P-^L>hZNlm5c5U z#5}IvzgL(RUzIS_-I&v;DV;4Y;9!xrj=f^lG6!yPeEkY{I2!&t{cCh<2m&}y^@mYp z%zSZSYc2GiMUa#gL(7>~8IiZ$$fnZV9~y6J*kh&a*Tay$L+vd|!HN5%mrN+g@3;YJ zBSX_OZ&K5JgWnH-C#it)8co?26{KAvVyNi1at`CXW56l%-WI9m#w?IC4syry6VOiF z_jC$&Z^|BWA2WY@n0wT+9voE%FZUOWw&%~A?6i1&x@1VuO&Wm-yta8a1ncZY|ZI+ zfJm&`CRCntyva}Yl`Fz6f~zgDPDWcc8BH-BINk@(6K0PNRLbU~wAQ{smE+G*fhgADpXL9{%whTCL4}x#(#0>D7;aMS>gH{u` z2Q4Uubj4NZC2iH()Tvug9?D({VFi!nS6ti(%7@$INx*Diz^lzvN7TN}1`n}DtwYV(W;#XAYbR~f04AI6J;z7X%QOvM%6l0fw=ayfIZ zbNK)XF&5u1?;DjLYuEGkZ({B{`}CWB@F2;PP|85g^VRb2C|$hURNC%0d|ZzM4iO$l zjD-9)9Va69h^y}>8wpyoiTpCV*W~mKv{#NOi z-=0kjQvNr}w0wsPl1U+74?N`O&_E0&J$JT%e+m?WONYYzK^_hGwMOQiZLCHo#+OZA zM-vNi%!48@IZfJ!V`^eH#_#;dowaHCDP#e?*6p5t|N5q)yKNl`Vr-e+OOmWv)TLFD zfwhtrQC_SD+0jdAi-gf=Sj2j0x$o9Us)w@gC;MY2kpEfkZ6eBOAHoEgiB3H)Rz(H; z@eYHw&l?wP-hv>ttt8%zr;|ZPE(r1$OC^57hRZUYTYMjNgyBU%M4*$ZOYW{OI=kLJW3;L8#)I`E<~MF+TDdboWG;5t9lPZ zu%+l+-15H~h@yjLegt%`n}vH9uQ2aU5~ceFzpp3C^3UfHwO=Ah-$etz+dh>W&hX>w zU}NZGlneZ|(1*j5oDPKOigU7fGeRT>HXAwXD51MwGI<(^KwCS!fYgY+yqEmG&bRGjFUO4U*vbFtAz=wX~6ncEr{o*-3c zX+0)6w(6wfFPOj*U;lI4|MUXXeVkq@b>V9g8ihQn@ifm1H zh>j!{CT;Ec;k|9R{pscM&EJ{e9{%={606Ig925!hQb8`tA$D$z?zY}n#wuqb(yzwl z?9aoF{3A8Qs(!`kct#}v$2Z-YpI$i#1Yzmdl)tA6+)HAp8N0pipII)uypCB+LS+>a zo2p?g^`^6Z$rUpPZp?~BT_SmPYM3P@@IybFg|CM$yC_TZc)p?>P-ey%Jng<1{@Hnf z?E^x-%RH)x0~lXP8-09RHfeb7a2HLUfo}ck#~!HSMYotJ#~@FYO(&vEEKa%~9!SX+ z`|NFm2VU})d29&D{~*k(ew3GsyR-55#(=i*@JS;n(wkY|blX;&nK!RM>hd-Lr?Hw( zYBJT|5U>C^S!6lz8Gm_G^ut#r-y_~6|24_?0|Ok!f2{y6t`_qD{TgtD{rgYP z|GjDdzpb&;-JR;*5W2Aty1c)sv$$Znzf`(s)#r6|-pcvw_0kHn|C6nsYP6uE z@?8uPxcw!ywYy23R?_7nSX|}oTxv2`|9W?-ImYhxq{jWYz&!um3pqOnHB%0fKQr3w z-5=!X*}Eau-hrxlL*>46d{~s8013ql2UK_Z%5Fzk0qUz$<9?sY9kL(?CgNG@zW?i% zrRDu|ve}fBfRP$O@9hL(iQ&*QfUr(pVNe8|K?#l&Lp3+DgJOAFSVhE%Xcj zdpkS2q<*I(BUaEoNuM?dIETY^P8;mqzow?7&CO91VlbcL$$(BVAn6DcWY!&>f+0T- zSzKIPoQriuy}ZbP{F`Il8#_Cyt#{ia=)+zvJUzNBVH#GN;svDV`~V=pF#_)_ENC{R)1`_e6m8)6jyyMkR(3d)!$nN(Z{sz2VR zbl8r^V;1+ztStk>K1=4)<-Qbt=NksgO0Wai8wG{bhJ*Sbz~KYX$XOXZ9||Cm))5wW z2qdMx+}#)eF(OhcX0X=3@K>Ka^dX__`%wW6ujuILhR3%g0%pDm>5suHb)SSC4LDu3`fvK%hgxrdagvcR08$maaM4YlTydy$f*?Q_@kxJWHCAy2X5Am?uO=Zz$g3WF-8uil2Bll!irW^6o=e!=_w zQqt_|BY@!6xzL3`#>Nlh8&}``sLZ^Eh6Lfu3m6#?6GTJ&YYI#P9%}r#B*p+3Wl~uM zx8!VLSBm>9MmB^YxDI#@=l z(a<0QEFf>#Zz!0FZ;o3b-mgEw;dL-@QaLtj8F4=~BX13qY-}C|+%r5}vBAR1+P+yW zeS7rpymEiq!(*u))VL0TgjW$l9SUUfj_wvCMU&Yb!p%y22Cp8lNWj+8Fj?o98B2Ka zzutNk;_VALX7k_i1k#j?9+ZR3gFqc*(c>{_lI$HD1M2qBqZKI8zR&*cqwDKy-(?!g z<^v}%@i3Gj2;HA+sJ+N&0Cz#Rc1q69fC>nc%SS5#HK?NMe;aVL{xJxnv{t=qC#xrq zycZY}3cfWoqygEqMtW304gh8{UN(pEvn=39GVxJK;Eorz;NZ|F$XJe8=uYMUfPlg6 zRVE#3pu{8`*vgAMSQ$G27YQ^`9CO}$)6~^8F3C|TP56n zHIUkH5|jUG5oI5Gf>&GG@vxdEYFhdYV1pVxYbRw_SbzOolPi^n76Ab(=8d6D^Y`w_ zi7(X;TM?_-pJodh;@u3}9&sejQ(nbqn4yC8BnG0DagG zW0&^(w;?(Gz#5)npRS+?tvQCUVH6Aknf~{Nwk#RCyF6Ga@|Z3zT9HYQf`wxlBi&Cc zpdPNnK!)^HB{-l;F3;;Y#2;I zfgS{qUv9XX5~F&UsBZGgG_1ik!Ba5PyoYNuxUE4|Jg7n&~Y!M`6AbAbFMT|&W9w;p%7=h+J^O1=Rg z)y=5!Lf~k1v{jNJ_L;VJC^Ok&>KK4pPnfS4Z#HV$|SzlGTjQ+s>=8u=fM|K#c0 zFFTQjcc}Usw^FMebp=UcO{ewUW6=+gHjJyMNg@y-qnG$8ejD&aUAeoo@g3pEHi2^qOKd76T zn-OOwcU~Z(g|veo@q2ak6<}u2J~%AiqcO$$;|f64@z;{Z=FWjJkTIESaH}Y6NK11) zppQQVNWZV|m>!M*a-Ktu@$DTY>Az*c_%-PNhPM(42?;<#DD(s-?BzzuFsVg(d4G|S zGqGPd?{=1JTQxCZCR6=X?uY5h7oW#Cc!mgKwE<~~t3&d@$JiKR;7&-xZ2z$L@dMmr zB(i9mXd;NU$+UKfo~29Cg-=20e(7EM$qfHJ8s9=@-1i zH;qIM?DB{aoet!=c`NA$wXL8JesIC>%!*v_{7;sQ@cwLF5D?`7v^{s*1tFD!Q}1fS z7l~OlWtkcC`hX?@^vyqhG|Fb+b=oI4fOT_woKJ6%kdwEc2pC;0b*5Nw&rB zi*8|`J2GHRRJ62Y=+l-^h6SKN1@*UdFi=wl+B>{%_X1#`!r0L0(y=!X$3Gf9P8rtU0&c+c#=JHIOZ`jaK>CS}k*eiw6zkjyO zy>*wcXMIIGWx`yFqp$YN!+|uhb(#p9zi9!9x9D;`3lKqZ=JV+qXp#8Mc<-NS%>{-M zTWJ01gIlX@0^8uK_c~r-B${sWWvf{HnBL8T)K=R70?^?NVsdD}P95;}03(bTgBnL3 zx&mgSnEC@<)6a-(uw#xlx3C~w5 zN1sr)ZTDYSYy&ExZ%`0k|K}$>{E)qk*-QIOw$IJ^7OA=a`}b# z9Ka#(U)7%uC&|cgC9SL$sLCrrAGG+pBGrLpKpW6qEZt%8fhq+v7ORT}p1zvjWpn4g z5)6(w3}K5TYcoKdehU7RqpkDSN4t-3qwU3-F$2$za&KCDne?O^aNEU$``Pgizw zdXdl9Mr!8SzGsu{!f}v@4Pmj~owQ&=3@5C`#VwC=y_T5KkkI^)lr>{;OX_a3PFtPE zuXwpb<)e3;PR#*WrC6&n6`Y!zQ;Zc;ry~;ga}PYmAB`znTRcEW{b@pPPSkDZr%z8R zv#Ju~<8eW}Bb_rUNA&wX24E3+$jv;Q@PUdWkO5izh11bNCr6|wjzk-7|A_V-!e^@N zU|qbF^!7B0-WpZ88P@>RB--}1aFHjF>kk>|)4b!&&luVUSv-c%NC=A9+f5CVlMDuG zQaE@ch^UabQKZGQk-vXcsme>xTjWhzNHBlGO&ciDGf=F(enw2#P9!OAInKpylf5Cz z6~N6OD6gP*$Nd-)_eBSjU56YrzQWeT%ymnJTmBLaCq#`n-m)Jb|6H93Cn8MkX*Q#Y^jM&~Gk$n6^hzAElfP5bGI@ zqpgg{iPFtv4OXroU065hX0&Wg+kNVGFOwc?{+Z=!9W~&PV0q!5WL6VNZLxisO7((k_0970D_LV)tQ zqy)Q-dN?jdBy2O_W_|#@QL`J0no9GFFF&hFz=D33Fv#}m)j)+Y)Z@HvBaf{0tvvF8;s+iS;sccP<2y!f)!avFW4i%huEIf7gM3HP$jjji|2e9*vv|0n5T ziFFnC%I`crHx8@j{Nwl<4+u0O~LZ7@Pa?m4qZBGt`U zy@oC@M|xOvC}t6pvSZ7e(Cr8MY&059pe6rZNHXS31!=YMrz#o*WdiTWv*q)B=+<-@ zW$A3E*sF#0I3w-@1SEG}xJF&%6yh!to@l4_2KNg6>teNm_88|8X-e^G*iOB0=zIxTnDC%P2xI zoY@-RZX&kPyHbhEHy>hGFvy>}oqN4N-7efKu&6=!$L`-e|Iok210HK8EC>b&JQyOc zNY`3!Hx|Cm`qq#aDKiVEdVFWiFI&xu=VT#o=Iur9^gpzQ4k(U{0&9 zR`=c05SFl#L!T2Ayq(jy52*Of4xs2o8fDAXgK2fJd#|eU!R^F6uK&aTveoA z_(K-YsoE$!(Gl{{rMbh%cIfWfsc1(Xf*~|-y0+U|Q!hSndh<{$v{c9wDBL_>uCx)l z{~cHEF)Dd~7>ed&?g4h#|FR%GUrr$3oy9u2|K#+@!sn9 z8>}!c15$0(&J`Ybhb10FuKoIK7>*Rf%Uw4(xiMOGgq6NzW*vJb`j>tV(BY1p;#UUn z_ckq&S~ZXDT!Uu|poeB*IL&P89Tb{H3$e;ou|Tpdf}7J`wN1qQ@~PrQoPC%Q+_q@0 zmK&M+8S;QM#nuK4Ze)}PQ3_`XZTGCjUu!Ow-<~*S$h<9$OVWkkY!JColUosrT}p)% zeASu@J2&W}5E4C~njGuF^g}ALq-dTIlAJII-*qP?+0i;x|NfrDtm{SaR!6aRnDW~Q z^Q-x(AdUVoU3!;(UCSS(iScadf7Z=WS+$$&@p)H|ARPBrwos;+QIP^nx3=UF? zEIxU;ce5i8j>gYS1Qpqjy-x(2nuy;FuDsLMmVFTqpeDaZXQ0!S8)H92xYC{xqcUlB z40AzH*xF5NS18<9%;QI!PL%5XRD#Rk5O80wO5M0I6jRgUD5n_NeFH$R3@=}*Tnly~ z_KMSC?!jssORK`GfjqxNL&hFAN*JZ^JdC4*jt&qjnMRj8dq>FyuSo&6W)bng3matR zsO}G?qGMntL34KD30)LC(cIG>do%`ip`R8_4s%%wtqEvwa;aE_{FOB$wEhG#*^CE> z?3u^n1O^5o0KJKF8+3i)fnz%W48)6(oF7p^6)r+(gIhCQOXqic0AV{-G@wB1y6-}_ zItu6g)mkv^%y9BfcxNNfKPdRK0r1oLsHKx_HdU^(t zgC} z^MfKOoWCQefl7VkXfhNXq!>irpb@vP@dZ)UCFGJZ!0@zTO9y89#Vj2_T zz~wC3{U&o(ppg!6VauGH52s*h)Epq-6s#S8`FOT7qcuM(4$1zY%K`;M?I7P~;_mML zd&3~+P>QWj8~ou0FxF!2Pmze{TW@7qqRQlBR61g2yP)a|&^$e!yj&fl| zBPH%qAkG<-=8l@F^t*SFpuWZ3bvbA&o`(BO1-WL+>bGYRDBAAUr8{JAVV2dwy?^sM z*u@sZ?N&g)c;k358$uJ3R&mKATtbX!rq3G0i5Z1RugCrL= z$5V7}dBWSdZuT)8P)Lr{)hpDV=r zxD_;jF|=u6Qfz)yuHVtlXb_=XuRG3zhs;6HLBU_dCzGOe=B0CP^m@OQ(W0BNIG`l6 zl|m?%+gIN-=8BHkw1hZ^`DA83jjSWB)}PzusGj>cBqRiQe#n`_>%W%hvQ?e~Civ^u z{-&TvgT8 zC4pmXG$~Q<<(V9-H2UjT>jZNRz~vl&Mn;ej&DqvWd_}iwc&|(LcIK&&y@#kDjihoD zA1k5+wV;U+x!VtXw_T<;p5fA%z_^JlItzuRQ@k0%3ZQ5&wuKhY$?u=K9YYJmgJs!^ zCTJr+(-eOtrRU=G=XaF-vT^L0!h&}=R{HS@!Y(l+@K6~`QAYD7CSirw&$|=~F`pOr)}TLb>}6@}Eu9<;ZwARffaGx6;NRTe zPx13v>*Lb?%#9zV;B?u83qWh$dIM*BJB%zW-6sM-Bap|fISAyThYc!0XaRt)XM20E zxW-nnFJb7EFl_yT+ZC^LZEV@2`gfj(es%pdiL zVPIlA-QPZvlw_R@YVw&;kBo`}*)6~-90d$CO3y=hm8~b=jg5=7sv?0bDxjQKp3okz z7+(CR7r=Zd!_;K;_@-kC2lMRAT600sLhE_P&hNQ7F}5eE2J3hORoMJm(4`>0|Vo+YluC7Mjk^xVE*XNvxh*cj8 zK}}tKq&h3HnxF*Gqd+~O$;T&M&?{pSaU% zkOo757p}3J>EmCxM+M+^RCp@)1uyrj)*^BtPv_t4&JpFenhKW0rTpFqH83@kbH~RR zDe3P5*!Y8$>(k|2Zp3TJiy$caj(B-6>it8jsL~n^_5kB#Y1S~@7ow4A7_(;7iYAM=`eckJtW_?^!Bh(cU$dd!BqVGc zk&iouXh)Ks9zFue#Efub*R9RK-PFe%eD$n9s(7!9P+Ulu^1C=?T|cJ)5lH@SM<|71 za_t?8+T5%a2yEEjJWGB}cGNvo;Faw?bHHnnUP@&&ZVLh(5aO=)cfJ2J+%VaqVg-Wl zD8M>!fce<1G#z*lfoh$*%JCm7C~hm~@E%>_buS@#wM`n>IN{>709k_{@x%xwI?90l=W#hHUr*P=?;AzdIbczf0$zbcpc}D|}`=X4_fV zqNlJO@K#JKu#5OnBC3@`Xcl8}cky)axIM}xI!-&%meHp!oOsK0^kLdLa!H7bkUMTW zV?I)AGMTc{oX_)tT>gqe?)+Y94AnVQ*~j1THmBpbJ>Vf%>^n8aPu?v$3$`%gFv_N~ zB^`w9&Z;2<CMOqDg|^Zu{G(Yq zXWRyZf&Y_8nVQt*|%#8wI_uZ{xk+iWq*XS(>@To&C)T&g^3n6a?Tk=-=p@ z7^Ul)A?M!UEU6TKZhC=A!lXXa<@D8v;1b;-ESAPx3da@4Yp~%^h26vq8FB@Wh#V(s zgrP}}`sB00IHzTOLV6jF@@zP6xIcu}$>MCLk>N0x$OJ2y%WRGGkf2cHYYQ9=QkDsC z9p{3CqW)V7!qM)0&7ko{9SY&kQ~_|@X=NU6aBi1KZ<4n|8_Tr`POAfx6RqC8buGL( zuC|*lI#1aWlg6e?YkjFz8XB*7VNj_G&=B1-`*U)lJn{w$4;7!WiLt}O4(wmeuFSqI z*q~5mVNer0UB?SZk21Q(Mgc(Ha{o_d+I+LGzQ-ALwT;t(px1Q0Dd%TXnjDk&a%2TjLWw+eIa6 zMu;12I93eRK7yO_b;+Q6=PH~3WMfu!ThsThO-D|eK%9`CxY~0IIgTbhn(thriQ)rK z5R{Gf^wPp<2o z>!EG81BG2nTg3 zC@FxWADQp|b{165H+vH(N4Ef0Bx zE@JEVU$bkI?*ksSZ4{w~1bQDL9&8-OfA3u3)IdT=OH1W(-(<^E%%pjOcGP^)O!iz5 zh-<#l(_;pSV?(x{!o-))Sy-06lmsMLk~)4cJzS!mmup3H69s^X>L!`GxJW;L93~d- zJt3I?Se(m7bNE4BSIdLMHxv8$Gf)({zBavGOxf71pHdJNO$G<2_pyf1P&}O!s5Byp z(eR1sB@1iMe&N6n6|MFEJhAppUHvT%*63&KMZubbkgGMO{%pV>DeRM_jYoa|JNcL5nls1-m`q>wD-Mcnt-)hxTRz*3o(S zpwLP9goB@7&eZe~9o=`}rJt>UUs!B%Jd-dmG^lrW z-}yscTSo_g1{l(~YCN2(K>~Era05K;Yas4v*Wd=| z(ec=$b94QNJ&cu=l>h>JNS1l``hh}ObAP6+n8_;#NI7mTJ6{?%<_=m9E=GZF6^BRS zfgLdp3snC?KbW9%%Nr~2=2^K|;i01pe)(cVH3X!3V&`023S*o$4?aZ5L@%38^m0&PMABma_%k}7@Rhl6-Gq(iJG-U2>LEq`6W`RuA&7s}R(-d8_JU8KaaR!dG zR&Y|LQ*`&!w;s~8n8Lp(I%&fW z7GESex?Of4_lpTOOmKhGBAB4nx*31r8ar?!-)Qx! z6)zXtX`0J1kE%oPia9Z4su88(4$-|r2n5d@uOm>OEH&({yr8Ra6IL9STU;&Qt}!h0 zVwI#WG&4xA6 z7X07$!-gd$HQ@dGQ%hE-F1hY$jk*x2pD98|Pg+)D00MZn${c8hSA~`Ntr)mpqYN9}SkK=diapp!P^AYg zBSF@&{0Fmb{FENapl_)8Fv-9SzzVxVJ@;E+`a*XFN}wv-`mI9>_f5IWj+xL|FRWEn zNw@=F$v3m2f?d7TdgboG2fP3CIIxt1Kl?Q%r+&4jaBf^C2*VIS$#!?TDGJ+n@vd>_ zi=8>_9uc$wWhE-X zw*5@_ga&JJn!dUC%znM z<;${Re}3!nkb_K?r|$s2^YRvUx4`SRFR=9}B?#a$_-z-uxnXBB-Z#Gw*!YtS@2*|E z_iKEEKr{zr)j>)myEZ>u$!|LWoJQ!Z%{_9gd(e}oZnG7Mao(3f;o3iZl;gt%;VhZkTS!j79g%DK5L=FEgoo-CeBpPpeJcD3AcfRN}m zZ{dc#&B2Nk>s|8xJ|!s`Y3)WvgN60`#W>jM zFppnz(4Rlr^A{Oz6?d`xJcoU4Ruh`hl34=w3o}z*7kaxlOXvdkO#u7C`EY{W95f^? zT*nLp%|B?@K4tR}>jJ1_$1U9LEi6M>sTO}9cqc^>HI%V?B*2Y^;TE)Q-Q#k2#4zb` zN2QZ=eo@A6Mup@L)CHm?e#=k`n0sl0-_wg;i+%vv{gB#ev~>6Bv~`f06Me$?8f4*V zi+|*VwBQtNDGsE%rnectpgT~pSE%n9dpfiQJ+!&c?|TP6w0KT@eT{Wl!KLUc#FxuZ zJ}oDcb=2Wdw^0*&|M2l{_uOUmz0@q&Fdfi$7Ga${BQix_H*#<>JmXdWOT$IJ&Yb8=?%;)TI1}fS6cmP41ZktAyX_)s7T43G0FXF25sTO$ks zBHV&5ZCC!fOA+7dw+wy@TLDR8(kM@R>L4S-w?Z8JMYe%W&v zRp^z)^aRh8__q0V=}^NV5cSgT=!uVl=SsD6tc7XHG(9;+?xRiH+#aQ*&6-tMDe2Ts zt5hEEFnvYUvtn4CnVAGeMMRulR<=9Q|xJx zU&zc=V;`~DM}F>DkYW8)ZOCN}O{@JH_-RC9FYcB-V8HI?^_30mb>qE8{4^3?z=}!h zF{EZbjNcxIcg}6kr`BJ1kpb^H(5{Z96ns#g1TDt`C%Y%)0`AF|0~ozKNEbdrRKMQ` z|Bh!*O-su!Eq%!5-1cVr1iX6@VnK|7fu+)7P10);2mqKPTNrJIAEY6x3djhLh`H47 zT&0zYz8+4I916^?W^exj;mmZSl^40BTJ=R z$bD*$gvLqa6;^VM+*dZ*h~A0mCOM;($t8yy;({osnBamYkA-%{2i2f0hDr<_ApoRn z(@TA>oQ6BLd>@0jN_~z{+yRk8Fh;8qo=!WCdtUVX*FwG#OoeuoeYTfnpl{qiKx@pt zV=tAKCW+eXwCDxfo*6uRo00cRP;3*aVr*pCp&M%4&UvHun$Gc0%#7xD0r8e+bua!tS!TlvWN}aj7y1CSgB_nTFoh>8yTR=)_lsGdf$Hy{ZCt z1DqfawB$iKeBc!@UZnoxF7}_~H4~vR=>Hv?7(f0Kk#IY>a1T4ta#U4i2b%b>tqUQP zD@gL%nrX3qQ)FQwnh*>H785l$XTqTch?gX3#O7y(<3C?ZtS;nH9)9cW?x)e1kAMr~_dl7U&^tbX`VzxCUTw!PSf&S=cUACxVV0;QWU? zdi3x`;OdTr?^l&P_OGZuO6P~+f#3XUY*JNCt1DN5tc(;D37iO%E+ADudDgS$DE%Dd z4$sf40PXZR&+q9DcWbLs&n`I${8j5H5qAaC@wcivbYiiU*}{ zOJR`}Y2bIL{z^e!CVjm82q*~wInTo@Z_pi=0lLBCL5cDFkHb(pKeQC|V_RE)1xnEX z^934eK%419F~EeQ>(n{@{vO`C-^kJOxKLEYf5vu395lItGj>JJ;GPU*{#4p8FL`?3 zeO$QvvS0)fIw@b|KA9LE?lij@zK0UOwS>C`nSm*3fRU=PNFYN ziVN^+HRTozW^;tIzbk8f%JTn|`776xFXS3z3`aJL(;H`J+zTeY-pqEmF?jZxKf3DH zwm3CrirH&SO5Sxz-<>ee4YvFP-CwH$IXHkjcVDzILa{LgGH$C;Quj^dD=MKe%S@=& zpEu{@E$tu`tvpieT=5dM17Q?Jx=|OwBT3w@vA^`3c{&>zrVWUIom!N$oc zrmT!hyYLNUu>e+Ysm`u+^jxwwR!+%|>gW=q?iO=zcbB|kix|+o45|I$)XTJGK%|;^ zKgIysJLkD!zBEW=e9Dd!>fJ7K z`6fB1KR!XPkW%~p#Ke+x#KGRqnz(RaU|G>QU%uFz`VGDDi<{CrpTg?doY=sPMTLuv zpvcYMX}Hv61=7-^Ck?E4s>SDBpSXQ{m{-e8|?W_ zkoaQtF*8DNTYb`ej!pf>6qO&T?$O!fGSKxrGCJCS6^kWw6cJbHwBgFf;_P^|EWx6? zNgFTEvfaqKR!^ByjJA8_Ed=t@a!>CV0Lt6?^o22&&qUg6`#}F4K zkrYiKm8D>5@fP?XJc(8$Qg4{tbBZzF`DoyW`t6Kx5Wgp>o%W-{#w~cYo_l(a3ZcdV zTVQyX7Or;ORrP9B1wkdZ@v_y0Qwg5UCOXR#;R5F7(ugKz+At+*ViY|aVy*^%!R?OG zdy1idKMUU~Bi79M9*Neyy)}FyKxDj7cG!N)Jz{{VW7hY)FR(p9h zdvHq+(oDUA&*~yfx;oTzMDkHH6O33uQ?t z!)1SZs`}h$bo41+L=iGvlFIn#G0dv>xO4|J+4r0}d;5~*etg~yg<4ozf|fx%7Sz#^ z5qSuNh6;pzwQdLV5<3=FI)LHlIfpTDi(7J^;O8eJ^Ei=!;Gt1U>AnvNltj}O4wmf0 zfxp|=-;avSI;rlgm$bBmyN>6iVe&g?!~&%J2L=WT7xPv=$mYpB#e0g^d$o`}Xx5uO z>aGGni8yb5jl0BWWa#fVTXDnou!V-NPAGJD_j5s+26WC$Vjv&@yKYQMFWBTWz>=T1 zSQ=x!Y@7hj##Vw9h&F~8+B$XYye#~K-=#YKG?jd%+V9F#skMXQMA3T@3{$5*O8=bSuU5hH;mI7Fg~i<24_lHdk&r^VRbF{ zkVGD!Vel?ab0{WZ=Tc~B5m&r|{FqQ{ya89xM=kGcIAPP4uNk%LE-8rKOxV%MXxnP*1!una zwO+U`z*JT#m(F|-WpHMjx# z#A9i`hWg_UW!q-DJ2RQ*fFK!V+xP6FHCGsfI5{`KR0~vNa*ap9an2#cUT28O_DVn= zM589)MMBQuhofTXj<{J)>)! z&=@z1XjA5o<2^sYJT*8b|!fNkx7 zI6MmU`Dssg_f-=N?5D5P`P^)7Zl-5Cn-_?4v;kkVq=Y%0zby2Ae0%~l)*uhLIji7c zIYIYkKyYCM3($C=sa>HF5@y_`-j<<-sYDmT0M&vQ%)u@R?IRDZdx0@D);F9^!`MGs4X zDWhP|klw!ipF(^Oy1mpxg8YSDUQ5BNGc-sbYd+s~-6uXb!oDGzFHE6pV#&U%3e|cD zZSChQ3B8BgZDRhZ(oabv$}Luq6Ms(%_kie|M>swdLfmOI>U_Z@gbdL24+wa$73Jj> zN5;m&EmAu{p}4VsrvF5MW1778+2r@MXTi0fe%6Gq@SL<|%jqha)=z&9!NEaAa?ndk zN$E;3uPv41`c6Mr@5~60DS&`3OpB;l~>i1>h6YKEujn3 zCMqhc>L~zrlZ#8$Y_$jH20x!W@F@#v{%BmyzGV#l^ikaCD2VF$cy)S83Jp1y@rGR8 zEZbHaS7P2uLmgeCsck;}cVR?361mGSR0RcNND8_tl~_IB@(rU=zj?O( zt#yjmzH%GBZ#SF|jPXOlZZn107gR+_g`rn-VOCe{Z%nsEd;7xJt+*Bk zF+kl4bVbekGv>Eq5JGAM{jI04{6R<8_gczEa!JvUdZ?CX<|lJto{A>v+H-iIE#D}= z>OQMHD4b2Vw3;ifPtyIchl(ICS2daOy_7E1HLAAv|KaMbqpFO$c5k{H=>`Gm7U`6f z?(XhxknR#`0cj~o>5%U3mJaDg>MWk;eb4uebN@k%0qo7*>t1WlYyRdnag_iNOoUIo zN%Zn42R3w0PDU}A!PhC>YuF0v+7m_=?`75P%QtN-*f zlOSo!vR&b$@!$(Sk#$uqEpkzAC|ah-V!}SjhN}2>mfZGSaYwZ$x~{_>UCRE1vH3SO=6sA~k*0J7_XBC8z9TR(rtSr489N^@?Lk$Twz8$owX zGR$aVejRktynQeb{C%-&o{>SgShMq9y+S*Z)zH#1O_lv4XmKoVNUeOHo|h_S8UK2G zxd;dma|?^~$nk_x(?K3I!1>_!?OaZ?qCoiz!NM9bUb;2oV>!#CO>^QUFG8r@&ec1x zK3GQrkpUw}@y;2S=(AM@Oi#&Oe!q?&MI6kklg=3K+p0g;_;nf^;%ACzTb(lYN4iF8buz z_g*4;FSv?sKHMg%6;vKKPEx%T*!`JDJ>}Zbe2?1gzP~re@j}(CH=`|?7EA1Z>C;v` zw~>yEm?vG{!a@K5-A0ps_@^(m@%}zepfK1axC>KXNRZWqiODQxoHKfuW9TJ!>{zIU<3F^|UrWEc)Y%J?L;c8H8bQ&7VpehOrPn=PCU+LoKq;IMuUKD_INSi$#a3yvC$%?V) zXqCnJ}>N?=mEkam)bS5)L4xIvH!}(gIKO`He zS9~lorr)ASZO#4tbKF8jUw^EonHLoPh8ZeJx05x{Yc77hhp4w-9C3NLaXL4?{o+9n zbPxWAmCF^6b%EEq$HBA(kL%sLQ?FY-&+Xjs1ZCvY)pb84tga*yN1(Jci*wHpBXKQKLxz7G96({HslWZ>Pq5804tSFswAFH^MLyDM8M4 zn|90#v&@VoP%o$ZoxQ|Z9MhPpu9Qs39{HZcr5W7Ju)PNH1Ay1J&K2!P7>#PHd*M!N<$;|d@9ma^ z|6c5fQzkx&lYRk;RSHvCervgHyZ^@a%o`JvEpa4dIwmGF(5BtAsD%;a_~DbR3kR51 zd&zZgpYj#!KpLf+93RDa`R4I02{(Nriyj8T9UT*0XT7a)wN=5_g?hQ=DBKzlAwOjX7@dnox? z3g8AXD2G^+Q}t0onl^a)39B7AO_uA&zuLLFYyPT60OCQ7I+t{+(I0I{-M4EiF!JkV zcib;neTvV`>6L>>P>e$A$28?&e9!=d(~jfpU+f<-555?B-%l7mAs?7I>@VF)X#OIq z);{UXB%$&-Dl0`z(QijZDGSfHmp9`jqJhZ+4FZl~aa+18#WfgsRT?X0G*zV?BpzpH zT$R|As(E2#@OdyNk8-=GqbNih4B{!ACUf?bKJTP%_JvAq3UL&%)tY$RPFdY?Yn%1V z+Q(6X_b7dh)Yg~KHb&95Mo~)O5pyw8tiBAIOBGi>XMrxltpCt z??Z1pQu8(`(MSZNtnm5oe)}D@*`SXLHpGK=2Zk_x$Uo)-ZXFSeRU~Cbv>$lBWv7A~ zs?Uy?=C4d#RHw_$Cr=X#)?w{4yBCxu?3W*blMuLu4khu501e3*&W5!br< zw`v0;#1d8v=CizRwE-pVV^eXBdp}SDzo;LflBtVKP1gQV;h5L9Lr>tl^6(4`6C{{?T4rEDh+`kwFUx*P5vf;lW_!JkO^_b z01Ci&StviqwH>PU0MvA0K%t#HsP}_K4tc{>$(5Lx3TO|(>UTpZ08aGIMB=T6ri+BzoOFb>F+NbPP#{QU(VZcaAl zpNCn_Zw;O#$Fe#;TWOVB+ZNEJQ!zTX$Sg^B)z;S48*JIUT%iiypT|!vRDCh&SGWiO z1(zTU@?1dYg3)bNc7Ji1^_xeC;L#^tG;+m*2tFFLQm3Qn8Nv(TC;wH0-A{}jTv5at zftmYb6=U|`Sn=-zR;0jebV^2WwJn*pl7RV(6IQ2^xCGnJqWD~#8LW;g5=IBU3_g`m zOS5DXFTSsT2rtMj{4CANoGR}*Bgdd4zS_nVwy>2pjJ7DZISWXvnha=4QJ3UoNeov4 z5)ZvTl7vu0KN|h>F&WL;1^+=Z5o>7!6_v`u)+qW)*a@D^5t6nvmBc9XN~U1+;yq!H z#U#UAKFcCnb9B!yjVg9kVX!i_HX#tclsgY(z;;N?72j9(2e38mZGCS0C?0|k|bJ^JYzn`Sah;(ooCw^Gz$qe7qiw^D{M~=hT|MTb1nIJiC^z`z4|I0&z z!DeoFJ&>T=_^*4mr0Brp>xQ`Y(Q|CtQ(cnTdWs|J$w#r6k2-8Aw%tG0Al0FKn(=IE zkQ2SBTxAw$c7ID3;Ph*@lH49Dl+4;#QJIKr9>U@rYt&(6)u=Im$t3qpY z#hHo`Hife(ClDyU=o@o~vdP1#y=AJa;cDc5EWP-K9A`9+@_q64MxWy$K9~Wu%@&mo z{fV^ta=j<}NO4UOxk|w9ZAh3>3*LNt3?_yo_5kumE+E;7D5SPMBshtsBqY6B>$rx1X37IrSN*&5`G1Hb>Ps z$SyGX_mN--kH4Ayi$@R%A?>6Ke!j6lo;&(Jv#D>2_#RPO7QE^?>^VfPu`*>oN6&Zw zbpEbU-4_XEdTt0PtG;SD1rxOFuI#dUq?BUVM3um5Q+PTZ|7h`X9Zf&UYT6~XP_$o8 zy2E7Q$z-a>^rSSC*Kp?jHVeqzxhFy6+}+6x8a&Qdb~pV05aMZR^HZ#xD)N)|vV8(O zVZEB{cfIXPxzR|C-xHfp_o0H!Kidx7E*%-Z&Mq^ZKuE~@1Qys{e6p~ZU+tbLhq+l$ z8_A~HH=pWLJ%K`$@%-M3%7v*a5D#TdBfTbqOwig z)EO0S8!|4+56z9Qez0&2TL^U^_j~d{xoxI0f5XEeocC9*u7>6+>Uv????3x8!L`W^ zu_%rCY>)rn(;y6~pI3@H}fJ1!R~j-bVJLQTZ0JnxlEf07V1cPPFO zaa^vrHkv!vE!ed3-zd3h!}a()&iV}=`Ewo9Ky?+gWkmMcFCfSFcLQZgN>+A@Bj@?8 zH@k;<*C)s4E?9qXGlJ$?;|vArJ3s#?7g>h-uP#Af%(Qe{<~mc` zA8|;R8{++Z$aV&4NyTKO8F9C+xzS$xEn1V3Of4*!?uvY<3b#iVYe6e|jmffDxj{=< z$MZ6a(?(ZeXv-Sra{c+e`Q3#^`_P&|{8W`Q?BDqc*xz-ZczIhscwgZMcYEB1wE6DP zN0-ujT_pmyrOS@~zwm3@qut?6&c9#-|5W$ioG&~J@IM98CMbhe!6wHItjczOi1@wd z;{(2(q1m!GIJi|N5AWOW?q5*5C#r2TLC(TZ**-QnZ4t395_1jz*y{cVm`Z5Pfn+b> zX^VwCggjA-m7l+>R_Klb47q%ZbxR7r5(UE{ns@y1=+BQD^x>S}6RXylINdkb*bevA z^jDY)&788x1le!>PjbLMqoE%hO9nX#yI?4xHhZ?=Vy$E$vT)Wm5v}pvER`x#5an|n z^}NeUZ)QLUm#ew{!dXk`Fhn z0`Y(F$q1I?0hPxGf=7{Py0Z28shq~tW8(^;iQ;4OC7g`Nz9gaM!C z2cpZBsoRV8g6)MVV+1B8jvl#LhvxriUpu)$Eq7&D_8$r9~io?4JG6K0Io;I?{GCt6?VY94ZE>~aLvtjx$ke*_R2jn3{z z=28_13T{RUw^YSG)nZ%Zb~r=ae1%_3`=RJVoXi!yZcwt+r5loh4Il?JF?{m${OtQ? zZmG>@c{V9bXTnipj1)XfV)c8_VvObLC@sQf+s)q)ppsKfAMyroEv&iQDwGl0%HL8} z(%z-g)HjIje36c_Wyclo3&J?3>olzqQ^ROmg;-=e^o+9w0ANiQLPz9`%= z3^@g+l9%_UQN-Afhd+P2Nn6Tc+6IC|DV620Y+o?uJUMO_X77V7tb%#U#t4wfP5ey< z0Me#7*d0Dj5jy2*awWM<+WtZAgI@I)4GX!n!b^`8JG| zjww0S@(Qabsh1EU?$isq)*2Zikiwd0dfr7P$<*$CdS5IlF7OF@WcqF2bl#>n&95lkAb6 z+=xl@&9|W-Kzy|&5qV+FrnF6#$e6;^Q~6ir+1vU`yy5A*HKG^Rn|-tEvEJ@d)c)Xs z9|oZ|5C5wAej1`?tF61G7M!q?s=IeiXZTt`ywxm1;*0*5jF47`j6M@)f0->dwHCs^ zu!d&F@&(({oHogBEu4h7Kl`YMu<0|GclzHFjFaV`h`DP|zY*r{Mmtawl;xuG{Y^(I z+d23Hzn%0F3Kr8FkC$!=dr-bX!$yDs<~Qcv-Wj0u%dFSl^(zhy>_GcRhvt@^5mIku zev=ta@kzhqAKu=63k)K4+m=QSIQBs2T8omM;F~T!p2+b%6B>-)9BiQ0EtJY^uyGfu z;$j^2^~>VoqUi^xG!S@8^!Hghbo3o?SP&4C7PYlywFd_3M7itsr(kug=RBgSc(x^h zXxka=^}rUWX>L*k6g;yd5fi?McNP4q@MAXhLW4BPH>vw&0PK9MnaO_6|E0!9VdoMU z1%4eM^xnBhX1KaWpIGyc&E25Ln|wE^i4xx*9MCsDGJ>);C=C}LZe?e)wZGiIe{vul z28WDw{$VQ{ky)>~YeY`1^Hky;ao^wT&0BbY`4%@dg<9nxS)s!~>Dt+wo4WeyuiNP0 zg5A*7)qUey-dGD48(aU8!{3)fu*gpk{BNKP2^!;6I2-EOI9$p+odFbw=_XWE1YX#p z+OpTiN8fe=@YAi>`rxqAKYa^oxpj5FTsF7qv~`wu!9t1UwxvtYzL{K_^Ov2hu0D5a zZuZ&5Bf(_leJ`)G%tcd7g9sA@_80JYL4oAih%A17Pv1H`6KgHTIXFQBZ-Vu53e0Y{ z7ebCh*rd`d>G(H+abQN7mX+1!)cV;_*h?D?6?HvafM#*;Q(4Ck1sv`Rbb6NUMC74m zCc?%^cql0%KK_!vH<&qx!=j|yBM>*99J^;)N0B$n%RHSDdnfcx6kLAUg$y$WmSIgG#5AuUT4_KPp{p*~8s@&V>%=XTIn z)!739yHX*8h-5`SB~!RzAGvGl23bBzJ2Q>ro<||8lIqM`!um?|O(PglKhhiH*oJpL zcGRjbm=`~)WqOMJyH(LaddjH4@HSM1LX~;6GnZm%X^9{JJ;V_{{5E4T;br`z(qi7S zjvD-buJBtNN@}9p-F0ymJMxRZ07B4tM(O?qd|6Xe!feRBcnvfzh>;LbEg5agjwm>BQ|6FG|IQ>);o&UKYA=1;R zC;x#CKM_+;DElGEo&WEL&vM8l5nLVrb15zql^VWA_5X97xkO?U9{ulk|M&Bm;BQQ* z|9w*b^W4CvHTVyr--G|(tNQ`s|Ni~|KFWVSdXoKlgX{#pydw~vccUK~UI1bxU|W#6 z$0-T{XL2!AR>{H6E3;aoO}h3g3D3ZrAl@QKoU-~1BitisSCm0FOurFjStx{SEr?Z_ zHRV`rOx{G!of4V~Io|u!v2df*2ZfI_PGB*g{;?hU!r7&yLT~6$P0rNR)DbkWF7ffE zrJu8`Tno^$u%vR8X(Z<4j01}Xd=jGmn)1+=HY${ELnSq8z{e{q>C2m|(?CE5e0~mu z1h)b{5mA48eCZ%~M=L-!V$76MjI->6%EQ;@$Q^nj!D1)Qq8|zNK*F||Z&|vH^DRMA zqaPXU#ASzjiJKx&ye6VF^2~6{-t&7C#Hi}J`6MP1j3h>5pfmB0Lg#Cgi!qc+$`0!^ zB3fk*oV|^V)1biAYP`2Vs<7nImZxUN?IPr@b-d&gv$W(IWa0GG?4>3%|wHo4f$g2sWG_w0rS7B1T2SX&YId9OQ4n1ENMfmFyk?( z&tRj4pL^4ugPp+&8zt<6|K?l$%G9uAhP$lwXE|ow=8*BdhZ5vKZ4oArV|ii`+%T^^ zUIrKC?=Xp0uAz0~RnhzmWI)X~$GdSQyE&Q(_<`|G*Q${)yOXd*l#{V8bI%trZN^aG^9F> z_q0&Lfbw8%t3APTgMhf)HP*%DrIfU^2voJ@*TE9HpYI#~(=*N6!ir6U)pW+mEj1`w zsjD#5XFxUiM1#l?VJ;;WCGvcSuczruFfWLoX_frv@C=Whk+mrq`emR6T^>B&Tw#W0rerj zOX+X;n=>CDeSsU-bv8oaaO(VqdsEt(_qnJ2!fi%|_08$JEM@kel_q2%HHtG4yPI)7 zFE1}!&5k$)zr!sALs!?B&fJaAmQ~!>?A2E2B&;~Hp-r5U{fUDXW^+uK#p?I3T@H}P zolWCHxuw?B%GT%N%?Iy7A*R(vtn{YUhYGAB*EgLFyoCaCMY|=3#5J?H3@!qyEi!?X z=%M{l_3%K1!$u$)8rpxiobGX$BCDyz)$aAj<5b6%mCqLmcYj~*h4v2*cdw!0n?Sw= zQC|l#99Gw-U6oea#!NvZt0o}Qu<@;HHs0^SFL&Kso1kWOXPk&-unB{}rw=Xm<*Fz1 z7J~vt=e>jJa(%_C88tEg6)qs{K>EkLa;?|7<2*T0_2uOWIcM)oNAeB;kX{?imuK)$ ztqRq;|H}e!Hq^0ir2pPZZ)7=b^-28lMYMYe2Xw6n4oCg-UK8{W+S9*7e1Z-}_-?Po zq#$_yq2n%HVX`VOo8@Iyh?SGwIRL8Q>wY1)^O)%zT0->^M7@~zlQX~Wbt@1MARBuv zCmOM^Z-;tpJg>62I}+Eve2Cm78(Sx*o|126$PO3!yDwXBGQ%Ik<4C-C0X3EVaS7Mm z5vukDW^S7+;^}c6KHQ^^#rI#*_Rfy;pWh<--aEfSGuN?6XUtz(YhA6l`09nkt)vkH z{;j`!3+$xuZGXT_hw)xBzRKe*An5+F@_Eq^hLnJ@4vWCvV5kf4}bw%s7E(PLHP@gaq%6X21}lWwV^hdLkq4f)gTAtLL6 zw31(G}%z5Gkam)xl`!)scbyEH5_@oSBwMAG0#dudY3Gv$P|J*;& zU((a1U(ebATe4_8CQ%t_SsSA9jx$P=t!xiQO)0v)NY)g)KvmMu^>(Z0tr+eky$SIqpt71ow4Z@cA$wa~&5m_&Alstp;4BW=5r!Z8Z z3xgfO+oeC%6{Br-roq0E22Go|Aua9>D{<{!x9QEP(R^W^0aA_+k;i0k%c7>K`Kgs^ zJnnGg-&5aHIMb7p_7QeOEqb7|SyOp05{i36iU+3!(}FvS+@knP$49Y(UGO+yTM{qW zqpEblx5-J3p*Ddx4IENTqJPel;mm3lt{_!HqFmPB&>=P|OO&HYXQe zMFC{KH|>ooH&`x-dV${vD4njgO!@g6DwmJVuS;pN?PI4)qb-)Ct+b; zUQll|u8}qkNtPb4X@&E9f_{UCR~>LlDtI=NG&4J^oRA}VJVgj94#37boBB<=li$3w zg51tQaZaB1pz-s4=1Y)=GuGR3YirZ~8@rkL`NWJbLo-*LuhQ1c0Y^;C2>Qpi^q3eF zAlt~gT^sw~CSXmYLIfpAX&lL$l-lCUl?xfv$3p&Ao|bQ*?yEzp+@4KcrF= ztV46(Q!B+7r0VA_A%>@exP0|Ptyi&2LdJX|;wken+6plJCUHSkX@SPs9K+>`a&BUg zC-)PBohXY=Y!wrh9SbG)kd0aaUg>S2D^6rRUKMT21kzteE`NNRD|X6!1SSVIN+xt; z5x2EY6>bx zT+T|1Fr(yVmkx%E%i$?XpHox<%e?W<8P@v=o4(tMbca_c9&(DXSiIA^|7K=Cjsy;l4JxHFfMhxv=oNz0If5`&YV#0Uv8a z#g88m#o3-QAU=FPIqi_)B>afrw0^ogm0|sA&iv2KW^il_IDJhmE%gCw>)jnimzLET zRTiVuHA$^@TY{4}+|ba$K%v;h?L3iCQ=|5|<(JWxL8@%^{|uB+IXFb}C2saxj8G%kcB~%MGpVVjX7ca_@!Nu;=6Xlkc^SL2*m5 zh5hEGGMh*5D%TC}vB9L` zCdYFumFb346#%K9Sk-b&O_!9FnT+SRl<3#s=URF4lQJ8$4^5i%0~_6i`q5Y4od$cR zFe7ri%;sh@m-$x`gUs(7oyYwV1_;2frLK_}lrTvN&-32YNz^Q0i9NN@h^04u00Nha zdj~1tu1I^LP3XMOz0>yR z@;;%go1$TzN}xbDKgDD`<%(GQ%4=%WLCtiOPEo#bskt@kQTGwzMwP{UlUhoAH&)4% zG7q=XmD~D$`^Iw7EMf#}B+}jO>0C>gG9U7&#D0e)O)YMK-UY-9;mdyzUV}n3bSVIR zymlWfH0&vXrt*Z}Ac6-y!5w~kEg_-}QG`Nr$}4oX>o*vn{pY}n%$!mLWIQ?dGqh4Y zTe^*|>7M)&`ue0f&)fYarR7~UOrUR=4JCNT&24*o+j0L4Q$9=3$P-&P;Ri8iomHyj-`|FqGKfuOMl{cY6?7daIq?ogEicH|b2zX6=rR{sXdJ zT{XPglSq4movS1c=C_mxi$2JlK5h=xE_6DDFhDbP;O3iir#XRB@toOj|nn2Lf(kw_+a1B=nkK}zzw!5S`ZI8&Y2iyw%aTYHWN&8xy%ysw`SvN%2Awz(6Fo3hVuRgs{CH>3^7ObklV3sheb27wr+mYK{uXyMNreVYFe zycg+167!a*$=Rd9+f6lf!P>G0F$}{ICBPRebplO3c+Ef2*%Qx^|tD>tzxkCRe7IiD6bjHh9D;K+JPv&=E?#rvNT*O&u_fH<@ zBo&n%?pJ#$$WTSkQu)Xm!x^&^Z}PPgR<0jSA;^wFCq)hJSjw<$z=|u3(aaVShgf@+~HnaDRx9LDI*JO4b=IrZg8K75k zXTn!adYn>goVsA+(^rXpGrxX$s0a28aI+`7OlAdwkd6XH*f3T%tz|cJk~0gqAinAk zzlAykii-dmcz1{Te zsu*SB_i86l(kwR!E?pu5RcFVjO0d2ndNbSKK2orC>@`{vBb zUh_{CEv-(lQ>SOB<~4xeYdQ?|b;`i>aFl-hP5d=gq*qK?kIw(E^o17}QVRLt(R%{@D0`U}O^?OZgP<+4@m8EEY zVFk%BXY!AF2XtWL^QuKlNS>xavym!HEG`b#6mk_56|#Nc|0F3%+o^~!tX$78z!ZB? z!w&AFh+gAQo^8OqrpjSF|LvyJ>u>@v3&+c=sa&r%qd40ah+~~V6^9b>)+QYH-M5;W zn)={(Lrgke;U~k0+fg>^-m+@DR2LZTaq^a%I~=hRkB(X^Jsp^}#7<3NwckFGfkq2D zMn+R`%&4k=bvdZf{zgnhV&d+eax}sSM7ZFXE6$}#H`WiF;`;Jv02Pr|kOlLA2pJar zfxf2I;y$YD{k8qsUzZs#{&-zh#RNLV43GiwiEnO0FvRneczJp8o?Ch9?N__Tj+fuD zu$VYoBTd&042>vK{4VT$JnJ9l;NlwH-+$NL-3_wQP4^}sz7PJHIP;6Wz1mEU_{x}U z;Mt50>i$d(NQn*j{G%=pbVePR6^?ZHbVIn}(0^pahATye>1myIS{4G?5>jf27;;1k zc0iSVKp0{J2oE}_KRHKiSju`4Uym@P`O^5ZP!bL#3iN&ZD{4Mr4pw;iQa}fXY)G(J zfnrwa`Bw(LMLZ2cOWrx*i#85rR%S}#zS zy#4}aKy7SN|N94z5EfLft2B-sFVq)_UF>-|p>uU}o0y#b3K*RS7H5-VMLJjH2bYwD z1_g@uLG`fsA~WUVPdT&t#2dPkh%eEIx=@I1MLb%ALixxdT}Wb-6pL`>UROiA?6>Z7B&7XpCgkG%J8(i$uCWg-^p6^rqaQ%8hlLM-^y!vVk|8f!Qj@Xmoi|Zh5_Bp47f(Dp1 zjD;mIBX3f_Y!*hUD+c=JzTwn29+cOBg;gvn!(zK15h{bm#caSMFCL2?4QYI>@x8O)Ss?A*cE8Zj zz`(vP-=td>xLeTLu5l~d-9Lk|Ik;+S7riXLU^2)Y2Z-+e!lOBFADf7qu%$3pZ1fB! zJw9o?ATHNC;AEe6m8!{czAx|DyF8C|cxLc_L6Jj6wOwpZFCSs#loLZ>8?U|L1znK^ z#f5CVynVnv9<(F(Yc%;f?+%bCoag_IP5`wn{?6kqyDrDr_VYj3L4A zJmM?mXTjXxv6+ZIB9V)jEmiByYk6s&FuGJ zm1s=&v*Ox)gzNu$?1|S^dAjE}%@wQ1S6}OXZ4JWnyUF+*3uo)7FPp~MzK?9`71qY@ z`G@~Zu$Gk2@z`9$8X9-W%DcZ;;IGdCX<1ymUkvve|0jM@6Ezho1)=M=J3Bir zqTexsfIkBu1|#icU-=W@RhpEYJvKL*dA~0y*z^44a}MhJyrsCUAs7XP#ZHfful2t* zcOwy*NC3+L0>uq%FayKGTd~36p7*o!yG)J+1qBJIIsIVZ0hk{~^9HHb+YS3Q0`}TG zf2ynh#UXf-Tay=r?ssNY!_MKk(VN-?@Q?bs5}o>(^~a;iYM1`^$?n_lMgIWJO3&`* z3w5-YKLeaGox$8eG~y3Z@7@&s zv?q8y9o=o$B^=fvBmtlXP*lLR16#lB#7>RpXPZxQ>kOQlKYEily4kS>3iz}bm`-74g6q)xV@WFS62koDydahi_}^guI%8YSyt=_f5ODj64k|gF zQk`nIA0&>cWLk-u5b7fb^kM-2mBu#mGgvblcLF752o?XA4+dAu?53IL#f|ZIFPlk{ zM5P%0W&3n1Gy%DVb!s0TTs| z$8VQWz>mI_1q0>$qO7v|IJbgPU$u8CBV!pMP}&x|3bF*wtv(W>3VY!9JN82{7P}ro z!RP^<5XV9z02TNJTZRtLex6D$5;YYiEeMeETE&?Os*~3Bp`+85Hg!btDJN(#QHstO3T(`}R_nUWx`E#xp*7234;+)X6tK`bjc#H=}1R%I}W2!m!6I54>;^GfA(xaB^_ zyosSk4`d)I+rt3Rw97d6MF zK@T`u&8L3IgwG_%pC_aiZyqe~XAjrWBkBNGxmVqF|O&lo$GWQzgriqYdhe5<9&V2f9 z$4PT>2?~6=ghsiHaQJQgQyog9FDV&Az{U>nIj#4*0N^Rc6lLAGc0Ru_LyAUPygrj} zzic~I(N=%0SBq|77(6Y_pTnyH6l4Egy~pzEe7$vqM90)hhW39oJltlu&}H*6@XLN0yI#0^ZrBrBPYSWSaqxvj)?1Yw~YPm&6qm&|kMl z5JD>Jw0s4nk`-TX;cHBf+Qq+f<+*hQAtxvHailGi2K9AyiAhP_ROqM1;LySzTL3QqqgT6$uL^!(kPU64s@gMufBg;wHTf=BempDaZMAMkW{Xm=%Hoz#dtwt40YJ196wI%c2 z##aK-(Mz^UfXO8sK~4;=Ka%@J(@R| zzTxAeg#%}gp5{Hnz1992Mj)Ap=X0ye+bx^-o%WmIgF_#ps`Y2w>pa5Lm-(F0Wjsi; zLh5gP0fDWp_oS@1(MTV&gcKs5#`Fs zwB^1`2W0=~SNPL7l)vspLl_m1V=EQdQErJ)7mQRu$Wm**wSPYPw}&|4N}aaPe03ya zyAHulnn7EHxU9U<6grHdRmg4Ege*bmtt3#=^1?uQ+~=2M{-*X8KQe`ka_SOPRKPnG zg42PyX`IQ*7Ya%+nuye@=C~BDct9PzFh_^v3*51SUKRyK8|4}Y+syDD`H@oTPCfD8 zK1!O3!tU<4BqUjH?KAE^<|~09?)=jyMNLg%;DRL;X{Yr`z7*rv-vwnX6ga0QU6v)#QcA_9!Je zIVXvL2O>LrFp9#cL+X@z-u+R*kX!Kq04>PLsgZAY$3?B*o9_|Pf4y0c6b3IwvQ&ye zaEeyYz&mjU{ak*HUp@+@rj9W^10Sist$w1+>&mWx(N?Ehz^xDHbcu#l)AX5%PtghW z>oB_Grm{=Rl2LzXe@++g>qhW^>ynaYmibCWC1~2?5xoa0X3ev>Qjy?~tG9|GdJ^;k zN$DzxkSp=M?vL512g!mGkEQHGxv)+>Ny-uY`UdR8FS)Il&{UseNsuW=wag?9Pa2Fe z>DRv}y5AyR`8ikK%FVkC4Gj+r4jTPU87((xu+P@~nvo$YA;HV>3;{}q+R8D*$6Xv7 zS=f-3*a#twbCc3$y=|ep@V!D?w5{amR-W&J#6uSe!^Xhb)wAi;s*)5UdU=`QMbio_ z+eQ{^^17gqkS?@lX9f(Odvi=OBAb2=@34b-kQ~5NGc!c>DfWVu?A0hk^_sDo-bw_K7YV;C?PG$^v7 z4Eep|JR0ja2Ee!WxsFPf|78LEeC`6C4_|htc82Q={{HEL#X)r{ez3#m1#==mm7Atj zKdAG5+}GCkn^S!FC=vHq=#UU=vrgpY2@2zjB6~+pC`zo!C<&?Eqysg(D1$xOK3(0g-)QgYpa+18!l6ygv`hlXVu`%L?-0|bDf zW}%Q&9Lw&JEdPM6zliOTB}8&YXN7Gsfwa1)ew_YNNXr2>_ZV_So$k@1l@v>>5XG2d zzJ{HWe!K`exItGJlg@bSOKjU1^gcq3EWi7m6(`Q5&G+HM$`D_j?W+cVY5;JO`@V+f zWDCJNPFdVCV+xH-ox8aX-^{b4*uW#7NUQXQ@`$B+R8evmmyIvSgD^|oRF~bAs%!`> zs{%tRXCl>4`3uiR04Ydb&`?E}+DXPk-_n0V#5#bDSfLm9CN+P-EGQJ`wd${?S(2*b zZ#b*7HpIIw(PcRZB`+krW5T~E2{3rk`MuJr`%Y;hru34rhh2o;XcwS?K({tdZLbIk zeLGd3@JxX-MC%$dk&rUTG*5G!+3L<2G9mK)WOy`*8quX!JR}T*(D7mLYqL5vr?N)*j?Mddp56hAqDSubWnBT-#(2fl86dVlHf>L^hcZ>tYO@+yQVaYK*{Gi^;xV%e6 z_Ds$toANph`Y{j!fKZoxonkP-w||pq)R;$0>~+fo13`iyOzuptRvoo*S!HNkwp5s# zeSb_NP!C&Qanp2Q9(AM~v(#q!aag*pBd=B10o(c-NIR@4f>G6$$mG>eRGMpO+xs6; zI5KSWZ~ebd#ZZcE4Iz83!Rh=mOMetlUgMQcs?T6FShvV-I2IMZaG(J5MTTgg$V0Hy zD>N?%agS_t23_72OgM2127@ewj=DZTYBh5Mc_1T;o@LIia)%TFHBfj7(Y^_eZuSYw z>ieoInf*hy!Asj_d-f#Lk2vEs$y@Jr|HqX}{N}hKD^TEzoYxnU^1KQALlYv5Ao3QA z0s|rMulJM-w}13r&e!*Id}mnM@r}nERdx@!ndC|1>XxOb1Yv>ctPVCp0EllNe2|@X z<&?8a#(zysKqW6@xgB6)Q7%X#hnxDo_)pTR*oAbdJ*?=N50TV;gVExWqAb8rd3GOC=$*sY)M zm07s9$^E!tDr_xGH!%35a;teQ@EV34&;L|vGerdRJV2wQ>3kfCZo6|bv;%SL4$nIn z=G4H$d3!k}Bdt>5Oe6fmur}zB?4Wi_WU!C_+=v^uw(bUsytT-y?X4h8tHSuN6vaEgAD za&~GoLSlKg;ui`WRZrcTYw|~tM6iJn7#T9KY^y#=-#vW*T%|1yed5+pFvWS*8Y5C=E)7ba$tOv~+h%NrxaHCEd9x>8?$8b0_EAbH_Kn^+SgY zWV6>^bI$jD-Y4Q7{X`YA1cgQ!_gc2msI19xqdEEBeE{3OdWG)hS^=YFP2LttLP7$d z;G|~AsX?0I;-E%rH&jLh7o|AXj8VG3U$@xPZ@D7_?HLzb>h~Nx7V$wHcqLk)1uO$w`&OR@&e!x~r&!RpD1?SJB?TM&)?D@?>_@lDE%0 z0=KN^(%`VQB*3R{%Jv-&w6EKLVKAtX;^N+Z{$V_rG_!herhRnMdcnUxw{%hMws~&+ zuZEgIGf6_GM#`W1{kIp-`dUZgE`lL_M}?~q`9+I_Q}?dXcKqGdCyWrpWX1<_7ROI89{UL1!uqI&bTmU5;%kbgUZt z6VY<=@&XFCOiSq09gc*E{x#W&fCApM^VVgkq`19?$#l46b*)==V{`knvU0!-baSVA z{rrki{r8nW@j>UVoft4~2&iNAKi<&4(h=?PtkJBqQ?a-I_;viO$dAYDo}J<^o>C|U zLWOA?DrTP~t6m_V4VCNstNoijF)`f*rf}E zyKkYfa<0qT6zc&4Hp|%}kKb06r#nsGYyQ(wpXB3C5$+iv%_SO0yL>2{Jb0tv1+&`! znZ@}JU6X!d&O(ki1wL$GFw&bG^>PVwgv+T127^zI@Tp;WI11{Hn@MuDDg@EDh1d)) z`gsQl{3cN)Y>R8O?qP6$J%_YW|0y&`Loiym*n&^Qnb%l4_$uJML-L~<|3u>1V{>nG z1`OX5tsnH+-v2$KOH_zgC?X&v?DFYy6 zjf@1QoTI@s$DTrg;$YV|wYQg|-&#Th0pw#YK{N6T#md09H6wNv6J%gB+t?5bdvQVI z%AkZEu}E0>UU)p4^Zonn?Gzs+C3m6F^{JUl%7S=iA|&{?Z{Ow=6u2xn-Lrv=g4|pR zz~}6~LB^wJWh}^V2)sLN3gL6I+&wyKE1(>6um)m>F_(Z(wrId;Tf%T(03EPycMlKR z`>8D&8J*E7>)~5|A{14=Fl#_f{X>DCsnI6$9k=)s1;%Y<30xXlR!$sZp0{RdB~!|v zN{_BU5E5=~^2i6)5(=05CI&P|FPOk6HI;Z(CRj@!OtSYI2_+4~Gw4Y2{aU_D?y~Jy z{?0LZ4-6ky^?%7k0k8@Fx?`ZzARn9k^EfZABAS*oiRpoB6_$oNx_*7O3U25TcsRvr zfm+q<+iHl4T%9Nn=A+knbHB#09hH&MLYB^#FEtAm=~La{+QjWze^tT7b$ZSzvIdM& z>7F?vRqH{?I|tAv3-z02Wbpv;X8^IoAGT0MT{)%_cNtdHLz9m&iP>l`f&Q)4F9H1` z(W{c~CZ6Q|!`k?dQI~5dTn2KQMYKsLFETWu2bQMY2wG~Nvy)+rOM>( zJlMgJO|XjQfzKkA_(|-2YH95GhHsMJmi@$FY+_CZ%oD@$SPj8j0netRlT(UdV^|_% z5>a5BU4n0k=3KsnoWog4tr0h|*yP!kn6rs|OCfQ7^+gh@WAavYdxs?Z) z3*`cf^=2t}`KF7Tj~{jI!-)?>@pUgA@YfSf+=AG~I&gSTME4KR z1}3Pc1CF=I{a+#}_Rt)_{oQRri2|Ph=6i-bt>DI>lXM|l;UY_~*=mRogHLJboL1h65DW7z8737EIXw_9)N-5cZ-Rewf zT&#+U9Hodzwmt9AM!)J%246r%#!45%QJ9G)7Z(=?KVgxlBhJ~@Fb(nRSZov}9ih}z zPE#-yi(?iM6chtb#fgOfrfhOdc5H=doD5_!BdiORoQvmi%}m!w7ij|^@c zz7hqR7p6UUGUHRbWOQzf-;fe6)|l7Yi5Il>D=U!q2c-i_+a!FbKIUuRPzs<)fI>8Nnk+M$er#t4mrlcNK;Zi4cFSJ{JIYk%kb?;4LY?~PzbK8Lk8X@wvR?5f82M9j6NIUSo$F>)R! z`#ab%8B?Nk`SHwhUNzkb=6_63V$BmgX1XPI9eueW)fDDnT2w7CvZ8ryk)PSI+Z3nz zotKIvRjw3r%;Xj4{D6iN!W0v&+en;e$=AP%X1rgd2R}G2oG9wNr4~_4)Q)24HkIcA zi_du|dA1qb>WW{q{m>}0_}VOoplO}!cJdt2T)C>c3n4QYa{e`&)1NUiKkwZDmM z^4RneNY-n6&yi|4-tkgJRc#Cc5to!iyd){(i6RjRtst5lN*S?x+QOSX+q2qtU!?(^ zEV~dbUptI;y|uw@gvI2H-U_4M13!38&C-IaFye!5OTIw6buwVDv!CQ<&)?hSM;M;j zW$S~;=Yi@eO(KO@x=SwhjopMq>ocl?FDQ>bS1Fl$sNySw=ZUq$lK&f9vy3Lv%4|%O z;T3m8ThG_->I5q%gW}YP>QoBO-u)l;F-&od3SV8_5V$-Rz<-ocooO4AeM>!h{3fjWIwc{eu zndix^$69fPMv@|;x(T%ctI^nCQxA}K$8W4EAJs6}pq0^fF2*?-X4#S8Nr$XIETk2xp@@o zqt{RxB1|H$M7WB+O1Xr)6WFf?ZQt`&ld^f?t1?zKv3?sHleJ_oDihx^>PVDmsv^2H zEW7hQw@Ckst&)!3NaMaGRPiP3?$B|lN_lqJE>BT6R96d8tn<$;?W}af01l{ACS599 zQ%`IgJWz1>3h+7W^+64==p$w#HNxD2nLbr*e_P35^+A!vvztTEN#x^RSDRryi8xxF zL49^tN)6g*v!dafB*US(p^V&3>69We5;Fq^|SqRZ98`eWusS1~k?4iZKgUGbt|7YntbLfpp*HK2&?M#0Z~K;Xn|<&(JW- z4syo&Wl7T$arYh)Q*Aqe=W3|ufn&BZjr zHs+rK&6eocOqZhhuGWET-(_7VuXp6}3oXIHT(yadQA)|LY(+L5oKMP@t2|>7Q^)+% zK{}dl8gz5T30ZtJDd3nI^17L*FrVuGos?DFTF8;9e!?>^}!h;=iu{)IQeay9KD4~-yPnqN)Y!NQ1f?O`5ue^ zw)+|*S#P&~0@$eHMpSk!-bihu0wY4#Ek1T%uMX%4!%zNh-Cf^TdL;q7P(jS?iA~1) zjYbl|r;cz*#K@%Ak`UC^p1>pJ2d;Z#v3Y$?m&!EjRky2|V3C6z%REZnY!+K34ZV+5WZyAb|`a zbw%Z~cc*7|Zw(cJZzHJ90s_RX8=r)QuQrMt@6pR zlKpfn0R33x$(qIYvh5Lgf<-AZxk>`9`Sr}v!?)O2%;`#<&Xz0duJaV2&$8di?7Y8T z&T1)`-*-8r{uL4&>|xuB?Cj!l(np1UM(2C3calO14rJinNBwkPSJW^)$Ir4li6}o; z)8KxBwfwMz-tg4=^=9$u&JBPjARv|n28Ta`e3!SZ7GIE)+E2E&C-T&J1V zT7^8i5S_P~L|IPldgT#^_^1%1UA+cQ!~Gr}DI(#av=Fv!5kEPUwR2rs?-P?+DU(Zn z@YyfAMr`=C^`TYRRMCT>2t%O0Zg+dm$qP3O<1R=GIIE!a0q&+tzOeXs$$FF$ zpEak389ed8m7Fa=8XfZTY>NXca^l~=#KTAB`=AOkV3n{{bb<$nCEf@xchPGF$@^4wm+Jy6FVt-C0L7<55jarmcPE=S$UNc#HcFBxhOGb zVeIwEL{7ffPA(xRs*nET;AfUDB){6VJ&zvGe%gSPSzJZ{ZR7;0CaDtmy4CB*LqK2j z8t7lYG}y<+nsZs*Y@MWRd^r7CRz^-o7Xp3p^wrZ7bjBrEq2&F;{kb(ISmHAlmbN2W zWk8|@L@oeAdX76ht0Q_n6c=YWFgSpO5CDn?r^k+NYmB?sY%`qs=-;EX#cRXT&do0aC?-*R)NUkNq7yWy#3JpEKHV$57ALv;g8@cAN&x z*J-sHU@@5_c69SIRc1!J3KCA_J`T?3d?-;V>+I?qCh(&(yiK&Su}!^gbB^hMt5B;g zR9THI1*tp~D3a>`e*Tt&9VM!u%!p%D_nHdSCo$u;ChSfR>EkGXxHBeiTqd~)xmXt8 z-18ZRkwT=KF5L)URpnsBZj()UVNxBZ95E)uKtCW=bP6ibGbVVnbJp;5f`s(Tv<_A`Y>ur7ZzGefP0&(^6qhcVyfA2Rw+*Iz@&P)Ig(x1P=?+ zNWsP?MI9$WX#fJcEi7z?qjG`GiLKm{vA{3bH#{r~ zagOVU7ZPfzt#fOpEB!g&Zw=vk&XiVD!Fav-!-_xDIIgwT2N00}U0sTvo`Ip+B)!ti zL9f~T1uClg?hrLsqp**Pn!=BwqUb`?f}s}|OJccSK7RaYKA^#|wY~jWOG`%7M_9KK zaj=GfnD9qSOTg07l0dLEAT|MT?s=>nE>Yv%3^tsKiVEv5Qs4ajVF5=C&~lOM3F<4T zU~kNWI1{11wT!=FIdg|xJmR#&AP$7(?%X{Yl9<6E8$`ZAxT$4^w|u|TdQs}5)yoKk zYxOZHRSGv^Ge6RXsLIVWTfP$szbH8s6L%LcDPx8G^TL&oZ!Vg1p<)fXn>F8u_kL%m zz$r#eDUxJny={+FqUyM(fd-!AT^Kg>GwxV@!x8kmeNdXLrxP8i((LXKu@g*SFfNlA zvbrHUH{)hWCN+^A-Vo-2tsd2K`|Ds%560+uz1yLBq41~DdC)*{XhH*Y5( z4)P6Q@1OT%yyR;0;4lZyJA;UUO$~Kj8t>cG(1I^M(#Ia0YM)>Yt3NYAx5{!^{&SJh z-saQbyF^oPi5kxMrn}ERqo8nkMeAwy2ZjI|1y>Kb7sYwa%?^zd$1A}(T3m-48W6`5 zcim`u=lTpS{O7v)=WvDT^56g96N=eRc}@=%)+g-Y;bD+L6RIkV5lDyVG;TJITu3qWx2sdrm|x*`!gl|6Uf=j*Xo#-?rYTIh zKB4IWRerk1C^p>xxB%#D&KVN!&tM56B-&+_CgZUbyY!){u(>QkKArm$fZWblo2394 z=;CCl*<$%Jit6UMUNUD2PH;4w$|geI8om28M;8dZldG)zv2dAEdN)cKfT?D&O5E>D zg(wn%5kRdnKibLAxS$0gq%DT@S^v=G1-Z~HXsY=Y;)E|VLqnE`3dxx5UT6J@HmD=+ zuodx(xKLoaI6~+A#q>_Nl$*!vg3RtT2sXM2Fxx6;Z~}czlFjQeS^P`+N*3c}aNWDbDxb*f6q) zk2k(uhVx{+mXAq~7x&R6{ZmK`F9)H1^O~YyFP@AWY}hp6xJnu-eTl&z_n@PSOdL_@ zdESK<&#>)R9vkDw=Em75c>rhW>tp1;qD@Tx{P_gqm+92;mR~)u>wQ<3azt-+OuxZ? zd(yt5=Lu7-Jg+^v8c<)V)uF^!0oKUKoQZTLx)83J?I)6Vl-X7cFbS@ z7@1*sKH-s*u7T%f;`^jY>5L6-vjKvAu&rR8o)-95J{sRb6z3T4$;O1HVD#t93&igLqh%I+m5SeE z%O`kJHOPFNLR&yXWBsKqX*xk04|=`S@+O z=9C8^P-pEz3acGA9h*syQV7XN0TR%AbTvLU)(4dFbfk|`pH(&6&9bhk8yg$XFE7WJ zmlZiY1WYnuI>Xsrs64wVC30Yt@+={uI{>5|r&Rs^iD0@wD2DI_z32{-9(?Zy1tG;ptlPd?{pV(lb6Izw|3)yb`UJXf7}4C5$t_{x4EFmN~ zek@!^x=S8{?Uq?ZsgG|xWl&f)G3G~@=&|)cL^)#3zgQm|!vH@pC-;N_9LOLd%}Sfd zjzlmJ!(W2q#*cp9He?2BusN_flqD3N?ACT#5%w26G@uq)XtJQ1C-eFK`_dJSgjeq2 z1{nnvZT0NtXK`vjkbrB}IRfvW?_kKFrKMe6rp?%7VkmXc>bBf~_v_GTXUeK7ZAnQc z-I+LF;*qQA%=NWdzVb!?(qNy;8yJIYHmiDrD;^4E_I-!2zgQ8wSCoQ}+3A#Hn*r_K z$wx0=q=oX`QDm6&xcXu@BYwz{LwS@lY0?!WYH=hO`BVy!8V@}%9;pEeI{)^+ydPFZ z1LTQj;*;?Vt`60OC&J!?YFkBjL@1s6c-YQ2c}90xc73W;BCoz0ZAu;#p+l(yg#T~JHZTi3f%20wCWPJNo!0Ib zIXf1|Dgv{depQ4&Fcp+z&nHvGI2S5}3uM#d2fvb1Dt_m%a$sWX=XM{1WthQlT}-gPVSbY_6eK%po! zx<|SIQmlGf-OT>q*=lDwEXk{wchq+Ik;6&hDFQYSjBvExS1(H}$u zcTw{_x7}~1CCeObR3s*%CNy>p+(>?G0XgY1H|DpZ5LNARY7P=aNXVE_>M}`{Qi=R& z52vVkNyXtyd1aoRd{ysWB@+_lk9XCTEs=8FlQ#!kt}}e^e>yF_paAwcD_3`*u@0oQ zCl2{qH@3wfC1!Md%GeVf@ML{vHbCLhEkJpV_q26{k54Qyi; zMTuzt@VtHAhbH}Lk3_GWFcHk9bJ zU7@rFhSDh40zu{OrHn1U?j+d}<}g0h9mnaAf=QOBj85*oCsVwMvSf)fF_MW#MYrKcrghJGf&u5#NmTv)ak(AAiNOowTRTmV! zV1eZEWbGcb4njvaZ@*5Wyo=H~vPwhyK6Iz>ZLIf-F@KK}d{FB3q7W|dNc=XzB#B&| z<++t-!|#G#V1#!|6-?f@bIE(@vI=Ljay})&G_m+1v|vU~9XI~@9oja00Fpc$b=ugmyU@3^ z>~2n@gRb8uN8%99bx$m+^?&S1P@7i>0q$~~0_ai&6W{?goaa`O0j(ki5;;e`=?(&t zsNgy($K|aU5*Sg&+VgVOpM6$Rl4D4uB~(ophk^C$g;HRJuCU8f>uXwo5`F@E%jd`~ z>nD`|=H><-_Qk!hNUT&K5Jd+EhxDAI!?lTgn{hHCO^_)F?Oo3==aRaR^A={+t&EnS z^qYZxKoaFhH3p*(De8#C-|v5VB<2!gy$O%o-0-WO;a>C z=CiiQ{dv4`SkGGYKx;XAy@b;isoO$G0PT_P##ntl#1~BtahNE|?y(hc5XZgnTXaBh zI_zaHnfd8SMbbB!c{IM6=l5S^zEM`Jv5`rGEqhex#saXBL}fzi(OWu2d@tzJ(kfe^ z)N27BXIwy81BCpD8f?C#;1I8^uLDQyQ{V$4+SuY^JhS>2l`cBrM||jXMPXT4!nk_| zXVK`%dbZ#oD1v+CiI?#}JLhy_drM_pn?AW~78AgL8}_*o<(I2~Ic@)gZzSggT2v$$ zje5_~a_OaPJOaNLaq}Pud!cQ|Pu&2d4M0(vwUMkQyadAtr`B^=8>d%a3sDw8>!bK3j7m%w3@;2{i0*0RhcU*GQ~KK0)B_!@ajv zKx;b*8dVnf%I$kA^5lPci3Utbj(09YKrb$G8!s}PWk)H71in^fa`pg5O>zDmULS)c zfvldo>8iN{`t|AsPerxYKMtSMeJ8=12LlF1+LKJ*2U5u>((Sf~wzkoG4=1qSGwJ;a zw9zJ8`Kl<=Da(3b$mJs*>zb)u$NJ{A!)PI@^;dhwckei18Yag2Km}N{JvDQPk8Pn~ z!+f?qN(DgiXuTUk;D;)B+vj??SQ`tN1s{KW*7d${$@=>E!S!%1(jv`z`FG^DYjYo{ z+xZ`aar6iPM+4viPg+TB!6~=rCr1)K@dg7~>S)0rh&Smf zpqr$JXxKy07z`+0}!~^9h1w)C^~`&q~u$$IUU!tUbIrtae%iO(d0x8-npNMPt@EAnA)C_1L|V6Wx+Y`j#> zaO7Xq)IGOW6-J9CLC5jxxgL$nW$&yF9R?B@?_5vc)747ROjM6ECa7>z zutA`$t0GJkgdd}0ZUZE0?HWls7O8&q32uv=bXc_fn*icq;>GX z{6-lwEtGSiq;>Fi`&YV|mNCbFV5#EZ-_4eRw#LiRee~@qzh61+A-p5ia@hY3I_QHA zOTGyFQv4C%R2OTX(smy%QdE3HxO{)<2J8(-NS`Lx$v`wZj@P>O@+z3qP{-4o%4PRF zTrCeiTrG(MD6f<1^Aev|N@kPpR(U`OsI3QNG@DFu=Id?pN=g9M*xq>Ssq1aS%6haE zuS)!i;%C{o zKOz^agpoa7(*Z-~uF9vm$~e5x7@30a5*ttj$HBUz=}|F-p3BE9c=ZJOn?zH}Ex zLOtxEa0!VCVfY%$#_(s3=yfIVt)C2O)385-A=2c%CvMDn7*|O>+a* zrgD)Am~s2SM)!s%#91g8>8H_-3bzG-Mv_(HvetA*`KVL zOzd(^O~p4JWyvr6vY=|x^GF0*A|NvQ*W@N5;=V=jc=>dXgU6!N!6vu2HOW!n$pPGx zz3%iL6ECCCwuVcM=BE+AZ)}b)cs=h}e+Qec*m!?(&$;NB{hOe9b}W)jVxnBRg8ZB6 z&QjMini*`eFmK>rJ?v4|RT}^{CLZqd;(#j?r%+HV;+rK@+1wK@;1;W(J(SIptSE9K zP3onOocoEz_p!)Vj6ZYDO3^#M(&vDPnvMhn!V>|x~tu4?bswR3}_S0#! zdeM8tO1UEP2&b)7K3yK!+0mac>!gUP5r9?DOKzmtai~5w>7ox-SMpvi#A~Yf(Vg`FNp~@kqNr?FRU%<{nci zPNd2Q`@E4(7U&(vKQ`rin$D5)6-5FT9W^xN%@MS6K56P8MM9^)bfA!*&!ZRd@d0K> zTDR|gn@>2fn#l6@K?L3}z#lP*RY!&mLx1K_C_~VL5W)t~a*+6wxG!>y`*fXHiS;VP zW2e3Re%kI4p}gFNi;KI)?kOFRyd9oeu4*hgJ#X(2d0lQ%U|Zafg&&H0PuvBGJ-6A!AbrFAG^`@%Ix|!2Ttb1J6kN|#K zDkI3O2lHT%C4L6ZhYlP7Q%DAl2s6JuA@YI$VAcT3i(JdLUZf zsFozlEvfMb^s?&h#2Ir-%T|xLj8(OR`O6hJCY_qV!z@zWrH9wQjec400(hrmvfBe+g;9`~iIVVD~ z4@8l1mv-X6EjsyirTd1Pt;Pc;OJYRc0X~4gOqfmS*sOk&JG1sv>Gg)9lZVW63?$-! z>ekRr@2{oMkJj!_e4bO5$HCPs&EGlQQJt}w>)$xtdTW3(aO9E5@h7TTKJvz00*M&; zJtmkf&SsiP>n*`uI7l?W=2^a+wU`K{*m%#01ztp7PWM%s=> zUSOE%y5^~elHI)>V4IFPpHDQp<9dSwD6X{u8w^UXxIAz?8?dYzybYCm@(2cMj2^L( z^pNi6C?=>f%}rritVy@Z5HbDkBVZ3;q@YPh9O0{-DL$;OZbG}iL`2>Fce;Na5Bd75 z@JG2EU}(Go*zr-=@(cN1OCkRVR{(6XmUm{{y^`p(0_XCuFgmD$Ip!j#d|;sijJ$9& z9JsDLc{p;C*?&Y^W-jBwq~hgb-o~LnUF`2jh{x;HsJ!H%_Fu=+`I%~mhbI()BItlt zel)O5;j;D>48z|;R3he@EzdWQT<;wA#q~b8JFyp*b!OavW@+ji*L9l@%g+1N(dn|v zz!_sDU|};R^L3Uk*|npbgRw~D9e_DA4w35<4?2zwMjG_!If$-A`J=!dq0Ry)-}&b> zO$p5`3#T=v<}XqB>PtHovA0&8lf`79+6QktHqTq;Ce5R8rcHKqK~euomm0vs#K_F` zz&Z5gb%(~b+s|&0rwe<7Z*o>Or>M`<63RJQ(yD2&l6v-*@{8Y;=bL}NID*3G1Bv*` z(|(Cvwrwu^%JLB4ypiXtzf$NxfsYcCogLC!TBcjd!1%A`#s`-rWmL9Fw-8LUnxz6g z&bCfxsiHyTm#CFCBS>R>-thr-djlNopuB(CEX?|j)2wRq@eZiPws*-MiK;as0Eyp2 z|IE8gtJ>fD4jr^Zku%T&aI#C1d8B;P>u;T>AQz|rf!VdM!aAj> z?-~NzdcX)V(DgMU^N1BZY&4^~TKW~M!qC0MlVIpJkYGvruQjq88Si*RK(b-l5jA^8 zQd5%vx?M@&MgkIYz+Q3PQTgz?v-5M7?pV8Q3Dc=K}z#7%=mFl_;3BwCI@`Kg_sL z#&#XXKU%cjE(|NJRt6rAtZ56gFo_ZQ2LLO_K9 zbfLPsfoW+3wRLq4zzJDPYres?{3ZDGMn}yS=iY(f6o0aZYdWdjDj(pgD{@axHU1Wi z1a2lod|tLKj{;D##uml>jET_b_S|BqxSR$b>DcXQ zIoYlzu6ZP1deeHjH4^Zw?Qe$&T!CVY6r$qob_Q&|a84h{1jWVIReg@n(Sh(}?N{~< z*YeHbRfFT^>!Oj2VFt5aO1U)hsA(q7w&`C+e9oxU_>|nmJ+4gio#$nXsyvO>5oFB{N(rHs(!cA#j|$RlMKap+%*Z<9VX>fzO`VmI z`4_7aDkrlY+ggQP^Z-AhkXvMkZ-P7)ZdQ7$_PYG%$8OuEa}Kq$7aN}h@JY{qr0xW9 zjsxIVqAAvBt!0rJfV5nf3AE-CrQ~Y}RtIfsDjfEDHDrAKVXf$%^{E5m%qd8E1puH+ERN{N~P}#0n zJV!^tKns$lR(@`dL+XY402C2h@1W?xih_pvYzfcurJ+w71R>5WEAqafWMz$}3CH6! z8zHXXerGZWe5sxZT+b`BMW_Xoo!0YP$FWA>#-c~}ZUC+Nv%-tGw{a8x-u3yx!g_Uf zyM(c%m#X6W4LU9+DAW&V&U zVM6~;CGvBSCWr{6GUgz|;{u4nR{n6v$`o)}h!nTZcd<=W+ghkPt7}MS_?6BMBgTeO zBx2;*BWT$YfPVB6kmRc{j7}NYnl&Qr^y`}g(4T8s6qV}5mqoOZ@2W*iYB(xMy565lEVK3K9b;B_In8i!2SRdvgBcLJbn^u`RqN!tGQ#ODJhNDn%Oe+Wq<_Ug8EBO5W6TgHY$61X=-C-G z3dBl-JF$ANWaESmDMYhcyru)7iMia32~c~;<1DZ^0k z6_nqMo0W7qS>CqAmXf;f`i+*AMp@a*7UtF;KoOvJF{+{1GxvcUCEF8?S<)UMEs;YI z8~xK@HiG(cSEvg`y5#ZD1Fu3YQR!xtlE3s_us;h)?_pcml=J(bhAOO*#{&#Pg&DrG z-$pSJ3@=-E#aTg~=x*0;BX4!jK{W($yvLA>DDFLY#cjY@8_SDCKQ6PClgtWlVf3yg z6!P^0J#1&<&Np@!I0ZK$h4bJX^dn5u^&yHL+lFZCNNAZF3saPh>=I_PIh2T7FuSj+S&LmObX#4dm z9ZWq}+RM?8PqWmeiy%Y5gcr<-C$rn0`z64L`aq7a-@o=@s5o<22LNCpAB;sa8_C$bXt@JxkJ3hy$0I`{ z>4VX_DgdfwaapPHk*Yw69HT^8kY8K&MjkR2IFdpB{eN75mHVJ@Vs|LH(4tU6!W*Lk z$o)X2ptP#VbAL*SkJNnR@gpJ5U2tTH=kk}q1OMqMWrmGM~l9wb0!(e#CCC~Jcii)yC+V_;P8v}_5 zOS6?sT*|v#PbY3Z$QaMk2GFLDb8TX^I(9p5O^%E(z!u48KF26OiF~~xVd+iGc6?_0MJq`OWAlZ;_6!6v zSl{|U;uz02O{xvvtaj-7fJwCIVZ#GA$O`WOT&rdGL+uQKWoiDzAEiy{lB7Q06$Kxt zMebK+ot-mGp8Ns;0ueF#c&@ivUcY2Iy5t=Z5vA*8ZlBU)to?9Q$BS@QM4b|+Vjq-> z98;K!8()02s%wzasBAujg?G>OLREB7%b#ZcRhrtcC&uh5gt`TBn~NYm`^#(C4-!j7 zZ^M38R(7VOG7cKxD=90lGrsWYlG)^V&RhkUfRXzw8F@n3vwQ>`pScB%sm=_;hXO|# zS)Hc46EsXZf8f~?7;%(7cL8-3;CM{EM08iDBH$MtYfa2?A(41D)i{t=oo7~^w~ds% zBu6u(UsDe6Vv~{|NWvc)$`Kg;AWi8ITjg>v6rTnoC)MDE7r46%N2-laE8K>tPJBVJ zA%X6?FIeb8B7NlJYY)#h9-h8)DPHx1P)>&kyfL{yNdx^yYad7C(XHeL;=5$+D6E&o zW@~H})f$oVG`P+00Mxe)^M}o0V?2G}3pBw0!q?ma)r={1=#) zRyN-fhR@I6oEK%~HWr7TSpEDW4}OJo4x*1I8Trkkq|T7n^~rL&VYmp%#2v zlLZTPm_WhGhQoWzro1I*U`0j_IJ<=6fvB2}9#kj+S@z2H3HJWt6>d;C3TqZ~qKrX# zH~-+;rK?(nM!WArr^wpW%#yyNO$y*3&&|)%tCmU@@-yqj%Z%$4XYY4% zvGh)|zSu^ZgUMCdgO*M4r3Oxb?;8npYt8KM2qI0-jj!7)-tc7xKHs=i z)5G*Rt(VMDq`CT)k0bgVsDTIR4DAo{QB8dtkUh_O4uMM<^L3T8(bMhSynT?Ko%JBQY=DPUy+KgCW@z7fH#>gT zMN;{`PHZHpCv?z5X{*++j$F2)~~ZQnfVy%wpixfHRlU5PhU6D0KHNSj8zs#MBBMKJGT007Joe+{E zTH&*a^X=`hysk|LCG9M7a~(;xs*aEb-V9kO&r2gj#c#6qzr_}AXp74g4Qqydi1bF- zh3d&ip6;4URMW{Q58b=bg?yq74n}%rTRW+R5DP=HnH)*ZL#&M ze301n_mVMG96zO<=Q>`j{k!;7q0+Z~3ZBtlKtfgXj|nV`=G!^whLcCw6h&SAvL~Z# zGRx|sc!*|mLuPmQ{EI$QtE%Z53&=;@y1LKw>Kbjf%oi6ff_IU2_V-=VdYC+168OQ^ z^M8WU-K?`OUROl7`-uVqR}Zk9hk2VU$?JU02J$#fcH?Lu(M=}X!-1lIODo&j+uhJ# zfUKgh`E`wIFFv=UR;SzQQuWHM!smSw9?ArrseJi&W&5$T-&7O4Wcd6;cP6zouWxHO z7@JKvOds{Knpi&VmZ5`5xn!o(#;Hf^T??80cHTZWK84%XtqJqCiy!zC*FsoZ1chG1t<=pd=QlQ{_6k2Df%ghp z>~H=3seJFStwOPVo1H%Q==;NDoIQP^$G-M0tfx?Ku$KQ-8La!qXo&)6;5pC$M+I~tjw z)8!@?d#d;M%MbH-D6IXazd)}LQ+`5sJ2QZm*^x;1N{a_$l9kqc8N~r}{*L80o0uh3 zImUw8dLHI;lJU7<1)JDd4T3V{{n+o=91mtTXih(T3VGMa(OQa1;WjJWUo9yHjAXVU zVq8Ar5n4G#2|Y+eKldF&Ovg?}{QKmt+1z-ELDw7XEvrx3=_Zh#i!!aojCs4JMB_`* zLiWk2X~XioidNg(E%4c#1{E`QL{Uf4ClBBe=k!QNv?FSXVpJKQ7!nc@?(Xo)x?L^g zet+!@qobpH1uO2P zj7^IqWi5t*5NIHUA1q-Y#Sqpfzx6gr#`u?JS{fk%h&=St@qwaY<4Jp>-ww3(Y&J|# zi_U)>Fw+apk0`dFHDCNjazyi`IdR~`?rv<2pW`1b*<)Q9#h1EtK00jL!2G~h%B{Ju z*UMTKuvet*{da0<$DPM!;o;vZVW^PUD%e-7Ph}KCen^@7{e-Ue`HAAUbe^fbKlsQT~Lnhdz5~$fSuV6z=lzVNW_%19iMd? ztZ}$ra}^eji!A?aTTFCn&FxXC+Rmu_;ZTjvmN(SAMW3wC_$kYHq;*TlI)X$fpmfMn zmna@3`vhtupI5n;x}VwjM3`c&?DA6|(RO-U@=J&O<&C$&?|(dV)l=j-T{>r-J;!iyZHP);;bAPvfG@kUMJ@FN;wd)UCVx$t90BMGXSO% z?d<^}yq^2K4{&hQ(jCAFw$Wuq0n}$h5Jh3(?M9{I@4zT|k7&CV@W1capYDrz>;@ zx8&2W>wIq+onR@9U^~Wma%GDk34ABcZ?yAzcE}jf8--bhosKbPPRo*TBHM8=uGL{r|qB@8~=F z?gNgP``)qE+H0?Mt!wSqOuysS$ZMAmRn#xMr~O7+BqgWv*jMzmPQ5WtVPPR?$m|E( z8-&^ncuK7npwG_EZuxj+eNg6x{2+lC25z^vwyD`elcg+A1`;M zrxvuH+v}nOT8B>~;RtvU!_RM*uqvuWsBxl_=3d^>v|39&xp@8h^{=t9uG3RY4vnJp z<0o6$br=wN;`4M#`S~2GH@ennt&~_(%fMp0TGc~}wajmD2*f&=faEFB0#{jFDMlAt zgsY+=2X9A=Q}wU*P4RRT=9@pZDg{*Ez%L@uK}{Eeu-HOvfyHDaxVGJaI~sYC(AN+_ zi*{Zrnq$`omKZp@9?MpPfc!Q=_~fn;Xe(R4B0QK=jVehi&Nq2HU#5#0QgtTW>;L+U zYSxDTeM~3cD~#KAD6Pk=}jGr(X8KZUb2j0!&uZro#6PKX@duG5EO55a zFCT5}^&3W@^!BPr?c;(Kg5hfNIHB+c&MS`O)DQa(iQ0+pk4DDaF2ckQPfnaR3aji7 z!jT&BlA7+v+spDVvB9Bcbbf3DI>)6#P<~Bv0Ao7{T@=1t;B~@G1rr6wq1jgA2DdH z5%jz!w#tgM4}e0_orlP;VlF-tlfnAmQ%OlW;Y8^MLtdg8bi7t`C1+~ ztQ43Q>EHXL`s%~%-Y}c|W2ywhP{Lu{dm0XgLPxsZb(ts7o5e@|_6f53f)|oI^Rf~V z8wHL_Z=(hpFl{z&O$yxkZo@Q*5(0x}4`>SAf|^}2Y3OgCvyq2*tZ_g_`<*)rEz;btfJnZQn8e*O(KUun?tH1hQ%Ma6af!W}J9 zw0Fj>cr?|lq;HKsq9mw+A(S7BbF>|xP(K0|GHlcddI#1gtj_P1M~96^7y;U#tta>u z#c_x+oH9g^93aKGn)Y$gV>_q-;VNnrkF^9O9rQgMDUkF!k(4ttqd+ecUVgHJA?h}( z*H{FsH3x?kCA-ZeE@bqeM~|WBuF$wV)5md+Ywkti1xpwFWHNpw7G#x;7nu|&`J71o zAyfIvpsaIR9fRM4gH|_MkNcTBcqBzkFCCBMJWODpStL#p2??Wr`AwN>q!>OW$Gqvy z#I!REc&oYUR}t4N%+0USfWQ@qGoQ8kj?m}H?bP4)^V!L%G{OeK$Yc>`Dl40#>E9)` zTU5yvW2*NB0?NntY<;?=TFI(_|C!^661{9@$!{S@Ww3<5sNN)xY9V39U`6pFGb~Td zxCL6mlT)Fx(H4g!C9Ec#Cwj%24x}2C9ltB3D(?m_4r}P=Qt?$+&lPwVNlENTo_BV> z09BSk4y}a9+df`rXCJh*lY%&SV&wRLRI7j(?b-R=G|Jq((nt`KRwN|DhCPRN1TTjw zh646@kCu_klf@!YJ60S#2>Q&a{cHQ>Ud;r~2#O0FW z`?<@AJwe#m5ebw!P-5UMw^`U`j}Y@pT7&`-i@9%}QyXu%ah zW^788z;npXZXq+xv3XhyJ+ILo)5-XNm{Uvf{qYI$vrjfmUm+et(Y3_{XyXZT>e9&5n4{dCVB+xM!PkR-25YHWU3jwvw$+;U{)D~0V#Cf9}vS?kz zy@E)z234Hr>wWN0-plsJO0xX4av8xsy?5jo21{sDhtMo1E|q zVoVXl4+`9N8J$APq?%K8&bH!)b`&a?~pt-G)Q>Fi-B*DGWsq@?u zZTzDn>&beD*V$to?J`drm73qm2_&UJ8#*c1H#R_Lt9vy)TUvm|5CpJH``1V6Ys_{I zfuKx+P{s1Mm#wSglve=(3%gv?7??z+`hXV;wsqqcOFaav()i z(aw(EZgZkH3IIoPB@Q0A%#=lg23Zb=<9i`=gD1yFrPgaI;7TunI0kAK`|#~kBO{EI z6y5R{?{pZoTb2V}eIO*}Y`81xlIjMB*oykt;q&{_)j_tKKObZ&(07HO zoLvlUdP3q^blzhR<=4GBBQ%l^Y(qM)$g1NZgPf6txq*Q?=_+~L+}wb>Ijw8GP2b$@ zqqzGCpi`@x@El8#U)cxjQ3OUeY$q#Y+;?XCGw!`yrKg;;jF3i@8|jWuU&{-Nc&Z?7 zTSlKm-E2$+j`vqKmqIxX!bMII^uG|_(yGko*bJsvS}RVFjK)Z*z_O3M;AX71<=a-D z7PqNYJ^hK^QEec4@{rA;tKiaDT%P2+9gEg`Q5iU6R3zU4g(o1}#D0HNAz1BxVtW-G zlg%aun7H3#V|zmp(=X36pM%ZgazEKlYFa%vEHp7RjPk`MEOp*61xyHxjx{u(t0^!i z`$Om3CI>5F;5JJg8Xo5GsQ;kbW@h@2_cXsNXyg()>vUm9bd2rUR2W5}nO1l2K zpK7IV|4DFmTH4)RG{O_5e7rsN@@3astTXIa*>8jp@$vEcBl$5PA4BfdgldfgUPg^R zT~5KuH?)mB0TJhn6j6?+>+dYgm`ppR$fA2%K#kHx;8V|U6x}7z7%;M&;#T0`Zp(G( zaIg=eXR$PrBy}1hkKgQyVMXoeABxQfPn9%a%U~%!(P{q8bzkSbD9i8@k3->2cYdJVEurjHA-aTW?x(2I{w|qzQ5s9eh1y@jRWbJ2P}tehPhN-;X6A4IV)xhYvU(+;Odc`u)td zmCSW+UwXV-LOws%{>WB-7JxOvH4to${m01=hbf1^OF&ul0CzW9h+g$HnYIWt#~9V zc%-O>!?x}Bqt;aD5cd2|NZSM`_vV=}^%-@pZVe=4EB3j?1lPBmcqH+eQ$c^7JaY1w zpOe$*r6_9mm|@@hsAuXHxlQZ$q7${4uwG9hU3hrXV~=q)S)wcusxqXCm3HV0_Q=L*;%G1idbPP^25XV4ZDkcfo&c&vPUhg_dgL$`PNG-Gr zR{t3NeL7H&H4Al@ygIoy21JD%582E_QTCUG+Jfaq=Pse%PKdf6%lpX^!z~JeGwrz& zNIc}z$k|lA^>xddtxDa^>GynjhnsV!Pe--({cZph7Pu26&vrhE)5K)V%vdWXw6<#x2yC0SDuvHeK&TVY`v0*}*z~8A z?#J~Vb3YmI@$omup(H0W?t~E3FAYlCx?1OQ&4i<~IlwuPDn1CJt?$qBSV0R8dR~5hdC!KlvD57t*S(QqiQdSQ z__31@;|=GpfgzM~btSaho{)n;=qoBJK1w~Q%vmXOFRq$}23$b_+;?h{7Fl0AZco2g zStAv-YfQLW?W=*G9o}`UIk}4|62PILBz%SsgqKmvRWknP;GR}$dzK~ePl<}A6@FIx z^aSmkhG-5Lc8LTjdIYTID1MsUrAg$Li^CtYI(EfKxcgkX*u4lH~ow&$KkF^ z8DSFjM@Ite@r*^xe#w9kx#}Q29f#j&6YA(>PqOIzd)DxEMK>pmc-7Ig%@>cE?;)z` ziicxZuMDe*9*fS0+aK@39LY^lSvxX*S?AzH>Owt-$6Yo;L7yu`jm4BY*fw==J-JIo zlUY<0UboX20{9gP`l#ugE25Y|vlG3{#zqiA+qdtKEtdWP1jY1wayjZQCX<%wwi&R?>*Zcq8FAn zNZGb;A~#X(fWe{h?TS((>&*j~?fL`{i9ykGLY>N12APL;P336-eSx@~G^+vrnDDz( zoD^N%b_aP%|L>KRB~D!~xE)Mfm%AnaZV{jXsc=vxFya`WfAdVv^@$BNaM&dKes@*} z(ODaOEU_V3mWDL0mZS+sQBJ|)?I#|6-5gv&>`jP1-z>4k{Kj^E??VFTPV) z6JS2`@&aWZBPMlV>64=-@i1SWmrQOT4ljktTu=Jv_vGI>5k_((@rf_s4+LWT3UF@B zJd4=5tlcD6+1O_N3i06xJ4ez|*c?*uFPvzmE%G{a$u%Y!ZD?gGL>RJu@Fc1}H1+5j z)5xWU>8#hii(40Xf2T<3#jgtjnH?#g%CKk3qk>wz{`z|YKGqzz^-hK1k1c5W4v*<(U;QRBX4u#7~m9KG!bs?E>K@ z5YOTI7VE(!z;ah)jass>hA_G)*KwgvXGWrZet}8qTtGf&D9wDVl9+QsYWhMzW{2?= z`&@EUy4opQjx$DHf5f@}lM0*o50DArq?FZ!)O&&zYsyix7$9B;R+rh?j-=A&um#c! z!h7+ZCLg!70~MQA5s+lb;uV_Cfa1c9i?bA)mSrrSgb(t)R64eEWhsC^X1>t%_@Fdm zx=WgHT9GA%MTQt$2joCRHhIg+#s;`^c7`3nCdQVQ;LuF3$o5m?&;THy{>U#c^HdFw zH>bMt)i}mI$8^OGen$gyczvK0M51FK>GtfAuyS{PV!nkpZVsDZUD3bI%*2u2ho2v|)NpIX8%YZ8ia-Ds?kOd_3Gn6n-UOztOlx> zn3VK2K}{D4#g7QRGESpKZ~Iv#k^BL(;(Z%-^}=X_xGVFhm@3wvuU0T#;l#RVw1}GX zqX2BDW+27^kcRQ+lE=!HR#pHglLJyspchv;$X)U#^xfy6D$b3KCBO0=Qv`R?3kzd} zUTapl;8!?T(1^T?pG}>WD_%C_;_zqF!f7$ zL6T#QJi9aU)7cG~psYm`pU&lvy{?gyhd44DB09nd-{#ngcxyff1Rc21D~XeX1PFUB zr1|@|gY!A-kS23dih};8%7x_n!e}gT$M3tJQ}W3VAs)W|?AcORhgR?IE;q>{35AXP zT=EwsHud6x0opV3LKARB54zQ4P16OCk^4Ka#j4XjGl`Ba2}k+}anMUk>x$47eHy~G zzmh7xbfDozY*9!rkVM_upQhi~L-xY+dE2gqHGF>U^{3AV%+gm!w8zNYc#UvCj0{3g zNBHIUODTxLo2eCq;+mxV@~L2u;zhHo7M_YyYCGAB1o1oK0>>nD6#jc8Ulqk2ItUXO z;-U4lYw7hTgfd&Fp~bZpZlqDXF1wi7;!P1-q97vr#4U2ZXe5}(%t*&^t;E96F$$bM zNpMsld_FqO&HqYxMq|lYSXtknr(B?r5U96wK=_=O>}G+&f!~eMvz=jKHgY)-h4_?o za zrM!Dg_U>SC!lP?LL$k*6=GD@sowf5_19IR##9)8C;WVJk9*3ldUEZWSKfNk6krvxK zHJYOVP2m@);V5!Ay5-;?4N%;CF}k|43wxg7Nghlz^?064x0RX5$z;D7Z|CI|X(fbw zNGOIaYJ=S5sEKcfTt2A>nM!tZ_DYVaP5ECez`=KnmoivY2AO!3JAs6x*F;Cdwmd1K zEtuEDR}03k+3OowLw^dge^tL#E9>1eU;LC^4tSG3i7$YsWo>c85rdhQ%jj1Hux~y# zZ$oIsBeo>v9ajQA2Y<<`6qjDlG4n`|qh{Ftkkr%C)UKe;kscjfoS(mhKsCzOh94FU z-Z7@iOFk9Y+p|-I3kDRpa98ZfudXE1%W9<>Ut+EmsqD6?7Z?OwUoe8VF9!q(z>

>$lBEaZH` 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%0D9h9}922(_s=Sk<5p1&dPnVur_mph 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 LD9h!`8v6YolOhHelp&About` and scroll to the bottom. +You can expand "Access token" to copy it. + +![Obatining an admin access token with Element](assets/obtain_admin_access_token_element.png) + +**IMPORTANT**: once you copy the token, just close the Matrix client window/tab. Do not "log out", as that would invalidate the token. + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file: + +```yaml +matrix_bot_matrix_registration_bot_enabled: true +# Token obtained via logging into the bot account (see above) +matrix_bot_matrix_registration_bot_bot_access_token: "syt_bW9hbm9z_XXXXXXXXXXXXXr_2kuzbE" + +# Enables registration +matrix_synapse_enable_registration: true + +# Restrict registration to users with a token +matrix_synapse_registration_requires_token: true +``` + + +## Installing + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +To use the bot, create a **non-encrypted** room and invite `@bot.matrix-reminder-bot:DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). + +In this room send `help` and the bot will reply with all options. + +You can also refer to the upstream [Usage documentation](https://github.com/moan0s/matrix-registration-bot#supported-commands). +If you have any questions, or if you need help setting it up, read the [troublshooting guide](https://github.com/moan0s/matrix-registration-bot/blob/main/docs/troubleshooting.md) +or join [#matrix-registration-bot:hyteck.de](https://matrix.to/#/#matrix-registration-bot:hyteck.de). diff --git a/docs/configuring-playbook.md b/docs/configuring-playbook.md index c842a8703..5233a6626 100644 --- a/docs/configuring-playbook.md +++ b/docs/configuring-playbook.md @@ -151,6 +151,7 @@ When you're done with all the configuration you'd like to do, continue with [Ins - [Setting up Mjolnir](configuring-playbook-bot-mjolnir.md) - a moderation tool/bot (optional) +- [Setting up matrix-registration-bot](configuring-playbook-bot-matrix-registration-bot.md) - a bot to create and manage registration tokens to invite users (optional) ### Backups diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 91324025e..738c71ba7 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -987,6 +987,35 @@ matrix_bot_matrix_reminder_bot_container_image_self_build: "{{ matrix_architectu # ###################################################################### + +###################################################################### +# +# matrix-bot-matrix-registration-bot +# +###################################################################### + +# We don't enable bots by default. +matrix_bot_matrix_registration_bot_enabled: false + +matrix_bot_matrix_registration_bot_container_image_self_build: "{{ matrix_architecture not in ['amd64'] }}" + +matrix_bot_matrix_registration_bot_systemd_required_services_list: | + {{ + ['docker.service'] + + + ['matrix-' + matrix_homeserver_implementation + '.service'] + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + + +###################################################################### +# +# /matrix-bot-matrix-registration-bot +# +###################################################################### + + ###################################################################### # # matrix-bot-honoroit diff --git a/roles/matrix-bot-matrix-registration-bot/defaults/main.yml b/roles/matrix-bot-matrix-registration-bot/defaults/main.yml new file mode 100644 index 000000000..40538478e --- /dev/null +++ b/roles/matrix-bot-matrix-registration-bot/defaults/main.yml @@ -0,0 +1,49 @@ +--- +# matrix-registration-bot creates and manages registration tokens for a matrix server +# See: https://github.com/moan0s/matrix-registration-bot + +matrix_bot_matrix_registration_bot_enabled: true +matrix_bot_matrix_registration_bot_container_image_self_build: false +matrix_bot_matrix_registration_bot_docker_repo: "https://github.com/moan0s/matrix-registration-bot.git" +matrix_bot_matrix_registration_bot_docker_src_files_path: "{{ matrix_bot_matrix_registration_bot_base_path }}/docker-src" + +matrix_bot_matrix_registration_bot_version: latest +matrix_bot_matrix_registration_bot_docker_image: "{{ matrix_container_global_registry_prefix }}moanos/matrix-registration-bot:{{ matrix_bot_matrix_registration_bot_version }}" +matrix_bot_matrix_registration_bot_docker_image_force_pull: "{{ matrix_bot_matrix_registration_bot_docker_image.endswith(':latest') }}" + +matrix_bot_matrix_registration_bot_base_path: "{{ matrix_base_data_path }}/matrix-registration-bot" +matrix_bot_matrix_registration_bot_config_path: "{{ matrix_bot_matrix_registration_bot_base_path }}/config" +matrix_bot_matrix_registration_bot_data_path: "{{ matrix_bot_matrix_registration_bot_base_path }}/data" + +matrix_bot_matrix_registration_bot_bot_server: "https://{{ matrix_server_fqn_matrix }}" +matrix_bot_matrix_registration_bot_api_base_url: "https://{{ matrix_server_fqn_matrix }}" + +# The access token that the bot uses to communicate in Matrix chats +# This does not necessarily need to be a privileged (admin) access token. +matrix_bot_matrix_registration_bot_bot_access_token: '' + +# The access token that the bot uses to call the Matrix API for creating registration tokens. +# This needs to be a privileged (admin) access token. +# By default, we assume `matrix_bot_matrix_registration_bot_bot_access_token` is such a privileged token and we use it as is. +# If necessary, you can define your own other access token here, which might even be for a different Matrix user. +matrix_bot_matrix_registration_bot_api_token: "{{ matrix_bot_matrix_registration_bot_bot_access_token }}" + +matrix_bot_matrix_registration_bot_logging_level: info +matrix_bot_matrix_registration_environment_variables_extension: '' + +# A list of extra arguments to pass to the container +matrix_bot_matrix_registration_bot_container_extra_arguments: [] + +# List of systemd services that matrix-bot-matrix-registration-bot.service depends on +matrix_bot_matrix_registration_bot_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-bot-matrix-registration-bot.service wants +matrix_bot_matrix_registration_bot_systemd_wanted_services_list: [] + +# The bot's username. This user needs to be created manually beforehand. +# Also see `matrix_bot_matrix_registration_bot_user_password`. +matrix_bot_matrix_registration_bot_matrix_user_id_localpart: "bot.matrix-registration-bot" + +matrix_bot_matrix_registration_bot_matrix_user_id: '@{{ matrix_bot_matrix_registration_bot_matrix_user_id_localpart }}:{{ matrix_domain }}' + +matrix_bot_matrix_registration_bot_matrix_homeserver_url: "{{ matrix_homeserver_container_url }}" diff --git a/roles/matrix-bot-matrix-registration-bot/tasks/init.yml b/roles/matrix-bot-matrix-registration-bot/tasks/init.yml new file mode 100644 index 000000000..03235b805 --- /dev/null +++ b/roles/matrix-bot-matrix-registration-bot/tasks/init.yml @@ -0,0 +1,5 @@ +--- + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-bot-matrix-registration-bot.service'] }}" + when: matrix_bot_matrix_registration_bot_enabled|bool diff --git a/roles/matrix-bot-matrix-registration-bot/tasks/main.yml b/roles/matrix-bot-matrix-registration-bot/tasks/main.yml new file mode 100644 index 000000000..c90da6a8b --- /dev/null +++ b/roles/matrix-bot-matrix-registration-bot/tasks/main.yml @@ -0,0 +1,23 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_bot_matrix_registration_bot_enabled|bool" + tags: + - setup-all + - setup-bot-matrix-registration-bot + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_bot_matrix_registration_bot_enabled|bool" + tags: + - setup-all + - setup-bot-matrix-registration-bot + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_bot_matrix_registration_bot_enabled|bool" + tags: + - setup-all + - setup-bot-matrix-registration-bot diff --git a/roles/matrix-bot-matrix-registration-bot/tasks/setup_install.yml b/roles/matrix-bot-matrix-registration-bot/tasks/setup_install.yml new file mode 100644 index 000000000..716d67bc3 --- /dev/null +++ b/roles/matrix-bot-matrix-registration-bot/tasks/setup_install.yml @@ -0,0 +1,73 @@ +--- + +- name: Ensure matrix-registration-bot paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - {path: "{{ matrix_bot_matrix_registration_bot_config_path }}", when: true} + - - {path: "{{ matrix_bot_matrix_registration_bot_data_path }}", when: true} + - {path: "{{ matrix_bot_matrix_registration_bot_docker_src_files_path }}", when: true} + when: "item.when|bool" + +- name: Ensure matrix-registration-bot configuration file created + template: + src: "{{ role_path }}/templates/config/config.yml.j2" + dest: "{{ matrix_bot_matrix_registration_bot_config_path }}/config.yml" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: 0640 + +- name: Ensure matrix-registration-bot image is pulled + docker_image: + name: "{{ matrix_bot_matrix_registration_bot_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_bot_matrix_registration_bot_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_bot_matrix_registration_bot_docker_image_force_pull }}" + when: "not matrix_bot_matrix_registration_bot_container_image_self_build|bool" + register: result + retries: "{{ matrix_container_retries_count }}" + delay: "{{ matrix_container_retries_delay }}" + until: result is not failed + +- name: Ensure matrix-registration-bot repository is present on self-build + git: + repo: "{{ matrix_bot_matrix_registration_bot_docker_repo }}" + dest: "{{ matrix_bot_matrix_registration_bot_docker_src_files_path }}" + force: "yes" + become: true + become_user: "{{ matrix_user_username }}" + register: matrix_bot_matrix_registration_bot_git_pull_results + when: "matrix_bot_matrix_registration_bot_container_image_self_build|bool" + +- name: Ensure matrix-registration-bot image is built + docker_image: + name: "{{ matrix_bot_matrix_registration_bot_docker_image }}" + source: build + force_source: "{{ matrix_bot_matrix_registration_bot_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mailer_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_bot_matrix_registration_bot_docker_src_files_path }}" + pull: true + when: "matrix_bot_matrix_registration_bot_container_image_self_build|bool" + +- name: Ensure matrix-bot-matrix-registration-bot.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-bot-matrix-registration-bot.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-bot-matrix-registration-bot.service" + mode: 0644 + register: matrix_bot_matrix_registration_bot_systemd_service_result + +- name: Ensure systemd reloaded after matrix-bot-matrix-registration-bot.service installation + service: + daemon_reload: true + when: "matrix_bot_matrix_registration_bot_systemd_service_result.changed|bool" + +- name: Ensure matrix-bot-matrix-registration-bot.service restarted, if necessary + service: + name: "matrix-bot-matrix-registration-bot.service" + state: restarted diff --git a/roles/matrix-bot-matrix-registration-bot/tasks/setup_uninstall.yml b/roles/matrix-bot-matrix-registration-bot/tasks/setup_uninstall.yml new file mode 100644 index 000000000..9881592fe --- /dev/null +++ b/roles/matrix-bot-matrix-registration-bot/tasks/setup_uninstall.yml @@ -0,0 +1,36 @@ +--- + +- name: Check existence of matrix-matrix-registration-bot service + stat: + path: "{{ matrix_systemd_path }}/matrix-bot-matrix-registration-bot.service" + register: matrix_bot_matrix_registration_bot_service_stat + +- name: Ensure matrix-matrix-registration-bot is stopped + service: + name: matrix-bot-matrix-registration-bot + state: stopped + enabled: false + daemon_reload: true + register: stopping_result + when: "matrix_bot_matrix_registration_bot_service_stat.stat.exists|bool" + +- name: Ensure matrix-bot-matrix-registration-bot.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-bot-matrix-registration-bot.service" + state: absent + when: "matrix_bot_matrix_registration_bot_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-bot-matrix-registration-bot.service removal + service: + daemon_reload: true + when: "matrix_bot_matrix_registration_bot_service_stat.stat.exists|bool" + +- name: Ensure Matrix matrix-registration-bot paths don't exist + file: + path: "{{ matrix_bot_matrix_registration_bot_base_path }}" + state: absent + +- name: Ensure matrix-registration-bot Docker image doesn't exist + docker_image: + name: "{{ matrix_bot_matrix_registration_bot_docker_image }}" + state: absent diff --git a/roles/matrix-bot-matrix-registration-bot/tasks/validate_config.yml b/roles/matrix-bot-matrix-registration-bot/tasks/validate_config.yml new file mode 100644 index 000000000..d5db028d7 --- /dev/null +++ b/roles/matrix-bot-matrix-registration-bot/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_bot_matrix_registration_bot_bot_access_token" + - "matrix_bot_matrix_registration_bot_api_token" diff --git a/roles/matrix-bot-matrix-registration-bot/templates/config/config.yml.j2 b/roles/matrix-bot-matrix-registration-bot/templates/config/config.yml.j2 new file mode 100644 index 000000000..756efb018 --- /dev/null +++ b/roles/matrix-bot-matrix-registration-bot/templates/config/config.yml.j2 @@ -0,0 +1,12 @@ +bot: + server: {{ matrix_bot_matrix_registration_bot_bot_server|to_json }} + username: {{ matrix_bot_matrix_registration_bot_matrix_user_id_localpart|to_json }} + access_token: {{ matrix_bot_matrix_registration_bot_bot_access_token|to_json }} +api: + # API endpoint of the registration tokens + base_url: {{ matrix_bot_matrix_registration_bot_api_base_url|to_json }} + # Access token of an administrator on the server + token: {{ matrix_bot_matrix_registration_bot_api_token|to_json }} +logging: + level: {{ matrix_bot_matrix_registration_bot_logging_level|to_json }} + diff --git a/roles/matrix-bot-matrix-registration-bot/templates/systemd/matrix-bot-matrix-registration-bot.service.j2 b/roles/matrix-bot-matrix-registration-bot/templates/systemd/matrix-bot-matrix-registration-bot.service.j2 new file mode 100644 index 000000000..ba2a95931 --- /dev/null +++ b/roles/matrix-bot-matrix-registration-bot/templates/systemd/matrix-bot-matrix-registration-bot.service.j2 @@ -0,0 +1,38 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix registration bot +{% for service in matrix_bot_matrix_registration_bot_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_bot_matrix_registration_bot_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-registration-bot 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-matrix-registration-bot 2>/dev/null || true' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-matrix-registration-bot \ + --log-driver=none \ + --cap-drop=ALL \ + -e "CONFIG_PATH=/config/config.yml" \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --read-only \ + --mount type=bind,src={{ matrix_bot_matrix_registration_bot_config_path }},dst=/config,ro \ + --mount type=bind,src={{ matrix_bot_matrix_registration_bot_data_path }},dst=/data \ + --network={{ matrix_docker_network }} \ + --env-file={{ matrix_bot_matrix_registration_bot_config_path }}/env \ + {{ matrix_bot_matrix_registration_bot_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-registration-bot 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-matrix-registration-bot 2>/dev/null || true' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-bot-matrix-registration-bot + +[Install] +WantedBy=multi-user.target diff --git a/setup.yml b/setup.yml index de86665b6..d24c3c991 100755 --- a/setup.yml +++ b/setup.yml @@ -37,6 +37,7 @@ - matrix-bridge-heisenbridge - matrix-bridge-hookshot - matrix-bot-matrix-reminder-bot + - matrix-bot-matrix-registration-bot - matrix-bot-honoroit - matrix-bot-go-neb - matrix-bot-mjolnir From e435c55458571ee379a821070bc4877e8103e5eb Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 21 Apr 2022 11:10:45 +0300 Subject: [PATCH 50/83] Announce matrix-registration-bot support Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1771 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7800da08..a5811c68d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 2022-04-21 + +## matrix-registration-bot support + +Thanks to [Julian-Samuel Gebühr (@moan0s)](https://github.com/moan0s), the playbook can now help you set up [matrix-registration-bot](https://github.com/moanos/matrix-registration-bot) - a bot that is used to create and manage registration tokens for a Matrix server. + +See our [Setting up matrix-registration-bot](docs/configuring-playbook-bot-matrix-registration-bot.md) documentation to get started. + + # 2022-04-19 ## Borg backup support From 27ec1d8bde2711817f78937dc7a38ed0ffeb8bc6 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 21 Apr 2022 11:21:29 +0300 Subject: [PATCH 51/83] Fix matrix-registration-bot repository URL --- CHANGELOG.md | 2 +- docs/configuring-playbook-bot-matrix-registration-bot.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5811c68d..0fdac2aa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## matrix-registration-bot support -Thanks to [Julian-Samuel Gebühr (@moan0s)](https://github.com/moan0s), the playbook can now help you set up [matrix-registration-bot](https://github.com/moanos/matrix-registration-bot) - a bot that is used to create and manage registration tokens for a Matrix server. +Thanks to [Julian-Samuel Gebühr (@moan0s)](https://github.com/moan0s), the playbook can now help you set up [matrix-registration-bot](https://github.com/moan0s/matrix-registration-bot) - a bot that is used to create and manage registration tokens for a Matrix server. See our [Setting up matrix-registration-bot](docs/configuring-playbook-bot-matrix-registration-bot.md) documentation to get started. diff --git a/docs/configuring-playbook-bot-matrix-registration-bot.md b/docs/configuring-playbook-bot-matrix-registration-bot.md index 78d0bd0bf..b1f65a894 100644 --- a/docs/configuring-playbook-bot-matrix-registration-bot.md +++ b/docs/configuring-playbook-bot-matrix-registration-bot.md @@ -1,9 +1,9 @@ # Setting up matrix-registration-bot (optional) -The playbook can install and configure [matrix-registration-bot](https://github.com/moanos/matrix-registration-bot) for you. +The playbook can install and configure [matrix-registration-bot](https://github.com/moan0s/matrix-registration-bot) for you. The bot allows you to easily **create and manage registration tokens**. It can be used for an invitation-based server, -where you invite someone by sending them a registration token. They can register as normal but have to provide a valid +where you invite someone by sending them a registration token. They can register as normal but have to provide a valid registration token in a final step of the registration. See the project's [documentation](https://github.com/moan0s/matrix-registration-bot#supported-commands) to learn what it @@ -26,7 +26,7 @@ Choose a strong password for the bot. You can generate a good password with a co ## Obtaining an admin access token -In order to use the bot you need to add an admin user's access token token to the configuration. As you created an admin user for the +In order to use the bot you need to add an admin user's access token token to the configuration. As you created an admin user for the bot, it is recommended to obtain an access token by logging into Element/Schildichat with the bot account (using the password you set) and navigate to `Settings->Help&About` and scroll to the bottom. You can expand "Access token" to copy it. From 12198a147ba6fd5014b97a635ec66e821b2c2449 Mon Sep 17 00:00:00 2001 From: Erick Wibben Date: Thu, 21 Apr 2022 06:49:07 -0500 Subject: [PATCH 52/83] Update matrix-bot-matrix-registration-bot.service.j2 --- .../systemd/matrix-bot-matrix-registration-bot.service.j2 | 1 - 1 file changed, 1 deletion(-) diff --git a/roles/matrix-bot-matrix-registration-bot/templates/systemd/matrix-bot-matrix-registration-bot.service.j2 b/roles/matrix-bot-matrix-registration-bot/templates/systemd/matrix-bot-matrix-registration-bot.service.j2 index ba2a95931..e1aa89548 100644 --- a/roles/matrix-bot-matrix-registration-bot/templates/systemd/matrix-bot-matrix-registration-bot.service.j2 +++ b/roles/matrix-bot-matrix-registration-bot/templates/systemd/matrix-bot-matrix-registration-bot.service.j2 @@ -25,7 +25,6 @@ ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-matrix-reg --mount type=bind,src={{ matrix_bot_matrix_registration_bot_config_path }},dst=/config,ro \ --mount type=bind,src={{ matrix_bot_matrix_registration_bot_data_path }},dst=/data \ --network={{ matrix_docker_network }} \ - --env-file={{ matrix_bot_matrix_registration_bot_config_path }}/env \ {{ matrix_bot_matrix_registration_bot_docker_image }} ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-registration-bot 2>/dev/null || true' From 92384360f53cd1a111997deb522dfecc22dca155 Mon Sep 17 00:00:00 2001 From: Erick Wibben Date: Thu, 21 Apr 2022 06:50:24 -0500 Subject: [PATCH 53/83] Fixed documentation to reflect needed user --- docs/configuring-playbook-bot-matrix-registration-bot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuring-playbook-bot-matrix-registration-bot.md b/docs/configuring-playbook-bot-matrix-registration-bot.md index b1f65a894..c47d5bfd7 100644 --- a/docs/configuring-playbook-bot-matrix-registration-bot.md +++ b/docs/configuring-playbook-bot-matrix-registration-bot.md @@ -63,7 +63,7 @@ ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start ## Usage -To use the bot, create a **non-encrypted** room and invite `@bot.matrix-reminder-bot:DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). +To use the bot, create a **non-encrypted** room and invite `@bot.matrix-registration-bot:DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). In this room send `help` and the bot will reply with all options. From de3fc61129beef1368d2cd8bfd685ba05028ec21 Mon Sep 17 00:00:00 2001 From: GoliathLabs Date: Thu, 21 Apr 2022 13:53:01 +0200 Subject: [PATCH 54/83] Updated: mautrix-signal v0.3.0 & signald 0.18.0 --- roles/matrix-bridge-mautrix-signal/defaults/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/matrix-bridge-mautrix-signal/defaults/main.yml b/roles/matrix-bridge-mautrix-signal/defaults/main.yml index 4e95f1f92..c63874e0d 100644 --- a/roles/matrix-bridge-mautrix-signal/defaults/main.yml +++ b/roles/matrix-bridge-mautrix-signal/defaults/main.yml @@ -8,8 +8,8 @@ matrix_mautrix_signal_container_image_self_build: false matrix_mautrix_signal_docker_repo: "https://mau.dev/mautrix/signal.git" matrix_mautrix_signal_docker_src_files_path: "{{ matrix_base_data_path }}/mautrix-signal/docker-src" -matrix_mautrix_signal_version: v0.2.3 -matrix_mautrix_signal_daemon_version: 0.17.0 +matrix_mautrix_signal_version: v0.3.0 +matrix_mautrix_signal_daemon_version: 0.18.0 # See: https://mau.dev/mautrix/signal/container_registry matrix_mautrix_signal_docker_image: "dock.mau.dev/mautrix/signal:{{ matrix_mautrix_signal_version }}" matrix_mautrix_signal_docker_image_force_pull: "{{ matrix_mautrix_signal_docker_image.endswith(':latest') }}" From 0b5e4aa784a7d4dc72e5a2b389c5882658f92676 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 21 Apr 2022 15:53:05 +0300 Subject: [PATCH 55/83] Use non-root image for Signald MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1775 Related to https://signald.org/articles/install/docker/#migrating-from-versions-before-0180 > Prior to 0.18.0 the signald container image used the root user, which is not recommended for security reasons. This was fixed in the 0.18.0 release which will start as root, fix permissions on the volume, then drop to the non-root user and start signald. Future images will start as the non-root user, so if you’re upgrading make sure to run 0.18.0 at least once. > A special tag, 0.18.0-non-root, will be published. it starts as the non-root user and does not fix permissions on the volume. --- roles/matrix-bridge-mautrix-signal/defaults/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/roles/matrix-bridge-mautrix-signal/defaults/main.yml b/roles/matrix-bridge-mautrix-signal/defaults/main.yml index c63874e0d..e6b9678dd 100644 --- a/roles/matrix-bridge-mautrix-signal/defaults/main.yml +++ b/roles/matrix-bridge-mautrix-signal/defaults/main.yml @@ -18,8 +18,9 @@ matrix_mautrix_signal_daemon_container_image_self_build: false matrix_mautrix_signal_daemon_docker_repo: "https://mau.dev/maunium/signald.git" matrix_mautrix_signal_daemon_docker_src_files_path: "{{ matrix_base_data_path }}/mautrix-signald/docker-src" -matrix_mautrix_signal_daemon_docker_image: "docker.io/signald/signald:{{ matrix_mautrix_signal_daemon_version }}" -matrix_mautrix_signal_daemon_docker_image_force_pull: "{{ matrix_mautrix_signal_daemon_docker_image.endswith(':latest') }}" +matrix_mautrix_signal_daemon_docker_image: "docker.io/signald/signald:{{ matrix_mautrix_signal_daemon_docker_image_tag }}" +matrix_mautrix_signal_daemon_docker_image_force_pull: "{{ matrix_mautrix_signal_daemon_docker_image_tag.endswith(':latest') }}" +matrix_mautrix_signal_daemon_docker_image_tag: "{{ matrix_mautrix_signal_daemon_version }}-non-root" matrix_mautrix_signal_base_path: "{{ matrix_base_data_path }}/mautrix-signal" matrix_mautrix_signal_config_path: "{{ matrix_mautrix_signal_base_path }}/bridge" From 69f684255cf89ac36c5e33037d30eaade1b4a759 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 21 Apr 2022 15:54:29 +0300 Subject: [PATCH 56/83] Fix Signald git repository to unbreak self-building The maunium fork of Signald is no longer up-to-date (does not publish 0.18.0.. at least not yet) and all the necessary changes are now upstream. Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1775 --- roles/matrix-bridge-mautrix-signal/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-mautrix-signal/defaults/main.yml b/roles/matrix-bridge-mautrix-signal/defaults/main.yml index e6b9678dd..a7532fed3 100644 --- a/roles/matrix-bridge-mautrix-signal/defaults/main.yml +++ b/roles/matrix-bridge-mautrix-signal/defaults/main.yml @@ -15,7 +15,7 @@ matrix_mautrix_signal_docker_image: "dock.mau.dev/mautrix/signal:{{ matrix_mautr matrix_mautrix_signal_docker_image_force_pull: "{{ matrix_mautrix_signal_docker_image.endswith(':latest') }}" matrix_mautrix_signal_daemon_container_image_self_build: false -matrix_mautrix_signal_daemon_docker_repo: "https://mau.dev/maunium/signald.git" +matrix_mautrix_signal_daemon_docker_repo: "https://gitlab.com/signald/signald" matrix_mautrix_signal_daemon_docker_src_files_path: "{{ matrix_base_data_path }}/mautrix-signald/docker-src" matrix_mautrix_signal_daemon_docker_image: "docker.io/signald/signald:{{ matrix_mautrix_signal_daemon_docker_image_tag }}" From 380e8656442cd432c8c1eec7a76ebc36ba9bf79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian-Samuel=20Geb=C3=BChr?= Date: Fri, 22 Apr 2022 07:41:10 +0200 Subject: [PATCH 57/83] Change list of public servers, old as not functional The old link returned a 404 so I thought I throw in joinmatrix.org :) --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index d9c7a5866..f2df8698b 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -55,7 +55,7 @@ There are 3 ways to get into Martix, depending on your technical ability and nee - **using the existing default server** - the easiest way is to use an existing server. The largest public Matrix server is `matrix.org` and it's configured as a default server in clients such as [Element](https://element.io) and many others. Just use Element on the browser via that link (or download the Element app on a smartphone), create an account and start chatting. -- **using some other server** - instead of using the largest public server (`matrix.org`), you can use another public one. Here's a [list of public Matrix servers](https://publiclist.anchel.nl/) to choose from. Again, you download [Element](https://element.io) or [some other client](https://matrix.org/clients/) of your choosing and adjust the homeserver URL during login. +- **using some other server** - instead of using the largest public server (`matrix.org`), you can use another public one. Here's a [list of public Matrix servers](https://joinmatrix.org/servers/) to choose from. Again, you download [Element](https://element.io) or [some other client](https://matrix.org/clients/) of your choosing and adjust the homeserver URL during login. - **using your own server** - running your own server puts you in ultimate control of your data. It also lets you have your own user identifiers (e.g. `@bob:your-domain.com`). See [How do I set up my own Matrix server](#how-do-i-set-up-my-own-matrix-server). From 9e0d969ba47480a6355e47c550d828dca5c45afc Mon Sep 17 00:00:00 2001 From: Toni Spets Date: Fri, 22 Apr 2022 13:56:34 +0300 Subject: [PATCH 58/83] Upgrade Heisenbridge (1.10.1 -> 1.12.0) --- roles/matrix-bridge-heisenbridge/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-heisenbridge/defaults/main.yml b/roles/matrix-bridge-heisenbridge/defaults/main.yml index 80b3c95af..96ab33821 100644 --- a/roles/matrix-bridge-heisenbridge/defaults/main.yml +++ b/roles/matrix-bridge-heisenbridge/defaults/main.yml @@ -4,7 +4,7 @@ matrix_heisenbridge_enabled: true -matrix_heisenbridge_version: 1.10.1 +matrix_heisenbridge_version: 1.12.0 matrix_heisenbridge_docker_image: "{{ matrix_container_global_registry_prefix }}hif1/heisenbridge:{{ matrix_heisenbridge_version }}" matrix_heisenbridge_docker_image_force_pull: "{{ matrix_heisenbridge_docker_image.endswith(':latest') }}" From fa108b8ae4a4876e8254c1d432d4491cd0e76685 Mon Sep 17 00:00:00 2001 From: Matthew Cengia Date: Fri, 22 Apr 2022 21:47:30 +1000 Subject: [PATCH 59/83] Bump signald to 0.18.1 According to https://signald.org/articles/install/docker/#migrating-from-versions-before-0180, This release only chowns files if the container is running as root. See also this upstream commit: https://gitlab.com/signald/signald/-/commit/3bb7e8d2c128681473e324f811cff25e0883b88d --- roles/matrix-bridge-mautrix-signal/defaults/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/matrix-bridge-mautrix-signal/defaults/main.yml b/roles/matrix-bridge-mautrix-signal/defaults/main.yml index a7532fed3..14a2c35fa 100644 --- a/roles/matrix-bridge-mautrix-signal/defaults/main.yml +++ b/roles/matrix-bridge-mautrix-signal/defaults/main.yml @@ -9,7 +9,7 @@ matrix_mautrix_signal_docker_repo: "https://mau.dev/mautrix/signal.git" matrix_mautrix_signal_docker_src_files_path: "{{ matrix_base_data_path }}/mautrix-signal/docker-src" matrix_mautrix_signal_version: v0.3.0 -matrix_mautrix_signal_daemon_version: 0.18.0 +matrix_mautrix_signal_daemon_version: 0.18.1 # See: https://mau.dev/mautrix/signal/container_registry matrix_mautrix_signal_docker_image: "dock.mau.dev/mautrix/signal:{{ matrix_mautrix_signal_version }}" matrix_mautrix_signal_docker_image_force_pull: "{{ matrix_mautrix_signal_docker_image.endswith(':latest') }}" @@ -20,7 +20,7 @@ matrix_mautrix_signal_daemon_docker_src_files_path: "{{ matrix_base_data_path }} matrix_mautrix_signal_daemon_docker_image: "docker.io/signald/signald:{{ matrix_mautrix_signal_daemon_docker_image_tag }}" matrix_mautrix_signal_daemon_docker_image_force_pull: "{{ matrix_mautrix_signal_daemon_docker_image_tag.endswith(':latest') }}" -matrix_mautrix_signal_daemon_docker_image_tag: "{{ matrix_mautrix_signal_daemon_version }}-non-root" +matrix_mautrix_signal_daemon_docker_image_tag: "{{ matrix_mautrix_signal_daemon_version }}" matrix_mautrix_signal_base_path: "{{ matrix_base_data_path }}/mautrix-signal" matrix_mautrix_signal_config_path: "{{ matrix_mautrix_signal_base_path }}/bridge" From 68424e68e595e01e68824992062898ec92b26d3f Mon Sep 17 00:00:00 2001 From: Andrea Tartaglia Date: Sat, 23 Apr 2022 11:13:36 +0100 Subject: [PATCH 60/83] feat: make synapse htpasswd file path configurable When setting `matrix_nginx_proxy_enabled: false` and enabling authentication on the metrics endpoint, the htpasswd file is hardcoded to the nginx-proxy container dir, this changes the hardcoded value to a variable so the path can be updated --- roles/matrix-nginx-proxy/defaults/main.yml | 1 + .../templates/nginx/conf.d/matrix-synapse.conf.j2 | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/roles/matrix-nginx-proxy/defaults/main.yml b/roles/matrix-nginx-proxy/defaults/main.yml index 0aaa53edc..8067b916e 100644 --- a/roles/matrix-nginx-proxy/defaults/main.yml +++ b/roles/matrix-nginx-proxy/defaults/main.yml @@ -221,6 +221,7 @@ matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled: false # e.g. `htpasswd -c mypass.htpasswd prometheus` and enter `mysecurepw` when prompted yields `prometheus:$apr1$wZhqsn.U$7LC3kMmjUbjNAZjyMyvYv/` # The part after `prometheus:` is needed here. matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_key: "$apr1$wZhqsn.U$7LC3kMmjUbjNAZjyMyvYv/" matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_key: "" +matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_path: "/nginx-data/matrix-synapse-metrics-htpasswd" # The addresses where the Matrix Client API is. # Certain extensions (like matrix-corporal) may override this in order to capture all traffic. diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 index b15546fe6..9a1576d48 100644 --- a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 @@ -161,7 +161,7 @@ server { {% if matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled %} auth_basic "protected"; - auth_basic_user_file /nginx-data/matrix-synapse-metrics-htpasswd; + auth_basic_user_file {{ matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_path }}; {% endif %} } {% endif %} @@ -177,7 +177,7 @@ server { {% if matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled %} auth_basic "protected"; - auth_basic_user_file /nginx-data/matrix-synapse-metrics-htpasswd; + auth_basic_user_file {{ matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_path }}; {% endif %} } {% endif %} From 290754371a8407c8b21044d40678799d2ad6d633 Mon Sep 17 00:00:00 2001 From: Aine Date: Sat, 23 Apr 2022 16:19:24 +0300 Subject: [PATCH 61/83] add matrix-bot-buscarron --- docs/configuring-dns.md | 3 + docs/configuring-playbook-bot-buscarron.md | 75 +++++++++++++ group_vars/matrix_servers | 42 +++++++ roles/matrix-base/defaults/main.yml | 3 + roles/matrix-bot-buscarron/defaults/main.yml | 96 ++++++++++++++++ roles/matrix-bot-buscarron/tasks/init.yml | 5 + roles/matrix-bot-buscarron/tasks/main.yml | 23 ++++ .../tasks/setup_install.yml | 100 +++++++++++++++++ .../tasks/setup_uninstall.yml | 36 ++++++ .../tasks/validate_config.yml | 9 ++ roles/matrix-bot-buscarron/templates/env.j2 | 19 ++++ .../systemd/matrix-bot-buscarron.service.j2 | 39 +++++++ roles/matrix-nginx-proxy/defaults/main.yml | 7 ++ .../tasks/setup_nginx_proxy.yml | 13 +++ .../nginx/conf.d/matrix-bot-buscarron.conf.j2 | 104 ++++++++++++++++++ setup.yml | 1 + 16 files changed, 575 insertions(+) create mode 100644 docs/configuring-playbook-bot-buscarron.md create mode 100644 roles/matrix-bot-buscarron/defaults/main.yml create mode 100644 roles/matrix-bot-buscarron/tasks/init.yml create mode 100644 roles/matrix-bot-buscarron/tasks/main.yml create mode 100644 roles/matrix-bot-buscarron/tasks/setup_install.yml create mode 100644 roles/matrix-bot-buscarron/tasks/setup_uninstall.yml create mode 100644 roles/matrix-bot-buscarron/tasks/validate_config.yml create mode 100644 roles/matrix-bot-buscarron/templates/env.j2 create mode 100644 roles/matrix-bot-buscarron/templates/systemd/matrix-bot-buscarron.service.j2 create mode 100644 roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-bot-buscarron.conf.j2 diff --git a/docs/configuring-dns.md b/docs/configuring-dns.md index c16ab2fca..666f8a63d 100644 --- a/docs/configuring-dns.md +++ b/docs/configuring-dns.md @@ -38,6 +38,7 @@ If you are using Cloudflare DNS, make sure to disable the proxy and set all reco | CNAME | `sygnal` | - | - | - | `matrix.` | | CNAME | `hydrogen` | - | - | - | `matrix.` | | CNAME | `cinny` | - | - | - | `matrix.` | +| CNAME | `buscarron` | - | - | - | `matrix.` | ## Subdomains setup @@ -60,6 +61,8 @@ The `hydrogen.` subdomain may be necessary, because this playbook c The `cinny.` subdomain may be necessary, because this playbook could install the [Cinny](https://github.com/ajbura/cinny) web client. The installation of cinny is disabled by default, it is not a core required component. To learn how to install it, see our [configuring cinny guide](configuring-playbook-client-cinny.md). If you do not wish to set up cinny, feel free to skip the `cinny.` DNS record. +The `buscarron.` subdomain may be necessary, because this playbook could install the [buscarron](https://github.com/etke.cc/buscarron) bot. The installation of buscarron is disabled by default, it is not a core required component. To learn how to install it, see our [configuring buscarron guide](configuring-playbook-bot-buscarron.md). If you do not wish to set up buscarron, feel free to skip the `buscarron.` DNS record. + ## `_matrix-identity._tcp` SRV record setup To make the [ma1sd](https://github.com/ma1uta/ma1sd) Identity Server (which this playbook may optionally install for you) enable its federation features, set up an SRV record that looks like this: diff --git a/docs/configuring-playbook-bot-buscarron.md b/docs/configuring-playbook-bot-buscarron.md new file mode 100644 index 000000000..3e2a395a2 --- /dev/null +++ b/docs/configuring-playbook-bot-buscarron.md @@ -0,0 +1,75 @@ +# Setting up Buscarron (optional) + +The playbook can install and configure [buscarron](https://gitlab.com/etke.cc/buscarron) for you. + +It's a bot you can use to setup **your own helpdesk on matrix** +It's a bot you can use to send any form (HTTP POST, HTML) to a (encrypted) matrix room + +## Registering the bot user + +By default, the playbook will set up the bot with a username like this: `@buscarron:DOMAIN`. + +(to use a different username, adjust the `matrix_bot_buscarron_login` variable). + +You **need to register the bot user manually** before setting up the bot. You can use the playbook to [register a new user](registering-users.md): + +``` +ansible-playbook -i inventory/hosts setup.yml --extra-vars='username=buscarron password=PASSWORD_FOR_THE_BOT admin=no' --tags=register-user +``` + +Choose a strong password for the bot. You can generate a good password with a command like this: `pwgen -s 64 1`. + + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file: + +```yaml +matrix_bot_buscarron_enabled: true + +# Adjust this to whatever password you chose when registering the bot user +matrix_bot_buscarron_password: PASSWORD_FOR_THE_BOT + +# Adjust accepted forms +matrix_bot_buscarron_forms: + - name: contact # (mandatory) Your form name, will be used as endpoint, eg: buscarron.DOMAIN/contact + room: "!yourRoomID:DOMAIN" # (mandatory) Room ID where form submission will be posted + redirect: https://DOMAIN # (mandatory) To what page user will be redirected after the form submission + ratelimit: 1r/m # (optional) rate limit of the form, format: r/, eg: 1r/s or 54r/m + extensions: [] # (optional) list of form extensions (not used yet) + +matrix_bot_buscarron_spam_hosts: [] # (optional) list of email domains/hosts that should be rejected automatically +matrix_bot_buscarron_spam_emails: [] # (optional) list of email addresses that should be rejected automatically +``` + +You will also need to add a DNS record so that buscarron can be accessed. +By default buscarron will use https://buscarron.DOMAIN so you will need to create an CNAME record for `buscarron`. +See [Configuring DNS](configuring-dns.md). + +If you would like to use a different domain, add the following to your configuration file (changing it to use your preferred domain): + +```yaml +matrix_server_fqn_buscarron: "form.{{ matrix_domain }}" +``` + + +## Installing + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +To use the bot, invite the `@buscarron:DOMAIN` to the room you specified in a config, after that any point your form to the form url, example for the `contact` form: + +```html +
+ +
+``` + +You can also refer to the upstream [documentation](https://gitlab.com/etke.cc/buscarron). diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 738c71ba7..225d29134 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -1047,6 +1047,37 @@ matrix_bot_honoroit_container_image_self_build: "{{ matrix_architecture not in [ # ###################################################################### +###################################################################### +# +# matrix-bot-buscarron +# +###################################################################### + +# We don't enable bots by default. +matrix_bot_buscarron_enabled: false + +matrix_bot_buscarron_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_bot_buscarron_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_bot_buscarron_database_password: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'buscarron.bot.db') | to_uuid }}" +matrix_bot_buscarron_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm32', 'arm64'] }}" + +###################################################################### +# +# /matrix-bot-buscarron +# +###################################################################### + ###################################################################### # @@ -1472,6 +1503,7 @@ matrix_nginx_proxy_proxy_matrix_enabled: true matrix_nginx_proxy_proxy_element_enabled: "{{ matrix_client_element_enabled }}" matrix_nginx_proxy_proxy_hydrogen_enabled: "{{ matrix_client_hydrogen_enabled }}" matrix_nginx_proxy_proxy_cinny_enabled: "{{ matrix_client_cinny_enabled }}" +matrix_nginx_proxy_proxy_buscarron_enabled: "{{ matrix_bot_buscarron_enabled }}" matrix_nginx_proxy_proxy_dimension_enabled: "{{ matrix_dimension_enabled }}" matrix_nginx_proxy_proxy_bot_go_neb_enabled: "{{ matrix_bot_go_neb_enabled }}" matrix_nginx_proxy_proxy_jitsi_enabled: "{{ matrix_jitsi_enabled }}" @@ -1556,6 +1588,8 @@ matrix_nginx_proxy_systemd_wanted_services_list: | + (['matrix-client-cinny.service'] if matrix_client_cinny_enabled else []) + + (['matrix-bot-buscarron.service'] if matrix_bot_buscarron_enabled else []) + + (['matrix-client-element.service'] if matrix_client_element_enabled else []) + (['matrix-client-hydrogen.service'] if matrix_client_hydrogen_enabled else []) @@ -1587,6 +1621,8 @@ matrix_ssl_domains_to_obtain_certificates_for: | + ([matrix_server_fqn_cinny] if matrix_client_cinny_enabled else []) + + ([matrix_server_fqn_buscarron] if matrix_bot_buscarron_enabled else []) + + ([matrix_server_fqn_dimension] if matrix_dimension_enabled else []) + ([matrix_server_fqn_bot_go_neb] if matrix_bot_go_neb_enabled else []) @@ -1698,6 +1734,12 @@ matrix_postgres_additional_databases: | 'password': matrix_bot_honoroit_database_password, }] if (matrix_bot_honoroit_enabled and matrix_bot_honoroit_database_engine == 'postgres' and matrix_bot_honoroit_database_hostname == 'matrix-postgres') else []) + + ([{ + 'name': matrix_bot_buscarron_database_name, + 'username': matrix_bot_buscarron_database_username, + 'password': matrix_bot_buscarron_database_password, + }] if (matrix_bot_buscarron_enabled and matrix_bot_buscarron_database_engine == 'postgres' and matrix_bot_buscarron_database_hostname == 'matrix-postgres') else []) + + ([{ 'name': matrix_registration_database_name, 'username': matrix_registration_database_username, diff --git a/roles/matrix-base/defaults/main.yml b/roles/matrix-base/defaults/main.yml index 498a6c321..645563781 100644 --- a/roles/matrix-base/defaults/main.yml +++ b/roles/matrix-base/defaults/main.yml @@ -37,6 +37,9 @@ matrix_server_fqn_hydrogen: "hydrogen.{{ matrix_domain }}" # This is where you access the Cinny web client from (if enabled via matrix_client_cinny_enabled; disabled by default). matrix_server_fqn_cinny: "cinny.{{ matrix_domain }}" +# This is where you access the buscarron bot from (if enabled via matrix_bot_buscarron_enabled; disabled by default). +matrix_server_fqn_buscarron: "buscarron.{{ matrix_domain }}" + # This is where you access the Dimension. matrix_server_fqn_dimension: "dimension.{{ matrix_domain }}" diff --git a/roles/matrix-bot-buscarron/defaults/main.yml b/roles/matrix-bot-buscarron/defaults/main.yml new file mode 100644 index 000000000..ca13bf308 --- /dev/null +++ b/roles/matrix-bot-buscarron/defaults/main.yml @@ -0,0 +1,96 @@ +--- +# buscarron is a helpdesk bot +# See: https://gitlab.com/etke.cc/buscarron + +matrix_bot_buscarron_enabled: true + +matrix_bot_buscarron_container_image_self_build: false +matrix_bot_buscarron_docker_repo: "https://gitlab.com/etke.cc/buscarron.git" +matrix_bot_buscarron_docker_src_files_path: "{{ matrix_base_data_path }}/buscarron/docker-src" + +matrix_bot_buscarron_version: latest +matrix_bot_buscarron_docker_image: "{{ matrix_bot_buscarron_docker_image_name_prefix }}buscarron:{{ matrix_bot_buscarron_version }}" +matrix_bot_buscarron_docker_image_name_prefix: "{{ 'localhost/' if matrix_bot_buscarron_container_image_self_build else 'registry.gitlab.com/etke.cc/' }}" +matrix_bot_buscarron_docker_image_force_pull: "{{ matrix_bot_buscarron_docker_image.endswith(':latest') }}" + +matrix_bot_buscarron_base_path: "{{ matrix_base_data_path }}/buscarron" +matrix_bot_buscarron_config_path: "{{ matrix_bot_buscarron_base_path }}/config" +matrix_bot_buscarron_data_path: "{{ matrix_bot_buscarron_base_path }}/data" +matrix_bot_buscarron_data_store_path: "{{ matrix_bot_buscarron_data_path }}/store" + +# A list of extra arguments to pass to the container +matrix_bot_buscarron_container_extra_arguments: [] + +# List of systemd services that matrix-bot-buscarron.service depends on +matrix_bot_buscarron_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-bot-buscarron.service wants +matrix_bot_buscarron_systemd_wanted_services_list: [] + + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_bot_buscarron_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_bot_buscarron_database_*` variables +matrix_bot_buscarron_database_engine: 'sqlite' + +matrix_bot_buscarron_sqlite_database_path_local: "{{ matrix_bot_buscarron_data_path }}/bot.db" +matrix_bot_buscarron_sqlite_database_path_in_container: "/data/bot.db" + +matrix_bot_buscarron_database_username: 'buscarron' +matrix_bot_buscarron_database_password: 'some-password' +matrix_bot_buscarron_database_hostname: 'matrix-postgres' +matrix_bot_buscarron_database_port: 5432 +matrix_bot_buscarron_database_name: 'buscarron' + +matrix_bot_buscarron_database_connection_string: 'postgres://{{ matrix_bot_buscarron_database_username }}:{{ matrix_bot_buscarron_database_password }}@{{ matrix_bot_buscarron_database_hostname }}:{{ matrix_bot_buscarron_database_port }}/{{ matrix_bot_buscarron_database_name }}?sslmode=disable' + +matrix_bot_buscarron_storage_database: "{{ + { + 'sqlite': matrix_bot_buscarron_sqlite_database_path_in_container, + 'postgres': matrix_bot_buscarron_database_connection_string, + }[matrix_bot_buscarron_database_engine] +}}" + +matrix_bot_buscarron_database_dialect: "{{ + { + 'sqlite': 'sqlite3', + 'postgres': 'postgres', + }[matrix_bot_buscarron_database_engine] +}}" + + +# The bot's username. This user needs to be created manually beforehand. +# Also see `matrix_bot_buscarron_password`. +matrix_bot_buscarron_login: "buscarron" + +# The password that the bot uses to authenticate. +matrix_bot_buscarron_password: '' + +# the homeserver URL, uses internal synapse container address by default +matrix_bot_buscarron_homeserver: "{{ matrix_homeserver_container_url }}" + +# forms configuration +matrix_bot_buscarron_forms: [] + +# Sentry DSN +matrix_bot_buscarron_sentry: + +# Log level +matrix_bot_buscarron_loglevel: INFO + +# spam hosts/domains +matrix_bot_buscarron_spam_hosts: [] + +# spam email addresses +matrix_bot_buscarron_spam_emails: [] + +# Additional environment variables to pass to the buscarron container +# +# Example: +# matrix_bot_buscarron_environment_variables_extension: | +# BUSCARRON_LOGLEVEL=DEBUG +matrix_bot_buscarron_environment_variables_extension: '' diff --git a/roles/matrix-bot-buscarron/tasks/init.yml b/roles/matrix-bot-buscarron/tasks/init.yml new file mode 100644 index 000000000..3da32948f --- /dev/null +++ b/roles/matrix-bot-buscarron/tasks/init.yml @@ -0,0 +1,5 @@ +--- + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-bot-buscarron.service'] }}" + when: matrix_bot_buscarron_enabled|bool diff --git a/roles/matrix-bot-buscarron/tasks/main.yml b/roles/matrix-bot-buscarron/tasks/main.yml new file mode 100644 index 000000000..63e87dfb8 --- /dev/null +++ b/roles/matrix-bot-buscarron/tasks/main.yml @@ -0,0 +1,23 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_bot_buscarron_enabled|bool" + tags: + - setup-all + - setup-bot-buscarron + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_bot_buscarron_enabled|bool" + tags: + - setup-all + - setup-bot-buscarron + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_bot_buscarron_enabled|bool" + tags: + - setup-all + - setup-bot-buscarron diff --git a/roles/matrix-bot-buscarron/tasks/setup_install.yml b/roles/matrix-bot-buscarron/tasks/setup_install.yml new file mode 100644 index 000000000..0ebe7e426 --- /dev/null +++ b/roles/matrix-bot-buscarron/tasks/setup_install.yml @@ -0,0 +1,100 @@ +--- +- set_fact: + matrix_bot_buscarron_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_bot_buscarron_sqlite_database_path_local }}" + register: matrix_bot_buscarron_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_bot_buscarron_sqlite_database_path_local }}" + dst: "{{ matrix_bot_buscarron_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_bot_buscarron_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-bot-buscarron.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_bot_buscarron_requires_restart: true + when: "matrix_bot_buscarron_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_bot_buscarron_database_engine == 'postgres'" + +- name: Ensure buscarron paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - {path: "{{ matrix_bot_buscarron_config_path }}", when: true} + - {path: "{{ matrix_bot_buscarron_data_path }}", when: true} + - {path: "{{ matrix_bot_buscarron_data_store_path }}", when: true} + - {path: "{{ matrix_bot_buscarron_docker_src_files_path }}", when: true} + when: "item.when|bool" + +- name: Ensure buscarron environment variables file created + template: + src: "{{ role_path }}/templates/env.j2" + dest: "{{ matrix_bot_buscarron_config_path }}/env" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: 0640 + +- name: Ensure buscarron image is pulled + docker_image: + name: "{{ matrix_bot_buscarron_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_bot_buscarron_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_bot_buscarron_docker_image_force_pull }}" + when: "not matrix_bot_buscarron_container_image_self_build|bool" + register: result + retries: "{{ matrix_container_retries_count }}" + delay: "{{ matrix_container_retries_delay }}" + until: result is not failed + +- name: Ensure buscarron repository is present on self-build + git: + repo: "{{ matrix_bot_buscarron_docker_repo }}" + dest: "{{ matrix_bot_buscarron_docker_src_files_path }}" + force: "yes" + become: true + become_user: "{{ matrix_user_username }}" + register: matrix_bot_buscarron_git_pull_results + when: "matrix_bot_buscarron_container_image_self_build|bool" + +- name: Ensure buscarron image is built + docker_image: + name: "{{ matrix_bot_buscarron_docker_image }}" + source: build + force_source: "{{ matrix_bot_buscarron_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mailer_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_bot_buscarron_docker_src_files_path }}" + pull: true + when: "matrix_bot_buscarron_container_image_self_build|bool" + +- name: Ensure matrix-bot-buscarron.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-bot-buscarron.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-bot-buscarron.service" + mode: 0644 + register: matrix_bot_buscarron_systemd_service_result + +- name: Ensure systemd reloaded after matrix-bot-buscarron.service installation + service: + daemon_reload: true + when: "matrix_bot_buscarron_systemd_service_result.changed|bool" + +- name: Ensure matrix-bot-buscarron.service restarted, if necessary + service: + name: "matrix-bot-buscarron.service" + state: restarted + when: "matrix_bot_buscarron_requires_restart|bool" diff --git a/roles/matrix-bot-buscarron/tasks/setup_uninstall.yml b/roles/matrix-bot-buscarron/tasks/setup_uninstall.yml new file mode 100644 index 000000000..cc70e79a3 --- /dev/null +++ b/roles/matrix-bot-buscarron/tasks/setup_uninstall.yml @@ -0,0 +1,36 @@ +--- + +- name: Check existence of matrix-buscarron service + stat: + path: "{{ matrix_systemd_path }}/matrix-bot-buscarron.service" + register: matrix_bot_buscarron_service_stat + +- name: Ensure matrix-buscarron is stopped + service: + name: matrix-bot-buscarron + state: stopped + enabled: false + daemon_reload: true + register: stopping_result + when: "matrix_bot_buscarron_service_stat.stat.exists|bool" + +- name: Ensure matrix-bot-buscarron.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-bot-buscarron.service" + state: absent + when: "matrix_bot_buscarron_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-bot-buscarron.service removal + service: + daemon_reload: true + when: "matrix_bot_buscarron_service_stat.stat.exists|bool" + +- name: Ensure Matrix buscarron paths don't exist + file: + path: "{{ matrix_bot_buscarron_base_path }}" + state: absent + +- name: Ensure buscarron Docker image doesn't exist + docker_image: + name: "{{ matrix_bot_buscarron_docker_image }}" + state: absent diff --git a/roles/matrix-bot-buscarron/tasks/validate_config.yml b/roles/matrix-bot-buscarron/tasks/validate_config.yml new file mode 100644 index 000000000..5a517d394 --- /dev/null +++ b/roles/matrix-bot-buscarron/tasks/validate_config.yml @@ -0,0 +1,9 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_bot_buscarron_password" diff --git a/roles/matrix-bot-buscarron/templates/env.j2 b/roles/matrix-bot-buscarron/templates/env.j2 new file mode 100644 index 000000000..c833f27be --- /dev/null +++ b/roles/matrix-bot-buscarron/templates/env.j2 @@ -0,0 +1,19 @@ +BUSCARRON_LOGIN={{ matrix_bot_buscarron_login }} +BUSCARRON_PASSWORD={{ matrix_bot_buscarron_password }} +BUSCARRON_HOMESERVER={{ matrix_bot_buscarron_homeserver }} +BUSCARRON_DB_DSN={{ matrix_bot_buscarron_database_connection_string }} +BUSCARRON_DB_DIALECT={{ matrix_bot_buscarron_database_dialect }} +BUSCARRON_SPAM_HOSTS={{ matrix_bot_buscarron_spam_hosts|join(" ") }} +BUSCARRON_SPAM_EMAILS={{ matrix_bot_buscarron_spam_emails|join(" ") }} +BUSCARRON_SENTRY={{ matrix_bot_buscarron_sentry }} +BUSCARRON_LOGLEVEL={{ matrix_bot_buscarron_loglevel }} +{% set forms = [] %} +{% for form in matrix_bot_buscarron_forms -%}{{- forms.append(form.name) -}} +BUSCARRON_{{ form.name|upper }}_ROOM={{ form.room|default('') }} +BUSCARRON_{{ form.name|upper }}_REDIRECT={{ form.redirect|default('') }} +BUSCARRON_{{ form.name|upper }}_RATELIMIT={{ form.ratelimit|default('') }} +BUSCARRON_{{ form.name|upper }}_EXTENSIONS={{ form.extensions|default('')|join(' ') }} +{% endfor %} +BUSCARRON_LIST={{ forms|join(" ") }} + +{{ matrix_bot_buscarron_environment_variables_extension }} diff --git a/roles/matrix-bot-buscarron/templates/systemd/matrix-bot-buscarron.service.j2 b/roles/matrix-bot-buscarron/templates/systemd/matrix-bot-buscarron.service.j2 new file mode 100644 index 000000000..fd6d03100 --- /dev/null +++ b/roles/matrix-bot-buscarron/templates/systemd/matrix-bot-buscarron.service.j2 @@ -0,0 +1,39 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix web forms bot +{% for service in matrix_bot_buscarron_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_bot_buscarron_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-buscarron 2>/dev/null || true' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-buscarron 2>/dev/null || true' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-buscarron \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + --env-file={{ matrix_bot_buscarron_config_path }}/env \ + --mount type=bind,src={{ matrix_bot_buscarron_data_path }},dst=/data \ + {% for arg in matrix_bot_buscarron_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_bot_buscarron_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-buscarron 2>/dev/null || true' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-buscarron 2>/dev/null || true' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-bot-buscarron + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-nginx-proxy/defaults/main.yml b/roles/matrix-nginx-proxy/defaults/main.yml index 0aaa53edc..ed8a88171 100644 --- a/roles/matrix-nginx-proxy/defaults/main.yml +++ b/roles/matrix-nginx-proxy/defaults/main.yml @@ -159,6 +159,10 @@ matrix_nginx_proxy_proxy_hydrogen_hostname: "{{ matrix_server_fqn_hydrogen }}" matrix_nginx_proxy_proxy_cinny_enabled: false matrix_nginx_proxy_proxy_cinny_hostname: "{{ matrix_server_fqn_cinny }}" +# Controls whether proxying the buscarron domain should be done. +matrix_nginx_proxy_proxy_buscarron_enabled: false +matrix_nginx_proxy_proxy_buscarron_hostname: "{{ matrix_server_fqn_buscarron }}" + # Controls whether proxying the matrix domain should be done. matrix_nginx_proxy_proxy_matrix_enabled: false matrix_nginx_proxy_proxy_matrix_hostname: "{{ matrix_server_fqn_matrix }}" @@ -303,6 +307,9 @@ matrix_nginx_proxy_proxy_hydrogen_additional_server_configuration_blocks: [] # A list of strings containing additional configuration blocks to add to Cinny's server configuration (matrix-client-cinny.conf). matrix_nginx_proxy_proxy_cinny_additional_server_configuration_blocks: [] +# A list of strings containing additional configuration blocks to add to buscarron's server configuration (matrix-bot-buscarron.conf). +matrix_nginx_proxy_proxy_buscarron_additional_server_configuration_blocks: [] + # A list of strings containing additional configuration blocks to add to Dimension's server configuration (matrix-dimension.conf). matrix_nginx_proxy_proxy_dimension_additional_server_configuration_blocks: [] diff --git a/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml b/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml index 30001dd29..a559e1090 100644 --- a/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml +++ b/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml @@ -110,6 +110,13 @@ mode: 0644 when: matrix_nginx_proxy_proxy_cinny_enabled|bool +- name: Ensure Matrix nginx-proxy configuration for buscarron domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-bot-buscarron.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-bot-buscarron.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_buscarron_enabled|bool + - name: Ensure Matrix nginx-proxy configuration for dimension domain exists template: src: "{{ role_path }}/templates/nginx/conf.d/matrix-dimension.conf.j2" @@ -259,6 +266,12 @@ state: absent when: "not matrix_nginx_proxy_proxy_cinny_enabled|bool" +- name: Ensure Matrix nginx-proxy configuration for buscarron domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-bot-buscarron.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_buscarron_enabled|bool" + - name: Ensure Matrix nginx-proxy configuration for dimension domain deleted file: path: "{{ matrix_nginx_proxy_confd_path }}/matrix-dimension.conf" diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-bot-buscarron.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-bot-buscarron.conf.j2 new file mode 100644 index 000000000..0ce1473be --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-bot-buscarron.conf.j2 @@ -0,0 +1,104 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json application/javascript text/css image/x-icon font/ttf image/gif; + + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options SAMEORIGIN; + add_header Content-Security-Policy "frame-ancestors 'none'"; + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + + {% for configuration_block in matrix_nginx_proxy_proxy_buscarron_additional_server_configuration_blocks %} + {{- configuration_block }} + {% endfor %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-bot-buscarron:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:8080; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For {{ matrix_nginx_proxy_x_forwarded_for }}; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + listen [::]:{{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + + + server_name {{ matrix_nginx_proxy_proxy_buscarron_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_buscarron_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_buscarron_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_buscarron_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != "" %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_buscarron_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/setup.yml b/setup.yml index d24c3c991..ce36d1cec 100755 --- a/setup.yml +++ b/setup.yml @@ -38,6 +38,7 @@ - matrix-bridge-hookshot - matrix-bot-matrix-reminder-bot - matrix-bot-matrix-registration-bot + - matrix-bot-buscarron - matrix-bot-honoroit - matrix-bot-go-neb - matrix-bot-mjolnir From 5ae93fbf2bc5c2288ede6f338c9f404dd3035cf5 Mon Sep 17 00:00:00 2001 From: Aine Date: Sat, 23 Apr 2022 17:11:24 +0300 Subject: [PATCH 62/83] add buscarron to the readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 631dc297d..26f109404 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,8 @@ Using this playbook, you can get the following services configured on your serve - (optional) the [Borg](https://borgbackup.org) backup - see [docs/configuring-playbook-backup-borg.md](docs/configuring-playbook-backup-borg.md) for setup documentation +- (optional) the [Buscarron](https://gitlab.com/etke.cc/buscarron) bot - see [docs/configuring-playbook-bot-buscarron.md](docs/configuring-playbook-bot-buscarron.md) for setup documentation + Basically, this playbook aims to get you up-and-running with all the necessities around Matrix, without you having to do anything else. **Note**: the list above is exhaustive. It includes optional or even some advanced components that you will most likely not need. From b720b15de64d5e12e2543cb9d57e710c4236148d Mon Sep 17 00:00:00 2001 From: Aine Date: Sun, 24 Apr 2022 09:50:23 +0300 Subject: [PATCH 63/83] buscarron v1.0.0 --- roles/matrix-bot-buscarron/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bot-buscarron/defaults/main.yml b/roles/matrix-bot-buscarron/defaults/main.yml index ca13bf308..6322144eb 100644 --- a/roles/matrix-bot-buscarron/defaults/main.yml +++ b/roles/matrix-bot-buscarron/defaults/main.yml @@ -8,7 +8,7 @@ matrix_bot_buscarron_container_image_self_build: false matrix_bot_buscarron_docker_repo: "https://gitlab.com/etke.cc/buscarron.git" matrix_bot_buscarron_docker_src_files_path: "{{ matrix_base_data_path }}/buscarron/docker-src" -matrix_bot_buscarron_version: latest +matrix_bot_buscarron_version: v1.0.0 matrix_bot_buscarron_docker_image: "{{ matrix_bot_buscarron_docker_image_name_prefix }}buscarron:{{ matrix_bot_buscarron_version }}" matrix_bot_buscarron_docker_image_name_prefix: "{{ 'localhost/' if matrix_bot_buscarron_container_image_self_build else 'registry.gitlab.com/etke.cc/' }}" matrix_bot_buscarron_docker_image_force_pull: "{{ matrix_bot_buscarron_docker_image.endswith(':latest') }}" From 2d21a70b3e9f835c94080f0298cec68c56956c3d Mon Sep 17 00:00:00 2001 From: Sekki21956 Date: Mon, 25 Apr 2022 02:05:13 +0200 Subject: [PATCH 64/83] Update path to signald Dockerfile --- roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml index c7202f05a..06f77348b 100644 --- a/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml +++ b/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml @@ -70,7 +70,7 @@ force_source: "{{ matrix_mautrix_signal_daemon_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_signal_daemon_git_pull_results.changed }}" build: - dockerfile: Dockerfile + dockerfile: Containerfile path: "{{ matrix_mautrix_signal_daemon_docker_src_files_path }}" pull: true when: "matrix_mautrix_signal_daemon_container_image_self_build|bool" From c83c70ac35687cb620c3c23656c62bdfd8ac7a9f Mon Sep 17 00:00:00 2001 From: Matthew Cengia Date: Mon, 25 Apr 2022 10:21:48 +1000 Subject: [PATCH 65/83] Don't self-build signald image on arm64, as upstream image exists --- group_vars/matrix_servers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 738c71ba7..bcd26b993 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -458,7 +458,7 @@ matrix_mautrix_signal_database_engine: 'postgres' matrix_mautrix_signal_database_password: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'mau.signal.db') | to_uuid }}" matrix_mautrix_signal_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" -matrix_mautrix_signal_daemon_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" +matrix_mautrix_signal_daemon_container_image_self_build: "{{ matrix_architecture != ['amd64', 'arm64'] }}" ###################################################################### # From 47e5bab784339b416f67b5a5d5a006c1df9a289a Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Mon, 25 Apr 2022 09:22:01 +0300 Subject: [PATCH 66/83] Fix self-building if condition --- group_vars/matrix_servers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index bcd26b993..d42567347 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -458,7 +458,7 @@ matrix_mautrix_signal_database_engine: 'postgres' matrix_mautrix_signal_database_password: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'mau.signal.db') | to_uuid }}" matrix_mautrix_signal_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" -matrix_mautrix_signal_daemon_container_image_self_build: "{{ matrix_architecture != ['amd64', 'arm64'] }}" +matrix_mautrix_signal_daemon_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" ###################################################################### # From c92af9fe894d466e9b02e1279ecda9f7161b1a60 Mon Sep 17 00:00:00 2001 From: Aine Date: Mon, 25 Apr 2022 09:40:49 +0300 Subject: [PATCH 67/83] matrix-bot-buscarron: feedback --- docs/configuring-playbook-bot-buscarron.md | 14 +++++++------- roles/matrix-bot-buscarron/defaults/main.yml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/configuring-playbook-bot-buscarron.md b/docs/configuring-playbook-bot-buscarron.md index 3e2a395a2..5a2c327d8 100644 --- a/docs/configuring-playbook-bot-buscarron.md +++ b/docs/configuring-playbook-bot-buscarron.md @@ -7,7 +7,7 @@ It's a bot you can use to send any form (HTTP POST, HTML) to a (encrypted) matri ## Registering the bot user -By default, the playbook will set up the bot with a username like this: `@buscarron:DOMAIN`. +By default, the playbook will set up the bot with a username like this: `@bot.buscarron:DOMAIN`. (to use a different username, adjust the `matrix_bot_buscarron_login` variable). @@ -32,11 +32,11 @@ matrix_bot_buscarron_password: PASSWORD_FOR_THE_BOT # Adjust accepted forms matrix_bot_buscarron_forms: - - name: contact # (mandatory) Your form name, will be used as endpoint, eg: buscarron.DOMAIN/contact - room: "!yourRoomID:DOMAIN" # (mandatory) Room ID where form submission will be posted - redirect: https://DOMAIN # (mandatory) To what page user will be redirected after the form submission - ratelimit: 1r/m # (optional) rate limit of the form, format: r/, eg: 1r/s or 54r/m - extensions: [] # (optional) list of form extensions (not used yet) + - name: contact # (mandatory) Your form name, will be used as endpoint, eg: buscarron.DOMAIN/contact + room: "!yourRoomID:DOMAIN" # (mandatory) Room ID where form submission will be posted + redirect: https://DOMAIN # (mandatory) To what page user will be redirected after the form submission + ratelimit: 1r/m # (optional) rate limit of the form, format: r/, eg: 1r/s or 54r/m + extensions: [] # (optional) list of form extensions (not used yet) matrix_bot_buscarron_spam_hosts: [] # (optional) list of email domains/hosts that should be rejected automatically matrix_bot_buscarron_spam_emails: [] # (optional) list of email addresses that should be rejected automatically @@ -64,7 +64,7 @@ ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start ## Usage -To use the bot, invite the `@buscarron:DOMAIN` to the room you specified in a config, after that any point your form to the form url, example for the `contact` form: +To use the bot, invite the `@bot.buscarron:DOMAIN` to the room you specified in a config, after that any point your form to the form url, example for the `contact` form: ```html
diff --git a/roles/matrix-bot-buscarron/defaults/main.yml b/roles/matrix-bot-buscarron/defaults/main.yml index 6322144eb..96e8ef91a 100644 --- a/roles/matrix-bot-buscarron/defaults/main.yml +++ b/roles/matrix-bot-buscarron/defaults/main.yml @@ -65,7 +65,7 @@ matrix_bot_buscarron_database_dialect: "{{ # The bot's username. This user needs to be created manually beforehand. # Also see `matrix_bot_buscarron_password`. -matrix_bot_buscarron_login: "buscarron" +matrix_bot_buscarron_login: "bot.buscarron" # The password that the bot uses to authenticate. matrix_bot_buscarron_password: '' From 4a0b8397680119432287c175dad68f479324cb82 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Mon, 25 Apr 2022 09:42:36 +0300 Subject: [PATCH 68/83] Automatically do the right thing with regards to Synapse Metrics htpasswd .. regardless of whether matrix-nginx-proxy runs in a container or not --- roles/matrix-nginx-proxy/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-nginx-proxy/defaults/main.yml b/roles/matrix-nginx-proxy/defaults/main.yml index 8067b916e..3c68e7753 100644 --- a/roles/matrix-nginx-proxy/defaults/main.yml +++ b/roles/matrix-nginx-proxy/defaults/main.yml @@ -221,7 +221,7 @@ matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled: false # e.g. `htpasswd -c mypass.htpasswd prometheus` and enter `mysecurepw` when prompted yields `prometheus:$apr1$wZhqsn.U$7LC3kMmjUbjNAZjyMyvYv/` # The part after `prometheus:` is needed here. matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_key: "$apr1$wZhqsn.U$7LC3kMmjUbjNAZjyMyvYv/" matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_key: "" -matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_path: "/nginx-data/matrix-synapse-metrics-htpasswd" +matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_path: "{{ matrix_nginx_proxy_data_path_in_container if matrix_nginx_proxy_enabled else matrix_nginx_proxy_data_path }}/matrix-synapse-metrics-htpasswd" # The addresses where the Matrix Client API is. # Certain extensions (like matrix-corporal) may override this in order to capture all traffic. From 2f33b330ff58d676e5db5081a60d6a3fadcf56c6 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Mon, 25 Apr 2022 10:29:09 +0300 Subject: [PATCH 69/83] Announce Buscarron bot support Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1782 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fdac2aa1..03ce5c7b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 2022-04-25 + +## buscarron bot support + +Thanks to [Aine](https://gitlab.com/etke.cc) of [etke.cc](https://etke.cc/), the playbook can now set up [the Buscarron bot](https://gitlab.com/etke.cc/buscarron). It's a bot you can use to send any form (HTTP POST, HTML) to a (encrypted) Matrix room + +See our [Setting up Buscarron](docs/configuring-playbook-bot-buscarron.md) documentation to get started. + + # 2022-04-21 ## matrix-registration-bot support From 1163e9880fda08bb55d46e6b388c35efb0c4fc75 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Mon, 25 Apr 2022 10:37:35 +0300 Subject: [PATCH 70/83] Link to Buscarron bot from configuring docs page Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1782 --- docs/configuring-playbook.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/configuring-playbook.md b/docs/configuring-playbook.md index 5233a6626..16a7aeeb6 100644 --- a/docs/configuring-playbook.md +++ b/docs/configuring-playbook.md @@ -145,13 +145,16 @@ When you're done with all the configuration you'd like to do, continue with [Ins - [Setting up matrix-reminder-bot](configuring-playbook-bot-matrix-reminder-bot.md) - a bot to remind you about stuff (optional) +- [Setting up matrix-registration-bot](configuring-playbook-bot-matrix-registration-bot.md) - a bot to create and manage registration tokens to invite users (optional) + - [Setting up honoroit](configuring-playbook-bot-honoroit.md) - a helpdesk bot (optional) - [Setting up Go-NEB](configuring-playbook-bot-go-neb.md) - an extensible multifunctional bot (optional) - [Setting up Mjolnir](configuring-playbook-bot-mjolnir.md) - a moderation tool/bot (optional) -- [Setting up matrix-registration-bot](configuring-playbook-bot-matrix-registration-bot.md) - a bot to create and manage registration tokens to invite users (optional) +- [Setting up Buscarron](configuring-playbook-bot-buscarron.md) - a bot you can use to send any form (HTTP POST, HTML) to a (encrypted) Matrix room (optional) + ### Backups From 4d08e935a2f235f36f261a54aa5233e77848a70e Mon Sep 17 00:00:00 2001 From: Aine Date: Mon, 25 Apr 2022 12:36:27 +0300 Subject: [PATCH 71/83] matrix-bot-buscarron: fix username in docs --- docs/configuring-playbook-bot-buscarron.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuring-playbook-bot-buscarron.md b/docs/configuring-playbook-bot-buscarron.md index 5a2c327d8..3a5822abd 100644 --- a/docs/configuring-playbook-bot-buscarron.md +++ b/docs/configuring-playbook-bot-buscarron.md @@ -14,7 +14,7 @@ By default, the playbook will set up the bot with a username like this: `@bot.bu You **need to register the bot user manually** before setting up the bot. You can use the playbook to [register a new user](registering-users.md): ``` -ansible-playbook -i inventory/hosts setup.yml --extra-vars='username=buscarron password=PASSWORD_FOR_THE_BOT admin=no' --tags=register-user +ansible-playbook -i inventory/hosts setup.yml --extra-vars='username=bot.buscarron password=PASSWORD_FOR_THE_BOT admin=no' --tags=register-user ``` Choose a strong password for the bot. You can generate a good password with a command like this: `pwgen -s 64 1`. From cbb924dec7d54f738077b45b749add2135a13155 Mon Sep 17 00:00:00 2001 From: Devin Dooley Date: Mon, 25 Apr 2022 19:17:40 -0700 Subject: [PATCH 72/83] Support ansible vault strings for homeserver secret key --- group_vars/matrix_servers | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 24ecc5f28..67a9339ae 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -533,14 +533,14 @@ matrix_mautrix_twitter_systemd_required_services_list: | (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) }} -matrix_mautrix_twitter_appservice_token: "{{ matrix_homeserver_generic_secret_key | password_hash('sha512', 'twt.as.token') | to_uuid }}" +matrix_mautrix_twitter_appservice_token: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'twt.as.token') | to_uuid }}" -matrix_mautrix_twitter_homeserver_token: "{{ matrix_homeserver_generic_secret_key | password_hash('sha512', 'twt.hs.token') | to_uuid }}" +matrix_mautrix_twitter_homeserver_token: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'twt.hs.token') | to_uuid }}" matrix_mautrix_twitter_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" matrix_mautrix_twitter_database_hostname: "{{ 'matrix-postgres' if matrix_postgres_enabled else '' }}" -matrix_mautrix_twitter_database_password: "{{ matrix_homeserver_generic_secret_key | password_hash('sha512', 'mau.twt.db') | to_uuid if matrix_postgres_enabled else '' }}" +matrix_mautrix_twitter_database_password: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'mau.twt.db') | to_uuid if matrix_postgres_enabled else '' }}" ###################################################################### # @@ -2357,9 +2357,9 @@ matrix_dendrite_container_https_host_bind_address: "{{ '' if matrix_nginx_proxy_ matrix_dendrite_sync_api_real_ip_header: "{{ 'X-Forwarded-For' if matrix_nginx_proxy_enabled else '' }}" -matrix_dendrite_registration_shared_secret: "{{ matrix_homeserver_generic_secret_key | password_hash('sha512', 'dendrite.rss') | to_uuid }}" +matrix_dendrite_registration_shared_secret: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'dendrite.rss') | to_uuid }}" -matrix_dendrite_database_password: "{{ matrix_homeserver_generic_secret_key | password_hash('sha512', 'dendrite.db') | to_uuid }}" +matrix_dendrite_database_password: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'dendrite.db') | to_uuid }}" # Even if TURN doesn't support TLS (it does by default), # it doesn't hurt to try a secure connection anyway. From e41fcf27464f7175e6b43b093d4115cd24ee2243 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 26 Apr 2022 15:44:02 +0300 Subject: [PATCH 73/83] Fix file name (vars.yaml -> vars.yml) to prevent confusion --- docs/alternative-architectures.md | 4 ++-- roles/matrix-dendrite/defaults/main.yml | 2 +- roles/matrix-synapse/defaults/main.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/alternative-architectures.md b/docs/alternative-architectures.md index a6c2a02a3..c8097b60b 100644 --- a/docs/alternative-architectures.md +++ b/docs/alternative-architectures.md @@ -2,7 +2,7 @@ As stated in the [Prerequisites](prerequisites.md), currently only `x86_64` is fully supported. However, it is possible to set the target architecture, and some tools can be built on the host or other measures can be used. -To that end add the following variable to your `vars.yaml` file: +To that end add the following variable to your `vars.yml` file (see [Configuring playbook](configuring-playbook.md)): ```yaml matrix_architecture: @@ -13,7 +13,7 @@ Currently supported architectures are the following: - `arm64` - `arm32` -so for the Raspberry Pi, the following should be in your `vars.yaml` file: +so for the Raspberry Pi, the following should be in your `vars.yml` file: ```yaml matrix_architecture: "arm32" diff --git a/roles/matrix-dendrite/defaults/main.yml b/roles/matrix-dendrite/defaults/main.yml index 7f2e629a8..f3876875e 100644 --- a/roles/matrix-dendrite/defaults/main.yml +++ b/roles/matrix-dendrite/defaults/main.yml @@ -61,7 +61,7 @@ matrix_dendrite_systemd_wanted_services_list: [] # Specifies which template files to use when configuring Dendrite. # If you'd like to have your own different configuration, feel free to copy and paste # the original files into your inventory (e.g. in `inventory/host_vars//`) -# and then change the specific host's `vars.yaml` file like this: +# and then change the specific host's `vars.yml` file like this: # matrix_dendrite_template_dendrite_config: "{{ playbook_dir }}/inventory/host_vars//dendrite.yaml.j2" matrix_dendrite_template_dendrite_config: "{{ role_path }}/templates/dendrite/dendrite.yaml.j2" diff --git a/roles/matrix-synapse/defaults/main.yml b/roles/matrix-synapse/defaults/main.yml index 44b82e954..db61cb724 100644 --- a/roles/matrix-synapse/defaults/main.yml +++ b/roles/matrix-synapse/defaults/main.yml @@ -74,7 +74,7 @@ matrix_synapse_in_container_python_packages_path: "/usr/local/lib/python3.9/site # Specifies which template files to use when configuring Synapse. # If you'd like to have your own different configuration, feel free to copy and paste # the original files into your inventory (e.g. in `inventory/host_vars//`) -# and then change the specific host's `vars.yaml` file like this: +# and then change the specific host's `vars.yml` file like this: # matrix_synapse_template_synapse_homeserver: "{{ playbook_dir }}/inventory/host_vars//homeserver.yaml.j2" matrix_synapse_template_synapse_homeserver: "{{ role_path }}/templates/synapse/homeserver.yaml.j2" matrix_synapse_template_synapse_log: "{{ role_path }}/templates/synapse/synapse.log.config.j2" From 4f1f3555f2c5f54658da9419e3733a72957f8671 Mon Sep 17 00:00:00 2001 From: Aine <97398200+etkecc@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:10:36 +0000 Subject: [PATCH 74/83] Update element 1.10.10 -> 1.10.11 --- roles/matrix-client-element/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-client-element/defaults/main.yml b/roles/matrix-client-element/defaults/main.yml index 205f3480a..e45458754 100644 --- a/roles/matrix-client-element/defaults/main.yml +++ b/roles/matrix-client-element/defaults/main.yml @@ -9,7 +9,7 @@ matrix_client_element_container_image_self_build_repo: "https://github.com/vecto # - https://github.com/vector-im/element-web/issues/19544 matrix_client_element_container_image_self_build_low_memory_system_patch_enabled: "{{ ansible_memtotal_mb < 4096 }}" -matrix_client_element_version: v1.10.10 +matrix_client_element_version: v1.10.11 matrix_client_element_docker_image: "{{ matrix_client_element_docker_image_name_prefix }}vectorim/element-web:{{ matrix_client_element_version }}" matrix_client_element_docker_image_name_prefix: "{{ 'localhost/' if matrix_client_element_container_image_self_build else matrix_container_global_registry_prefix }}" matrix_client_element_docker_image_force_pull: "{{ matrix_client_element_docker_image.endswith(':latest') }}" From 1ee118bd49e87181640991bc2e528bc6871f9e21 Mon Sep 17 00:00:00 2001 From: Aine <97398200+etkecc@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:48:28 +0000 Subject: [PATCH 75/83] matrix-change-user-admin-status: do not allocate tty --- .../templates/usr-local-bin/matrix-change-user-admin-status.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-postgres/templates/usr-local-bin/matrix-change-user-admin-status.j2 b/roles/matrix-postgres/templates/usr-local-bin/matrix-change-user-admin-status.j2 index 6c3082ef4..f378a10f5 100644 --- a/roles/matrix-postgres/templates/usr-local-bin/matrix-change-user-admin-status.j2 +++ b/roles/matrix-postgres/templates/usr-local-bin/matrix-change-user-admin-status.j2 @@ -9,7 +9,7 @@ if [ $# -ne 2 ]; then fi docker run \ - -it \ + -i \ --rm \ --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ --cap-drop=ALL \ From 7776c2e0bb8e216fa0b733768a2555a331d2491a Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Mon, 2 May 2022 12:37:56 -0500 Subject: [PATCH 76/83] Upgrade to Grafana 8.5.1 https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-5-1/ https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-5-0/ https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-4-7/ https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-4-6/ https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-4-5/ https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-4-4/ https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-4-3/ https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-4-2/ --- roles/matrix-grafana/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-grafana/defaults/main.yml b/roles/matrix-grafana/defaults/main.yml index 0ee7a86a9..ee184e1d4 100644 --- a/roles/matrix-grafana/defaults/main.yml +++ b/roles/matrix-grafana/defaults/main.yml @@ -4,7 +4,7 @@ matrix_grafana_enabled: false -matrix_grafana_version: 8.4.1 +matrix_grafana_version: 8.5.1 matrix_grafana_docker_image: "{{ matrix_container_global_registry_prefix }}grafana/grafana:{{ matrix_grafana_version }}" matrix_grafana_docker_image_force_pull: "{{ matrix_grafana_docker_image.endswith(':latest') }}" From 7adc167412b95917ff04012ff5a01577211ef41c Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 3 May 2022 08:10:16 +0300 Subject: [PATCH 77/83] Fail if trying to use Jitsi on an architecture other than amd64 Related to https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1797 --- roles/matrix-jitsi/tasks/init.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/roles/matrix-jitsi/tasks/init.yml b/roles/matrix-jitsi/tasks/init.yml index efab8745b..c4ed61a6c 100644 --- a/roles/matrix-jitsi/tasks/init.yml +++ b/roles/matrix-jitsi/tasks/init.yml @@ -3,3 +3,8 @@ - set_fact: matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-jitsi-web.service', 'matrix-jitsi-prosody.service', 'matrix-jitsi-jicofo.service', 'matrix-jitsi-jvb.service'] }}" when: matrix_jitsi_enabled|bool + +- name: Fail if on an unsupported architecture + fail: + msg: "Jitsi only supports the amd64 architecture right now. See https://github.com/jitsi/docker-jitsi-meet/issues/1069 and https://github.com/jitsi/docker-jitsi-meet/issues/1214" + when: matrix_jitsi_enabled|bool and matrix_architecture != 'amd64' From 03674e1a36e8a7591a506333038ff4ec5b341b2c Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 3 May 2022 14:32:32 +0300 Subject: [PATCH 78/83] Upgrade Synapse (1.57.1 -> 1.58.0) --- roles/matrix-synapse/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-synapse/defaults/main.yml b/roles/matrix-synapse/defaults/main.yml index db61cb724..77694dba7 100644 --- a/roles/matrix-synapse/defaults/main.yml +++ b/roles/matrix-synapse/defaults/main.yml @@ -9,7 +9,7 @@ matrix_synapse_container_image_self_build_repo: "https://github.com/matrix-org/s matrix_synapse_docker_image: "{{ matrix_synapse_docker_image_name_prefix }}matrixdotorg/synapse:{{ matrix_synapse_docker_image_tag }}" matrix_synapse_docker_image_name_prefix: "{{ 'localhost/' if matrix_synapse_container_image_self_build else matrix_container_global_registry_prefix }}" -matrix_synapse_version: v1.57.1 +matrix_synapse_version: v1.58.0 matrix_synapse_docker_image_tag: "{{ matrix_synapse_version }}" matrix_synapse_docker_image_force_pull: "{{ matrix_synapse_docker_image.endswith(':latest') }}" From 1439be2743cfb0659aeb506b83aa2f91606125a4 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Wed, 4 May 2022 11:10:00 +0300 Subject: [PATCH 79/83] Upgrade matrix-appservice-irc (0.33 -> 0.34) Related to https://matrix.org/blog/2022/05/04/0-34-0-security-release-for-matrix-appservice-irc-high-severity --- roles/matrix-bridge-appservice-irc/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-bridge-appservice-irc/defaults/main.yml b/roles/matrix-bridge-appservice-irc/defaults/main.yml index 5dfe3623f..d0843836d 100644 --- a/roles/matrix-bridge-appservice-irc/defaults/main.yml +++ b/roles/matrix-bridge-appservice-irc/defaults/main.yml @@ -8,7 +8,7 @@ matrix_appservice_irc_container_image_self_build: false matrix_appservice_irc_docker_repo: "https://github.com/matrix-org/matrix-appservice-irc.git" matrix_appservice_irc_docker_src_files_path: "{{ matrix_base_data_path }}/appservice-irc/docker-src" -matrix_appservice_irc_version: release-0.33.0 +matrix_appservice_irc_version: release-0.34.0 matrix_appservice_irc_docker_image: "{{ matrix_container_global_registry_prefix }}matrixdotorg/matrix-appservice-irc:{{ matrix_appservice_irc_version }}" matrix_appservice_irc_docker_image_force_pull: "{{ matrix_appservice_irc_docker_image.endswith(':latest') }}" From 549e4418b9d107e9b7a0c4dd1873bd7ab5d88168 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 6 May 2022 08:56:06 +0200 Subject: [PATCH 80/83] Upgrade Synapse (1.58.0 -> 1.58.1) --- roles/matrix-synapse/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/matrix-synapse/defaults/main.yml b/roles/matrix-synapse/defaults/main.yml index 77694dba7..ad1d863f5 100644 --- a/roles/matrix-synapse/defaults/main.yml +++ b/roles/matrix-synapse/defaults/main.yml @@ -9,7 +9,7 @@ matrix_synapse_container_image_self_build_repo: "https://github.com/matrix-org/s matrix_synapse_docker_image: "{{ matrix_synapse_docker_image_name_prefix }}matrixdotorg/synapse:{{ matrix_synapse_docker_image_tag }}" matrix_synapse_docker_image_name_prefix: "{{ 'localhost/' if matrix_synapse_container_image_self_build else matrix_container_global_registry_prefix }}" -matrix_synapse_version: v1.58.0 +matrix_synapse_version: v1.58.1 matrix_synapse_docker_image_tag: "{{ matrix_synapse_version }}" matrix_synapse_docker_image_force_pull: "{{ matrix_synapse_docker_image.endswith(':latest') }}" From 058fedff9124ee3bcfdcf2c6d67d26555e968a83 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 6 May 2022 09:02:24 +0200 Subject: [PATCH 81/83] Fix "endpoint seems conditional" determination in workers-doc-to-yaml.awk" This prevented us from keeping our workers reverse-proxying definitions updated since Synapse v1.54.0. The last `workers.md` file we could parse is at commit 02632b3504ad4512c5f5a4f859b3fe326b19c788. Parsing regressed at commit c56bfb08bc071368db23f3b1c593724eb4f205f0, because the introduction message for `synapse.app.generic_worker` said "If": > If a worker is set up to handle a.. .. which made the AWK script think that definitions below were conditional (which they're not in this case). This patch fixes up the regex for determining if a line is conditional or not, so that it doesn't trip up. Hopefully, it doesn't miss something important. --- .../files/workers-doc-to-yaml.awk | 2 +- roles/matrix-synapse/vars/workers.yml | 155 ++++++++++++++---- 2 files changed, 122 insertions(+), 35 deletions(-) diff --git a/roles/matrix-synapse/files/workers-doc-to-yaml.awk b/roles/matrix-synapse/files/workers-doc-to-yaml.awk index ca58b4862..5b99d3964 100755 --- a/roles/matrix-synapse/files/workers-doc-to-yaml.awk +++ b/roles/matrix-synapse/files/workers-doc-to-yaml.awk @@ -120,7 +120,7 @@ enable_parsing { worker_stanza_append(" # " line linefeed) # and take note of words hinting at additional conditions to be met - if (line ~ /(^| )[Ii]f |(^| )[Ff]or /) { + if (line ~ /(^[Ii]f|care must be taken|can be handled for)/) { endpoints_seem_conditional = 1 } } diff --git a/roles/matrix-synapse/vars/workers.yml b/roles/matrix-synapse/vars/workers.yml index 48530312c..f1dfb9406 100644 --- a/roles/matrix-synapse/vars/workers.yml +++ b/roles/matrix-synapse/vars/workers.yml @@ -1,12 +1,15 @@ --- matrix_synapse_workers_generic_worker_endpoints: - # This worker can handle API requests matching the following regular - # expressions: + # This worker can handle API requests matching the following regular expressions. + # These endpoints can be routed to any worker. If a worker is set up to handle a + # stream then, for maximum efficiency, additional endpoints should be routed to that + # worker: refer to the [stream writers](#stream-writers) section below for further + # information. # Sync requests - - ^/_matrix/client/(v2_alpha|r0|v3)/sync$ - - ^/_matrix/client/(api/v1|v2_alpha|r0|v3)/events$ + - ^/_matrix/client/(r0|v3)/sync$ + - ^/_matrix/client/(api/v1|r0|v3)/events$ - ^/_matrix/client/(api/v1|r0|v3)/initialSync$ - ^/_matrix/client/(api/v1|r0|v3)/rooms/[^/]+/initialSync$ @@ -20,19 +23,14 @@ matrix_synapse_workers_generic_worker_endpoints: - ^/_matrix/federation/v1/query/ - ^/_matrix/federation/v1/make_join/ - ^/_matrix/federation/v1/make_leave/ - - ^/_matrix/federation/v1/send_join/ - - ^/_matrix/federation/v2/send_join/ - - ^/_matrix/federation/v1/send_leave/ - - ^/_matrix/federation/v2/send_leave/ - - ^/_matrix/federation/v1/invite/ - - ^/_matrix/federation/v2/invite/ - - ^/_matrix/federation/v1/query_auth/ + - ^/_matrix/federation/(v1|v2)/send_join/ + - ^/_matrix/federation/(v1|v2)/send_leave/ + - ^/_matrix/federation/(v1|v2)/invite/ - ^/_matrix/federation/v1/event_auth/ - ^/_matrix/federation/v1/exchange_third_party_invite/ - ^/_matrix/federation/v1/user/devices/ - ^/_matrix/federation/v1/get_groups_publicised$ - ^/_matrix/key/v2/query - - ^/_matrix/federation/unstable/org.matrix.msc2946/spaces/ - ^/_matrix/federation/(v1|unstable/org.matrix.msc2946)/hierarchy/ # Inbound federation transaction request @@ -45,22 +43,25 @@ matrix_synapse_workers_generic_worker_endpoints: - ^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/context/.*$ - ^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/members$ - ^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/state$ - - ^/_matrix/client/unstable/org.matrix.msc2946/rooms/.*/spaces$ - ^/_matrix/client/(v1|unstable/org.matrix.msc2946)/rooms/.*/hierarchy$ - ^/_matrix/client/unstable/im.nheko.summary/rooms/.*/summary$ - - ^/_matrix/client/(api/v1|r0|v3|unstable)/account/3pid$ - - ^/_matrix/client/(api/v1|r0|v3|unstable)/devices$ - - ^/_matrix/client/(api/v1|r0|v3|unstable)/keys/query$ - - ^/_matrix/client/(api/v1|r0|v3|unstable)/keys/changes$ + - ^/_matrix/client/(r0|v3|unstable)/account/3pid$ + - ^/_matrix/client/(r0|v3|unstable)/devices$ - ^/_matrix/client/versions$ - ^/_matrix/client/(api/v1|r0|v3|unstable)/voip/turnServer$ - - ^/_matrix/client/(api/v1|r0|v3|unstable)/joined_groups$ - - ^/_matrix/client/(api/v1|r0|v3|unstable)/publicised_groups$ - - ^/_matrix/client/(api/v1|r0|v3|unstable)/publicised_groups/ + - ^/_matrix/client/(r0|v3|unstable)/joined_groups$ + - ^/_matrix/client/(r0|v3|unstable)/publicised_groups$ + - ^/_matrix/client/(r0|v3|unstable)/publicised_groups/ - ^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/event/ - ^/_matrix/client/(api/v1|r0|v3|unstable)/joined_rooms$ - ^/_matrix/client/(api/v1|r0|v3|unstable)/search$ + # Encryption requests + - ^/_matrix/client/(r0|v3|unstable)/keys/query$ + - ^/_matrix/client/(r0|v3|unstable)/keys/changes$ + - ^/_matrix/client/(r0|v3|unstable)/keys/claim$ + - ^/_matrix/client/(r0|v3|unstable)/room_keys/ + # Registration/login requests - ^/_matrix/client/(api/v1|r0|v3|unstable)/login$ - ^/_matrix/client/(r0|v3|unstable)/register$ @@ -74,11 +75,27 @@ matrix_synapse_workers_generic_worker_endpoints: - ^/_matrix/client/(api/v1|r0|v3|unstable)/join/ - ^/_matrix/client/(api/v1|r0|v3|unstable)/profile/ + # Device requests + - ^/_matrix/client/(r0|v3|unstable)/sendToDevice/ + + # Account data requests + - ^/_matrix/client/(r0|v3|unstable)/.*/tags + - ^/_matrix/client/(r0|v3|unstable)/.*/account_data + + # Receipts requests + - ^/_matrix/client/(r0|v3|unstable)/rooms/.*/receipt + - ^/_matrix/client/(r0|v3|unstable)/rooms/.*/read_markers + + # Presence requests + - ^/_matrix/client/(api/v1|r0|v3|unstable)/presence/ + # Additionally, the following REST endpoints can be handled for GET requests: # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually # ^/_matrix/federation/v1/groups/ + # ^/_matrix/client/(api/v1|r0|v3|unstable)/pushrules/ + # ^/_matrix/client/(r0|v3|unstable)/groups/ # Pagination requests can also be handled, but all requests for a given # room must be routed to the same instance. Additionally, care must be taken to @@ -155,16 +172,17 @@ matrix_synapse_workers_generic_worker_endpoints: # #### Stream writers - # Additionally, there is *experimental* support for moving writing of specific - # streams (such as events) off of the main process to a particular worker. (This - # is only supported with Redis-based replication.) - - # Currently supported streams are `events` and `typing`. + # Additionally, the writing of specific streams (such as events) can be moved off + # of the main process to a particular worker. + # (This is only supported with Redis-based replication.) # To enable this, the worker must have a HTTP replication listener configured, - # have a `worker_name` and be listed in the `instance_map` config. For example to - # move event persistence off to a dedicated worker, the shared configuration would - # include: + # have a `worker_name` and be listed in the `instance_map` config. The same worker + # can handle multiple streams, but unless otherwise documented, each stream can only + # have a single writer. + + # For example, to move event persistence off to a dedicated worker, the shared + # configuration would include: # ```yaml # instance_map: @@ -176,8 +194,20 @@ matrix_synapse_workers_generic_worker_endpoints: # events: event_persister1 # ``` - # The `events` stream also experimentally supports having multiple writers, where - # work is sharded between them by room ID. Note that you *must* restart all worker + # An example for a stream writer instance: + + # ```yaml + # {{#include systemd-with-workers/workers/event_persister.yaml}} + # ``` + + # Some of the streams have associated endpoints which, for maximum efficiency, should + # be routed to the workers handling that stream. See below for the currently supported + # streams and the endpoints associated with them: + + # ##### The `events` stream + + # The `events` stream experimentally supports having multiple writers, where work + # is sharded between them by room ID. Note that you *must* restart all worker # instances when adding or removing event persisters. An example `stream_writers` # configuration with multiple writers: @@ -188,9 +218,51 @@ matrix_synapse_workers_generic_worker_endpoints: # - event_persister2 # ``` + # ##### The `typing` stream + + # The following endpoints should be routed directly to the worker configured as + # the stream writer for the `typing` stream: + + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(api/v1|r0|v3|unstable)/rooms/.*/typing + + # ##### The `to_device` stream + + # The following endpoints should be routed directly to the worker configured as + # the stream writer for the `to_device` stream: + + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(r0|v3|unstable)/sendToDevice/ + + # ##### The `account_data` stream + + # The following endpoints should be routed directly to the worker configured as + # the stream writer for the `account_data` stream: + + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(r0|v3|unstable)/.*/tags + # ^/_matrix/client/(r0|v3|unstable)/.*/account_data + + # ##### The `receipts` stream + + # The following endpoints should be routed directly to the worker configured as + # the stream writer for the `receipts` stream: + + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(r0|v3|unstable)/rooms/.*/receipt + # ^/_matrix/client/(r0|v3|unstable)/rooms/.*/read_markers + + # ##### The `presence` stream + + # The following endpoints should be routed directly to the worker configured as + # the stream writer for the `presence` stream: + + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(api/v1|r0|v3|unstable)/presence/ + # #### Background tasks - # There is also *experimental* support for moving background tasks to a separate + # There is also support for moving background tasks to a separate # worker. Background tasks are run periodically or started via replication. Exactly # which tasks are configured to run depends on your Synapse configuration (e.g. if # stats is enabled). @@ -206,6 +278,12 @@ matrix_synapse_workers_generic_worker_endpoints: # You might also wish to investigate the `update_user_directory` and # `media_instance_running_background_jobs` settings. + # An example for a dedicated background worker instance: + + # ```yaml + # {{#include systemd-with-workers/workers/background_worker.yaml}} + # ``` + # pusher worker (no API endpoints) [ # Handles sending push notifications to sygnal and email. Doesn't handle any # REST endpoints itself, but you should set `start_pushers: False` in the @@ -292,18 +370,27 @@ matrix_synapse_workers_user_dir_endpoints: # Handles searches in the user directory. It can handle REST endpoints matching # the following regular expressions: - - ^/_matrix/client/(api/v1|r0|v3|unstable)/user_directory/search$ + - ^/_matrix/client/(r0|v3|unstable)/user_directory/search$ - # When using this worker you must also set `update_user_directory: False` in the + # When using this worker you must also set `update_user_directory: false` in the # shared configuration file to stop the main synapse running background # jobs related to updating the user directory. + # Above endpoint is not *required* to be routed to this worker. By default, + # `update_user_directory` is set to `true`, which means the main process + # will handle updates. All workers configured with `client` can handle the above + # endpoint as long as either this worker or the main process are configured to + # handle it, and are online. + + # If `update_user_directory` is set to `false`, and this worker is not running, + # the above endpoint may give outdated results. + matrix_synapse_workers_frontend_proxy_endpoints: # Proxies some frequently-requested client endpoints to add caching and remove # load from the main synapse. It can handle REST endpoints matching the following # regular expressions: - - ^/_matrix/client/(api/v1|r0|v3|unstable)/keys/upload + - ^/_matrix/client/(r0|v3|unstable)/keys/upload # If `use_presence` is False in the homeserver config, it can also handle REST # endpoints matching the following regular expressions: From be95918a2f28ee516c844dc7d5b335dd06221cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20T=C3=B6tterman?= Date: Fri, 6 May 2022 11:37:40 +0300 Subject: [PATCH 82/83] typo --- docs/configuring-playbook-bridge-hookshot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuring-playbook-bridge-hookshot.md b/docs/configuring-playbook-bridge-hookshot.md index 38e13a8a9..208ce4e6b 100644 --- a/docs/configuring-playbook-bridge-hookshot.md +++ b/docs/configuring-playbook-bridge-hookshot.md @@ -16,7 +16,7 @@ Refer to the [official instructions](https://matrix-org.github.io/matrix-hooksho 2. Take special note of the `matrix_hookshot_*_enabled` variables. Services that need no further configuration are enabled by default (GitLab, Generic), while you must first add the required configuration and enable the others (GitHub, Jira, Figma). 3. If you're setting up the GitHub bridge, you'll need to generate and download a private key file after you created your GitHub app. Copy the contents of that file to the variable `matrix_hookshot_github_private_key` so the playbook can install it for you, or use one of the [other methods](#manage-github-private-key-with-matrix-aux-role) explained below. 4. If you've already installed Matrix services using the playbook before, you'll need to re-run it (`--tags=setup-all,start`). If not, proceed with [configuring other playbook services](configuring-playbook.md) and then with [Installing](installing.md). Get back to this guide once ready. Hookshot can be set up individually using the tag `setup-hookshot`. -5. Refer to [Hookshot's official instructions](https://matrix-org.github.io/matrix-hookshot/usage.html) to start using the bridge. **Important:** Note that the different listeners are bound to certain paths which might differe from those assumed by the hookshot documentation, see [URLs for bridges setup](urls-for-bridges-setup) below. +5. Refer to [Hookshot's official instructions](https://matrix-org.github.io/matrix-hookshot/usage.html) to start using the bridge. **Important:** Note that the different listeners are bound to certain paths which might differ from those assumed by the hookshot documentation, see [URLs for bridges setup](urls-for-bridges-setup) below. Other configuration options are available via the `matrix_hookshot_configuration_extension_yaml` and `matrix_hookshot_registration_extension_yaml` variables, see the comments in [main.yml](/roles/matrix-bridge-hookshot/defaults/main.yml) for how to use them. From 83b7fcee453f39388456f0bccc8f2783905f75ae Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Sat, 7 May 2022 09:36:40 +0200 Subject: [PATCH 83/83] Do not proxy some endpoints to the generic Synapse worker These endpoints should not be proxied to a generic Synapse worker without other preparation (setting up stream writers, sending traffic to a specific stream writer, etc.). Disabling them for now. In the future, we'd like to fix up our awk script to disable them automatically. This is a fix up for 058fedff9124ee3bcf --- roles/matrix-synapse/vars/workers.yml | 30 +++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/roles/matrix-synapse/vars/workers.yml b/roles/matrix-synapse/vars/workers.yml index f1dfb9406..33bf585b3 100644 --- a/roles/matrix-synapse/vars/workers.yml +++ b/roles/matrix-synapse/vars/workers.yml @@ -75,19 +75,23 @@ matrix_synapse_workers_generic_worker_endpoints: - ^/_matrix/client/(api/v1|r0|v3|unstable)/join/ - ^/_matrix/client/(api/v1|r0|v3|unstable)/profile/ - # Device requests - - ^/_matrix/client/(r0|v3|unstable)/sendToDevice/ - - # Account data requests - - ^/_matrix/client/(r0|v3|unstable)/.*/tags - - ^/_matrix/client/(r0|v3|unstable)/.*/account_data - - # Receipts requests - - ^/_matrix/client/(r0|v3|unstable)/rooms/.*/receipt - - ^/_matrix/client/(r0|v3|unstable)/rooms/.*/read_markers - - # Presence requests - - ^/_matrix/client/(api/v1|r0|v3|unstable)/presence/ + # These appear to be conditional and should not be enabled by default. + # We need to fix up our workers-doc-to-yaml.awk parsing script to exclude them. + # For now, they've been commented out manually. + # + # # Device requests + # - ^/_matrix/client/(r0|v3|unstable)/sendToDevice/ + + # # Account data requests + # - ^/_matrix/client/(r0|v3|unstable)/.*/tags + # - ^/_matrix/client/(r0|v3|unstable)/.*/account_data + + # # Receipts requests + # - ^/_matrix/client/(r0|v3|unstable)/rooms/.*/receipt + # - ^/_matrix/client/(r0|v3|unstable)/rooms/.*/read_markers + + # # Presence requests + # - ^/_matrix/client/(api/v1|r0|v3|unstable)/presence/ # Additionally, the following REST endpoints can be handled for GET requests: