diff --git a/CHANGELOG.md b/CHANGELOG.md index cd8d2089e..4502b7512 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 2025-10-02 + +## Element Admin support + +The playbook now supports [Element Admin](./docs/configuring-playbook-element-admin.md) - a new web-based administration panel for Synapse and [Matrix Authentication Service](./docs/configuring-playbook-matrix-authentication-service.md). + +Deployments based on Matrix Authentication Service may find it useful to run both Synapse Admin and Element Admin at the same time. + +Deployments that don't rely on Matrix Authentication Service are unlikely to find anything useful in Element Admin right now (it's too basic in its current form). + + # 2025-04-26 ## Continuwuity support diff --git a/docs/configuring-playbook-element-admin.md b/docs/configuring-playbook-element-admin.md new file mode 100644 index 000000000..d0b676971 --- /dev/null +++ b/docs/configuring-playbook-element-admin.md @@ -0,0 +1,71 @@ + + +# Setting up Element Admin (optional) + +The playbook can install and configure [Element Admin](https://github.com/element-hq/element-admin) for you. + +Element Admin is a web-based administration panel for Synapse and [Matrix Authentication Service](./configuring-playbook-matrix-authentication-service.md). + +See the project's [documentation](https://github.com/element-hq/element-admin) to learn more. + +💡 **Note**: This project is still very young and doesn't have many features. For now, it's recommended to use [Synapse Admin](./configuring-playbook-synapse-admin.md) instead. Deployments that use [Matrix Authentication Service](./configuring-playbook-matrix-authentication-service.md) can use Element Admin for user-management (something that Synapse Admin can't do), while continuing to use Synapse Admin for all other purposes. + +## Prerequisites + +- A [Synapse](configuring-playbook-synapse.md) homeserver with its Admin API enabled (the playbook automatically enables it for you when you enable Element Admin) +- [Matrix Authentication Service](./configuring-playbook-matrix-authentication-service.md) with its Admin API enabled (the playbook automatically enables it for you when you enable Element Admin) + +## Decide on a domain and path + +By default, the Element Admin is configured to be served on the `admin.element.example.com` domain. + +If you'd like to run Element Admin on another hostname, see the [Adjusting the Element Admin URL](#adjusting-the-element-admin-url-optional) section below. + +## Adjusting DNS records (optional) + +By default, this playbook installs Element Admin on the `admin.element.` subdomain (`admin.element.example.com`) and requires you to create a `CNAME` record for `admin.element`, which targets `matrix.example.com`. + +When setting these values, replace `example.com` with your own. + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.example.com/vars.yml` file: + +```yaml +matrix_element_admin_enabled: true +``` + +### Adjusting the Element Admin URL (optional) + +By tweaking the `matrix_element_admin_hostname` variable, you can easily make the service available at a **different hostname** than the default one. + +Example additional configuration for your `vars.yml` file: + +```yaml +matrix_element_admin_hostname: element-admin.example.com +``` + +> [!WARNING] +> A `matrix_element_admin_path_prefix` variable is also available and mean to let you configure a path prefix for the Element Admin service, but **Element Admin does not support running under a sub-path yet**. + +## Installing + +After configuring the playbook and potentially [adjusting your DNS records](#adjusting-dns-records), run the playbook with [playbook tags](playbook-tags.md) as below: + + +```sh +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + +The shortcut commands with the [`just` program](just.md) are also available: `just install-all` or `just setup-all` + +`just install-all` is useful for maintaining your setup quickly ([2x-5x faster](../CHANGELOG.md#2x-5x-performance-improvements-in-playbook-runtime) than `just setup-all`) when its components remain unchanged. If you adjust your `vars.yml` to remove other components, you'd need to run `just setup-all`, or these components will still remain installed. Note these shortcuts run the `ensure-matrix-users-created` tag too. + +## Usage + +Once installed, Element Call integrates seamlessly with Matrix clients like [Element Web](configuring-playbook-client-element-web.md) and Element X on mobile (iOS and Android). diff --git a/docs/configuring-playbook-matrix-authentication-service.md b/docs/configuring-playbook-matrix-authentication-service.md index 9173bd399..b1a110cf0 100644 --- a/docs/configuring-playbook-matrix-authentication-service.md +++ b/docs/configuring-playbook-matrix-authentication-service.md @@ -51,7 +51,7 @@ This section details what you can expect when switching to the Matrix Authentica - ❌ **Synapse password providers will need to be disabled**. You can no longer use [shared-secret-auth](./configuring-playbook-shared-secret-auth.md), [rest-auth](./configuring-playbook-rest-auth.md), [LDAP auth](./configuring-playbook-ldap-auth.md), etc. When the authentication flow is handled by MAS (not by Synapse anymore), it doesn't make sense to extend the Synapse authentication flow with additional modules. Many bridges used to rely on shared-secret-auth for doing double-puppeting (impersonating other users), but most (at least the mautrix bridges) nowadays use [Appservice Double Puppet](./configuring-playbook-appservice-double-puppet.md) as a better alternative. Older/maintained bridges may still rely on shared-secret-auth, as do other services like [matrix-corporal](./configuring-playbook-matrix-corporal.md). -- ❌ Certain **tools like [synapse-admin](./configuring-playbook-synapse-admin.md) do not have full compatibility with MAS yet**. synapse-admin already supports [login with access token](https://github.com/etkecc/synapse-admin/pull/58), browsing users (which Synapse will internally fetch from MAS) and updating user avatars. However, editing users (passwords, etc.) now needs to happen directly against MAS using the [MAS Admin API](https://element-hq.github.io/matrix-authentication-service/api/index.html), which synapse-admin cannot interact with yet. +- ❌ Certain **tools like [synapse-admin](./configuring-playbook-synapse-admin.md) do not have full compatibility with MAS yet**. synapse-admin already supports [login with access token](https://github.com/etkecc/synapse-admin/pull/58), browsing users (which Synapse will internally fetch from MAS) and updating user avatars. However, editing users (passwords, etc.) now needs to happen directly against MAS using the [MAS Admin API](https://element-hq.github.io/matrix-authentication-service/api/index.html), which synapse-admin cannot interact with yet. You may be interested in using [Element Admin](./configuring-playbook-element-admin.md) for these purposes. - ❌ **Some services experience issues when authenticating via MAS**: diff --git a/docs/configuring-playbook-synapse-admin.md b/docs/configuring-playbook-synapse-admin.md index 4c992e09b..b35b28ccb 100644 --- a/docs/configuring-playbook-synapse-admin.md +++ b/docs/configuring-playbook-synapse-admin.md @@ -18,6 +18,8 @@ synapse-admin is a web UI tool you can use to **administrate users, rooms, media 💡 **Note**: the latest version of synapse-admin is hosted by [etke.cc](https://etke.cc/) at [admin.etke.cc](https://admin.etke.cc/). If you only need this service occasionally and trust giving your admin credentials to a 3rd party Single Page Application, you can consider using it from there and avoiding the (small) overhead of self-hosting. +💡 **Note**: The playbook also supports an alternative management UI in the shape of [Element Admin](./configuring-playbook-element-admin.md). However, it's currently less feature-rich than Synapse Admin and has a dependency on [Matrix Authentication Service](./configuring-playbook-matrix-authentication-service.md). + ## Adjusting DNS records (optional) By default, this playbook installs Synapse Admin on the `matrix.` subdomain, at the `/synapse-admin` path (https://matrix.example.com/synapse-admin). This makes it easy to install it, because it **doesn't require additional DNS records to be set up**. If that's okay, you can skip this section. @@ -40,7 +42,7 @@ matrix_synapse_admin_enabled: true By default, synapse-admin installation will be [restricted to only work with one homeserver](https://github.com/etkecc/synapse-admin/blob/e21e44362c879ac41f47c580b04210842b6ff3d7/README.md#restricting-available-homeserver) — the one managed by the playbook. To adjust these restrictions, tweak the `matrix_synapse_admin_config_restrictBaseUrl` variable. > [!WARNING] -> If you're using [Matrix Authentication Service](./configuring-playbook-matrix-authentication-service.md) (MAS) for authentication, you will be able to [log into synapse-admin with an access token](https://github.com/etkecc/synapse-admin/pull/58), but certain synapse-admin features (especially those around user management) will be limited or not work at all. +> If you're using [Matrix Authentication Service](./configuring-playbook-matrix-authentication-service.md) (MAS) for authentication, you will be able to [log into synapse-admin with an access token](https://github.com/etkecc/synapse-admin/pull/58), but certain synapse-admin features (especially those around user management) will be limited or not work at all. You may be interested in using [Element Admin](docs/configuring-playbook-element-admin.md) for these purposes. ### Adjusting the Synapse Admin URL (optional) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 8f56c8d74..a7cbb0369 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -453,6 +453,8 @@ devture_systemd_service_manager_services_list_auto: | + ([{'name': 'matrix-pantalaimon.service', 'priority': 4000, 'groups': ['matrix', 'pantalaimon']}] if matrix_pantalaimon_enabled else []) + + ([{'name': 'matrix-element-admin.service', 'priority': 4000, 'groups': ['matrix', 'element-admin']}] if matrix_element_admin_enabled else []) + + ([{'name': 'matrix-element-call.service', 'priority': 4000, 'groups': ['matrix', 'element-call']}] if matrix_element_call_enabled else []) + ([{'name': 'matrix-livekit-jwt-service.service', 'priority': 3500, 'groups': ['matrix', 'livekit-jwt-service']}] if matrix_livekit_jwt_service_enabled else []) @@ -682,6 +684,8 @@ matrix_authentication_service_config_email_port: "{{ 8025 if exim_relay_enabled matrix_authentication_service_config_email_mode: "{{ 'plain' if exim_relay_enabled else 'starttls' }}" matrix_authentication_service_config_email_from_address: "{{ exim_relay_sender_address }}" +matrix_authentication_service_admin_api_enabled: "{{ matrix_element_admin_enabled }}" + matrix_authentication_service_container_image_registry_prefix_upstream: "{{ matrix_container_global_registry_prefix_override if matrix_container_global_registry_prefix_override else matrix_authentication_service_container_image_registry_prefix_upstream_default }}" matrix_authentication_service_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" @@ -4960,7 +4964,7 @@ matrix_synapse_container_labels_matrix_labels_enabled: "{{ not matrix_synapse_wo matrix_synapse_container_labels_public_client_root_redirection_enabled: "{{ matrix_synapse_container_labels_public_client_root_redirection_url != '' }}" matrix_synapse_container_labels_public_client_root_redirection_url: "{{ (('https://' if matrix_playbook_ssl_enabled else 'http://') + matrix_server_fqn_element) if matrix_client_element_enabled else '' }}" -matrix_synapse_container_labels_public_client_synapse_admin_api_enabled: "{{ matrix_synapse_admin_enabled }}" +matrix_synapse_container_labels_public_client_synapse_admin_api_enabled: "{{ matrix_synapse_admin_enabled or matrix_element_admin_enabled }}" matrix_synapse_container_labels_internal_client_synapse_admin_api_enabled: "{{ (matrix_bot_draupnir_enabled and matrix_bot_draupnir_admin_api_enabled) }}" matrix_synapse_container_labels_internal_client_synapse_admin_api_traefik_entrypoints: "{{ matrix_playbook_internal_matrix_client_api_traefik_entrypoint_name }}" @@ -6437,6 +6441,45 @@ traefik_certs_dumper_container_image_registry_prefix_upstream: "{{ matrix_contai # # ######################################################################## +######################################################################## +# # +# matrix-element-admin # +# # +######################################################################## + +# We don't enable this by default. +matrix_element_admin_enabled: false + +matrix_element_admin_scheme: "{{ 'https' if matrix_playbook_ssl_enabled else 'http' }}" + +matrix_element_admin_container_image_registry_prefix_upstream: "{{ matrix_container_global_registry_prefix_override if matrix_container_global_registry_prefix_override else matrix_element_admin_container_image_registry_prefix_upstream_default }}" + +matrix_element_admin_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" + +matrix_element_admin_container_network: "{{ matrix_addons_container_network }}" + +matrix_element_admin_container_additional_networks_auto: |- + {{ + ( + ([] if matrix_addons_homeserver_container_network == '' else [matrix_addons_homeserver_container_network]) + + + ([matrix_playbook_reverse_proxyable_services_additional_network] if (matrix_playbook_reverse_proxyable_services_additional_network and matrix_element_admin_container_labels_traefik_enabled) else []) + ) | unique + }} + +matrix_element_admin_container_labels_traefik_enabled: "{{ matrix_playbook_reverse_proxy_type in ['playbook-managed-traefik', 'other-traefik-container'] }}" +matrix_element_admin_container_labels_traefik_docker_network: "{{ matrix_playbook_reverse_proxyable_services_additional_network }}" +matrix_element_admin_container_labels_traefik_entrypoints: "{{ traefik_entrypoint_primary }}" +matrix_element_admin_container_labels_traefik_tls_certResolver: "{{ traefik_certResolver_primary }}" + +matrix_element_admin_systemd_required_services_list_auto: "{{ matrix_addons_homeserver_systemd_services_list }}" + +###################################################################### +# # +# /matrix-element-admin # +# # +###################################################################### + ######################################################################## # # diff --git a/roles/custom/matrix-element-admin/defaults/main.yml b/roles/custom/matrix-element-admin/defaults/main.yml new file mode 100644 index 000000000..ea76aa97a --- /dev/null +++ b/roles/custom/matrix-element-admin/defaults/main.yml @@ -0,0 +1,97 @@ +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# SPDX-FileCopyrightText: 2024 - 2025 Suguru Hirahara +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +# Element Admin is a web-based administration panel for Synapse and Matrix Authentication Service +# Project source code URL: https://github.com/element-hq/element-admin + +matrix_element_admin_enabled: true + +# renovate: datasource=docker depName=oci.element.io/element-admin +matrix_element_admin_version: 0.1.3 + +matrix_element_admin_scheme: https + +# The hostname at which Element Admin is served. +matrix_element_admin_hostname: "admin.{{ matrix_server_fqn_element }}" + +# The path at which Element Admin is served. +# This value must either be `/` or not end with a slash (e.g. `/element-admin`). +matrix_element_admin_path_prefix: / + +matrix_element_admin_base_path: "{{ matrix_base_data_path }}/element-admin" + +matrix_element_admin_container_image_self_build: false +matrix_element_admin_container_image_self_build_repo: https://github.com/element-hq/element-admin +matrix_element_admin_container_image_self_build_repo_version: "{{ 'main' if matrix_element_admin_version == 'main' else matrix_element_admin_version }}" +matrix_element_admin_container_src_path: "{{ matrix_element_admin_base_path }}/container-src" + +matrix_element_admin_container_image: "{{ matrix_element_admin_container_image_registry_prefix }}element-admin:{{ matrix_element_admin_container_image_tag }}" +matrix_element_admin_container_image_tag: "{{ matrix_element_admin_version }}" +matrix_element_admin_container_image_force_pull: "{{ matrix_element_admin_container_image.endswith(':main') }}" +matrix_element_admin_container_image_registry_prefix: "{{ matrix_element_admin_container_image_registry_prefix_upstream }}" +matrix_element_admin_container_image_registry_prefix_upstream: "{{ matrix_element_admin_container_image_registry_prefix_upstream_default }}" +matrix_element_admin_container_image_registry_prefix_upstream_default: "oci.element.io/" + +# The base container network. It will be auto-created by this role if it doesn't exist already. +matrix_element_admin_container_network: '' + +# A list of additional container networks that the container would be connected to. +# The role does not create these networks, so make sure they already exist. +matrix_element_admin_container_additional_networks: "{{ matrix_element_admin_container_additional_networks_default + matrix_element_admin_container_additional_networks_auto + matrix_element_admin_container_additional_networks_custom }}" +matrix_element_admin_container_additional_networks_default: [] +matrix_element_admin_container_additional_networks_auto: [] +matrix_element_admin_container_additional_networks_custom: [] + +# matrix_element_admin_container_labels_traefik_enabled controls whether labels to assist a Traefik reverse-proxy will be attached to the container. +# See `../templates/labels.j2` for details. +# +# To inject your own other container labels, see `matrix_element_admin_container_labels_additional_labels`. +matrix_element_admin_container_labels_traefik_enabled: true +matrix_element_admin_container_labels_traefik_docker_network: "{{ matrix_element_admin_container_network }}" +matrix_element_admin_container_labels_traefik_hostname: "{{ matrix_element_admin_hostname }}" +# The path prefix must either be `/` or not end with a slash (e.g. `/element-admin`). +matrix_element_admin_container_labels_traefik_path_prefix: "{{ matrix_element_admin_path_prefix }}" +matrix_element_admin_container_labels_traefik_rule: "Host(`{{ matrix_element_admin_container_labels_traefik_hostname }}`){% if matrix_element_admin_container_labels_traefik_path_prefix != '/' %} && PathPrefix(`{{ matrix_element_admin_container_labels_traefik_path_prefix }}`){% endif %}" +matrix_element_admin_container_labels_traefik_priority: 0 +matrix_element_admin_container_labels_traefik_entrypoints: web-secure +matrix_element_admin_container_labels_traefik_tls: "{{ matrix_element_admin_container_labels_traefik_entrypoints != 'web' }}" +matrix_element_admin_container_labels_traefik_tls_certResolver: default # noqa var-naming + +# matrix_element_admin_container_labels_additional_labels contains a multiline string with additional labels to add to the container label file. +# See `../templates/labels.j2` for details. +# +# Example: +# matrix_element_admin_container_labels_additional_labels: | +# my.label=1 +# another.label="here" +matrix_element_admin_container_labels_additional_labels: '' + +# A list of extra arguments to pass to the container +matrix_element_admin_container_extra_arguments: [] + +# A list of extra arguments to pass to the container process. +matrix_element_admin_container_process_extra_arguments: [] + +# List of systemd services that the Element Admin service depends on +matrix_element_admin_systemd_required_services_list: "{{ matrix_element_admin_systemd_required_services_list_default + matrix_element_admin_systemd_required_services_list_auto + matrix_element_admin_systemd_required_services_list_custom }}" +matrix_element_admin_systemd_required_services_list_default: "{{ [devture_systemd_docker_base_docker_service_name] if devture_systemd_docker_base_docker_service_name else [] }}" +matrix_element_admin_systemd_required_services_list_auto: [] +matrix_element_admin_systemd_required_services_list_custom: [] + +# List of systemd services that the Element Admin service wants +matrix_element_admin_systemd_wanted_services_list: [] + +# Controls the `SERVER_NAME` environment variable, which should point to a Matrix homeserver domain name. +matrix_element_admin_environment_variable_server_name: "{{ matrix_domain }}" + +# Additional environment variables. +# +# Example: +# matrix_element_admin_environment_variables_additional_variables: | +# SOMETHING=1 +# ANOTHER="here" +matrix_element_admin_environment_variables_additional_variables: '' \ No newline at end of file diff --git a/roles/custom/matrix-element-admin/tasks/install.yml b/roles/custom/matrix-element-admin/tasks/install.yml new file mode 100644 index 000000000..d26973111 --- /dev/null +++ b/roles/custom/matrix-element-admin/tasks/install.yml @@ -0,0 +1,77 @@ +# SPDX-FileCopyrightText: 2024 David Mehren +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Ensure Element Admin paths exist + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_name }}" + group: "{{ matrix_group_name }}" + with_items: + - path: "{{ matrix_element_admin_base_path }}" + when: true + - path: "{{ matrix_element_admin_container_src_path }}" + when: "{{ matrix_element_admin_container_image_self_build }}" + when: item.when | bool + +- name: Ensure Element Admin support files installed + ansible.builtin.template: + src: "{{ role_path }}/templates/{{ item }}.j2" + dest: "{{ matrix_element_admin_base_path }}/{{ item }}" + mode: 0640 + owner: "{{ matrix_user_name }}" + group: "{{ matrix_group_name }}" + with_items: + - labels + - env + +- name: Ensure Element Admin container image is pulled + community.docker.docker_image: + name: "{{ matrix_element_admin_container_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_element_admin_container_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_element_admin_container_image_force_pull }}" + when: "not matrix_element_admin_container_image_self_build | bool" + register: result + retries: "{{ devture_playbook_help_container_retries_count }}" + delay: "{{ devture_playbook_help_container_retries_delay }}" + until: result is not failed + +- when: matrix_element_admin_container_image_self_build | bool + block: + - name: Ensure Element Admin repository is present on self-build + ansible.builtin.git: + repo: "{{ matrix_element_admin_container_image_self_build_repo }}" + version: "{{ matrix_element_admin_container_image_self_build_repo_version }}" + dest: "{{ matrix_element_admin_container_src_path }}" + force: "yes" + become: true + become_user: "{{ matrix_user_name }}" + register: matrix_element_admin_git_pull_results + + - name: Ensure Element Admin container image is built + ansible.builtin.command: + cmd: |- + {{ devture_systemd_docker_base_host_command_docker }} buildx build + --tag={{ matrix_element_admin_container_image }} + --file={{ matrix_element_admin_container_src_path }}/Dockerfile + {{ matrix_element_admin_container_src_path }} + changed_when: true + +- name: Ensure Element Admin container network is created + community.general.docker_network: + enable_ipv6: "{{ devture_systemd_docker_base_ipv6_enabled }}" + name: "{{ matrix_element_admin_container_network }}" + driver: bridge + driver_options: "{{ devture_systemd_docker_base_container_networks_driver_options }}" + +- name: Ensure Element Admin systemd service installed + ansible.builtin.template: + src: "{{ role_path }}/templates/systemd/matrix-element-admin.service.j2" + dest: "{{ devture_systemd_docker_base_systemd_path }}/matrix-element-admin.service" + mode: 0644 diff --git a/roles/custom/matrix-element-admin/tasks/main.yml b/roles/custom/matrix-element-admin/tasks/main.yml new file mode 100644 index 000000000..524aba91e --- /dev/null +++ b/roles/custom/matrix-element-admin/tasks/main.yml @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2024 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- tags: + - setup-all + - setup-element-admin + - install-all + - install-element-admin + block: + - when: matrix_element_admin_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/validate_config.yml" + + - when: matrix_element_admin_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/install.yml" + +- tags: + - setup-all + - setup-element-admin + block: + - when: not matrix_element_admin_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/uninstall.yml" diff --git a/roles/custom/matrix-element-admin/tasks/uninstall.yml b/roles/custom/matrix-element-admin/tasks/uninstall.yml new file mode 100644 index 000000000..d375d2179 --- /dev/null +++ b/roles/custom/matrix-element-admin/tasks/uninstall.yml @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Check existence of Element Admin service + ansible.builtin.stat: + path: "{{ devture_systemd_docker_base_systemd_path }}/matrix-element-admin.service" + register: matrix_element_admin_service_stat + +- when: matrix_element_admin_service_stat.stat.exists | bool + block: + - name: Ensure Element Admin is stopped + ansible.builtin.service: + name: matrix-element-admin + state: stopped + enabled: false + daemon_reload: true + + - name: Ensure Element Admin systemd service doesn't exist + ansible.builtin.file: + path: "{{ devture_systemd_docker_base_systemd_path }}/matrix-element-admin.service" + state: absent + + - name: Ensure Element Admin paths don't exist + ansible.builtin.file: + path: "{{ matrix_element_admin_base_path }}" + state: absent diff --git a/roles/custom/matrix-element-admin/tasks/validate_config.yml b/roles/custom/matrix-element-admin/tasks/validate_config.yml new file mode 100644 index 000000000..f531af567 --- /dev/null +++ b/roles/custom/matrix-element-admin/tasks/validate_config.yml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Fail if required Element Admin settings not defined + ansible.builtin.fail: + msg: > + You need to define a required configuration setting (`{{ item.name }}`). + when: "item.when | bool and lookup('vars', item.name, default='') | string | length == 0" + with_items: + - {'name': 'matrix_element_admin_hostname', when: true} + - {'name': 'matrix_element_admin_path_prefix', when: true} + - {'name': 'matrix_element_admin_container_network', when: true} + - {'name': 'matrix_element_admin_environment_variable_server_name', when: true} + +# Element Admin appears to hardcode all paths to `/` (e.g. `/config.json`, `/assets/...`). +# While we can properly serve the homepage and handle stripping the path prefix on our side, +# the hardcoded URLs in the Element Admin are pointing people to the wrong place, which is a problem. +- name: Fail if Element Admin path prefix is different than / + ansible.builtin.fail: + msg: >- + Element Admin with a path prefix other than '/' is not supported yet. + You have configured matrix_element_admin_path_prefix to '{{ matrix_element_admin_path_prefix }}'. + when: "matrix_element_admin_path_prefix != '/'" \ No newline at end of file diff --git a/roles/custom/matrix-element-admin/templates/env.j2 b/roles/custom/matrix-element-admin/templates/env.j2 new file mode 100644 index 000000000..b02e99c64 --- /dev/null +++ b/roles/custom/matrix-element-admin/templates/env.j2 @@ -0,0 +1,9 @@ +{# +SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +SERVER_NAME={{ matrix_element_admin_environment_variable_server_name }} + +{{ matrix_element_admin_environment_variables_additional_variables }} \ No newline at end of file diff --git a/roles/custom/matrix-element-admin/templates/labels.j2 b/roles/custom/matrix-element-admin/templates/labels.j2 new file mode 100644 index 000000000..f3b628258 --- /dev/null +++ b/roles/custom/matrix-element-admin/templates/labels.j2 @@ -0,0 +1,45 @@ +{# +SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if matrix_element_admin_container_labels_traefik_enabled %} +traefik.enable=true + +{% if matrix_element_admin_container_labels_traefik_docker_network %} +traefik.docker.network={{ matrix_element_admin_container_labels_traefik_docker_network }} +{% endif %} + +traefik.http.services.matrix-element-admin.loadbalancer.server.port=8080 + +{% set middlewares = [] %} + +{% if matrix_element_admin_container_labels_traefik_path_prefix != '/' %} +traefik.http.middlewares.matrix-element-admin-slashless-redirect.redirectregex.regex=({{ matrix_element_admin_container_labels_traefik_path_prefix | quote }})$ +traefik.http.middlewares.matrix-element-admin-slashless-redirect.redirectregex.replacement=${1}/ +{% set middlewares = middlewares + ['matrix-element-admin-slashless-redirect'] %} +{% endif %} + +{% if matrix_element_admin_container_labels_traefik_path_prefix != '/' %} +traefik.http.middlewares.matrix-element-admin-strip-prefix.stripprefix.prefixes={{ matrix_element_admin_container_labels_traefik_path_prefix }} +{% set middlewares = middlewares + ['matrix-element-admin-strip-prefix'] %} +{% endif %} + +traefik.http.routers.matrix-element-admin.rule={{ matrix_element_admin_container_labels_traefik_rule }} +{% if matrix_element_admin_container_labels_traefik_priority | int > 0 %} +traefik.http.routers.matrix-element-admin.priority={{ matrix_element_admin_container_labels_traefik_priority }} +{% endif %} +traefik.http.routers.matrix-element-admin.service=matrix-element-admin +{% if middlewares | length > 0 %} +traefik.http.routers.matrix-element-admin.middlewares={{ middlewares | join(',') }} +{% endif %} +traefik.http.routers.matrix-element-admin.entrypoints={{ matrix_element_admin_container_labels_traefik_entrypoints }} +traefik.http.routers.matrix-element-admin.tls={{ matrix_element_admin_container_labels_traefik_tls | to_json }} +{% if matrix_element_admin_container_labels_traefik_tls %} +traefik.http.routers.matrix-element-admin.tls.certResolver={{ matrix_element_admin_container_labels_traefik_tls_certResolver }} +{% endif %} + +{% endif %} + +{{ matrix_element_admin_container_labels_additional_labels }} diff --git a/roles/custom/matrix-element-admin/templates/systemd/matrix-element-admin.service.j2 b/roles/custom/matrix-element-admin/templates/systemd/matrix-element-admin.service.j2 new file mode 100644 index 000000000..35e64d9e6 --- /dev/null +++ b/roles/custom/matrix-element-admin/templates/systemd/matrix-element-admin.service.j2 @@ -0,0 +1,52 @@ +#jinja2: lstrip_blocks: True +[Unit] +Description=Element Admin +{% for service in matrix_element_admin_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_element_admin_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ devture_systemd_docker_base_systemd_unit_home_path }}" +ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop -t {{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-element-admin 2>/dev/null || true' +ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-element-admin 2>/dev/null || true' + +{# + We mount a tmpfs at /tmp, because `/docker-entrypoint.d/replace-config.sh` writes temporary files there. +#} +ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} create \ + --rm \ + --name=matrix-element-admin \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_element_admin_container_network }} \ + --env-file={{ matrix_element_admin_base_path }}/env \ + --label-file={{ matrix_element_admin_base_path }}/labels \ + --tmpfs=/tmp:rw,noexec,nosuid,size=1024m \ + {% for arg in matrix_element_admin_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_element_admin_container_image }} {{ matrix_element_admin_container_process_extra_arguments | join(' ') }} + +{% for network in matrix_element_admin_container_additional_networks %} +ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} network connect {{ network }} matrix-element-admin +{% endfor %} + +ExecStart={{ devture_systemd_docker_base_host_command_docker }} start --attach matrix-element-admin + +ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop -t {{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-element-admin 2>/dev/null || true' +ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-element-admin 2>/dev/null || true' + +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-element-admin + +[Install] +WantedBy=multi-user.target diff --git a/roles/custom/matrix-element-admin/templates/systemd/matrix-element-admin.service.j2.license b/roles/custom/matrix-element-admin/templates/systemd/matrix-element-admin.service.j2.license new file mode 100644 index 000000000..e18b238ea --- /dev/null +++ b/roles/custom/matrix-element-admin/templates/systemd/matrix-element-admin.service.j2.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/roles/custom/matrix-element-call/tasks/validate_config.yml b/roles/custom/matrix-element-call/tasks/validate_config.yml index fcc4f4bb8..86fb84705 100644 --- a/roles/custom/matrix-element-call/tasks/validate_config.yml +++ b/roles/custom/matrix-element-call/tasks/validate_config.yml @@ -28,7 +28,7 @@ # the hardcoded URLs in the Element Call are pointing people to the wrong place, which is a problem. - name: Fail if Element Call path prefix is different than / ansible.builtin.fail: - msg: > + msg: >- Element Call with a path prefix other than '/' is not supported yet. You have configured matrix_element_call_path_prefix to '{{ matrix_element_call_path_prefix }}'. when: "matrix_element_call_path_prefix != '/'" diff --git a/setup.yml b/setup.yml index c83fade82..7e4168440 100644 --- a/setup.yml +++ b/setup.yml @@ -135,6 +135,7 @@ - custom/matrix-media-repo - custom/matrix-pantalaimon + - custom/matrix-element-admin - custom/matrix-element-call - galaxy/livekit_server - custom/matrix-livekit-jwt-service