diff --git a/CHANGELOG.md b/CHANGELOG.md index 9857f468b..efe68fa44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 2025-03-15 + +## Element Call support + +The playbook now supports [Element Call](https://github.com/element-hq/element-call) as an optional feature. Thanks to [wjbeckett](https://github.com/wjbeckett) for getting us started via [PR#3562](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/3562). + +Element Call is a native Matrix video conferencing application developed by [Element](https://element.io/) that has the goal of replacing [Jitsi](./docs/configuring-playbook-jitsi.md) and the old WebRTC stack used in previous Element versions. + +💡 For now, Element Call is only supported with the [Synapse](docs/configuring-playbook-synapse.md) homeserver (with [federation](docs/configuring-playbook-federation.md) enabled) and [Element Web](docs/configuring-playbook-client-element-web.md) and Element X mobile clients. See the [Prerequisites](docs/configuring-playbook-element-call.md#prerequisites) section of the [Element Call documentation](docs/configuring-playbook-element-call.md) for more details. + +To get started, see the [Configuring Element Call](docs/configuring-playbook-element-call.md) documentation page. + + # 2025-03-08 ## 6️⃣ IPv6 support enablement recommended by default diff --git a/README.md b/README.md index 72ca57855..47b4074c4 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,8 @@ Services that run on the server to make the various parts of your installation w | [Exim](https://www.exim.org/) | ✅ | Mail server, through which all Matrix services send outgoing email (can be configured to relay through another SMTP server) | [Link](docs/configuring-playbook-email.md) | | [ma1sd](https://github.com/ma1uta/ma1sd) | ❌ | Matrix Identity Server | [Link](docs/configuring-playbook-ma1sd.md) | [ddclient](https://github.com/linuxserver/docker-ddclient) | ❌ | Dynamic DNS | [Link](docs/configuring-playbook-dynamic-dns.md) | +| [LiveKit Server](https://github.com/livekit/livekit) | ❌ | WebRTC server for audio/video calls | [Link](docs/configuring-playbook-livekit-server.md) | +| [Livekit JWT Service](https://github.com/livekit/livekit-jwt-service) | ❌ | JWT service for integrating [Element Call](./configuring-playbook-element-call.md) with [LiveKit Server](./configuring-playbook-livekit-server.md) | [Link](docs/configuring-playbook-livekit-jwt-service.md) | ### Authentication @@ -185,6 +187,7 @@ Various services that don't fit any other categories. | [Pantalaimon](https://github.com/matrix-org/pantalaimon) | ❌ | E2EE aware proxy daemon | [Link](docs/configuring-playbook-pantalaimon.md) | | [Sygnal](https://github.com/matrix-org/sygnal) | ❌ | Push gateway | [Link](docs/configuring-playbook-sygnal.md) | | [ntfy](https://ntfy.sh) | ❌ | Push notifications server | [Link](docs/configuring-playbook-ntfy.md) | +| [Element Call](https://github.com/element-hq/element-call) | ❌ | A native Matrix video conferencing application | [Link](docs/configuring-playbook-element-call.md) | ## 🆕 Changes diff --git a/docs/configuring-playbook-element-call.md b/docs/configuring-playbook-element-call.md new file mode 100644 index 000000000..87677b546 --- /dev/null +++ b/docs/configuring-playbook-element-call.md @@ -0,0 +1,82 @@ + + +# Setting up Element Call (optional) + +The playbook can install and configure [Element Call](https://github.com/element-hq/element-call) for you. + +Element Call is a native Matrix video conferencing application developed by [Element](https://element.io), designed for secure, scalable, privacy-respecting, and decentralized video and voice calls over the Matrix protocol. Built on MatrixRTC ([MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)), it utilizes [MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md) with [LiveKit Server](configuring-playbook-livekit-server.md) as its backend. + +See the project's [documentation](https://github.com/element-hq/element-call) to learn more. + +## Prerequisites + +- A [Synapse](configuring-playbook-synapse.md) homeserver (see the warning below) +- [Federation](configuring-playbook-federation.md) being enabled for your Matrix homeserver (federation is enabled by default, unless you've explicitly disabled it), because [LiveKit JWT Service](configuring-playbook-livekit-jwt-service.md) currently [requires it](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/3562#issuecomment-2725250554) ([relevant source code](https://github.com/element-hq/lk-jwt-service/blob/f5f5374c4bdcc00a4fb13d27c0b28e20e4c62334/main.go#L135-L146)) +- Various experimental features for the Synapse homeserver which Element Call [requires](https://github.com/element-hq/element-call/blob/93ae2aed9841e0b066d515c56bd4c122d2b591b2/docs/self-hosting.md#a-matrix-homeserver) (automatically done when Element Call is enabled) +- A [LiveKit Server](configuring-playbook-livekit-server.md) (automatically installed when Element Call is enabled) +- The [LiveKit JWT Service](configuring-playbook-livekit-jwt-service.md) (automatically installed when Element Call is enabled) +- A client compatible with Element Call. As of 2025-03-15, that's just [Element Web](configuring-playbook-client-element-web.md) and the Element X mobile clients (iOS and Android). + +> [!WARNING] +> Because Element Call [requires](https://github.com/element-hq/element-call/blob/93ae2aed9841e0b066d515c56bd4c122d2b591b2/docs/self-hosting.md#a-matrix-homeserver) a few experimental features in the Matrix protocol, it's **very likely that it only works with the Synapse homeserver**. + +## Decide on a domain and path + +By default, Element Call is configured to be served on the `call.element.example.com` domain. + +If you'd like to run Element Call on another hostname, see the [Adjusting the Element Call URL](#adjusting-the-element-call-url-optional) section below. + +## Adjusting DNS records + +By default, this playbook installs Element Call on the `call.element.` subdomain (`call.element.example.com`) and requires you to create a `CNAME` record for `call.element`, which targets `matrix.example.com`. + +When setting these values, replace `example.com` with your own. + +All dependency services for Element Call ([LiveKit Server](configuring-playbook-livekit-server.md) and [Livekit JWT Service](configuring-playbook-livekit-jwt-service.md)) are installed and configured automatically by the playbook. By default, these services are installed on subpaths on the `matrix.` domain (e.g. `/livekit-server`, `/livekit-jwt-service`), so no DNS record adjustments are required for them. + +## Adjusting firewall rules + +In addition to the HTTP/HTTPS ports (which you've already exposed as per the [prerequisites](prerequisites.md) document), you'll also need to open ports required by [LiveKit Server](configuring-playbook-livekit-server.md) as described in its own [Adjusting firewall rules](configuring-playbook-livekit-server.md#adjusting-firewall-rules) section. + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.example.com/vars.yml` file: + +```yaml +matrix_element_call_enabled: true +``` + +### Adjusting the Element Call URL (optional) + +By tweaking the `matrix_element_call_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_call_hostname: element-call.example.com +``` + +> [!WARNING] +> A `matrix_element_call_path_prefix` variable is also available and mean to let you configure a path prefix for the Element Call service, but [Element Call does not support running under a sub-path yet](https://github.com/element-hq/element-call/issues/3084). + +## Installing + +After configuring the playbook and potentially [adjusting your DNS records](#adjusting-dns-records) and [adjusting firewall rules](#adjusting-firewall-rules), 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-jitsi.md b/docs/configuring-playbook-jitsi.md index 59fd6b6c4..3a494d083 100644 --- a/docs/configuring-playbook-jitsi.md +++ b/docs/configuring-playbook-jitsi.md @@ -20,6 +20,8 @@ The playbook can install and configure the [Jitsi](https://jitsi.org/) video-con Jitsi is an open source video-conferencing platform. It can not only be integrated with Element clients ([Element Web](configuring-playbook-client-element-web.md)/Desktop, Android and iOS) as a widget, but also be used as standalone web app. +💡 If you're into experimental technology, you may also be interested in trying out [Element Call](configuring-playbook-element-call.md) - a native Matrix video conferencing application. + The [Ansible role for Jitsi](https://github.com/mother-of-all-self-hosting/ansible-role-jitsi) is developed and maintained by [the MASH (mother-of-all-self-hosting) project](https://github.com/mother-of-all-self-hosting). For details about configuring Jitsi, you can check them via: - 🌐 [the role's documentation at the MASH project](https://github.com/mother-of-all-self-hosting/ansible-role-jitsi/blob/main/docs/configuring-jitsi.md) online - 📁 `roles/galaxy/jitsi/docs/configuring-jitsi.md` locally, if you have [fetched the Ansible roles](installing.md#update-ansible-roles) diff --git a/docs/configuring-playbook-jwt-service.md b/docs/configuring-playbook-jwt-service.md new file mode 100644 index 000000000..22fe30e8f --- /dev/null +++ b/docs/configuring-playbook-jwt-service.md @@ -0,0 +1,47 @@ + + +# Setting up JWT Service (optional) + +The playbook can install and configure [LiveKit JWT Service](https://github.com/element-hq/lk-jwt-service) for you. + +LK-JWT-Service is currently used for a single reason: generate JWT tokens with a given identity for a given room, so that users can use them to authenticate against LiveKit SFU. + +See the project's [documentation](https://github.com/element-hq/lk-jwt-service/) to learn more. + +## Decide on a domain and path + +By default, JWT Service is configured to be served: + +- on the Matrix domain (`matrix.example.com`), configurable via `matrix_livekit_jwt_service_hostname` +- under a `/livekit-jwt-service` path prefix, configurable via `matrix_livekit_jwt_service_path_prefix` + +This makes it easy to set it up, **without** having to adjust your DNS records manually. + +## Adjusting DNS records + +If you've changed the default hostname, **you may need to adjust your DNS** records accordingly to point to the correct server. + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.example.com/vars.yml` file: + +```yaml +matrix_livekit_jwt_service_enabled: true +``` + +## Installing + +After configuring the playbook and potentially [adjusting your DNS records](#adjusting-dns-records), run the [installation](installing.md) command: `just install-all` or `just setup-all` + +## Usage + +Once installed, a new `org.matrix.msc4143.rtc_foci` section is added to the Element Web client to point to your JWT service URL (e.g., `https://matrix.example.com/livekit-jwt-service`). + +## Additional Information + +Refer to the LiveKit JWT-Service documentation for more details on configuring and using JWT Service. diff --git a/docs/configuring-playbook-livekit-jwt-service.md b/docs/configuring-playbook-livekit-jwt-service.md new file mode 100644 index 000000000..55bf3682f --- /dev/null +++ b/docs/configuring-playbook-livekit-jwt-service.md @@ -0,0 +1,18 @@ + + +# Setting up LiveKit JWT Service (optional) + +The playbook can install and configure [LiveKit JWT Service](https://github.com/element-hq/lk-jwt-service/) for you. + +This is a helper component that allows [Element Call](configuring-playbook-element-call.md) to integrate with [LiveKit Server](configuring-playbook-livekit-server.md). + +💡 LiveKit JWT Service is automatically installed and configured when [Element Call](configuring-playbook-element-call.md) is enabled, so you don't need to do anything extra. + +Take a look at: + +- `roles/custom/matrix-livekit-jwt-service/defaults/main.yml` for some variables that you can customize via your `vars.yml` file +- `roles/custom/matrix-livekit-jwt-service/templates/env.j2` for the component's default configuration. \ No newline at end of file diff --git a/docs/configuring-playbook-livekit-server.md b/docs/configuring-playbook-livekit-server.md new file mode 100644 index 000000000..95f86f3ad --- /dev/null +++ b/docs/configuring-playbook-livekit-server.md @@ -0,0 +1,28 @@ + + +# Setting up LiveKit Server (optional) + +The playbook can install and configure [LiveKit Server](https://github.com/livekit/livekit) for you. + +LiveKit Server is an open source project that provides scalable, multi-user conferencing based on WebRTC. It's designed to provide everything you need to build real-time video audio data capabilities in your applications. + +💡 LiveKit Server is automatically installed and configured when [Element Call](configuring-playbook-element-call.md) is enabled, so you don't need to do anything extra. + +The [Ansible role for LiveKit Server](https://github.com/mother-of-all-self-hosting/ansible-role-livekit-server) is developed and maintained by [the MASH (mother-of-all-self-hosting) project](https://github.com/mother-of-all-self-hosting). For details about configuring LiveKit Server, you can check them via: +- 🌐 [the role's documentation at the MASH project](https://github.com/mother-of-all-self-hosting/ansible-role-livekit-server/blob/main/docs/configuring-livekit-server.md) online +- 📁 `roles/galaxy/livekit-server/docs/configuring-livekit-server.md` locally, if you have [fetched the Ansible roles](installing.md#update-ansible-roles) + +## Adjusting firewall rules + +To ensure LiveKit Server functions correctly, the following firewall rules and port forwarding settings are required: + +- `7881/tcp`: ICE/TCP + +- `7882/udp`: ICE/UDP Mux + +💡 The suggestions above are inspired by the upstream [Ports and Firewall](https://docs.livekit.io/home/self-hosting/ports-firewall/) documentation based on how LiveKit is configured in the playbook. If you've using custom configuration for the LiveKit Server role, you may need to adjust the firewall rules accordingly. diff --git a/docs/configuring-playbook.md b/docs/configuring-playbook.md index fe1184ebd..3a105cb0c 100644 --- a/docs/configuring-playbook.md +++ b/docs/configuring-playbook.md @@ -237,6 +237,12 @@ Services that help you in administrating and monitoring your Matrix installation Various services that don't fit any other categories. +- [Setting up Element Call](configuring-playbook-element-call.md) — a native Matrix video conferencing application (optional) + +- [Setting up LiveKit JWT Service](configuring-playbook-livekit-jwt-service.md) (optional) + +- [Setting up LiveKit Server](configuring-playbook-livekit-server.md) (optional) + - [Setting up Synapse Auto Invite Accept](configuring-playbook-synapse-auto-accept-invite.md) - [Setting up synapse-auto-compressor](configuring-playbook-synapse-auto-compressor.md) for compressing the database on Synapse homeservers diff --git a/docs/container-images.md b/docs/container-images.md index 844cce810..b445cc001 100644 --- a/docs/container-images.md +++ b/docs/container-images.md @@ -54,6 +54,8 @@ Services that run on the server to make the various parts of your installation w | [Exim](configuring-playbook-email.md) | [devture/exim-relay](https://hub.docker.com/r/devture/exim-relay/) | ✅ | Mail server, through which all Matrix services send outgoing email (can be configured to relay through another SMTP server) | | [ma1sd](configuring-playbook-ma1sd.md) | [ma1uta/ma1sd](https://hub.docker.com/r/ma1uta/ma1sd/) | ❌ | Matrix Identity Server | | [ddclient](configuring-playbook-dynamic-dns.md) | [linuxserver/ddclient](https://hub.docker.com/r/linuxserver/ddclient) | ❌ | Update dynamic DNS entries for accounts on Dynamic DNS Network Service Provider | +| [LiveKit Server](configuring-playbook-livekit-server.md) | [livekit/livekit-server](https://hub.docker.com/r/livekit/livekit-server/) | ❌ | WebRTC server for audio/video calls | +| [Livekit JWT Service](configuring-playbook-livekit-jwt-service.md) | [element-hq/lk-jwt-service](https://ghcr.io/element-hq/lk-jwt-service) | ❌ | JWT service for integrating [Element Call](./configuring-playbook-element-call.md) with [LiveKit Server](./configuring-playbook-livekit-server.md) | ## Authentication @@ -167,6 +169,7 @@ Various services that don't fit any other categories. | [Pantalaimon](configuring-playbook-pantalaimon.md) | [matrixdotorg/pantalaimon](https://hub.docker.com/r/matrixdotorg/pantalaimon) | ❌ | E2EE aware proxy daemon | | [Sygnal](configuring-playbook-sygnal.md) | [matrixdotorg/sygnal](https://hub.docker.com/r/matrixdotorg/sygnal/) | ❌ | Reference Push Gateway for Matrix | | [ntfy](configuring-playbook-ntfy.md) | [binwiederhier/ntfy](https://hub.docker.com/r/binwiederhier/ntfy/) | ❌ | Self-hosted, UnifiedPush-compatible push notifications server | +| [Element Call](configuring-playbook-element-call.md) | [element-hq/element-call](https://ghcr.io/element-hq/element-call) | ❌ | A native Matrix video conferencing application | ## Container images of deprecated / unmaintained services diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index f7c8c9537..59fa442b2 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -447,6 +447,12 @@ devture_systemd_service_manager_services_list_auto: | + ([{'name': 'matrix-pantalaimon.service', 'priority': 4000, 'groups': ['matrix', 'pantalaimon']}] if matrix_pantalaimon_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 []) + + + ([{'name': (livekit_server_identifier + '.service'), 'priority': 3000, 'groups': ['matrix', 'livekit-server']}] if livekit_server_enabled else []) + + ([{'name': 'matrix-registration.service', 'priority': 4000, 'groups': ['matrix', 'registration', 'matrix-registration']}] if matrix_registration_enabled else []) + ([{'name': 'matrix-sliding-sync.service', 'priority': 1500, 'groups': ['matrix', 'sliding-sync']}] if matrix_sliding_sync_enabled else []) @@ -4533,7 +4539,7 @@ ntfy_visitor_request_limit_exempt_hosts_hostnames_auto: | # ###################################################################### -valkey_enabled: "{{ matrix_synapse_workers_enabled or (matrix_hookshot_enabled and matrix_hookshot_encryption_enabled) }}" +valkey_enabled: "{{ matrix_synapse_workers_enabled or (matrix_hookshot_enabled and matrix_hookshot_encryption_enabled) or matrix_element_call_enabled }}" valkey_identifier: matrix-valkey @@ -4605,6 +4611,14 @@ matrix_client_element_enable_presence_by_hs_url: |- matrix_client_element_jitsi_preferred_domain: "{{ matrix_server_fqn_jitsi if jitsi_enabled else '' }}" +matrix_client_element_features_feature_video_rooms: "{{ matrix_element_call_enabled }}" +matrix_client_element_features_feature_group_calls: "{{ matrix_element_call_enabled }}" +matrix_client_element_features_feature_element_call_video_rooms: "{{ matrix_element_call_enabled }}" +matrix_client_element_features_feature_oidc_native_flow: "{{ matrix_authentication_service_enabled }}" + +matrix_client_element_element_call_enabled: "{{ matrix_element_call_enabled }}" +matrix_client_element_element_call_url: "{{ matrix_element_call_public_url if matrix_element_call_enabled else '' }}" + ###################################################################### # # /matrix-client-element @@ -4920,6 +4934,8 @@ matrix_synapse_ext_media_repo_enabled: "{{ matrix_media_repo_enabled }}" matrix_synapse_report_stats: "{{ matrix_synapse_usage_exporter_enabled }}" matrix_synapse_report_stats_endpoint: "{{ (('http://' + matrix_synapse_usage_exporter_identifier + ':' + matrix_synapse_usage_exporter_container_port | string + '/report-usage-stats/push') if matrix_synapse_usage_exporter_enabled else '') }}" +matrix_synapse_experimental_features_msc3266_enabled: "{{ matrix_element_call_enabled }}" + matrix_synapse_experimental_features_msc3861_enabled: "{{ matrix_authentication_service_enabled and not matrix_authentication_service_migration_in_progress }}" matrix_synapse_experimental_features_msc3861_issuer: "{{ matrix_authentication_service_http_base_container_url if matrix_authentication_service_enabled else '' }}" matrix_synapse_experimental_features_msc3861_client_secret: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'syn.ngauth.cs', rounds=655555) | to_uuid }}" @@ -4928,6 +4944,10 @@ matrix_synapse_experimental_features_msc3861_account_management_url: "{{ matrix_ matrix_synapse_experimental_features_msc4108_enabled: "{{ matrix_authentication_service_enabled and not matrix_authentication_service_migration_in_progress }}" +matrix_synapse_experimental_features_msc4140_enabled: "{{ matrix_element_call_enabled }}" + +matrix_synapse_experimental_features_msc4222_enabled: "{{ matrix_element_call_enabled }}" + # Disable password authentication when delegating authentication to Matrix Authentication Service. # Unless this is done, Synapse fails on startup with: # > Error in configuration at 'password_config.enabled': @@ -6117,8 +6137,18 @@ matrix_static_files_file_matrix_client_property_m_tile_server_map_style_url: "{{ # See: https://github.com/etkecc/synapse-admin/pull/126 matrix_static_files_file_matrix_client_property_cc_etke_synapse_admin_auto: "{{ matrix_synapse_admin_configuration if matrix_homeserver_implementation == 'synapse' else {} }}" +matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_enabled: "{{ matrix_element_call_enabled }}" +matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_auto: |- + {{ + ( + [{'type': 'livekit', 'livekit_service_url': matrix_livekit_jwt_service_public_url}] if matrix_livekit_jwt_service_enabled else [] + ) + }} + matrix_static_files_file_matrix_server_property_m_server: "{{ matrix_server_fqn_matrix_federation }}:{{ matrix_federation_public_port }}" +matrix_static_files_file_element_element_json_property_call_widget_url: "{{ matrix_element_call_public_url if matrix_element_call_enabled else '' }}" + matrix_static_files_scheme: "{{ 'https' if matrix_playbook_ssl_enabled else 'http' }}" matrix_static_files_self_check_hostname_matrix: "{{ matrix_server_fqn_matrix }}" @@ -6231,3 +6261,124 @@ traefik_certs_dumper_container_image_registry_prefix_upstream: "{{ matrix_contai # /traefik_certs_dumper # # # ######################################################################## + + +######################################################################## +# # +# matrix-element-call # +# # +######################################################################## + +matrix_element_call_enabled: false + +matrix_element_call_scheme: "{{ 'https' if matrix_playbook_ssl_enabled else 'http' }}" + +matrix_element_call_container_network: "{{ matrix_addons_container_network }}" + +matrix_element_call_container_additional_networks_auto: "{{ [matrix_playbook_reverse_proxyable_services_additional_network] if (matrix_element_call_container_labels_traefik_enabled and matrix_playbook_reverse_proxyable_services_additional_network) else [] }}" + +matrix_element_call_container_labels_traefik_enabled: "{{ matrix_playbook_reverse_proxy_type in ['playbook-managed-traefik', 'other-traefik-container'] }}" +matrix_element_call_container_labels_traefik_docker_network: "{{ matrix_playbook_reverse_proxyable_services_additional_network }}" +matrix_element_call_container_labels_traefik_entrypoints: "{{ traefik_entrypoint_primary }}" +matrix_element_call_container_labels_traefik_tls_certResolver: "{{ traefik_certResolver_primary }}" + +matrix_element_call_config_livekit_livekit_service_url: "{{ matrix_livekit_jwt_service_public_url if matrix_livekit_jwt_service_enabled else '' }}" + +######################################################################## +# # +# /matrix-element-call # +# # +######################################################################## + +######################################################################## +# # +# livekit-server # +# # +######################################################################## + +livekit_server_enabled: "{{ matrix_element_call_enabled }}" + +livekit_server_identifier: matrix-livekit-server + +livekit_server_uid: "{{ matrix_user_uid }}" +livekit_server_gid: "{{ matrix_user_gid }}" + +livekit_server_base_path: "{{ matrix_base_data_path }}/livekit-server" + +livekit_server_hostname: "{{ matrix_server_fqn_matrix }}" +livekit_server_path_prefix: "/livekit-server" + +livekit_server_container_image_self_build: "{{ matrix_architecture not in ['arm64', 'amd64'] }}" + +livekit_server_container_network: "{{ matrix_addons_container_network }}" +livekit_server_container_additional_networks_auto: "{{ [matrix_playbook_reverse_proxyable_services_additional_network] if (livekit_server_container_labels_traefik_enabled and matrix_playbook_reverse_proxyable_services_additional_network) else [] }}" + +livekit_server_container_labels_traefik_enabled: "{{ matrix_playbook_reverse_proxy_type in ['playbook-managed-traefik', 'other-traefik-container'] }}" +livekit_server_container_labels_traefik_docker_network: "{{ matrix_playbook_reverse_proxyable_services_additional_network }}" +livekit_server_container_labels_traefik_entrypoints: "{{ traefik_entrypoint_primary }}" +livekit_server_container_labels_traefik_tls_certResolver: "{{ traefik_certResolver_primary }}" + +livekit_server_config_keys_auto: |- + {{ + {} + | combine( + {matrix_livekit_jwt_service_environment_variable_livekit_key: matrix_livekit_jwt_service_environment_variable_livekit_secret} + if matrix_livekit_jwt_service_enabled else {} + ) + }} + +# The playbook intentionally uses a non-standard port than the default used by the role (5349), +# because Coturn is already using that port. +# Note that TURN is not enabled by default. See `livekit_server_config_turn_enabled`. +livekit_server_config_turn_tls_port: 5350 + +# The playbook intentionally uses a non-standard port than the default used by the role (3478), +# because Coturn is already using that port. +# Note that TURN is not enabled by default. See `livekit_server_config_turn_enabled`. +livekit_server_config_turn_udp_port: 3479 + +######################################################################## +# # +# /livekit-server # +# # +######################################################################## + + +######################################################################## +# # +# matrix-livekit-jwt-service # +# # +######################################################################## + +matrix_livekit_jwt_service_enabled: "{{ matrix_element_call_enabled and livekit_server_enabled }}" + +matrix_livekit_jwt_service_scheme: "{{ 'https' if matrix_playbook_ssl_enabled else 'http' }}" + +matrix_livekit_jwt_service_hostname: "{{ matrix_server_fqn_matrix }}" +matrix_livekit_jwt_service_path_prefix: "/livekit-jwt-service" + +matrix_livekit_jwt_service_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" + +matrix_livekit_jwt_service_container_network: "{{ matrix_addons_container_network }}" + +matrix_livekit_jwt_service_container_additional_networks_auto: | + {{ + ([matrix_playbook_reverse_proxyable_services_additional_network] if (matrix_livekit_jwt_service_container_labels_traefik_enabled and matrix_playbook_reverse_proxyable_services_additional_network) else []) + }} + +matrix_livekit_jwt_service_container_labels_traefik_enabled: "{{ matrix_playbook_reverse_proxy_type in ['playbook-managed-traefik', 'other-traefik-container'] }}" +matrix_livekit_jwt_service_container_labels_traefik_docker_network: "{{ matrix_playbook_reverse_proxyable_services_additional_network }}" +matrix_livekit_jwt_service_container_labels_traefik_entrypoints: "{{ traefik_entrypoint_primary }}" +matrix_livekit_jwt_service_container_labels_traefik_tls_certResolver: "{{ traefik_certResolver_primary }}" + +matrix_livekit_jwt_service_environment_variable_livekit_url: "{{ livekit_server_websocket_public_url }}" + +matrix_livekit_jwt_service_environment_variable_livekit_key: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'lk.key', rounds=655555) | to_uuid }}" + +matrix_livekit_jwt_service_environment_variable_livekit_secret: "{{ '%s' | format(matrix_homeserver_generic_secret_key) | password_hash('sha512', 'lk.secret', rounds=655555) | to_uuid }}" + +######################################################################## +# # +# /matrix-livekit-jwt-service # +# # +######################################################################## diff --git a/requirements.yml b/requirements.yml index 556634277..76efb2363 100644 --- a/requirements.yml +++ b/requirements.yml @@ -27,6 +27,9 @@ - src: git+https://github.com/mother-of-all-self-hosting/ansible-role-jitsi.git version: v10078-1-0 name: jitsi +- src: git+https://github.com/mother-of-all-self-hosting/ansible-role-livekit-server.git + version: v1.8.4-2 + name: livekit_server - src: git+https://github.com/mother-of-all-self-hosting/ansible-role-ntfy.git version: v2.11.0-4 name: ntfy diff --git a/roles/custom/matrix-client-element/defaults/main.yml b/roles/custom/matrix-client-element/defaults/main.yml index 763ba1c97..5360ea1bf 100644 --- a/roles/custom/matrix-client-element/defaults/main.yml +++ b/roles/custom/matrix-client-element/defaults/main.yml @@ -215,6 +215,67 @@ matrix_client_element_branding_auth_header_logo_url: "{{ matrix_client_element_w # URL to Wallpaper, shown in background of welcome page matrix_client_element_branding_welcome_background_url: ~ # noqa var-naming +# Controls the `features` section of the Element Web configuration. +matrix_client_element_features: "{{ matrix_client_element_features_default | combine(matrix_client_element_features_auto, recursive=True) | combine(matrix_client_element_features_custom, recursive=True) }}" +matrix_client_element_features_default: |- + {{ + {} + + | combine( + {'feature_video_rooms': true} if matrix_client_element_features_feature_video_rooms else {} + ) + | combine( + {'feature_group_calls': true} if matrix_client_element_features_feature_group_calls else {} + ) + | combine( + {'feature_element_call_video_rooms': true} if matrix_client_element_features_feature_element_call_video_rooms else {} + ) + | combine( + {'feature_oidc_native_flow': true} if matrix_client_element_features_feature_oidc_native_flow else {} + ) + }} + +matrix_client_element_features_auto: {} +matrix_client_element_features_custom: {} + +matrix_client_element_features_feature_video_rooms: false +matrix_client_element_features_feature_group_calls: false +matrix_client_element_features_feature_element_call_video_rooms: false +matrix_client_element_features_feature_oidc_native_flow: false + +matrix_client_element_element_call_enabled: false +matrix_client_element_element_call: "{{ matrix_client_element_element_call_default | combine(matrix_client_element_element_call_auto, recursive=True) | combine(matrix_client_element_element_call_custom, recursive=True) }}" +matrix_client_element_element_call_default: |- + {{ + {} + | combine( + {'url': matrix_client_element_element_call_url} if matrix_client_element_element_call_url else {} + ) + | combine( + {'participant_limit': matrix_client_element_element_call_participant_limit} if matrix_client_element_element_call_participant_limit else {} + ) + | combine( + {'brand': matrix_client_element_element_call_brand} if matrix_client_element_element_call_brand else {} + ) + | combine( + {'use_exclusively': matrix_client_element_element_call_use_exclusively} if matrix_client_element_element_call_use_exclusively else {} + ) + }} +matrix_client_element_element_call_auto: {} +matrix_client_element_element_call_custom: {} + +# Controls the `element_call.url` setting in the Element Web configuration. +matrix_client_element_element_call_url: '' + +# Controls the `element_call.participant_limit` setting in the Element Web configuration. +matrix_client_element_element_call_participant_limit: 8 + +# Controls the `element_call.brand` setting in the Element Web configuration. +matrix_client_element_element_call_brand: "Element Call" + +# Controls the `element_call.use_exclusively` setting in the Element Web configuration. +matrix_client_element_element_call_use_exclusively: true + matrix_client_element_page_template_welcome_path: "{{ role_path }}/templates/welcome.html.j2" # By default, there's no Element Web homepage (when logged in). If you wish to have one, diff --git a/roles/custom/matrix-client-element/templates/config.json.j2 b/roles/custom/matrix-client-element/templates/config.json.j2 index 9d354a3c2..7516abe48 100644 --- a/roles/custom/matrix-client-element/templates/config.json.j2 +++ b/roles/custom/matrix-client-element/templates/config.json.j2 @@ -45,5 +45,7 @@ "auth_footer_links": {{ matrix_client_element_branding_auth_footer_links | to_json }}, "auth_header_logo_url": {{ matrix_client_element_branding_auth_header_logo_url | to_json }}, "welcome_background_url": {{ matrix_client_element_branding_welcome_background_url | to_json }} - } + }, + "features": {{ matrix_client_element_features | to_json }}, + "element_call": {{ (matrix_client_element_element_call if matrix_client_element_element_call_enabled else {}) | to_json }} } diff --git a/roles/custom/matrix-element-call/defaults/main.yml b/roles/custom/matrix-element-call/defaults/main.yml new file mode 100644 index 000000000..c69c0a682 --- /dev/null +++ b/roles/custom/matrix-element-call/defaults/main.yml @@ -0,0 +1,145 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +# Element Call is a native Matrix video conferencing application developed by Element. +# Project source code URL: https://github.com/element-hq/element-call + +matrix_element_call_enabled: false + +matrix_element_call_version: v0.7.2 + +matrix_element_call_scheme: https + +matrix_element_call_hostname: "call.{{ matrix_server_fqn_element }}" +matrix_element_call_path_prefix: / + +matrix_element_call_base_path: "{{ matrix_base_data_path }}/element-call" + +# The architecture for Element Call container images. +# Recognized values by us are 'amd64', 'arm32' and 'arm64'. +matrix_element_call_architecture: "{{ matrix_architecture }}" + +matrix_element_call_container_image: "{{ matrix_element_call_container_image_registry_prefix }}element-hq/element-call:{{ matrix_element_call_container_image_tag }}" +matrix_element_call_container_image_registry_prefix: "{{ matrix_element_call_container_image_registry_prefix_upstream }}" +matrix_element_call_container_image_registry_prefix_upstream: "{{ matrix_element_call_container_image_registry_prefix_upstream_default }}" +matrix_element_call_container_image_registry_prefix_upstream_default: ghcr.io/ +matrix_element_call_container_image_tag: "{{ matrix_element_call_version }}" +matrix_element_call_container_image_force_pull: "{{ matrix_element_call_container_image.endswith(':latest') }}" + +matrix_element_call_container_network: matrix-element-call + +matrix_element_call_container_http_host_bind_port: '' + +matrix_element_call_container_additional_networks: "{{ matrix_element_call_container_additional_networks_auto + matrix_element_call_container_additional_networks_custom }}" +matrix_element_call_container_additional_networks_auto: [] +matrix_element_call_container_additional_networks_custom: [] + +# Traefik Configuration for Element Call +matrix_element_call_container_labels_traefik_enabled: true +matrix_element_call_container_labels_traefik_docker_network: "{{ matrix_element_call_container_network }}" +matrix_element_call_container_labels_traefik_hostname: "{{ matrix_element_call_hostname }}" +# The path prefix must either be `/` or not end with a slash (e.g. `/element`). +matrix_element_call_container_labels_traefik_path_prefix: "{{ matrix_element_call_path_prefix }}" +matrix_element_call_container_labels_traefik_rule: "Host(`{{ matrix_element_call_container_labels_traefik_hostname }}`){% if matrix_element_call_container_labels_traefik_path_prefix != '/' %} && PathPrefix(`{{ matrix_element_call_container_labels_traefik_path_prefix }}`){% endif %}" +matrix_element_call_container_labels_traefik_priority: 0 +matrix_element_call_container_labels_traefik_entrypoints: web-secure +matrix_element_call_container_labels_traefik_tls: "{{ matrix_element_call_container_labels_traefik_entrypoints != 'web' }}" +matrix_element_call_container_labels_traefik_tls_certResolver: default # noqa var-naming + +# Controls which additional headers to attach to all HTTP responses. +# To add your own headers, use `matrix_element_call_container_labels_traefik_additional_response_headers_custom` +matrix_element_call_container_labels_traefik_additional_response_headers: "{{ matrix_element_call_container_labels_traefik_additional_response_headers_auto | combine(matrix_element_call_container_labels_traefik_additional_response_headers_custom) }}" +matrix_element_call_container_labels_traefik_additional_response_headers_auto: | + {{ + {} + | combine ({'X-XSS-Protection': matrix_element_call_http_header_xss_protection} if matrix_element_call_http_header_xss_protection else {}) + | combine ({'X-Frame-Options': matrix_element_call_http_header_frame_options} if matrix_element_call_http_header_frame_options else {}) + | combine ({'X-Content-Type-Options': matrix_element_call_http_header_content_type_options} if matrix_element_call_http_header_content_type_options else {}) + | combine ({'Content-Security-Policy': matrix_element_call_http_header_content_security_policy} if matrix_element_call_http_header_content_security_policy else {}) + | combine ({'Permission-Policy': matrix_element_call_http_header_content_permission_policy} if matrix_element_call_http_header_content_permission_policy else {}) + | combine ({'Strict-Transport-Security': matrix_element_call_http_header_strict_transport_security} if matrix_element_call_http_header_strict_transport_security and matrix_element_call_container_labels_traefik_tls else {}) + }} +matrix_element_call_container_labels_traefik_additional_response_headers_custom: {} + +# matrix_element_call_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_call_container_labels_additional_labels: | +# my.label=1 +# another.label="here" +matrix_element_call_container_labels_additional_labels: '' + +# A list of extra arguments to pass to the container +matrix_element_call_container_extra_arguments: [] + +# List of systemd services that matrix-element-call.service depends on +matrix_element_call_systemd_required_services_list: "{{ matrix_element_call_systemd_required_services_list_default + matrix_element_call_systemd_required_services_list_auto + matrix_element_call_systemd_required_services_list_custom }}" +matrix_element_call_systemd_required_services_list_default: "{{ [devture_systemd_docker_base_docker_service_name] if devture_systemd_docker_base_docker_service_name else [] }}" +matrix_element_call_systemd_required_services_list_auto: [] +matrix_element_call_systemd_required_services_list_custom: [] + +# Specifies the value of the `X-XSS-Protection` header +# Stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. +# +# Learn more about it is here: +# - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection +# - https://portswigger.net/web-security/cross-site-scripting/reflected +matrix_element_call_http_header_xss_protection: "1; mode=block" + +# Specifies the value of the `X-Frame-Options` header which controls whether framing can happen. +# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options +matrix_element_call_http_header_frame_options: '' + +# Specifies the value of the `X-Content-Type-Options` header. +# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options +matrix_element_call_http_header_content_type_options: nosniff + +# Specifies the value of the `Content-Security-Policy` header. +# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy +matrix_element_call_http_header_content_security_policy: frame-ancestors * + +# Specifies the value of the `Permission-Policy` header. +# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permission-Policy +matrix_element_call_http_header_content_permission_policy: "{{ 'interest-cohort=()' if matrix_element_call_floc_optout_enabled else '' }}" + +# Specifies the value of the `Strict-Transport-Security` header. +# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security +matrix_element_call_http_header_strict_transport_security: "max-age=31536000; includeSubDomains{{ '; preload' if matrix_element_call_hsts_preload_enabled else '' }}" + +# Controls whether to send a "Permissions-Policy interest-cohort=();" header along with all responses +# +# Learn more about what it is here: +# - https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea +# - https://paramdeo.com/blog/opting-your-website-out-of-googles-floc-network +# - https://amifloced.org/ +# +# Of course, a better solution is to just stop using browsers (like Chrome), which participate in such tracking practices. +# See: `matrix_element_call_content_permission_policy` +matrix_element_call_floc_optout_enabled: true + +# Controls if HSTS preloading is enabled +# +# In its strongest and recommended form, the [HSTS policy](https://www.chromium.org/hsts) includes all subdomains, and +# indicates a willingness to be "preloaded" into browsers: +# `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload` +# For more information visit: +# - https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security +# - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security +# - https://hstspreload.org/#opt-in +# See: `matrix_element_call_http_header_strict_transport_security` +matrix_element_call_hsts_preload_enabled: false + +# Controls the default_server_config/m.homeserver/base_url property in the config.json file. +matrix_element_call_config_default_server_config_m_homeserver_base_url: "{{ matrix_homeserver_url }}" + +# Controls the default_server_config/m.homeserver/server_name property in the config.json file. +matrix_element_call_config_default_server_config_m_homeserver_server_name: "{{ matrix_domain }}" + +# Controls the livekit/livekit_service_url property in the config.json file. +matrix_element_call_config_livekit_livekit_service_url: "" diff --git a/roles/custom/matrix-element-call/tasks/install.yml b/roles/custom/matrix-element-call/tasks/install.yml new file mode 100644 index 000000000..49c3078f8 --- /dev/null +++ b/roles/custom/matrix-element-call/tasks/install.yml @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Ensure Element Call paths exist + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - path: "{{ matrix_element_call_base_path }}" + +- name: Ensure Element Call config.json is in place + ansible.builtin.template: + src: "{{ role_path }}/templates/config.json.j2" + dest: "{{ matrix_element_call_base_path }}/config.json" + mode: 0640 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure Element Call container labels file is in place + ansible.builtin.template: + src: "{{ role_path }}/templates/labels.j2" + dest: "{{ matrix_element_call_base_path }}/labels" + mode: 0640 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure Element Call container image is pulled + community.docker.docker_image: + name: "{{ matrix_element_call_container_image }}" + source: pull + force_source: "{{ matrix_element_call_container_image_force_pull }}" + register: element_call_image_result + retries: "{{ devture_playbook_help_container_retries_count }}" + delay: "{{ devture_playbook_help_container_retries_delay }}" + until: element_call_image_result is not failed + +- name: Ensure Element Call container network is created + community.general.docker_network: + enable_ipv6: "{{ devture_systemd_docker_base_ipv6_enabled }}" + name: "{{ matrix_element_call_container_network }}" + driver: bridge + driver_options: "{{ devture_systemd_docker_base_container_networks_driver_options }}" + +- name: Ensure Element Call systemd service is installed + ansible.builtin.template: + src: "{{ role_path }}/templates/systemd/matrix-element-call.service.j2" + dest: "{{ devture_systemd_docker_base_systemd_path }}/matrix-element-call.service" + mode: 0644 diff --git a/roles/custom/matrix-element-call/tasks/main.yml b/roles/custom/matrix-element-call/tasks/main.yml new file mode 100644 index 000000000..c0b771877 --- /dev/null +++ b/roles/custom/matrix-element-call/tasks/main.yml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- tags: + - setup-all + - setup-element-call + - install-all + - install-element-call + block: + - when: matrix_element_call_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/validate_config.yml" + + - when: matrix_element_call_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/install.yml" + +- tags: + - setup-all + - setup-element-call + block: + - when: not matrix_element_call_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/uninstall.yml" diff --git a/roles/custom/matrix-element-call/tasks/uninstall.yml b/roles/custom/matrix-element-call/tasks/uninstall.yml new file mode 100644 index 000000000..26ae5303d --- /dev/null +++ b/roles/custom/matrix-element-call/tasks/uninstall.yml @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Check existence of matrix-element-call service + ansible.builtin.stat: + path: "{{ devture_systemd_docker_base_systemd_path }}/matrix-element-call.service" + register: matrix_element_call_service_stat + +- when: matrix_element_call_service_stat.stat.exists | bool + block: + - name: Ensure matrix-element-call is stopped + ansible.builtin.service: + name: matrix-element-call + state: stopped + enabled: false + daemon_reload: true + + - name: Ensure matrix-element-call.service doesn't exist + ansible.builtin.file: + path: "{{ devture_systemd_docker_base_systemd_path }}/matrix-element-call.service" + state: absent + + - name: Ensure Element Call paths don't exist + ansible.builtin.file: + path: "{{ matrix_element_call_base_path }}" + state: absent diff --git a/roles/custom/matrix-element-call/tasks/validate_config.yml b/roles/custom/matrix-element-call/tasks/validate_config.yml new file mode 100644 index 000000000..738b2de3c --- /dev/null +++ b/roles/custom/matrix-element-call/tasks/validate_config.yml @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Fail if Element Call architecture is not supported + ansible.builtin.fail: + msg: > + Element Call is only supported on amd64 and arm64 architectures. + Your architecture is configured as '{{ matrix_element_call_architecture }}'. + when: "matrix_element_call_architecture not in ['amd64', 'arm64']" + +- name: Fail if required Element Call settings are not defined + ansible.builtin.fail: + msg: > + You need to define a required configuration setting (`{{ item.name }}`). + when: "item.when | bool and vars[item.name] | length == 0" + with_items: + - {'name': 'matrix_element_call_container_network', when: true} + - {'name': 'matrix_element_call_hostname', when: true} + - {'name': 'matrix_element_call_config_livekit_livekit_service_url', when: true} + +# Element Call 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 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: > + 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/roles/custom/matrix-element-call/templates/config.json.j2 b/roles/custom/matrix-element-call/templates/config.json.j2 new file mode 100644 index 000000000..1ef5adb9f --- /dev/null +++ b/roles/custom/matrix-element-call/templates/config.json.j2 @@ -0,0 +1,11 @@ +{ + "default_server_config": { + "m.homeserver": { + "base_url": {{ matrix_element_call_config_default_server_config_m_homeserver_base_url | to_json }}, + "server_name": {{ matrix_element_call_config_default_server_config_m_homeserver_server_name | to_json}} + } + }, + "livekit": { + "livekit_service_url": {{ matrix_element_call_config_livekit_livekit_service_url | to_json }} + } +} diff --git a/roles/custom/matrix-element-call/templates/config.json.j2.license b/roles/custom/matrix-element-call/templates/config.json.j2.license new file mode 100644 index 000000000..085b430a3 --- /dev/null +++ b/roles/custom/matrix-element-call/templates/config.json.j2.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: 2022 MDAD project contributors +SPDX-FileCopyrightText: 2024 wjbeckett +SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/roles/custom/matrix-element-call/templates/labels.j2 b/roles/custom/matrix-element-call/templates/labels.j2 new file mode 100644 index 000000000..436c13882 --- /dev/null +++ b/roles/custom/matrix-element-call/templates/labels.j2 @@ -0,0 +1,51 @@ +{# +SPDX-FileCopyrightText: 2022 MDAD project contributors +SPDX-FileCopyrightText: 2024 wjbeckett + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if matrix_element_call_container_labels_traefik_enabled %} +traefik.enable=true + +{% if matrix_element_call_container_labels_traefik_docker_network %} +traefik.docker.network={{ matrix_element_call_container_labels_traefik_docker_network }} +{% endif %} + +traefik.http.services.matrix-element-call.loadbalancer.server.port=8080 + +{% set middlewares = [] %} + +{% if matrix_element_call_container_labels_traefik_path_prefix != '/' %} +traefik.http.middlewares.matrix-element-call-slashless-redirect.redirectregex.regex=({{ matrix_element_call_container_labels_traefik_path_prefix | quote }})$ +traefik.http.middlewares.matrix-element-call-slashless-redirect.redirectregex.replacement=${1}/ +{% set middlewares = middlewares + ['matrix-element-call-slashless-redirect'] %} + +traefik.http.middlewares.matrix-element-call-strip-prefix.stripprefix.prefixes={{ matrix_element_call_container_labels_traefik_path_prefix }} +{% set middlewares = middlewares + ['matrix-element-call-strip-prefix'] %} +{% endif %} + +{% if matrix_element_call_container_labels_traefik_additional_response_headers.keys() | length > 0 %} +{% for name, value in matrix_element_call_container_labels_traefik_additional_response_headers.items() %} +traefik.http.middlewares.matrix-element-call-add-headers.headers.customresponseheaders.{{ name }}={{ value }} +{% endfor %} +{% set middlewares = middlewares + ['matrix-element-call-add-headers'] %} +{% endif %} + +traefik.http.routers.matrix-element-call.rule={{ matrix_element_call_container_labels_traefik_rule }} +{% if matrix_element_call_container_labels_traefik_priority | int > 0 %} +traefik.http.routers.matrix-element-call.priority={{ matrix_element_call_container_labels_traefik_priority }} +{% endif %} +traefik.http.routers.matrix-element-call.service=matrix-element-call +{% if middlewares | length > 0 %} +traefik.http.routers.matrix-element-call.middlewares={{ middlewares | join(',') }} +{% endif %} +traefik.http.routers.matrix-element-call.entrypoints={{ matrix_element_call_container_labels_traefik_entrypoints }} +traefik.http.routers.matrix-element-call.tls={{ matrix_element_call_container_labels_traefik_tls | to_json }} +{% if matrix_element_call_container_labels_traefik_tls %} +traefik.http.routers.matrix-element-call.tls.certResolver={{ matrix_element_call_container_labels_traefik_tls_certResolver }} +{% endif %} + +{% endif %} + +{{ matrix_element_call_container_labels_additional_labels }} \ No newline at end of file diff --git a/roles/custom/matrix-element-call/templates/systemd/matrix-element-call.service.j2 b/roles/custom/matrix-element-call/templates/systemd/matrix-element-call.service.j2 new file mode 100644 index 000000000..f9cc7cd2b --- /dev/null +++ b/roles/custom/matrix-element-call/templates/systemd/matrix-element-call.service.j2 @@ -0,0 +1,46 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Element Call +{% for service in matrix_element_call_systemd_required_services_list %} +Requires={{ service }} +After={{ 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 --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-element-call 2>/dev/null || true' +ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-element-call 2>/dev/null || true' + +ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} create \ + --rm \ + --name=matrix-element-call \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_element_call_container_network }} \ + --mount type=bind,src={{ matrix_element_call_base_path }}/config.json,dst=/app/config.json,ro \ + {% if matrix_element_call_container_http_host_bind_port %} + -p {{ matrix_element_call_container_http_host_bind_port }}:8080 \ + {% endif %} + --label-file={{ matrix_element_call_base_path }}/labels \ + {% for arg in matrix_element_call_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_element_call_container_image }} + +{% for network in matrix_element_call_container_additional_networks %} +ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} network connect {{ network }} matrix-element-call +{% endfor %} + +ExecStart={{ devture_systemd_docker_base_host_command_docker }} start --attach matrix-element-call + +ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-element-call 2>/dev/null || true' +ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-element-call 2>/dev/null || true' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-element-call + +[Install] +WantedBy=multi-user.target diff --git a/roles/custom/matrix-element-call/templates/systemd/matrix-element-call.service.j2.license b/roles/custom/matrix-element-call/templates/systemd/matrix-element-call.service.j2.license new file mode 100644 index 000000000..085b430a3 --- /dev/null +++ b/roles/custom/matrix-element-call/templates/systemd/matrix-element-call.service.j2.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: 2022 MDAD project contributors +SPDX-FileCopyrightText: 2024 wjbeckett +SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/roles/custom/matrix-element-call/vars/main.yml b/roles/custom/matrix-element-call/vars/main.yml new file mode 100644 index 000000000..b07bb4c89 --- /dev/null +++ b/roles/custom/matrix-element-call/vars/main.yml @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +matrix_element_call_public_url: "{{ matrix_element_call_scheme }}://{{ matrix_element_call_hostname }}" diff --git a/roles/custom/matrix-livekit-jwt-service/defaults/main.yml b/roles/custom/matrix-livekit-jwt-service/defaults/main.yml new file mode 100644 index 000000000..cdba27bf1 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/defaults/main.yml @@ -0,0 +1,96 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +# Project source code URL: https://github.com/element-hq/lk-jwt-service + +matrix_livekit_jwt_service_enabled: false + +matrix_livekit_jwt_service_scheme: https +matrix_livekit_jwt_service_hostname: "" +matrix_livekit_jwt_service_path_prefix: "/livekit-jwt-service" + +matrix_livekit_jwt_service_base_path: "{{ matrix_base_data_path }}/livekit-jwt-service" + +matrix_livekit_jwt_service_container_network: '' + +matrix_livekit_jwt_service_container_http_host_bind_port: '' + +matrix_livekit_jwt_service_container_additional_networks: "{{ (matrix_livekit_jwt_service_container_additional_networks_auto + matrix_livekit_jwt_service_container_additional_networks_custom) | unique }}" +matrix_livekit_jwt_service_container_additional_networks_auto: [] +matrix_livekit_jwt_service_container_additional_networks_custom: [] + +# renovate: datasource=docker depName=ghcr.io/element-hq/lk-jwt-service +matrix_livekit_jwt_service_version: 0.2.0 + +matrix_livekit_jwt_service_container_image_self_build: false +matrix_livekit_jwt_service_container_repo: "https://github.com/element-hq/lk-jwt-service.git" +matrix_livekit_jwt_service_container_repo_version: "{{ 'main' if matrix_livekit_jwt_service_version == 'latest' else ('v' + livekit_server_version) }}" +matrix_livekit_jwt_service_container_src_files_path: "{{ matrix_livekit_jwt_service_base_path }}/container-src" + +matrix_livekit_jwt_service_container_image: "{{ matrix_livekit_jwt_service_container_image_registry_prefix }}element-hq/lk-jwt-service:{{ matrix_livekit_jwt_service_container_image_tag }}" +matrix_livekit_jwt_service_container_image_registry_prefix: "{{ 'localhost/' if matrix_livekit_jwt_service_container_image_self_build else matrix_livekit_jwt_service_container_image_registry_prefix_upstream }}" +matrix_livekit_jwt_service_container_image_registry_prefix_upstream: "{{ matrix_livekit_jwt_service_container_image_registry_prefix_upstream_default }}" +matrix_livekit_jwt_service_container_image_registry_prefix_upstream_default: ghcr.io/ +matrix_livekit_jwt_service_container_image_tag: "{{ matrix_livekit_jwt_service_version }}" +matrix_livekit_jwt_service_container_image_force_pull: "{{ matrix_livekit_jwt_service_container_image.endswith(':latest') }}" + +matrix_livekit_jwt_service_container_labels_traefik_enabled: true +matrix_livekit_jwt_service_container_labels_traefik_docker_network: "{{ matrix_livekit_jwt_service_container_network }}" +matrix_livekit_jwt_service_container_labels_traefik_hostname: "{{ matrix_livekit_jwt_service_hostname }}" +# The path prefix must either be `/` or not end with a slash (e.g. `/livekit-jwt-service`). +matrix_livekit_jwt_service_container_labels_traefik_path_prefix: "{{ matrix_livekit_jwt_service_path_prefix }}" +matrix_livekit_jwt_service_container_labels_traefik_rule: "Host(`{{ matrix_livekit_jwt_service_container_labels_traefik_hostname }}`){% if matrix_livekit_jwt_service_container_labels_traefik_path_prefix != '/' %} && PathPrefix(`{{ matrix_livekit_jwt_service_container_labels_traefik_path_prefix }}`){% endif %}" +matrix_livekit_jwt_service_container_labels_traefik_priority: 0 +matrix_livekit_jwt_service_container_labels_traefik_entrypoints: web-secure +matrix_livekit_jwt_service_container_labels_traefik_tls: "{{ matrix_livekit_jwt_service_container_labels_traefik_entrypoints != 'web' }}" +matrix_livekit_jwt_service_container_labels_traefik_tls_certResolver: default # noqa var-naming + +# Controls which additional headers to attach to all HTTP responses. +# To add your own headers, use `matrix_livekit_jwt_service_container_labels_traefik_additional_response_headers_custom` +matrix_livekit_jwt_service_container_labels_traefik_additional_response_headers: "{{ matrix_livekit_jwt_service_container_labels_traefik_additional_response_headers_auto | combine(matrix_livekit_jwt_service_container_labels_traefik_additional_response_headers_custom) }}" +matrix_livekit_jwt_service_container_labels_traefik_additional_response_headers_auto: {} +matrix_livekit_jwt_service_container_labels_traefik_additional_response_headers_custom: {} + +# matrix_livekit_jwt_service_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_livekit_jwt_service_container_labels_additional_labels: | +# my.label=1 +# another.label="here" +matrix_livekit_jwt_service_container_labels_additional_labels: '' + +# A list of extra arguments to pass to the container +matrix_livekit_jwt_service_container_extra_arguments: [] + +# Controls the LK_JWT_PORT environment variable +matrix_livekit_jwt_service_environment_variable_livekit_jwt_port: 8080 + +# Controls the LIVEKIT_KEY environment variable +matrix_livekit_jwt_service_environment_variable_livekit_key: "" + +# Controls the LIVEKIT_URL environment variable +matrix_livekit_jwt_service_environment_variable_livekit_url: "" + +# Controls the LIVEKIT_SECRET environment variable +matrix_livekit_jwt_service_environment_variable_livekit_secret: "" + +# Additional environment variables to pass to the container. +# +# Environment variables take priority over settings in the configuration file. +# +# Example: +# matrix_livekit_jwt_service_environment_variables_extension: | +# KEY=value +matrix_livekit_jwt_service_environment_variables_extension: '' + +# List of systemd services that LiveKit JWT Service service depends on +matrix_livekit_jwt_service_systemd_required_services_list: "{{ matrix_livekit_jwt_service_systemd_required_services_list_default + matrix_livekit_jwt_service_systemd_required_services_list_auto + matrix_livekit_jwt_service_systemd_required_services_list_custom }}" +matrix_livekit_jwt_service_systemd_required_services_list_default: "{{ [devture_systemd_docker_base_docker_service_name] if devture_systemd_docker_base_docker_service_name else [] }}" +matrix_livekit_jwt_service_systemd_required_services_list_auto: [] +matrix_livekit_jwt_service_systemd_required_services_list_custom: [] diff --git a/roles/custom/matrix-livekit-jwt-service/tasks/install.yml b/roles/custom/matrix-livekit-jwt-service/tasks/install.yml new file mode 100644 index 000000000..9193d6679 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/tasks/install.yml @@ -0,0 +1,76 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Ensure LiveKit JWT Service paths exist + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - path: "{{ matrix_livekit_jwt_service_base_path }}" + +- name: Ensure LiveKit JWT Service support files installed + ansible.builtin.template: + src: "{{ role_path }}/templates/{{ item }}.j2" + dest: "{{ matrix_livekit_jwt_service_base_path }}/{{ item }}" + mode: 0640 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - env + - labels + +- name: Ensure LiveKit JWT Service container image is pulled + community.docker.docker_image: + name: "{{ matrix_livekit_jwt_service_container_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_livekit_jwt_service_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_livekit_jwt_service_container_image_force_pull }}" + when: "not matrix_livekit_jwt_service_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_livekit_jwt_service_container_image_self_build | bool" + block: + - name: Ensure LiveKit JWT Service repository is present on self-build + ansible.builtin.git: + repo: "{{ matrix_livekit_jwt_service_container_repo }}" + version: "{{ matrix_livekit_jwt_service_container_repo_version }}" + dest: "{{ matrix_livekit_jwt_service_container_src_files_path }}" + force: "yes" + become: true + become_user: "{{ matrix_user_username }}" + register: matrix_livekit_jwt_service_git_pull_results + + - name: Ensure LiveKit JWT Service container image is built + community.docker.docker_image: + name: "{{ matrix_livekit_jwt_service_container_image }}" + source: build + force_source: "{{ matrix_livekit_jwt_service_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_livekit_jwt_service_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_livekit_jwt_service_container_src_files_path }}" + pull: true + +- name: Ensure LiveKit JWT Service container network is created + community.general.docker_network: + enable_ipv6: "{{ devture_systemd_docker_base_ipv6_enabled }}" + name: "{{ matrix_livekit_jwt_service_container_network }}" + driver: bridge + driver_options: "{{ devture_systemd_docker_base_container_networks_driver_options }}" + +- name: Ensure LiveKit JWT Service systemd service is installed + ansible.builtin.template: + src: "{{ role_path }}/templates/systemd/matrix-livekit-jwt-service.service.j2" + dest: "{{ devture_systemd_docker_base_systemd_path }}/matrix-livekit-jwt-service.service" + mode: 0644 diff --git a/roles/custom/matrix-livekit-jwt-service/tasks/main.yml b/roles/custom/matrix-livekit-jwt-service/tasks/main.yml new file mode 100644 index 000000000..29b49dde6 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/tasks/main.yml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- tags: + - setup-all + - setup-jwt-service + - install-all + - install-livekit-jwt-service + block: + - when: matrix_livekit_jwt_service_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/validate_config.yml" + + - when: matrix_livekit_jwt_service_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/install.yml" + +- tags: + - setup-all + - setup-livekit-jwt-service + block: + - when: not matrix_livekit_jwt_service_enabled | bool + ansible.builtin.include_tasks: "{{ role_path }}/tasks/uninstall.yml" diff --git a/roles/custom/matrix-livekit-jwt-service/tasks/uninstall.yml b/roles/custom/matrix-livekit-jwt-service/tasks/uninstall.yml new file mode 100644 index 000000000..d33c35760 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/tasks/uninstall.yml @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Check existence of LiveKit JWT Service systemd service + ansible.builtin.stat: + path: "{{ devture_systemd_docker_base_systemd_path }}/matrix-livekit-jwt-service.service" + register: matrix_livekit_jwt_service_service_stat + +- when: matrix_livekit_jwt_service_service_stat.stat.exists | bool + block: + - name: Ensure LiveKit JWT Service systemd service is stopped + ansible.builtin.service: + name: matrix-livekit-jwt-service + state: stopped + enabled: false + daemon_reload: true + + - name: Ensure LiveKit JWT Service systemd service doesn't exist + ansible.builtin.file: + path: "{{ devture_systemd_docker_base_systemd_path }}/matrix-livekit-jwt-service.service" + state: absent + + - name: Ensure LiveKit JWT Service paths don't exist + ansible.builtin.file: + path: "{{ matrix_livekit_jwt_service_base_path }}" + state: absent diff --git a/roles/custom/matrix-livekit-jwt-service/tasks/validate_config.yml b/roles/custom/matrix-livekit-jwt-service/tasks/validate_config.yml new file mode 100644 index 000000000..f731898f2 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/tasks/validate_config.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2022 MDAD project contributors +# SPDX-FileCopyrightText: 2024 wjbeckett +# SPDX-FileCopyrightText: 2024 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +- name: Fail if required LiveKit JWT Service settings are not defined + ansible.builtin.fail: + msg: > + You need to define a required configuration setting (`{{ item.name }}`). + when: "item.when | bool and vars[item.name] | length == 0" + with_items: + - {'name': 'matrix_livekit_jwt_service_hostname', when: true} + - {'name': 'matrix_livekit_jwt_service_container_network', when: true} + - {'name': 'matrix_livekit_jwt_service_environment_variable_livekit_key', when: true} + - {'name': 'matrix_livekit_jwt_service_environment_variable_livekit_url', when: true} + - {'name': 'matrix_livekit_jwt_service_environment_variable_livekit_secret', when: true} diff --git a/roles/custom/matrix-livekit-jwt-service/templates/env.j2 b/roles/custom/matrix-livekit-jwt-service/templates/env.j2 new file mode 100644 index 000000000..c32da08ef --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/templates/env.j2 @@ -0,0 +1,14 @@ +{# +SPDX-FileCopyrightText: 2024 wjbeckett +SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +LIVEKIT_JWT_PORT={{ matrix_livekit_jwt_service_environment_variable_livekit_jwt_port | int | to_json }} + +LIVEKIT_KEY={{ matrix_livekit_jwt_service_environment_variable_livekit_key }} +LIVEKIT_URL={{ matrix_livekit_jwt_service_environment_variable_livekit_url }} +LIVEKIT_SECRET={{ matrix_livekit_jwt_service_environment_variable_livekit_secret }} + +{{ matrix_livekit_jwt_service_environment_variables_extension }} diff --git a/roles/custom/matrix-livekit-jwt-service/templates/labels.j2 b/roles/custom/matrix-livekit-jwt-service/templates/labels.j2 new file mode 100644 index 000000000..c372cbb78 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/templates/labels.j2 @@ -0,0 +1,55 @@ +{# +SPDX-FileCopyrightText: 2024 wjbeckett +SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if matrix_livekit_jwt_service_container_labels_traefik_enabled %} +traefik.enable=true + +traefik.docker.network={{ matrix_livekit_jwt_service_container_labels_traefik_docker_network }} + +traefik.http.services.matrix-livekit-jwt-service.loadbalancer.server.port={{ matrix_livekit_jwt_service_environment_variable_livekit_jwt_port }} + +{% set middlewares = [] %} + +{% if matrix_livekit_jwt_service_container_labels_traefik_path_prefix != '/' %} +traefik.http.middlewares.matrix-livekit-jwt-service-slashless-redirect.redirectregex.regex=({{ matrix_livekit_jwt_service_container_labels_traefik_path_prefix | quote }})$ +traefik.http.middlewares.matrix-livekit-jwt-service-slashless-redirect.redirectregex.replacement=${1}/ +{% set middlewares = middlewares + ['matrix-livekit-jwt-service-slashless-redirect'] %} + +traefik.http.middlewares.matrix-livekit-jwt-service-strip-prefix.stripprefix.prefixes={{ matrix_livekit_jwt_service_container_labels_traefik_path_prefix }} +{% set middlewares = middlewares + ['matrix-livekit-jwt-service-strip-prefix'] %} +{% endif %} + +{% if matrix_livekit_jwt_service_container_labels_traefik_additional_response_headers.keys() | length > 0 %} +{% for name, value in matrix_livekit_jwt_service_container_labels_traefik_additional_response_headers.items() %} +traefik.http.middlewares.matrix-livekit-jwt-service-add-headers.headers.customresponseheaders.{{ name }}={{ value }} +{% endfor %} +{% set middlewares = middlewares + ['matrix-livekit-jwt-service-add-headers'] %} +{% endif %} + +traefik.http.routers.matrix-livekit-jwt-service.rule={{ matrix_livekit_jwt_service_container_labels_traefik_rule }} + +{% if matrix_livekit_jwt_service_container_labels_traefik_priority | int > 0 %} +traefik.http.routers.matrix-livekit-jwt-service.priority={{ matrix_livekit_jwt_service_container_labels_traefik_priority }} +{% endif %} + +traefik.http.routers.matrix-livekit-jwt-service.service=matrix-livekit-jwt-service + +{% if middlewares | length > 0 %} +traefik.http.routers.matrix-livekit-jwt-service.middlewares={{ middlewares | join(',') }} +{% endif %} + +traefik.http.routers.matrix-livekit-jwt-service.entrypoints={{ matrix_livekit_jwt_service_container_labels_traefik_entrypoints }} + +traefik.http.routers.matrix-livekit-jwt-service.tls={{ matrix_livekit_jwt_service_container_labels_traefik_tls | to_json }} + +{% if matrix_livekit_jwt_service_container_labels_traefik_tls %} +traefik.http.routers.matrix-livekit-jwt-service.tls.certResolver={{ matrix_livekit_jwt_service_container_labels_traefik_tls_certResolver }} +{% endif %} + +{% endif %} + +{{ matrix_livekit_jwt_service_container_labels_additional_labels }} diff --git a/roles/custom/matrix-livekit-jwt-service/templates/systemd/matrix-livekit-jwt-service.service.j2 b/roles/custom/matrix-livekit-jwt-service/templates/systemd/matrix-livekit-jwt-service.service.j2 new file mode 100644 index 000000000..073d27e36 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/templates/systemd/matrix-livekit-jwt-service.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix LiveKit JWT Service +{% for service in matrix_livekit_jwt_service_systemd_required_services_list %} +After={{ service }} +Requires={{ service }} +{% endfor %} + +[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 --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-livekit-jwt-service 2>/dev/null || true' +ExecStartPre=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-livekit-jwt-service 2>/dev/null || true' + +ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} create \ + --rm \ + --name=matrix-livekit-jwt-service \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_livekit_jwt_service_container_network }} \ + {% if matrix_livekit_jwt_service_container_http_host_bind_port %} + -p {{ matrix_livekit_jwt_service_container_http_host_bind_port }}:{{ matrix_livekit_jwt_service_environment_variable_livekit_jwt_port }} \ + {% endif %} + --env-file={{ matrix_livekit_jwt_service_base_path }}/env \ + --label-file={{ matrix_livekit_jwt_service_base_path }}/labels \ + {{ matrix_livekit_jwt_service_container_image }} + +{% for network in matrix_livekit_jwt_service_container_additional_networks %} +ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} network connect {{ network }} matrix-livekit-jwt-service +{% endfor %} + +ExecStart={{ devture_systemd_docker_base_host_command_docker }} start --attach matrix-livekit-jwt-service + +ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} stop --time={{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-livekit-jwt-service 2>/dev/null || true' +ExecStop=-{{ devture_systemd_docker_base_host_command_sh }} -c '{{ devture_systemd_docker_base_host_command_docker }} rm matrix-jwt-service 2>/dev/null || true' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-livekit-jwt-service + +[Install] +WantedBy=multi-user.target diff --git a/roles/custom/matrix-livekit-jwt-service/templates/systemd/matrix-livekit-jwt-service.service.j2.license b/roles/custom/matrix-livekit-jwt-service/templates/systemd/matrix-livekit-jwt-service.service.j2.license new file mode 100644 index 000000000..085b430a3 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/templates/systemd/matrix-livekit-jwt-service.service.j2.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: 2022 MDAD project contributors +SPDX-FileCopyrightText: 2024 wjbeckett +SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/roles/custom/matrix-livekit-jwt-service/vars/main.yml b/roles/custom/matrix-livekit-jwt-service/vars/main.yml new file mode 100644 index 000000000..a6070f646 --- /dev/null +++ b/roles/custom/matrix-livekit-jwt-service/vars/main.yml @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2024 Slavi Pantaleev +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +--- + +matrix_livekit_jwt_service_public_url: "{{ matrix_livekit_jwt_service_scheme }}://{{ matrix_livekit_jwt_service_hostname }}{{ matrix_livekit_jwt_service_path_prefix }}" diff --git a/roles/custom/matrix-static-files/defaults/main.yml b/roles/custom/matrix-static-files/defaults/main.yml index 2f51588a6..77340c7b9 100644 --- a/roles/custom/matrix-static-files/defaults/main.yml +++ b/roles/custom/matrix-static-files/defaults/main.yml @@ -20,6 +20,7 @@ matrix_static_files_config_path: "{{ matrix_static_files_base_path }}/config" matrix_static_files_public_path: "{{ matrix_static_files_base_path }}/public" matrix_static_files_public_well_known_path: "{{ matrix_static_files_public_path }}/.well-known" matrix_static_files_public_well_known_matrix_path: "{{ matrix_static_files_public_well_known_path }}/matrix" +matrix_static_files_public_well_known_element_path: "{{ matrix_static_files_public_well_known_path }}/element" # List of systemd services that matrix-static-files.service depends on matrix_static_files_systemd_required_services_list: "{{ [devture_systemd_docker_base_docker_service_name] if devture_systemd_docker_base_docker_service_name else [] }}" @@ -211,6 +212,16 @@ matrix_static_files_file_matrix_client_property_cc_etke_synapse_admin: "{{ matri matrix_static_files_file_matrix_client_property_cc_etke_synapse_admin_auto: {} matrix_static_files_file_matrix_client_property_cc_etke_synapse_admin_custom: {} +# Controls whether `org.matrix.msc4143.rtc_foci`-related entries should be added to the client well-known. +# By default, if there are entries in `matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci`, we show them (by enabling this). +matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_enabled: "{{ matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci | default({}) | dict2items | length > 0 }}" + +# Controls the org.matrix.msc4143.rtc_foci property in the /.well-known/matrix/client file. +# See `matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_enabled` +matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci: "{{ matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_auto | combine(matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_custom, recursive=True) }}" +matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_auto: {} +matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_custom: {} + # Default /.well-known/matrix/client configuration template which covers the generic use case. # You can customize it by controlling the various variables inside it. # @@ -358,6 +369,56 @@ matrix_static_files_file_matrix_support_configuration: "{{ matrix_static_files_f ######################################################################## +######################################################################## +# # +# Related to /.well-known/element/element.json # +# # +######################################################################## + +# Controls whether a `/.well-known/element/element.json` file is generated and used at all. +matrix_static_files_file_element_element_json_enabled: true + +# Controls the call.widget_url property in the /.well-known/element/element.json file +matrix_static_files_file_element_element_json_property_call_widget_url: '' + +# Default /.well-known/element/element.json 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_static_files_file_matrix_support_configuration_extension_json`) +# or completely replace this variable with your own template. +matrix_static_files_file_element_element_json_configuration_json: "{{ lookup('template', 'templates/public/.well-known/element/element.json.j2') }}" + +# Your custom JSON configuration for /.well-known/element/element.json should go to `matrix_static_files_file_element_element_json_configuration_extension_json`. +# This configuration extends the default starting configuration (`matrix_static_files_file_matrix_support_configuration_extension_json`). +# +# 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_static_files_file_matrix_support_configuration_json`. +# +# Example configuration extension follows: +# +# matrix_static_files_file_element_element_json_configuration_extension_json: | +# { +# "call": { +# "url": "value" +# } +# } +matrix_static_files_file_element_element_json_configuration_extension_json: '{}' + +matrix_static_files_file_element_element_json_configuration_extension: "{{ matrix_static_files_file_element_element_json_configuration_extension_json | from_json if matrix_static_files_file_element_element_json_configuration_extension_json | from_json is mapping else {} }}" + +# Holds the final /.well-known/matrix/support configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_static_files_file_element_element_json_configuration_json` or `matrix_static_files_file_element_element_json_configuration_extension_json`. +matrix_static_files_file_element_element_json_configuration: "{{ matrix_static_files_file_element_element_json_configuration_json | combine(matrix_static_files_file_element_element_json_configuration_extension, recursive=True) }}" + +######################################################################## +# # +# /Related to /.well-known/element/element.json # +# # +######################################################################## + + ######################################################################## # # # Related to index.html # diff --git a/roles/custom/matrix-static-files/tasks/install.yml b/roles/custom/matrix-static-files/tasks/install.yml index f23592644..5b8609011 100644 --- a/roles/custom/matrix-static-files/tasks/install.yml +++ b/roles/custom/matrix-static-files/tasks/install.yml @@ -7,17 +7,19 @@ - name: Ensure matrix-static-files paths exist ansible.builtin.file: - path: "{{ item }}" + path: "{{ item.path }}" state: directory mode: 0750 owner: "{{ matrix_user_username }}" group: "{{ matrix_user_groupname }}" with_items: - - "{{ matrix_static_files_base_path }}" - - "{{ matrix_static_files_config_path }}" - - "{{ matrix_static_files_public_path }}" - - "{{ matrix_static_files_public_well_known_path }}" - - "{{ matrix_static_files_public_well_known_matrix_path }}" + - {path: "{{ matrix_static_files_base_path }}", when: true} + - {path: "{{ matrix_static_files_config_path }}", when: true} + - {path: "{{ matrix_static_files_public_path }}", when: true} + - {path: "{{ matrix_static_files_public_well_known_path }}", when: true} + - {path: "{{ matrix_static_files_public_well_known_matrix_path }}", when: true} + - {path: "{{ matrix_static_files_public_well_known_element_path }}", when: true} + when: "item.when | bool" - name: Ensure matrix-static-files is configured ansible.builtin.template: @@ -57,6 +59,10 @@ dest: "{{ matrix_static_files_public_well_known_matrix_path }}/support" when: "{{ matrix_static_files_file_matrix_support_enabled }}" + - content: "{{ matrix_static_files_file_element_element_json_configuration | to_nice_json }}" + dest: "{{ matrix_static_files_public_well_known_element_path }}/element.json" + when: "{{ matrix_static_files_file_element_element_json_enabled }}" + # This one will not be deleted if `matrix_static_files_file_index_html_enabled` flips to `false`. # See the comment for `matrix_static_files_file_index_html_enabled` to learn why. - content: "{{ matrix_static_files_file_index_html_template }}" @@ -75,6 +81,12 @@ state: absent when: "not matrix_static_files_file_matrix_support_enabled | bool" +- name: Ensure /.well-known/element/element.json file deleted if not enabled + ansible.builtin.file: + path: "{{ matrix_static_files_public_well_known_element_path }}/element.json" + state: absent + when: "not matrix_static_files_file_element_element_json_enabled | bool" + - name: Ensure matrix-static-files container image is pulled community.docker.docker_image: name: "{{ matrix_static_files_container_image }}" diff --git a/roles/custom/matrix-static-files/templates/public/.well-known/element/element.json.j2 b/roles/custom/matrix-static-files/templates/public/.well-known/element/element.json.j2 new file mode 100644 index 000000000..68d13f098 --- /dev/null +++ b/roles/custom/matrix-static-files/templates/public/.well-known/element/element.json.j2 @@ -0,0 +1,7 @@ +{ + {% if matrix_static_files_file_element_element_json_property_call_widget_url %} + "call": { + "widget_url": {{ matrix_static_files_file_element_element_json_property_call_widget_url | to_json }} + } + {% endif %} +} diff --git a/roles/custom/matrix-static-files/templates/public/.well-known/element/element.json.j2.license b/roles/custom/matrix-static-files/templates/public/.well-known/element/element.json.j2.license new file mode 100644 index 000000000..3d67f3cc5 --- /dev/null +++ b/roles/custom/matrix-static-files/templates/public/.well-known/element/element.json.j2.license @@ -0,0 +1,4 @@ +SPDX-FileCopyrightText: 2024 wjbeckett +SPDX-FileCopyrightText: 2024 Slavi Pantaleev + +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/roles/custom/matrix-static-files/templates/public/.well-known/matrix/client.j2 b/roles/custom/matrix-static-files/templates/public/.well-known/matrix/client.j2 index 86882893a..600e8f4c7 100644 --- a/roles/custom/matrix-static-files/templates/public/.well-known/matrix/client.j2 +++ b/roles/custom/matrix-static-files/templates/public/.well-known/matrix/client.j2 @@ -57,4 +57,7 @@ {% if matrix_static_files_file_matrix_client_property_cc_etke_synapse_admin_enabled %}, "cc.etke.synapse-admin": {{ matrix_static_files_file_matrix_client_property_cc_etke_synapse_admin | to_json }} {% endif %} + {% if matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci_enabled %}, + "org.matrix.msc4143.rtc_foci": {{ matrix_static_files_file_matrix_client_property_org_matrix_msc4143_rtc_foci | to_json }} + {% endif %} } diff --git a/setup.yml b/setup.yml index 7149b3cdf..b5aa83412 100644 --- a/setup.yml +++ b/setup.yml @@ -133,6 +133,10 @@ - custom/matrix-media-repo - custom/matrix-pantalaimon + - custom/matrix-element-call + - galaxy/livekit_server + - custom/matrix-livekit-jwt-service + - role: galaxy/postgres_backup - role: galaxy/backup_borg