diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..8fd1c8d2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1820 @@ +# 2021-05-21 + +## Hydrogen support + +Thanks to [Aaron Raimist](https://github.com/aaronraimist), the playbook now supports [Hydrogen](https://github.com/vector-im/hydrogen-web) - a new lightweight matrix client with legacy and mobile browser support. + +By default, we still install Element, as Hydrogen is still not fully-featured. Still, people who'd like to try Hydrogen out can now install it via the playbook. + +Additional details are available in [Setting up Hydrogen](docs/configuring-playbook-client-hydrogen.md). + + +# 2021-05-19 + +## Heisenbridge support + +Thanks to [Toni Spets (hifi)](https://github.com/hifi), the playbook now supports bridging to [IRC](https://en.wikipedia.org/wiki/Internet_Relay_Chat) using yet another bridge (besides matrix-appservice-irc), called [Heisenbridge](https://github.com/hifi/heisenbridge). + +Additional details are available in [Setting up Heisenbridge bouncer-style IRC bridging](docs/configuring-playbook-bridge-heisenbridge.md). + + +# 2021-04-16 + +## Disabling TLSv1 and TLSv1.1 for Coturn + +To improve security, we've [removed TLSv1 and TLSv1.1 support](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/999) from our default [Coturn](https://github.com/coturn/coturn) configuration. + +If you need to support old clients, you can re-enable both (or whichever one you need) with the following configuration: + +```yaml +matrix_coturn_tls_v1_enabled: true +matrix_coturn_tls_v1_1_enabled: true +``` + + +# 2021-04-05 + +## Automated local Postgres backup support + +Thanks to [foxcris](https://github.com/foxcris), the playbook can now make automated local Postgres backups on a fixed schedule using [docker-postgres-backup-local](https://github.com/prodrigestivill/docker-postgres-backup-local). + +Additional details are available in [Setting up postgres backup](docs/configuring-playbook-postgres-backup.md). + + + +# 2021-04-03 + +## Mjolnir moderation tool (bot) support + +Thanks to [Aaron Raimist](https://github.com/aaronraimist), the playbook can now install and configure the [Mjolnir](https://github.com/matrix-org/mjolnir) moderation tool (bot). + +Additional details are available in [Setting up Mjolnir](docs/configuring-playbook-bot-mjolnir.md). + + +# 2021-03-20 + +## Sygnal push gateway support + +The playbook can now install the [Sygnal](https://github.com/matrix-org/sygnal) push gateway for you. + +This is only useful to people who develop/build their own Matrix client applications. + +Additional details are available in our [Setting up Sygnal](docs/configuring-playbook-sygnal.md) docs. + + +# 2021-03-16 + +## Go-NEB support + +Thanks to [Zir0h](https://github.com/Zir0h), the playbook can now install and configure the [Go-NEB](https://github.com/matrix-org/go-neb) bot. + +Additional details are available in [Setting up Go-NEB](docs/configuring-playbook-bot-go-neb.md). + + +# 2021-02-19 + +## GroupMe bridging support via mx-puppet-groupme + +Thanks to [Cody Neiman](https://github.com/xangelix), the playbook can now install the [mx-puppet-groupme](https://gitlab.com/robintown/mx-puppet-groupme) bridge for bridging to [GroupMe](https://groupme.com). + +This brings the total number of bridges supported by the playbook up to 18. See all supported bridges [here](docs/configuring-playbook.md#bridging-other-networks). + +To get started, follow our [Setting up MX Puppet GroupMe](docs/configuring-playbook-bridge-mx-puppet-groupme.md) docs. + +## Mautrix Instagram bridging support + +The playbook now supports bridging with [Instagram](https://www.instagram.com/) by installing the [mautrix-instagram](https://github.com/tulir/mautrix-instagram) bridge. This playbook functionality is available thanks to [@MarcProe](https://github.com/MarcProe). + +Additional details are available in [Setting up Mautrix Instagram bridging](docs/configuring-playbook-bridge-mautrix-instagram.md). + +## Synapse workers support + +After [lots and lots of work](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/456) (done over many months by [Marcel Partap](https://github.com/eMPee584), [Max Klenk](https://github.com/maxklenk), a few others from the [Technical University of Dresden, Germany](https://tu-dresden.de/) and various other contributors), support for Synapse workers has finally landed. + +Having support for workers makes the playbook suitable for larger homeserver deployments. + +Our setup is not yet perfect (we don't support all types of workers; scaling some of them (like `pusher`, `federation_sender`) beyond a single instance is not yet supported). Still, it's a great start and can already power homeservers with thousands of users, like the [Matrix deployment at TU Dresden](https://doc.matrix.tu-dresden.de/en/) discussed in [Matrix Live S06E09 - TU Dresden on their Matrix deployment](https://www.youtube.com/watch?v=UHJX2pmT2gk). + +By default, workers are disabled and Synapse runs as a single process (homeservers don't necessarily need the complexity and increased memory requirements of running a worker-based setup). + +To enable Synapse workers, follow our [Load balancing with workers](docs/configuring-playbook-synapse.md#load-balancing-with-workers) documentation. + + +# 2021-02-12 + +## (Potential Breaking Change) Monitoring/metrics support using Prometheus and Grafana + +Thanks to [@Peetz0r](https://github.com/Peetz0r), the playbook can now install a bunch of tools for monitoring your Matrix server: the [Prometheus](https://prometheus.io) time-series database server, the Prometheus [node-exporter](https://prometheus.io/docs/guides/node-exporter/) host metrics exporter, and the [Grafana](https://grafana.com/) web UI. + +To get get these installed, follow our [Enabling metrics and graphs (Prometheus, Grafana) for your Matrix server](docs/configuring-playbook-prometheus-grafana.md) docs page. + +This update comes with a **potential breaking change** for people who were already exposing Synapse metrics (for consumption via another Prometheus installation). From now on, `matrix_synapse_metrics_enabled: true` no longer exposes metrics publicly via matrix-nginx-proxy (at `https://matrix.DOMAIN/_synapse/metrics`). To do so, you'd need to explicitly set `matrix_nginx_proxy_proxy_synapse_metrics: true`. + + +# 2021-01-31 + +## Etherpad support + +Thanks to [@pushytoxin](https://github.com/pushytoxin), the playbook can now install the [Etherpad](https://etherpad.org) realtime collaborative text editor. It can be used in a [Jitsi](https://jitsi.org/) audio/video call or integrated as a widget into Matrix chat rooms via the [Dimension](https://dimension.t2bot.io) integration manager. + +To get it installed, follow [our Etherpad docs page](docs/configuring-playbook-etherpad.md). + + +# 2021-01-22 + +## (Breaking Change) Postgres changes that require manual intervention + +We've made a lot of changes to our Postgres setup and some manual action is required (described below). Sorry about the hassle. + +**TLDR**: people running an [external Postgres server](docs/configuring-playbook-external-postgres.md) don't need to change anything for now. Everyone else (the common/default case) is affected and manual intervention is required. + +### Why? + +- we had a default Postgres password (`matrix_postgres_connection_password: synapse-password`), which we think is **not ideal for security anymore**. We now ask you to generate/provide a strong password yourself. Postgres is normally not exposed outside the container network, making it relatively secure, but still: + - by tweaking the configuration, you may end up intentionally or unintentionally exposing your Postgres server to the local network (or even publicly), while still using the default default credentials (`synapse` + `synapse-password`) + - we can't be sure we trust all these services (bridges, etc). Some of them may try to talk to or attack `matrix-postgres` using the default credentials (`synapse` + `synapse-password`) + - you may have other containers running on the same Docker network, which may try to talk to or attack `matrix-postgres` using the default credentials (`synapse` + `synapse-password`) +- our Postgres usage **was overly-focused on Synapse** (default username of `synapse` and default/main database of `homeserver`). Additional homeserver options are likely coming in the future ([Dendrite](https://matrix.org/docs/projects/server/dendrite), [Conduit](https://matrix.org/docs/projects/server/conduit), [The Construct](https://matrix.org/docs/projects/server/construct)), so being too focused on `matrix-synapse` is not great. From now on, Synapse is just another component of this playbook, which happens to have an *additional database* (called `synapse`) on the Postgres server. +- we try to reorganize things a bit, to make the playbook even friendlier to people running an [external Postgres server](docs/configuring-playbook-external-postgres.md). Work on this will proceed in the future. + +So, this is some **effort to improve security** and to **prepare for a brighter future of having more homeserver options** than just Synapse. + +### What has really changed? + +- the default superuser Postgres username is now `matrix` (used to be `synapse`) +- the default Postgres database is now `matrix` (used to be `homeserver`) +- Synapse's database is now `synapse` (used to be `homeserver`). This is now just another "additional database" that the playbook manages for you +- Synapse's user called `synapse` is just a regular user that can only use the `synapse` database (not a superuser anymore) + +### What do I do if I'm using the integrated Postgres server (default)? + +By default, the playbook runs an integrated Postgres server for you in a container (`matrix-postgres`). Unless you've explicitly configured an [external Postgres server](docs/configuring-playbook-external-postgres.md), these steps are meant for you. + +To migrate to the new setup, expect a few minutes of downtime, while you follow these steps: + +1. We believe the steps below are safe and you won't encounter any data loss, but consider [making a Postgres backup](docs/maintenance-postgres.md#backing-up-postgresql) anyway. If you've never backed up Postgres, now would be a good time to try it. + +2. Generate a strong password to be used for your superuser Postgres user (called `matrix`). You can use `pwgen -s 64 1` to generate it, or some other tool. The **maximum length** for a Postgres password is 100 bytes (characters). Don't go crazy! + +3. Update your playbook's `inventory/host_vars/matrix.DOMAIN/vars.yml` file, adding a line like this: +```yaml +matrix_postgres_connection_password: 'YOUR_POSTGRES_PASSWORD_HERE' +``` + +.. where `YOUR_POSTGRES_PASSWORD_HERE` is to be replaced with the password you generated during step #2. + +4. Stop all services: `ansible-playbook -i inventory/hosts setup.yml --tags=stop` +5. Log in to the server via SSH. The next commands will be performed there. +6. Start the Postgres database server: `systemctl start matrix-postgres` +7. Open a Postgres shell: `/usr/local/bin/matrix-postgres-cli` +8. Execute the following query, while making sure to **change the password inside** (**don't forget the ending `;`**): + +```sql +CREATE ROLE matrix LOGIN SUPERUSER PASSWORD 'YOUR_POSTGRES_PASSWORD_HERE'; +``` + +.. where `YOUR_POSTGRES_PASSWORD_HERE` is to be replaced with the password you generated during step #2. + +9. Execute the following queries as you see them (no modifications necessary, so you can just **paste them all at once**): + +```sql +CREATE DATABASE matrix OWNER matrix; + +ALTER DATABASE postgres OWNER TO matrix; +ALTER DATABASE template0 OWNER TO matrix; +ALTER DATABASE template1 OWNER TO matrix; + +\c matrix; + +ALTER DATABASE homeserver RENAME TO synapse; + +ALTER ROLE synapse NOSUPERUSER NOCREATEDB NOCREATEROLE; + +\quit +``` + +You may need to press *Enter* after pasting the lines above. + +10. Re-run the playbook normally: `ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start` + +### What do I do if I'm using an external Postgres server? + +If you've explicitly configured an [external Postgres server](docs/configuring-playbook-external-postgres.md), there are **no changes** that you need to do at this time. + +The fact that we've renamed Synapse's database from `homeserver` to `synapse` (in our defaults) should not affect you, as you're already explicitly defining `matrix_synapse_database_database` (if you've followed our guide, that is). If you're not explicitly defining this variable, you may wish to do so (`matrix_synapse_database_database: homeserver`), to avoid the new `synapse` default and keep things as they were. + + +# 2021-01-20 + +## (Breaking Change) The mautrix-facebook bridge now requires a Postgres database + +A new version of the [mautrix-facebook](https://github.com/tulir/mautrix-facebook) bridge has been released. It's a full rewrite of its backend and the bridge now requires Postgres. New versions of the bridge can no longer run on SQLite. + +**TLDR**: if you're NOT using an [external Postgres server](docs/configuring-playbook-external-postgres.md) and have NOT forcefully kept the bridge on SQLite during [The big move to all-on-Postgres (potentially dangerous)](#the-big-move-to-all-on-postgres-potentially-dangerous), you will be automatically upgraded without manual intervention. All you need to do is send a `login` message to the Facebook bridge bot again. + +Whether this change requires your intervention depends mostly on: +- whether you're using an [external Postgres server](docs/configuring-playbook-external-postgres.md). If yes, then [you need to do something](#upgrade-path-for-people-running-an-external-postgres-server). +- or whether you've force-changed the bridge's database engine to SQLite (`matrix_mautrix_facebook_database_engine: 'sqlite'` in your `vars.yml`) some time in the past (likely during [The big move to all-on-Postgres (potentially dangerous)](#the-big-move-to-all-on-postgres-potentially-dangerous)). + +As already mentioned above, you most likely don't need to do anything. If you rerun the playbook and don't get an error, you've been automatically upgraded. Just send a `login` message to the Facebook bridge bot again. Otherwise, read below for a solution. + +### Upgrade path for people NOT running an external Postgres server (default for the playbook) + +If you're **not running an external Postgres server**, then this bridge either already works on Postgres for you, or you've intentionally kept it back on SQLite with custom configuration (`matrix_mautrix_facebook_database_engine: 'sqlite'` in your `vars.yml`) . + +Simply remove that custom configuration from your `vars.yml` file (if it's there) and re-run the playbook. It should upgrade you automatically. +You'll need to send a `login` message to the Facebook bridge bot again. + +Alternatively, [you can stay on SQLite for a little longer](#staying-on-sqlite-for-a-little-longer-temporary-solution). + +### Upgrade path for people running an external Postgres server + +For people using the internal Postgres server (the default for the playbook): +- we automatically create an additional `matrix_mautrix_facebook` Postgres database and credentials to access it +- we automatically adjust the bridge's `matrix_mautrix_facebook_database_*` variables to point the bridge to that Postgres database +- we use [pgloader](https://pgloader.io/) to automatically import the existing SQLite data for the bridge into the `matrix_mautrix_facebook` Postgres database + +If you are using an [external Postgres server](docs/configuring-playbook-external-postgres.md), unfortunately we currently can't do any of that for you. + +You have 3 ways to proceed: + +- contribute to the playbook to make this possible (difficult) +- or, do the migration "steps" manually: + - stop the bridge (`systemctl stop matrix-mautrix-facebook`) + - create a new `matrix_mautrix_facebook` Postgres database for it + - run [pgloader](https://pgloader.io/) manually (we import this bridge's data using default settings and it works well) + - define `matrix_mautrix_facebook_database_*` variables in your `vars.yml` file (credentials, etc.) - you can find their defaults in `roles/matrix-mautrix-facebook/defaults/main.yml` + - switch the bridge to Postgres (`matrix_mautrix_facebook_database_engine: 'postgres'` in your `vars.yml` file) + - re-run the playbook (`--tags=setup-all,start`) and ensure the bridge works (`systemctl status matrix-mautrix-facebook` and `journalctl -fu matrix-mautrix-facebook`) + - send a `login` message to the Facebook bridge bot again +- or, [stay on SQLite for a little longer (temporary solution)](#staying-on-sqlite-for-a-little-longer-temporary-solution) + +### Staying on SQLite for a little longer (temporary solution) + +To keep using this bridge with SQLite for a little longer (**not recommended**), use the following configuration in your `vars.yml` file: + +```yaml +# Force-change the database engine to SQLite. +matrix_mautrix_facebook_database_engine: 'sqlite' + +# Force-downgrade to the last bridge version which supported SQLite. +matrix_mautrix_facebook_docker_image: "{{ matrix_mautrix_facebook_docker_image_name_prefix }}tulir/mautrix-facebook:da1b4ec596e334325a1589e70829dea46e73064b" +``` + +If you do this, keep in mind that **you can't run this forever**. This SQLite-supporting bridge version is not getting any updates and will break sooner or later. The playbook will also drop support for SQLite at some point in the future. + + +# 2021-01-17 + +## matrix-corporal goes 2.0 + +[matrix-corporal v2 has been released](https://github.com/devture/matrix-corporal/releases/tag/2.0.0) and the playbook also supports it now. + +No manual intervention is required in the common case. + +The new [matrix-corporal](https://github.com/devture/matrix-corporal) version is also the first one to support Interactive Authentication. If you wish to enable that (hint: you should), you'll need to set up the [REST auth password provider](docs/configuring-playbook-rest-auth.md). There's more information in [our matrix-corporal docs](docs/configuring-playbook-matrix-corporal.md). + + +# 2021-01-14 + +## Moving from cronjobs to systemd timers + +We no longer use cronjobs for Let's Encrypt SSL renewal and `matrix-nginx-proxy`/`matrix-coturn` reloading. Instead, we've switched to systemd timers. + +The largest benefit of this is that we no longer require you to install a cron daemon, thus simplifying our install procedure. + +The playbook will migrate you from cronjobs to systemd timers automatically. This is just a heads up. + + +# 2021-01-08 + +## (Breaking Change) New SSL configuration + +SSL configuration (protocols, ciphers) can now be more easily controlled thanks to us making use of configuration presets. + +We define a few presets (old, intermediate, modern), following the [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/#server=nginx). + +A new variable `matrix_nginx_proxy_ssl_preset` controls which preset is used (defaults to `"intermediate"`). + +Compared to before, this changes nginx's `ssl_prefer_server_ciphers` to `off` (used to default to `on`). It also add some more ciphers to the list, giving better performance on mobile devices, and removes some weak ciphers. More information in the [documentation](docs/configuring-playbook-nginx.md). + +To revert to the old behaviour, set the following variables: + +```yaml +matrix_nginx_proxy_ssl_ciphers: "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH" +matrix_nginx_proxy_ssl_prefer_server_ciphers: "on" +``` + +Just like before, you can still use your own custom protocols by specifying them in `matrix_nginx_proxy_ssl_protocols`. Doing so overrides the values coming from the preset. + + +# 2021-01-03 + +## Signal bridging support via mautrix-signal + +Thanks to [laszabine](https://github.com/laszabine)'s efforts, the playbook now supports bridging to [Signal](https://www.signal.org/) via the [mautrix-signal](https://github.com/tulir/mautrix-signal) bridge. See our [Setting up Mautrix Signal bridging](docs/configuring-playbook-bridge-mautrix-signal.md) documentation page for getting started. + +If you had installed the mautrix-signal bridge while its Pull Request was still work-in-progress, you can migrate your data to the new and final setup by referring to [this comment](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/686#issuecomment-753510789). + + +# 2020-12-23 + +## The big move to all-on-Postgres (potentially dangerous) + +**TLDR**: all your bridges (and other services) will likely be auto-migrated from SQLite/nedb to Postgres, hopefully without trouble. You can opt-out (see how below), if too worried about breakage. + +Until now, we've only used Postgres as a database for Synapse. All other services (bridges, bots, etc.) were kept simple and used a file-based database (SQLite or nedb). + +Since [this huge pull request](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/740), **all of our services now use Postgres by default**. Thanks to [Johanna Dorothea Reichmann](https://github.com/jdreichmann) for starting the work on it and for providing great input! + +Moving all services to Postgres brings a few **benefits** to us: + +- **improved performance** +- **improved compatibility**. Most bridges are deprecating SQLite/nedb support or offer less features when not on Postgres. +- **easier backups**. It's still some effort to take a proper backup (Postgres dump + various files, keys), but a Postgres dump now takes you much further. +- we're now **more prepared to introduce other services** that need a Postgres database - [Dendrite](https://github.com/matrix-org/dendrite), the [mautrix-signal](https://github.com/tulir/mautrix-signal) bridge (existing [pull request](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/686)), etc. + +### Key takeway + +- existing installations that use an [external Postgres](https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook-external-postgres.md) server should be unaffected (they remain on SQLite/nedb for all services, except Synapse) + +- for existing installations which use our integrated Postgres database server (`matrix-postgres`, which is the default), **we automatically migrate data** from SQLite/nedb to Postgres and **archive the database files** (`something.db` -> `something.db.backup`), so you can restore them if you need to go back (see how below). + +### Opting-out of the Postgres migration + +This is a **very large and somewhat untested change** (potentially dangerous), so **if you're not feeling confident/experimental, opt-out** of it for now. Still, it's the new default and what we (and various bridges) will focus on going forward, so don't stick to old ways for too long. + +You can remain on SQLite/nedb (at least for now) by adding a variable like this to your `vars.yml` file for each service you use: `matrix_COMPONENT_database_engine: sqlite` (e.g. `matrix_mautrix_facebook_database_engine: sqlite`). + +Some services (like `appservice-irc` and `appservice-slack`) don't use SQLite, so use `nedb`, instead of `sqlite` for them. + +### Going back to SQLite/nedb if things went wrong + +If you went with the Postgres migration and it went badly for you (some bridge not working as expected or not working at all), do this: + +- stop all services (`ansible-playbook -i inventory/hosts setup.yml --tags=stop`) +- SSH into the server and rename the old database files (`something.db.backup` -> `something.db`). Example: `mv /matrix/mautrix-facebook/data/mautrix-facebook.db.backup /matrix/mautrix-facebook/data/mautrix-facebook.db` +- switch the affected service back to SQLite (e.g. `matrix_mautrix_facebook_database_engine: sqlite`). Some services (like `appservice-irc` and `appservice-slack`) don't use SQLite, so use `nedb`, instead of `sqlite` for them. +- re-run the playbook (`ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start`) +- [get in touch](README.md#support) with us + +# 2020-12-11 + +## synapse-janitor support removed + +We've removed support for the unmaintained [synapse-janitor](https://github.com/xwiki-labs/synapse_scripts) script. There's been past reports of it corrupting the Synapse database. Since there hasn't been any new development on it and it doesn't seem too useful nowadays, there's no point in including it in the playbook. + +If you need to clean up or compact your database, consider using the Synapse Admin APIs directly. See our [Synapse maintenance](docs/maintenance-synapse.md) and [Postgres maintenance](docs/maintenance-postgres.md) documentation pages for more details. + + +## Docker 20.10 is here + +(No need to do anything special in relation to this. Just something to keep in mind) + +Docker 20.10 got released recently and your server will likely get it the next time you update. + +This is the first major Docker update in a long time and it packs a lot of changes. +Some of them introduced some breakage for us initially (see [here](https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/d08b27784f222effcbce2abf924bf07bbe0893be) and [here](https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/7593d969e316cc0144bce378a5be58c76c2c37ee)), but it should be all good now. + + +# 2020-12-08 + +## openid APIs exposed by default on the federation port when federation disabled + +We've changed some defaults. People running with our default configuration (federation enabled), are not affected at all. + +If you are running an unfederated server (`matrix_synapse_federation_enabled: false`), this may be of interest to you. + +When federation is disabled, but ma1sd or Dimension are enabled, we'll now expose the `openid` APIs on the federation port. +These APIs are necessary for some ma1sd features to work. If you'd like to prevent this, you can: `matrix_synapse_federation_port_openid_resource_required: false`. + + +# 2020-11-27 + +## Recent Jitsi updates may require configuration changes + +We've recently [updated from Jitsi build 4857 to build 5142](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/719), which brings a lot of configuration changes. + +**If you use our default Jitsi settings, you won't have to do anything.** + +People who have [fine-tuned Jitsi](docs/configuring-playbook-jitsi.md#optional-fine-tune-jitsi) may find that some options got renamed now, others are gone and yet others still need to be defined in another way. + +The next time you run the playbook [installation](docs/installing.md) command, our validation logic will tell you if you're using some variables like that and will recommend a migration path for each one. + +Additionally, we've recently disabled transcriptions (`matrix_jitsi_enable_transcriptions: false`) and recording (`matrix_jitsi_enable_recording: false`) by default. These features did not work anyway, because we don't install the required dependencies for them (Jigasi and Jibri, respectively). If you've been somehow pointing your Jitsi installation to some manually installed Jigasi/Jibri service, you may need to toggle these flags back to enabled to have transcriptions and recordings working. + + +# 2020-11-23 + +## Breaking change matrix-sms-bridge + +Because of many problems using gammu as SMS provider, matrix-sms-bridge now uses (https://github.com/RebekkaMa/android-sms-gateway-server) by default. See (the docs)[./docs/configuring-playbook-bridge-matrix-bridge-sms.md] which new vars you need to add. + +If you are using this playbook to deploy matrix-sms-bridge and still really want to use gammu as SMS provider, we could possibly add support for both android-sms-gateway-server and gammu. + +# 2020-11-13 + +## Breaking change matrix-sms-bridge + +The new version of [matrix-sms-bridge](https://github.com/benkuly/matrix-sms-bridge) changed its database from neo4j to h2. You need to sync the bridge at the first start. Note that this only will sync rooms where the @smsbot:yourServer is member. For rooms without @smsbot:yourServer you need to kick and invite the telephone number **or** invite @smsbot:yourServer. + +1. Add the following to your `vars.yml` file: `matrix_sms_bridge_container_extra_arguments=['--env SPRING_PROFILES_ACTIVE=initialsync']` +2. Login to your host shell and remove old systemd file from your host: `rm /etc/systemd/system/matrix-sms-bridge-database.service` +2. Run `ansible-playbook -i inventory/hosts setup.yml --tags=setup-matrix-sms-bridge,start` +3. Login to your host shell and check the logs with `journalctl -u matrix-sms-bridge` until the sync finished. +4. Remove the var from the first step. +5. Run `ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start`. + +# 2020-11-10 + +## Dynamic DNS support + +Thanks to [Scott Crossen](https://github.com/scottcrossen), the playbook can now manage Dynamic DNS for you using [ddclient](https://ddclient.net/). + +To learn more, follow our [Dynamic DNS docs page](docs/configuring-playbook-dynamic-dns.md). + + +# 2020-10-28 + +## (Compatibility Break) https://matrix.DOMAIN/ now redirects to https://element.DOMAIN/ + +Until now, we used to serve a static page coming from Synapse at `https://matrix.DOMAIN/`. This page was not very useful to anyone. + +Since `matrix.DOMAIN` may be accessed by regular users in certain conditions, it's probably better to redirect them to a better place (e.g. to the [Element](docs/configuring-playbook-client-element.md) client). + +If Element is installed (`matrix_client_element_enabled: true`, which it is by default), we now redirect people to it, instead of showing them a Synapse static page. + +If you'd like to control where the redirect goes, use the `matrix_nginx_proxy_proxy_matrix_client_redirect_root_uri_to_domain` variable. +To restore the old behavior of not redirecting anywhere and serving the Synapse static page, set it to an empty value (`matrix_nginx_proxy_proxy_matrix_client_redirect_root_uri_to_domain: ""`). + + +# 2020-10-26 + +## (Compatibility Break) /_synapse/admin is no longer publicly exposed by default + +We used to expose the Synapse Admin APIs publicly (at `https://matrix.DOMAIN/_synapse/admin`). +These APIs require authentication with a valid access token, so it's not that big a deal to expose them. + +However, following [official Synapse's reverse-proxying recommendations](https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.md#synapse-administration-endpoints), we're no longer exposing `/_synapse/admin` by default. + +If you'd like to restore restore the old behavior and expose `/_synapse/admin` publicly, you can use the following configuration (in your `vars.yml`): + +```yaml +matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_admin_api_enabled: true +``` + + +# 2020-10-02 + +## Minimum Ansible version raised to v2.7.0 + +We were claiming to support [Ansible](https://www.ansible.com/) v2.5.2 and higher, but issues like [#662](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/662) demonstrate that we need at least v2.7.0. + +If you've been using the playbook without getting any errors until now, you're probably on a version higher than that already (or you're not using the `matrix-ma1sd` and `matrix-client-element` roles). + +Our [Ansible docs page](docs/ansible.md) contains information on how to run a more up-to-date version of Ansible. + + +# 2020-10-01 + +## Postgres 13 support + +The playbook now installs [Postgres 13](https://www.postgresql.org/about/news/postgresql-13-released-2077/) by default. + +If you have have an existing setup, it's likely running on an older Postgres version (9.x, 10.x, 11.x or 12.x). You can easily upgrade by following the [upgrading PostgreSQL guide](docs/maintenance-postgres.md#upgrading-postgresql). + +# 2020-09-01 + +## matrix-registration support + +The playbook can now help you set up [matrix-registration](https://github.com/ZerataX/matrix-registration) - an application that lets you keep your Matrix server's registration private, but still allow certain users (those having a unique registration link) to register by themselves. + +See our [Setting up matrix-registration](docs/configuring-playbook-matrix-registration.md) documentation page to get started. + + +# 2020-08-21 + +## rust-synapse-compress-state support + +The playbook can now help you use [rust-synapse-compress-state](https://github.com/matrix-org/rust-synapse-compress-state) to compress the state groups in your Synapse database. + +See our [Compressing state with rust-synapse-compress-state](docs/maintenance-synapse.md#compressing-state-with-rust-synapse-compress-state) documentation page to get started. + + +# 2020-07-22 + +## Synapse Admin support + +The playbook can now help you set up [synapse-admin](https://github.com/Awesome-Technologies/synapse-admin). + +See our [Setting up Synapse Admin](docs/configuring-playbook-synapse-admin.md) documentation to get started. + + +# 2020-07-20 + +## matrix-reminder-bot support + +The playbook can now help you set up [matrix-reminder-bot](https://github.com/anoadragon453/matrix-reminder-bot). + +See our [Setting up matrix-reminder-bot](docs/configuring-playbook-bot-matrix-reminder-bot.md) documentation to get started. + + +# 2020-07-17 + +## (Compatibility Break) Riot is now Element + +As per the official announcement, [Riot has been rebraned to Element](https://element.io/blog/welcome-to-element/). + +The playbook follows suit. Existing installations have a few options for how to handle this. + +See our [Migrating to Element](docs/configuring-playbook-riot-web.md#migrating-to-element) documentation page for more details. + + +# 2020-07-03 + +## Steam bridging support via mx-puppet-steam + +Thanks to [Hugues Morisset](https://github.com/izissise)'s efforts, the playbook now supports bridging to [Steam](https://steamapp.com/) via the [mx-puppet-steam](https://github.com/icewind1991/mx-puppet-steam) bridge. See our [Setting up MX Puppet Steam bridging](docs/configuring-playbook-bridge-mx-puppet-steam.md) documentation page for getting started. + + +# 2020-07-01 + +## Discord bridging support via mx-puppet-discord + +Thanks to [Hugues Morisset](https://github.com/izissise)'s efforts, the playbook now supports bridging to [Discord](https://discordapp.com/) via the [mx-puppet-discord](https://github.com/Sorunome/mx-puppet-discord) bridge. See our [Setting up MX Puppet Discord bridging](docs/configuring-playbook-bridge-mx-puppet-discord.md) documentation page for getting started. + +**Note**: this is a new Discord bridge. The playbook still retains Discord bridging via [matrix-appservice-discord](docs/configuring-playbook-bridge-appservice-discord.md). You're free too use the bridge that serves you better, or even both (for different users and use-cases). + + +# 2020-06-30 + +## Instagram and Twitter bridging support + +Thanks to [Johanna Dorothea Reichmann](https://github.com/jdreichmann)'s efforts, the playbook now supports bridging to [Instagram](https://www.instagram.com/) via the [mx-puppet-instagram](https://github.com/Sorunome/mx-puppet-instagram) bridge. See our [Setting up MX Puppet Instagram bridging](docs/configuring-playbook-bridge-mx-puppet-instagram.md) documentation page for getting started. + +Thanks to [Tulir Asokan](https://github.com/tulir)'s efforts, the playbook now supports bridging to [Twitter](https://twitter.com/) via the [mx-puppet-twitter](https://github.com/Sorunome/mx-puppet-twitter) bridge. See our [Setting up MX Puppet Twitter bridging](docs/configuring-playbook-bridge-mx-puppet-twitter.md) documentation page for getting started. + + +# 2020-06-28 + +## (Post Mortem / fixed Security Issue) Re-enabling User Directory search powered by the ma1sd Identity Server + +User Directory search requests used to go to the ma1sd identity server by default, which queried its own stores and the Synapse database. + +ma1sd's [security issue](https://github.com/ma1uta/ma1sd/issues/44) has been fixed in version `2.4.0`, with [this commit](ma1uta/ma1sd@2bb5a734d11662b06471113cf3d6b4cee5e33a85). `ma1sd 2.4.0` is now the default version for this playbook. For more information on what happened, please check the mentioned issue. + +We are re-enabling user directory search with this update. Those who would like to keep it disabled can use this configuration: `matrix_nginx_proxy_proxy_matrix_user_directory_search_enabled: false` + +As always, re-running the playbook is enough to get the updated bits. + +# 2020-06-11 + +## SMS bridging requires db reset + +The current version of [matrix-sms-bridge](https://github.com/benkuly/matrix-sms-bridge) needs you to delete the database to work as expected. Just remove `/matrix/matrix-sms-bridge/database/*`. It also adds a new requried var `matrix_sms_bridge_default_region`. + +To reuse your existing rooms, invite `@smsbot:yourServer` to the room or write a message. You are also able to use automated room creation with telephonenumers by writing `sms send -t 01749292923 "Hello World"` in a room with `@smsbot:yourServer`. See [the docs](https://github.com/benkuly/matrix-sms-bridge) for more information. + +# 2020-06-05 + +## SMS bridging support + +Thanks to [benkuly](https://github.com/benkuly)'s efforts, the playbook now supports bridging to SMS (with one telephone number only) via [matrix-sms-bridge](https://github.com/benkuly/matrix-sms-bridge). + +See our [Setting up Matrix SMS bridging](docs/configuring-playbook-bridge-matrix-bridge-sms.md) documentation page for getting started. + + +# 2020-05-19 + +## (Compatibility Break / Security Issue) Disabling User Directory search powered by the ma1sd Identity Server + +User Directory search requests used to go to the ma1sd identity server by default, which queried its own stores and the Synapse database. + +ma1sd current has [a security issue](https://github.com/ma1uta/ma1sd/issues/44), which made it leak information about all users - including users created by bridges, etc. + +Until the issue gets fixed, we're making User Directory search not go to ma1sd by default. You **need to re-run the playbook and restart services to apply this workaround**. + +*If you insist on restoring the old behavior* (**which has a security issue!**), you *might* use this configuration: `matrix_nginx_proxy_proxy_matrix_user_directory_search_enabled: "{{ matrix_ma1sd_enabled }}"` + + +# 2020-04-28 + +## Newer IRC bridge (with potential breaking change) + +This upgrades matrix-appservice-irc from 0.14.1 to 0.16.0. Upstream +made a change to how you define manual mappings. If you added a +`mapping` to your configuration, you will need to update it accoring +to the [upstream +instructions](https://github.com/matrix-org/matrix-appservice-irc/blob/master/CHANGELOG.md#0150-2020-02-05). +If you did not include `mappings` in your configuration for IRC, no +change is necessary. `mappings` is not part of the default +configuration. + + +# 2020-04-23 + +## Slack bridging support + +Thanks to [Rodrigo Belem](https://github.com/rbelem)'s efforts, the playbook now supports bridging to [Slack](https://slack.com) via the [mx-puppet-slack](https://github.com/Sorunome/mx-puppet-slack) bridge. + +See our [Setting up MX Puppet Slack bridging](docs/configuring-playbook-bridge-mx-puppet-slack.md) documentation page for getting started. + + +# 2020-04-09 + +## Skype bridging support + +Thanks to [Rodrigo Belem](https://github.com/rbelem)'s efforts, the playbook now supports bridging to [Skype](https://www.skype.com) via the [mx-puppet-skype](https://github.com/Sorunome/mx-puppet-skype) bridge. + +See our [Setting up MX Puppet Skype bridging](docs/configuring-playbook-bridge-mx-puppet-skype.md) documentation page for getting started. + + +# 2020-04-05 + +## Private Jitsi support + +The [Jitsi support](#jitsi-support) we had landed a few weeks ago was working well, but it was always open to the whole world. + +Running such an open instance is not desirable to most people, so [teutat3s](https://github.com/teutat3s) has contributed support for making Jitsi use authentication. + +To make your Jitsi server more private, see the [configure internal Jitsi authentication and guests mode](docs/configuring-playbook-jitsi.md#optional-configure-internal-jitsi-authentication-and-guests-mode) section in our Jitsi documentation. + + +# 2020-04-03 + +## (Potential Backward Compatibility Break) ma1sd replaces mxisd + +Thanks to [Marcel Partap](https://github.com/eMPee584)'s efforts, the [mxisd](https://github.com/kamax-io/mxisd) identity server, which has been deprecated for a long time, has finally been replaced by [ma1sd](https://github.com/ma1uta/ma1sd), a compatible fork. + +**If you're using the default playbook configuration**, you don't need to do anything -- your mxisd installation will be replaced with ma1sd and all existing data will be migrated automatically the next time you run the playbook. + +**If you're doing something more special** (defining custom `matrix_mxisd_*` variables), the playbook will ask you to rename them to `matrix_ma1sd_*`. +You're also encouraged to test that ma1sd works well for such a more custom setup. + + +# 2020-03-29 + +## Archlinux support + +Thanks to [Christian Lupus](https://github.com/christianlupus)'s efforts, the playbook now supports installing to an [Archlinux](https://www.archlinux.org/) server. + + +# 2020-03-24 + +## Jitsi support + +The playbook can now (optionally) install the [Jitsi](https://jitsi.org/) video-conferencing platform and integrate it with [Riot](docs/configuring-playbook-riot-web.md). + +See our [Jitsi documentation page](docs/configuring-playbook-jitsi.md) to get started. + + +# 2020-03-15 + +## Raspberry Pi support + +Thanks to [Gergely Horváth](https://github.com/hooger)'s effort, the playbook supports installing to a Raspberry Pi server, for at least some of the services. + +Since most ready-made container images do not support that architecture, we achieve this by building images locally on the device itself. +See our [Self-building documentation page](docs/self-building.md) for how to get started. + + +# 2020-02-26 + +## Riot-web themes are here + +The playbook now makes it easy to install custom riot-web themes. + +To learn more, take a look at our [riot-web documentation on Themes](docs/configuring-playbook-riot-web.md#themes). + + +# 2020-02-24 + +## Customize the server name in Riot's login page + +You can now customize the server name string that Riot-web displays in its login page. + +These playbook variables, with these default values, have been added: + +``` +matrix_riot_web_default_server_name: "{{ matrix_domain }}" +``` + +The login page previously said "Sign in to your Matrix account on matrix.example.org" (the homeserver's domain name). It will now say "Sign in ... on example.org" (the server name) by default, or "Sign in ... on Our Server" if you set the variable to "Our Server". + +To support this, the config.json template is changed to use the configuration key `default_server_config` for setting the default HS/IS, and the new configuration key `server_name` is added in there. + + +# 2020-01-30 + +## Disabling TLSv1.1 + +To improve security, we've removed TLSv1.1 support from our default matrix-nginx-proxy configuration. + +If you need to support old clients, you can re-enable it with the following configuration: `matrix_nginx_proxy_ssl_protocols: "TLSv1.1 TLSv1.2 TLSv1.3"` + + +# 2020-01-21 + +## Postgres collation changes (action required!) + +By default, we've been using a UTF-8 collation for Postgres. This is known to cause Synapse some troubles (see the [relevant issue](https://github.com/matrix-org/synapse/issues/6722)) on systems that use [glibc](https://www.gnu.org/software/libc/). We run Postgres in an [Alpine Linux](https://alpinelinux.org/) container (which uses [musl](https://www.musl-libc.org/), and not glibc), so our users are likely not affected by the index corruption problem observed by others. + +Still, we might become affected in the future. In any case, it's imminent that Synapse will complain about databases which do not use a C collation. + +To avoid future problems, we recommend that you run the following command: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=upgrade-postgres --extra-vars='{"postgres_force_upgrade": true}' +``` + +It forces a [Postgres database upgrade](docs/maintenance-postgres.md#upgrading-postgresql), which would recreate your Postgres database using the proper (`C`) collation. If you are low on disk space, or run into trouble, refer to the Postgres database upgrade documentation page. + + +# 2020-01-14 + +## Added support for Appservice Webhooks + +Thanks to a contribution from [Björn Marten](https://github.com/tripleawwy) from [netresearch](https://www.netresearch.de/), the playbook can now install and configure [matrix-appservice-webhooks](https://github.com/turt2live/matrix-appservice-webhooks) for you. This bridge provides support for Slack-compatible webhooks. + +Learn more in [Setting up Appservice Webhooks](docs/configuring-playbook-bridge-appservice-webhooks.md). + + +# 2020-01-12 + +## Added support for automatic Double Puppeting for all Mautrix bridges + +Double Puppeting can now be easily enabled for all Mautrix bridges supported by the playbook (Facebook, Hangouts, Whatsapp, Telegram). + +This is possible due to those bridges' integration with [matrix-synapse-shared-secret-auth](https://github.com/devture/matrix-synapse-shared-secret-auth) - yet another component that this playbook can install for you. + +To get started, following the playbook's documentation for the bridge you'd like to configure. + + +# 2019-12-06 + +## Added support for an alternative method for using another webserver + +We have added support for making `matrix-nginx-proxy` not being so invasive, so that it would be easier to [use your own webserver](docs/configuring-playbook-own-webserver.md). + +The documentation has been updated with a **Method 2**, which might make "own webserver" setup easier in some cases (such as [reverse-proxying using Traefik](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/296)). + +**Existing users** are not affected by this and **don't need to change anything**. +The defaults are still the same (`matrix-nginx-proxy` obtaining SSL certificates and doing everything for you automatically). + + +# 2019-11-10 + +## Tightened security around room directory publishing + +As per this [advisory blog post](https://matrix.org/blog/2019/11/09/avoiding-unwelcome-visitors-on-private-matrix-servers), we've decided to change the default publishing rules for the Matrix room directory. + +Our general goal is to favor privacy and security when running personal (family & friends) and corporate homeservers. +Both of these likely benefit from having a more secure default of **not showing the room directory without authentication** and **not publishing the room directory over federation**. + +As with anything else, these new defaults can be overriden by changing the `matrix_synapse_allow_public_rooms_without_auth` and `matrix_synapse_allow_public_rooms_over_federation` variables, respectively. + + +# 2019-10-05 + +## Improved Postgres upgrading/importing + +Postgres [upgrading](docs/maintenance-postgres.md#upgrading-postgresql) and [importing](docs/importing-postgres.md) have been improved to add support for multiple databases and roles. + +Previously, the playbook would only take care of the `homeserver` database and `synapse` user. +We now back up and restore all databases and users on the Postgres server. + +For now, the playbook only uses that one database (`homeserver`) and that one single user (`synapse`), so it's all the same. +However, in the future, additional components besides Synapse may also make use the Postgres database server. +One such example is the [matrix-appservice-slack](https://github.com/matrix-org/matrix-appservice-slack) bridge, which strongly encourages use of Postgres in its v1.0 release. We are yet to upgrade to it. + +Additionally, Postgres [upgrading](docs/maintenance-postgres.md#upgrading-postgresql) now uses gzipped dump files by default, to minimize disk space usage. + + +# 2019-10-04 + +## Postgres 12 support + +The playbook now installs [Postgres 12](https://www.postgresql.org/about/news/1976/) by default. + +If you have have an existing setup, it's likely running on an older Postgres version (9.x, 10.x or 11.x). You can easily upgrade by following the [upgrading PostgreSQL guide](docs/maintenance-postgres.md#upgrading-postgresql). + + +# 2019-10-03 + +## Synapse 1.4.0 + +Synapse 1.4.0 [is out](https://matrix.org/blog/2019/10/03/synapse-1-4-0-released) with lots of changes related to privacy. + +Its new defaults (which we adopt as well) mean that certain old data will automatically get purged after a certain number of days. 1.4.0 automatically garbage collects redacted messages (defaults to 7 days) and removes unused IP and user agent information stored in the user_ips table (defaults to 30 days). If you'd like to preserve this data, we encourage you to look at the `redaction_retention_period` and `user_ips_max_age` options (controllable by the `matrix_synapse_redaction_retention_period` and `matrix_synapse_user_ips_max_age` playbook variables, respectively) before doing the upgrade. If you'd like to keep data indefinitely, set these variables to `null` (e.g. `matrix_synapse_redaction_retention_period: ~`). + +From now on the `trusted_key_servers` setting for Synapse is configurable. It still defaults to `matrix.org` just like it always has, but in a more explicit way now. If you'd like to use another trusted key server, adjust the `matrix_synapse_trusted_key_servers` playbook variable. + +Synapse 1.4.0 also changes lots of things related to identity server integration. +Because Synapse will now by default be responsible for validating email addresses for user accounts, running without an identity server looks more feasible. +We still [have concerns](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/275/files#r331104117) over disabling the identity server by default, so for now it remains enabled. + + +# 2019-09-09 + +## Synapse Simple Antispam support + +There have been lots of invite-spam attacks lately and [Travis](https://github.com/t2bot) has created a Synapse module ([synapse-simple-antispam](https://github.com/t2bot/synapse-simple-antispam)) to let people protect themselves. + +From now on, you can easily install and configure this spam checker module through the playbook. + +Learn more in [Setting up Synapse Simple Antispam](docs/configuring-playbook-synapse-simple-antispam.md). + + +# 2019-08-25 + +## Extensible Riot-web configuration + +Similarly to [Extensible Synapse configuration](#extensible-synapse-configuration) (below), Riot-web configuration is also extensible now. + +From now on, you can extend/override Riot-web's configuration by making use of the `matrix_riot_web_configuration_extension_json` variable. +This should be enough for most customization needs. + +If you need even more power, you can now also take full control and override `matrix_riot_web_configuration_default` (or `matrix_riot_web_configuration`) directly. + +Learn more in [Configuring Riot-web](docs/configuring-playbook-riot-web.md). + + +# 2019-08-22 + +## Extensible Synapse configuration + +Previously, we had to create custom Ansible variables for each and every Synapse setting. +This lead to too much effort (and configuration ugliness) to all of Synapse's settings, so naturally, not all features of Synapse could be controlled through the playbook. + +From now on, you can extend/override the Synapse server's configuration by making use of the `matrix_synapse_configuration_extension_yaml` variable. +This should be enough for most customization needs. + +If you need even more power, you can now also take full control and override `matrix_synapse_configuration` (or `matrix_synapse_configuration_yaml`) directly. + +Learn more here in [Configuring Synapse](docs/configuring-playbook-synapse.md). + + +# 2019-08-21 + +## Slack bridging support + +Thanks to the [great work](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/205) of [kingoftheconnors](https://github.com/kingoftheconnors) and [Stuart Mumford (Cadair)](https://github.com/Cadair), the playbook now supports bridging to [Slack](https://slack.com) via the [appservice-slack](https://github.com/matrix-org/matrix-appservice-slack) bridge. + +Additional details are available in [Setting up Appservice Slack bridging](docs/configuring-playbook-bridge-appservice-slack.md). + +## Google Hangouts bridging support + +Thanks to the [great work](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/251) of [Eduardo Beltrame (Munfred)](https://github.com/Munfred) and [Robbie D (microchipster)](https://github.com/microchipster), the playbook now supports bridging to [Google Hangouts](https://hangouts.google.com/) via the [mautrix-hangouts](https://mau.dev/tulir/mautrix-hangouts) bridge. + +Additional details are available in [Setting up Mautrix Hangouts bridging](docs/configuring-playbook-bridge-mautrix-hangouts.md). + + +# 2019-08-05 + +## Email2Matrix support + +Support for [Email2Matrix](https://github.com/devture/email2matrix) has been added. + +It's an optional feature that you can enable via the playbook. + +To learn more, see the [playbook's documentation on Email2Matrix](./docs/configuring-playbook-email2matrix.md). + + +# 2019-08-03 + +## Synapse logging level has been reduced to WARNING + +After [some discussion in our support room](https://matrix.to/#/!PukFFdIcHgtaaHZflT:devture.com/$156476852524179TBeKy:matrix.org?via=devture.com&via=matrix.org&via=librem.one), we've decided to change the default logging level for Synapse from `INFO` to `WARNING`. + +This greatly reduces the number of log messages that are being logged, leading to: + +- much less disk space dedicated to Synapse and thus, logs kept for longer +- easier to find some important `WARNING`, `ERROR` and `CRITICAL` messages, as they're not longer buried in thousands of non-important `INFO` messages + +If you'd like to track down an issue, you [can always increase the logging level as described here](./docs/maintenance-and-troubleshooting.md#increasing-synapse-logging). + + + +# 2019-07-08 + +## Synapse Maintenance docs and synapse-janitor support are available + +The playbook can now help you with Synapse's maintenance. + +There's a new documentation page about [Synapse maintenance](./docs/maintenance-synapse.md) and another section on [Postgres vacuuming](./docs/maintenance-postgres.md#vacuuming-postgresql). + +Among other things, if your Postgres database has grown significantly over time, you may wish to [ask the playbook to purge unused data with synapse-janitor](./docs/maintenance-synapse.md#purging-unused-data-with-synapse-janitor) for you. + + +## (BC Break) Rename run control variables + +Some internal playbook control variables have been renamed. + +This change **only affects people who run this playbook's roles from another playbook**. +If you're using this playbook as-is, you're not affected and don't need to do anything. + +The following variables have been renamed: + +- from `run_import_postgres` to `run_postgres_import` +- from `run_import_sqlite_db` to `run_postgres_import_sqlite_db` +- from `run_upgrade_postgres` to `run_postgres_upgrade` +- from `run_import_media_store` to `run_synapse_import_media_store` +- from `run_register_user` to `run_synapse_register_user` +- from `run_update_user_password` to `run_synapse_update_user_password` + + +# 2019-07-04 + +## Synapse no longer logs to text files + +Following what the official Synapse Docker image is doing ([#5565](https://github.com/matrix-org/synapse/pull/5565)) and what we've been doing for mostly everything installed by this playbook, **Synapse no longer logs to text files** (`/matrix/synapse/run/homeserver.log*`). + +From now on, Synapse would only log to console, which goes to systemd's journald. +To see Synapse's logs, execute: `journalctl -fu matrix-synapse` + +Because of this, the following variables have become obsolete and were removed: + +- `matrix_synapse_max_log_file_size_mb` +- `matrix_synapse_max_log_files_count` + +To prevent confusion, it'd be better if you delete all old files manually after you've upgraded (`rm -f /matrix/synapse/run/homeserver.log*`). + +Because Synapse is incredibly chatty when it comes to logging (here's [one such issue](https://github.com/matrix-org/synapse/issues/4751) describing the problem), if you're running an ancient distribution (like CentOS 7.0), be advised that systemd's journald default logging restrictions may not be high enough to capture all log messages generated by Synapse. This is especially true if you've got a busy (Synapse) server. We advise that you manually add `RateLimitInterval=0` and `RateLimitBurst=0` under `[Storage]` in the `/etc/systemd/journald.conf` file, followed by restarting the logging service (`systemctl restart systemd-journald`). + + +# 2019-06-27 + +## (BC Break) Discord bridge configuration is now entirely managed by the playbook + +Until now, the `config.yaml` file for the [Discord bridge](docs/configuring-playbook-bridge-appservice-discord.md) was managed by the playbook, but the `registration.yaml` file was not. + +From now on, the playbook will keep both configuration files sync for you. + +This means that if you were making manual changes to the `/matrix/appservice-discord/discord-registration.yaml` configuration file, those would be lost the next time you run the playbook. + +The bridge now stores configuration in a subdirectory (`/matrix/appservice-discord/config`). + +Likewise, data is now also stored in a subdirectory (`/matrix/appservice-discord/data`). When you run the playbook with an existing database file (`/matrix/appservice-discord/discord.db`), the playbook will stop the bridge and relocate the database file to the `./data` directory. There's no data-loss involved. You'll need to restart the bridge manually though (`--tags=start`). + +The main directory (`/matrix/appservice-discord`) may contain some leftover files (`user-store.db`, `room-store.db`, `config.yaml`, `discord-registration.yaml`, `invite_link`). These are no longer necessary and can be deleted manually. + +We're now following the default sample configuration for the Discord bridge. +If you need to override some values, define them in `matrix_appservice_discord_configuration_extension_yaml`. + + +# 2019-06-24 + +## (BC Break) WhatsApp bridge configuration is now entirely managed by the playbook + +Until now, configuration files for the [WhatsApp bridge](docs/configuring-playbook-bridge-mautrix-whatsapp.md) were created by the playbook initially, but never modified later on. + +From now on, the playbook will keep the configuration in sync for you. + +This means that if you were making manual changes to the `/matrix/mautrix-whatsapp/config.yaml` or `/matrix/mautrix-whatsapp/registration.yaml` configuration files, those would be lost the next time you run the playbook. + +The bridge now stores configuration in a subdirectory (`/matrix/mautrix-whatsapp/config`), so your old configuration remains in the base directory (`/matrix/mautrix-whatsapp`). +You need to migrate any manual changes over to the new `matrix_mautrix_whatsapp_configuration_extension_yaml` variable, so that the playbook would apply them for you. + +Likewise, data is now also stored in a subdirectory (`/matrix/mautrix-whatsapp/data`). When you run the playbook with an existing database file (`/matrix/mautrix-whatsapp/mautrix-whatsapp.db`), the playbook will stop the bridge and relocate the database file to the `./data` directory. There's no data-loss involved. You'll need to restart the bridge manually though (`--tags=start`). + +We're now following the default configuration for the WhatsApp bridge. + + +# 2019-06-20 + +## (BC Break) IRC bridge configuration is now entirely managed by the playbook + +Until now, configuration files for the [IRC bridge](docs/configuring-playbook-bridge-appservice-irc.md) were created by the playbook initially, but never modified later on. + +From now on, the playbook will keep the configuration in sync for you. + +This means that if you were making manual changes to the `/matrix/appservice-irc/config.yaml` or `/matrix/appservice-irc/registration.yaml` configuration files, those would be lost the next time you run the playbook. + +The bridge now stores configuration in a subdirectory (`/matrix/appservice-irc/config`), so your old configuration remains in the base directory (`/matrix/appservice-irc`). + +Previously, we asked people to configure bridged IRC servers by extending the bridge configuration (`matrix_appservice_irc_configuration_extension_yaml`). While this is still possible and will continue working forever, **we now recommend defining IRC servers in the easier to use `matrix_appservice_irc_ircService_servers` variable**. See [our IRC bridge documentation page](docs/configuring-playbook-bridge-appservice-irc.md) for an example. + +If you decide to continue using `matrix_appservice_irc_configuration_extension_yaml`, you might be interested to know that `ircService.databaseUri` and a few other keys now have default values in the base configuration (`matrix_appservice_irc_configuration_yaml`). You may wish to stop redefining those keys, unless you really intend to override them. You most likely only need to override `ircService.servers`. + +Bridge data (`passkey.pem` and database files) is now also stored in a subdirectory (`/matrix/appservice-irc/data`). +When you run the playbook with an existing `/matrix/appservice-irc/passkey.pem` file, the playbook will stop the bridge and relocate the passkey and database files (`rooms.db` and `users.db`) to the `./data` directory. There's no data-loss involved. You'll need to restart the bridge manually though (`--tags=start`). + + +# 2019-06-15 + +## (BC Break) Telegram bridge configuration is now entirely managed by the playbook + +Until now, configuration files for the [Telegram bridge](docs/configuring-playbook-bridge-mautrix-telegram.md) were created by the playbook initially, but never modified later on. + +From now on, the playbook will keep the configuration in sync for you. + +This means that if you were making manual changes to the `/matrix/mautrix-telegram/config.yaml` or `/matrix/mautrix-telegram/registration.yaml` configuration files, those would be lost the next time you run the playbook. + +The bridge now stores configuration in a subdirectory (`/matrix/mautrix-telegram/config`), so your old configuration remains in the base directory (`/matrix/mautrix-telegram`). +You need to migrate any manual changes over to the new `matrix_mautrix_telegram_configuration_extension_yaml` variable, so that the playbook would apply them for you. + +Likewise, data is now also stored in a subdirectory (`/matrix/mautrix-telegram/data`). When you run the playbook with an existing database file (`/matrix/mautrix-telegram/mautrix-telegram.db`), the playbook will stop the bridge and relocate the database file to the `./data` directory. There's no data-loss involved. You'll need to restart the bridge manually though (`--tags=start`). + +Also, we're now following the default configuration for the Telegram bridge, so some default configuration values are different: + +- `edits_as_replies` (used to be `false`, now `true`) - previously replies were not sent over to Matrix at all; ow they are sent over as a reply to the original message +- `inline_images` (used to be `true`, now `false`) - this has to do with captioned images. Inline-image (included caption) are said to exhibit troubles on Riot iOS. When `false`, the caption arrives on the Matrix side as a separate message. +- `authless_portals` (used to be `false`, now `true`) - creating portals from the Telegram side is now possible +- `whitelist_group_admins` (used to be `false`, now `true`) - allows Telegram group admins to use the bot commands + +If the new values are not to your liking, use `matrix_mautrix_telegram_configuration_extension_yaml` to specify an override (refer to `matrix_mautrix_telegram_configuration_yaml` to figure out which variable goes where). + + +# 2019-06-12 + +## Synapse v1.0 + +With [Synapse v1.0 now available](https://matrix.org/blog/2019/06/11/introducing-matrix-1-0-and-the-matrix-org-foundation) and most people being on at least Synapse v0.99, it's time to remove the `_matrix._tcp` DNS SRV record that we've been keeping for compatibility with old Synapse versions (<= 0.34). + +According to the [Server Discovery specification](https://matrix.org/docs/spec/server_server/r0.1.2.html#server-discovery), it's no harm to keep the DNS SRV record. But since it's not necessary for federating with the larger Matrix network anymore, you should be safe to get rid of it. + +**Note**: don't confuse the `_matrix._tcp` and `_matrix-identity._tcp` DNS SRV records. The latter, **must not** be removed. + +For completeness, we must say that using a `_matrix._tcp` [SRV record for Server Delegation](docs/howto-server-delegation.md#server-delegation-via-a-dns-srv-record-advanced) is still valid and useful for certain deployments. It's just that our guide recommends the [`/.well-known/matrix/server` Server Delegation method](docs/howto-server-delegation.md#server-delegation-via-a-well-known-file), due to its easier implementation when using this playbook. + +Besides this optional/non-urgent DNS change, assuming you're already on Synapse v0.99, upgrading to Synapse v1.0 should be as simple as [re-running the playbook](docs/maintenance-upgrading-services.md). + + +# 2019-06-07 + +## (BC Break) Facebook bridge configuration is now entirely managed by the playbook + +Until now, configuration files for the [Facebook bridge](docs/configuring-playbook-bridge-mautrix-facebook.md) were created by the playbook initially, but never modified later on. + +From now on, the playbook will keep the configuration in sync for you. + +This means that if you were making manual changes to the `/matrix/mautrix-facebook/config.yaml` or `/matrix/mautrix-facebook/registration.yaml` configuration files, those would be lost the next time you run the playbook. + +The bridge now stores configuration in a subdirectory (`/matrix/mautrix-facebook/config`), so your old configuration remains in the base directory (`/matrix/mautrix-facebook`). +You need to migrate any manual changes over to the new `matrix_mautrix_facebook_configuration_extension_yaml` variable, so that the playbook would apply them for you. + +Likewise, data is now also stored in a subdirectory (`/matrix/mautrix-facebook/data`). When you run the playbook with an existing database file (`/matrix/mautrix-facebook/mautrix-facebook.db`), the playbook will stop the bridge and relocate the database file to the `./data` directory. There's no data-loss involved. You'll need to restart the bridge manually though (`--tags=start`). + + +# 2019-05-25 + +## Support for exposing container ports publicly (not just to the host) + +Until now, various roles supported a `matrix_*_expose_port` variable, which would expose their container's port to the host. This was mostly useful for reverse-proxying manually (in case `matrix-nginx-proxy` was disabled). It could also be used for installing some playbook services (e.g. bridges, etc.) and wiring them to a separate (manual) Matrix setup. + +`matrix_*_expose_port` variables were not granular enough - sometimes they would expose one port, other times multiple. They also didn't provide control over **where** to expose (to which port number and to which network interface), because they would usually hardcode something like `127.0.0.1:8080`. + +All such variables have been superseded by a better (more flexible) way to do it. + +**Most** people (including those not using `matrix-nginx-proxy`), **don't need** to bother with this. + +Porting examples follow for people having more customized setups: + +- **from** `matrix_synapse_container_expose_client_api_port: true` **to** `matrix_synapse_container_client_api_host_bind_port: '127.0.0.1:8008'` + +- **from** `matrix_synapse_container_expose_federation_api_port: true` **to** `matrix_synapse_container_federation_api_plain_host_bind_port: '127.0.0.1:8048'` and possibly `matrix_synapse_container_federation_api_tls_host_bind_port: '8448'` + +- **from** `matrix_synapse_container_expose_metrics_port: true` **to** `matrix_synapse_container_metrics_api_host_bind_port: '127.0.0.1:9100'` + +- **from** `matrix_riot_web_container_expose_port: true` **to** `matrix_riot_web_container_http_host_bind_port: '127.0.0.1:8765'` + +- **from** `matrix_mxisd_container_expose_port: true` **to** `matrix_mxisd_container_http_host_bind_port: '127.0.0.1:8090'` + +- **from** `matrix_dimension_container_expose_port: true` **to** `matrix_dimension_container_http_host_bind_port: '127.0.0.1:8184'` + +- **from** `matrix_corporal_container_expose_ports: true` **to** `matrix_corporal_container_http_gateway_host_bind_port: '127.0.0.1:41080'` and possibly `matrix_corporal_container_http_api_host_bind_port: '127.0.0.1:41081'` + +- **from** `matrix_appservice_irc_container_expose_client_server_api_port: true` **to** `matrix_appservice_irc_container_http_host_bind_port: '127.0.0.1:9999'` + +- **from** `matrix_appservice_discord_container_expose_client_server_api_port: true` **to** `matrix_appservice_discord_container_http_host_bind_port: '127.0.0.1:9005'` + +As always, if you forget to remove usage of some outdated variable, the playbook will warn you. + + +# 2019-05-23 + +## (BC Break) Ansible 2.8 compatibility + +Thanks to [@danbob](https://github.com/danbob), the playbook now [supports the new Ansible 2.8](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/187). + +A manual change is required to the `inventory/hosts` file, changing the group name from `matrix-servers` to `matrix_servers` (dash to underscore). + +To avoid doing it manually, run this: +- Linux: `sed -i 's/matrix-servers/matrix_servers/g' inventory/hosts` +- Mac: `sed -i '' 's/matrix-servers/matrix_servers/g' inventory/hosts` + + +# 2019-05-21 + +## Synapse no longer required + +The playbook no longer insists on installing [Synapse](https://github.com/matrix-org/synapse) via the `matrix-synapse` role. + +If you would prefer to install Synapse another way and just use the playbook to install other services, it should be possible (`matrix_synapse_enabled: false`). + +Note that it won't necessarily be the best experience, since the playbook wires things to Synapse by default. +If you're using your own Synapse instance (especially one not running in a container), you may have to override many variables to point them to the correct place. + +Having Synapse not be a required component potentially opens the door for installing alternative Matrix homeservers. + + +## Bridges are now separate from the Synapse role + +Bridges are no longer part of the `matrix-synapse` role. +Each bridge now lives in its own separate role (`roles/matrix-bridge-*`). + +These bridge roles are independent of the `matrix-synapse` role, so it should be possible to use them with a Synapse instance installed another way (not through the playbook). + + +## Renaming inconsistently-named Synapse variables + +For better consistency, the following variables have been renamed: + +- `matrix_enable_room_list_search` was renamed to `matrix_synapse_enable_room_list_search` +- `matrix_alias_creation_rules` was renamed to `matrix_synapse_alias_creation_rules` +- `matrix_nginx_proxy_matrix_room_list_publication_rulesdata_path` was renamed to `matrix_synapse_room_list_publication_rules` + + +# 2019-05-09 + +Besides a myriad of bug fixes and minor improvements, here are the more notable (bigger) features we can announce today. + +## Mautrix Facebook/Messenger bridging support + +The playbook now supports bridging with [Facebook](https://www.facebook.com/) by installing the [mautrix-facebook](https://github.com/tulir/mautrix-facebook) bridge. This playbook functionality is available thanks to [@izissise](https://github.com/izissise). + +Additional details are available in [Setting up Mautrix Facebook bridging](docs/configuring-playbook-bridge-mautrix-facebook.md). + +## mxisd Registration feature integration + +The playbook can now help you integrate with mxisd's [Registration](https://github.com/kamax-matrix/mxisd/blob/master/docs/features/registration.md) feature. + +Learn more in [mxisd-controlled Registration](docs/configuring-playbook-mxisd.md#mxisd-controlled-registration). + + +# 2019-04-16 + +## Caddy webserver examples + +If you prefer using the [Caddy](https://caddyserver.com/) webserver instead of our own integrated nginx, we now have examples for it in the [`examples/caddy`](examples/caddy) directory + +# 2019-04-10 + +## Goofys support for other S3-compatible object stores + +Until now, you could optionally host Synapse's media repository on Amazon S3, but we now also support [using other S3-compatible object stores](docs/configuring-playbook-s3.md), + + +# 2019-04-03 + +## Ansible >= 2.5 is required + +Due to recent playbook improvements and the fact that the world keeps turning, we're bumping the [version requirement for Ansible](docs/ansible.md#supported-ansible-versions) (2.4 -> 2.5). + +We've also started building our own Docker image of Ansible ([devture/ansible](https://hub.docker.com/r/devture/ansible/)), which is useful for people who can't upgrade their local Ansible installation (see [Using Ansible via Docker](docs/ansible.md#using-ansible-via-docker)). + + +# 2019-03-19 + +## TLS support for Coturn + +We've added TLS support to the Coturn TURN server installed by the playbook by default. +The certificates from the Matrix domain will be used for the Coturn server. + +This feature is enabled by default for new installations. +To make use of TLS support for your existing Matrix server's Coturn, make sure to rebuild both Coturn and Synapse: + +```bash +ansible-playbook -i inventory/hosts setup.yml --tags=setup-coturn,setup-synapse,start +``` + +People who have an extra firewall (besides the iptables firewall, which Docker manages automatically), will need to open these additional firewall ports: `5349/tcp` (TURN over TCP) and `5349/udp` (TURN over UDP). + +People who build their own custom playbook from our roles should be aware that: + +- the `matrix-coturn` role and actually starting Coturn (e.g. `--tags=start`), requires that certificates are already put in place. For this reason, it's usually a good idea to have the `matrix-coturn` role execute after `matrix-nginx-proxy` (which retrieves the certificates). + +- there are a few variables that can help you enable TLS support for Coturn. See the `matrix-coturn` section in [group_vars/matrix-servers](./group_vars/matrix-servers). + + +# 2019-03-12 + +## matrix-nginx-proxy support for serving the base domain + +If you don't have a dedicated server for your base domain and want to set up [Server Delegation via a well-known file](docs/howto-server-delegation.md#server-delegation-via-a-well-known-file), the playbook has got you covered now. + +It's now possible for the playbook to obtain an SSL certificate and serve the necessary files for Matrix Server Delegation on your base domain. +Take a look at the new [Serving the base domain](docs/configuring-playbook-base-domain-serving.md) documentation page. + + +## (BC break) matrix-nginx-proxy data variable renamed + +`matrix_nginx_proxy_data_path` was renamed to `matrix_nginx_proxy_base_path`. + +There's a new `matrix_nginx_proxy_data_path` variable, which has a different use-purpose now (it's a subdirectory of `matrix_nginx_proxy_base_path` and is meant for storing various data files). + + +# 2019-03-10 + +## Dimension Integration Manager support + +Thanks to [NullIsNot0](https://github.com/NullIsNot0), the playbook can now (optionally) install the [Dimension](https://dimension.t2bot.io/) Integration Manager. +To learn more, see the [Setting up Dimension](docs/configuring-playbook-dimension.md) documentation page. + + +# 2019-03-07 + +## Ability to customize mxisd's email templates + +Thanks to [Sylvia van Os](https://github.com/TheLastProject), mxisd's email templates can now be customized easily. +To learn more, see the [Customizing email templates](docs/configuring-playbook-mxisd.md#customizing-email-templates) documentation page. + + +# 2019-03-05 + +## Discord bridging support + +[@Lionstiger](https://github.com/Lionstiger) has done some great work adding Discord bridging support via [matrix-appservice-discord](https://github.com/Half-Shot/matrix-appservice-discord). +To learn more, see the [Setting up Appservice Discord bridging](docs/configuring-playbook-bridge-appservice-discord.md) documentation page. + + +# 2019-02-19 + +## Renaming variables + +The following playbook variables were renamed: + +- from `host_specific_hostname_identity` to `matrix_domain` +- from `hostname_identity` to `matrix_domain` +- from `hostname_matrix` to `matrix_server_fqn_matrix` +- from `hostname_riot` to `matrix_server_fqn_riot` +- from `host_specific_matrix_ssl_lets_encrypt_support_email` to `matrix_ssl_lets_encrypt_support_email` + +Doing that, we've simplified things, made names less confusing (hopefully) and moved all variable names under the `matrix_` prefix. + + +# 2019-02-16 + +## Riot v1.0.1 support + +You can now use the brand new and redesigned Riot. + +The new version no longer has a homepage by default, so we've also removed the custom homepage that we've been installing. + +However, we still provide you with hooks to install your own `home.html` file by specifying the `matrix_riot_web_embedded_pages_home_path` variable (used to be called `matrix_riot_web_homepage_template` before). + + +# 2019-02-14 + +## Synapse v0.99.1 + +As we're moving toward Synapse v1.0, things are beginning to stabilize. +Upgrading from v0.99.0 to v0.99.1 should be painless. + +If you've been overriding the default configuration so that you can terminate TLS at the Synapse side (`matrix_synapse_no_tls: false`), you'll now have to replace this custom configuration with `matrix_synapse_tls_federation_listener_enabled: true`. The `matrix_synapse_no_tls` variable is no more. + + +# 2019-02-06 + +## Synapse v0.99 support and preparation for Synapse v1.0 + +Matrix is undergoing a lot of changes as it matures towards Synapse v1.0. +The first step is the Synapse v0.99 transitional release, which this playbook now supports. + +If you've been using this playbook successfully until now, you'd be aware that we've been doing [Server Delegation](docs/howto-server-delegation.md) using a `_matrix._tcp` DNS SRV record (as per [Configuring DNS](docs/configuring-dns.md)). + +Due to changes related to certificate file requirements that will affect us at Synapse v1.0, we'll have to stop using a **`_matrix._tcp` DNS SRV record in the future** (when Synapse goes to v1.0 - around 5th of March 2019). We **still need to keep the SRV record for now**, for backward compatibility with older Synapse versions (lower than v0.99). + +**What you need to do now** is make use of this transitional Synapse v0.99 release to **prepare your federation settings for the future**. You have 2 choices to prepare yourself for compatibility with the future Synapse v1.0: + +- (recommended) set up [Server Delegation via a well-known file](docs/howto-server-delegation.md#server-delegation-via-a-well-known-file), unless you are affected by the [Downsides of well-known-based Server Delegation](docs/howto-server-delegation.md#downsides-of-well-known-based-server-delegation). If you had previously set up the well-known `client` file, depending on how you've done it, it may be that there is nothing new required of you (besides [upgrading](docs/maintenance-upgrading-services.md)). After upgrading, you can [run a self-check](docs/maintenance-checking-services.md), which will tell you if you need to do anything extra with regard to setting up [Server Delegation via a well-known file](docs/howto-server-delegation.md#server-delegation-via-a-well-known-file). After some time, when most people have upgraded to Synapse v0.99 and older releases have disappeared, be prepared to drop your `_matrix._tcp` SRV record. + +- (more advanced) if the [Downsides of well-known-based Server Delegation](docs/howto-server-delegation.md#downsides-of-well-known-based-server-delegation) are not to your liking, **as an alternative**, you can set up [Server Delegation via a DNS SRV record](docs/howto-server-delegation.md#server-delegation-via-a-dns-srv-record-advanced). In such a case, you get to keep using your existing `_matrix._tcp` DNS SRV record forever and need to NOT set up a `/.well-known/matrix/server` file. Don't forget that you need to do certificate changes though. Follow the guide at [Server Delegation via a DNS SRV record](docs/howto-server-delegation.md#server-delegation-via-a-dns-srv-record-advanced). + + +# 2019-02-01 + +## TLS v1.3 support + +Now that the [nginx Docker image](https://hub.docker.com/_/nginx) has [added support for TLS v1.3](https://github.com/nginxinc/docker-nginx/issues/190), we have enabled that protocol by default. + +When using: + +- the **integrated nginx server**: TLS v1.3 support might not kick in immediately, because the nginx version hasn't been bumped and you may have an older build of the nginx Docker image (currently `nginx:1.15.8-alpine`). Typically, we do not re-pull images that you already have. When the nginx version gets bumped in the future, everyone will get the update. Until then, you could manually force-pull the rebuilt Docker image by running this on the server: `docker pull nginx:1.15.8-alpine`. + +- **your own external nginx server**: if your external nginx server is too old, the new configuration we generate for you in `/matrix/nginx-proxy/conf.d/` might not work anymore, because it mentions `TLSv1.3` and your nginx version might not support that. You can adjust the SSL protocol list by overriding the `matrix_nginx_proxy_ssl_protocols` variable. Learn more in the documentation page for [Using your own webserver, instead of this playbook's nginx proxy](docs/configuring-playbook-own-webserver.md) + +- **another web server**: you don't need to do anything to accommodate this change + + +# 2019-01-31 + +## IRC bridging support + +[Devon Maloney (@Plailect)](https://github.com/Plailect) has done some great work bringing IRC bridging support via [matrix-appservice-irc](https://github.com/TeDomum/matrix-appservice-irc). +To learn more, see the [Setting up Appservice IRC](docs/configuring-playbook-bridge-appservice-irc.md) documentation page. + + +# 2019-01-29 + +## Running container processes as non-root, without capabilities and read-only + +To improve security, this playbook no longer starts container processes as the `root` user. +Most containers were dropping privileges anyway, but we were trusting them with `root` privileges until they would do that. +Not anymore -- container processes now start as a non-root user (usually `matrix`) from the get-go. + +For additional security, various capabilities are also dropped (see [why it's important](https://github.com/projectatomic/atomic-site/issues/203)) for all containers. + +Additionally, most containers now use a read-only filesystem (see [why it's important](https://www.projectatomic.io/blog/2015/12/making-docker-images-write-only-in-production/)). +Containers are given write access only to the directories they need to write to. + +A minor breaking change is the `matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size` variable having being renamed to `matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size_mb` (note the `_mb` suffix). The new variable expects a number value (e.g. `25M` -> `25`). +If you weren't customizing this variable, this wouldn't affect you. + + +## matrix-mailer is now based on Exim, not Postfix + +While we would have preferred to stay with [Postfix](http://www.postfix.org/), we found out that it cannot run as a non-root user. +We've had to replace it with [Exim](https://www.exim.org/) (via the [devture/exim-relay](https://hub.docker.com/r/devture/exim-relay) container image). + +The internal `matrix-mailer` service (running in a container) now listens on port `8025` (used to be `587` before). +The playbook will update your Synapse and mxisd email settings to match (`matrix-mailer:587` -> `matrix-mailer:8025`). + +Using the [devture/exim-relay](https://hub.docker.com/r/devture/exim-relay) container image instead of [panubo/postfix](https://hub.docker.com/r/panubo/postfix/) also gives us a nice disk usage reduction (~200MB -> 8MB). + + +# 2019-01-17 + +## (BC Break) Making the playbook's roles more independent of one another + +The following change **affects people running a more non-standard setup** - external Postgres or using our roles in their own other playbook. +**Most users don't need to do anything**, besides becoming aware of the new glue variables file [`group_vars/matrix-servers`](group_vars/matrix-servers). + +Because people like using the playbook's components independently (outside of this playbook) and because it's much better for maintainability, we've continued working on separating them. +Still, we'd like to offer a turnkey solution for running a fully-featured Matrix server, so this playbook remains important for wiring up the various components. + +With the new changes, **all roles are now only dependent on the minimal `matrix-base` role**. They are no longer dependent among themselves. + +In addition, the following components can now be completely disabled (for those who want/need to): +- `matrix-coturn` by using `matrix_coturn_enabled: false` +- `matrix-mailer` by using `matrix_mailer_enabled: false` +- `matrix-postgres` by using `matrix_postgres_enabled: false` + +The following changes had to be done: + +- glue variables had to be introduced to the playbook, so it can wire together the various components. Those glue vars are stored in the [`group_vars/matrix-servers`](group_vars/matrix-servers) file. When overriding variables for a given component (role), you need to be aware of both the role defaults (`role/ROLE/defaults/main.yml`) and the role's corresponding section in the [`group_vars/matrix-servers`](group_vars/matrix-servers) file. + +- `matrix_postgres_use_external` has been superceeded by the more consistently named `matrix_postgres_enabled` variable and a few other `matrix_synapse_database_` variables. See the [Using an external PostgreSQL server (optional)](docs/configuring-playbook-external-postgres.md) documentation page for an up-to-date replacement. + +- Postgres tools (`matrix-postgres-cli` and `matrix-make-user-admin`) are no longer installed if you're not enabling the `matrix-postgres` role (`matrix_postgres_enabled: false`) + +- roles, being more independent now, are more minimal and do not do so much magic for you. People that are building their own playbook using our roles will definitely need to take a look at the [`group_vars/matrix-servers`](group_vars/matrix-servers) file and adapt their playbooks with the same (or similar) wiring logic. + + +# 2019-01-16 + +## Splitting the playbook into multiple roles + +For better maintainability, the playbook logic (which all used to reside in a single `matrix-server` role) +has been split out into a number of different roles: `matrix-synapse`, `matrix-postgres`, `matrix-riot-web`, `matrix-mxisd`, etc. (see the `roles/` directory). + +To keep the filesystem more consistent with this separation, the **Postgres data had to be relocated**. + +The default value of `matrix_postgres_data_path` was changed from `/matrix/postgres` to `/matrix/postgres/data`. The `/matrix/postgres` directory is what we consider a base path now (new variable `matrix_postgres_base_path`). **Your Postgres data files will automatically be relocated by the playbook** (`/matrix/postgres/*` -> `/matrix/postgres/data/`) when you run with `--tags=setup-all` (or `--tags=setup-postgres`). While this shouldn't cause data-loss, **it's better if you do a Postgres backup just in case**. You'd need to restart all services after this migration (`--tags=start`). + + +# 2019-01-11 + +## (BC Break) mxisd configuration changes + +To be more flexible and to support the upcoming [mxisd](https://github.com/kamax-io/mxisd) 1.3.0 (when it gets released), +we've had to redo how mxisd gets configured. + +The following variables are no longer supported by this playbook: + +- `matrix_mxisd_ldap_enabled` +- `matrix_mxisd_ldap_connection_host` +- `matrix_mxisd_ldap_connection_tls` +- `matrix_mxisd_ldap_connection_port` +- `matrix_mxisd_ldap_connection_baseDn` +- `matrix_mxisd_ldap_connection_baseDns` +- `matrix_mxisd_ldap_connection_bindDn` +- `matrix_mxisd_ldap_connection_bindDn` +- `matrix_mxisd_ldap_connection_bindPassword` +- `matrix_mxisd_ldap_filter` +- `matrix_mxisd_ldap_attribute_uid_type` +- `matrix_mxisd_ldap_attribute_uid_value` +- `matrix_mxisd_ldap_connection_bindPassword` +- `matrix_mxisd_ldap_attribute_name` +- `matrix_mxisd_ldap_attribute_threepid_email` +- `matrix_mxisd_ldap_attribute_threepid_msisdn` +- `matrix_mxisd_ldap_identity_filter` +- `matrix_mxisd_ldap_identity_medium` +- `matrix_mxisd_ldap_auth_filter` +- `matrix_mxisd_ldap_directory_filter` +- `matrix_mxisd_template_config` + +You are encouraged to use the `matrix_mxisd_configuration_extension_yaml` variable to define your own mxisd configuration additions and overrides. +Refer to the [default variables file](roles/matrix-mxisd/defaults/main.yml) for more information. + +This new way of configuring mxisd is beneficial because: + +- it lets us support all mxisd configuration options, as the playbook simply forwards them to mxisd without needing to care or understand them +- it lets you upgrade to newer mxisd versions and make use of their features, without us having to add support for them explicitly + + +# 2019-01-08 + +## (BC Break) Cronjob schedule no longer configurable + +Due to the way we manage cronjobs now, you can no longer configure the schedule they're invoked at. + +If you were previously using `matrix_ssl_lets_encrypt_renew_cron_time_definition` or `matrix_nginx_proxy_reload_cron_time_definition` +to set a custom schedule, you should note that these variables don't affect anything anymore. + +If you miss this functionality, please [open an Issue](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/new) and let us know about your use case! + + +# 2018-12-23 + +## (BC Break) More SSL certificate retrieval methods + +The playbook now lets you decide between 3 different SSL certificate retrieval methods: +- (default) obtaining free SSL certificates from Let's Encrypt +- generating self-signed SSL certificates +- managing SSL certificates manually + +Learn more in [Adjusting SSL certificate retrieval](docs/configuring-playbook-ssl-certificates.md). + +For people who use Let's Encrypt (mostly everyone, since it's the default), you'll also have to rename a variable in your configuration: + +- before: `host_specific_matrix_ssl_support_email` +- after: `host_specific_matrix_ssl_lets_encrypt_support_email` + + +## (BC Break) mxisd upgrade with multiple base DN support + +mxisd has bee upgraded to [version 1.2.2](https://github.com/kamax-matrix/mxisd/releases/tag/v1.2.2), which supports [multiple base DNs](https://github.com/kamax-matrix/mxisd/blob/v1.2.2/docs/stores/ldap.md#base). + +If you were configuring this playbook's `matrix_mxisd_ldap_connection_baseDn` variable until now (a string containing a single base DN), you'll need to change to configuring the `matrix_mxisd_ldap_connection_baseDns` variable (an array containing multiple base DNs). + +Example change: + +- before: `matrix_mxisd_ldap_connection_baseDn: OU=Users,DC=example,DC=org` +- after: `matrix_mxisd_ldap_connection_baseDns: ['OU=Users,DC=example,DC=org']` + + +# 2018-12-21 + +## Synapse 0.34.0 and Python 3 + +Synapse has been upgraded to 0.34.0 and now uses Python 3. +Based on feedback from others, running Synapse on Python 3 is supposed to decrease memory usage significantly (~2x). + + +# 2018-12-12 + +## Riot homepage customization + +You can now customize some parts of the Riot homepage (or even completely replace it with your own custom page). +See the `matrix_riot_web_homepage_` variables in `roles/matrix-riot-web/defaults/main.yml`. + + +# 2018-12-04 + +## mxisd extensibility + +The [LDAP identity store for mxisd](https://github.com/kamax-matrix/mxisd/blob/master/docs/stores/ldap.md) can now be configured easily using playbook variables (see the `matrix_mxisd_ldap_` variables in `roles/matrix-server/defaults/main.yml`). + + +# 2018-11-28 + +## More scripts + +* matrix-remove-all allows to uninstall everything with a single command +* matrix-make-user-admin allows to upgrade a user's privileges + +## LDAP auth support via matrix-synapse-ldap3 + +The playbook can now install and configure [LDAP auth support](https://github.com/matrix-org/matrix-synapse-ldap3) for you. + +Additional details are available in [Setting up the LDAP authentication password provider module](docs/configuring-playbook-ldap-auth.md). + + +# 2018-11-23 + +## Support for controlling public registration and room auto-join + +The playbook now lets you enable public registration for users (controlled via `matrix_synapse_enable_registration`). +By default, public registration is forbidden. + +You can also make people automatically get auto-joined to rooms (controlled via `matrix_synapse_auto_join_rooms`). + +## Support for changing the welcome user id (welcome bot) + +By default, `@riot-bot:matrix.org` is used to welcome newly registered users. +This can be changed to something else (or disabled) via the new `matrix_riot_web_welcome_user_id` variable. + + +# 2018-11-14 + +## Ability to set Synapse log levels + +The playbook now allows you to set the log levels used by Synapse. The default logging levels remain the same. + +You can now override following variables with any of the supported log levels listed here: https://docs.python.org/3/library/logging.html#logging-levels + +``` +matrix_synapse_log_level: "INFO" +matrix_synapse_storage_sql_log_level: "INFO" +matrix_synapse_root_log_level: "INFO" +``` + + +# 2018-11-03 + +## Customize parts of Riot's config + +You can now customize some parts of Riot's `config.json`. These playbook variables, with these default values, have been added: + +``` +matrix_riot_web_disable_custom_urls: true +matrix_riot_web_disable_guests: true +matrix_riot_web_integrations_ui_url: "https://scalar.vector.im/" +matrix_riot_web_integrations_rest_url: "https://scalar.vector.im/api" +matrix_riot_web_integrations_widgets_urls: "https://scalar.vector.im/api" +matrix_riot_web_integrations_jitsi_widget_url: "https://scalar.vector.im/api/widgets/jitsi.html" +``` + +This now allows you use a custom integrations manager like [Dimesion](https://dimension.t2bot.io). For example, if you wish to use the Dimension instance hosted at dimension.t2bot.io, you can set the following in your vars.yml file: + +``` +matrix_riot_web_integrations_ui_url: "https://dimension.t2bot.io/riot" +matrix_riot_web_integrations_rest_url: "https://dimension.t2bot.io/api/v1/scalar" +matrix_riot_web_integrations_widgets_urls: "https://dimension.t2bot.io/widgets" +matrix_riot_web_integrations_jitsi_widget_url: "https://dimension.t2bot.io/widgets/jitsi" +``` + +## SSL protocols used to serve Riot and Synapse + +There's now a new `matrix_nginx_proxy_ssl_protocols` playbook variable, which controls the SSL protocols used to serve Riot and Synapse. Its default value is `TLSv1.1 TLSv1.2`. This playbook previously used `TLSv1 TLSv1.1 TLSv1.2` to serve Riot and Synapse. + +You may wish to reenable TLSv1 if you need to access Riot in older browsers. + +Note: Currently the dockerized nginx doesn't support TLSv1.3. See https://github.com/nginxinc/docker-nginx/issues/190 for more details. + + +# 2018-11-01 + +## Postgres 11 support + +The playbook now installs [Postgres 11](https://www.postgresql.org/about/news/1894/) by default. + +If you have have an existing setup, it's likely running on an older Postgres version (9.x or 10.x). You can easily upgrade by following the [upgrading PostgreSQL guide](docs/maintenance-postgres.md#upgrading-postgresql). + + +## (BC Break) Renaming playbook variables + +Due to the large amount of features added to this playbook lately, to keep things manageable we've had to reorganize its configuration variables a bit. + +The following playbook variables were renamed: + +- from `matrix_docker_image_mxisd` to `matrix_mxisd_docker_image` +- from `matrix_docker_image_mautrix_telegram` to `matrix_mautrix_telegram_docker_image` +- from `matrix_docker_image_mautrix_whatsapp` to `matrix_mautrix_whatsapp_docker_image` +- from `matrix_docker_image_mailer` to `matrix_mailer_docker_image` +- from `matrix_docker_image_coturn` to `matrix_coturn_docker_image` +- from `matrix_docker_image_goofys` to `matrix_s3_goofys_docker_image` +- from `matrix_docker_image_riot` to `matrix_riot_web_docker_image` +- from `matrix_docker_image_nginx` to `matrix_nginx_proxy_docker_image` +- from `matrix_docker_image_synapse` to `matrix_synapse_docker_image` +- from `matrix_docker_image_postgres_v9` to `matrix_postgres_docker_image_v9` +- from `matrix_docker_image_postgres_v10` to `matrix_postgres_docker_image_v10` +- from `matrix_docker_image_postgres_latest` to `matrix_postgres_docker_image_latest` + + +# 2018-10-26 + +## Mautrix Whatsapp bridging support + +The playbook now supports bridging with [Whatsapp](https://www.whatsapp.com/) by installing the [mautrix-whatsapp](https://github.com/tulir/mautrix-whatsapp) bridge. This playbook functionality is available thanks to [@izissise](https://github.com/izissise). + +Additional details are available in [Setting up Mautrix Whatsapp bridging](docs/configuring-playbook-bridge-mautrix-whatsapp.md). + + +# 2018-10-25 + +## Support for controlling Matrix federation + +The playbook can now help you with [Controlling Matrix federation](docs/configuring-playbook-federation), should you wish to run a more private (isolated) server. + + +# 2018-10-24 + +## Disabling riot-web guests + +From now on, Riot's configuration setting `disable_guests` would be set to `true`. +The homeserver was rejecting guests anyway, so this is just a cosmetic change affecting Riot's UI. + + +# 2018-10-21 + +## Self-check maintenance command + +The playbook can now [check if services are configured correctly](docs/maintenance-checking-services.md). + + +# 2018-10-05 + +## Presence tracking made configurable + +The playbook can now enable/disable user presence-status tracking in Synapse, through the playbook's `matrix_synapse_use_presence` variable (having a default value of `true` - enabled). + +If users participate in large rooms with many other servers, disabling presence will decrease server load significantly. + + +# 2018-09-27 + +## Synapse Cache Factor made configurable + +The playbook now makes the Synapse cache factor configurable, through the playbook's `matrix_synapse_cache_factor` variable (having a default value of `0.5`). + +Changing that value allows you to potentially decrease RAM usage or to increase performance by caching more stuff. +Some information on it is available here: https://github.com/matrix-org/synapse#help-synapse-eats-all-my-ram + + +# 2018-09-26 + +## Disabling Docker container logging + +`--log-driver=none` is used for all Docker containers now. + +All these containers are started through systemd anyway and get logged in journald, so there's no need for Docker to be logging the same thing using the default `json-file` driver. Doing that was growing `/var/lib/docker/containers/..` infinitely until service/container restart. + +As a result of this, things like `docker logs matrix-synapse` won't work anymore. `journalctl -u matrix-synapse` is how one can see the logs. + + +# 2018-09-17 + +## Service discovery support + +The playbook now helps you set up [service discovery](https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery) using a `/.well-known/matrix/client` file. + +Additional details are available in [Configuring service discovery via .well-known](docs/configuring-well-known.md). + + +## (BC Break) Renaming playbook variables + +The following playbook variables were renamed: + +- from `matrix_nginx_riot_web_data_path` to `matrix_riot_web_data_path` +- from `matrix_riot_web_default_identity_server_url` to `matrix_identity_server_url` + + +# 2018-09-07 + +## Mautrix Telegram bridging support + +The playbook now supports bridging with [Telegram](https://telegram.org/) by installing the [mautrix-telegram](https://github.com/tulir/mautrix-telegram) bridge. This playbook functionality is available thanks to [@izissise](https://github.com/izissise). + +Additional details are available in [Setting up Mautrix Telegram bridging](docs/configuring-playbook-bridge-mautrix-telegram.md). + + +## Events cache size increase and configurability for Matrix Synapse + +The playbook now lets you configure Matrix Synapse's `event_cache_size` configuration via the `matrix_synapse_event_cache_size` playbook variable. + +Previously, this value was hardcoded to `"10K"`. From now on, a more reasonable default of `"100K"` is used. + + +## Password-peppering support for Matrix Synapse + +The playbook now supports enabling password-peppering for increased security in Matrix Synapse via the `matrix_synapse_password_config_pepper` playbook variable. Using a password pepper is disabled by default (just like it used to be before this playbook variable got introduced) and is not to be enabled/disabled after initial setup, as that would invalidate all existing passwords. + + +## Statistics-reporting support for Matrix Synapse + +There's now a new `matrix_synapse_report_stats` playbook variable, which controls the `report_stats` configuration option for Matrix Synapse. It defaults to `false`, so no change is required to retain your privacy. + +If you'd like to start reporting statistics about your homeserver (things like number of users, number of messages sent, uptime, load, etc.) to matrix.org, you can turn on stats reporting. + + +# 2018-08-29 + +## Changing the way SSL certificates are retrieved + +We've been using [acmetool](https://github.com/hlandau/acme) (with the [willwill/acme-docker](https://hub.docker.com/r/willwill/acme-docker/) Docker image) until now. + +Due to the Docker image being deprecated, and things looking bleak for acmetool's support of the newer ACME v2 API endpoint, we've switched to using [certbot](https://certbot.eff.org/) (with the [certbot/certbot](https://hub.docker.com/r/certbot/certbot/) Docker image). + +Simply re-running the playbook will retrieve new certificates (via certbot) for you. +To ensure you don't leave any old files behind, though, you'd better do this: + +- `systemctl stop 'matrix*'` +- stop your custom webserver, if you're running one (only affects you if you've installed with `matrix_nginx_proxy_enabled: false`) +- `mv /matrix/ssl /matrix/ssl-acmetool-delete-later` +- re-run the playbook's [installation](docs/installing.md) +- possibly delete `/matrix/ssl-acmetool-delete-later` + + +# 2018-08-21 + +## Matrix Corporal support + +The playbook can now install and configure [matrix-corporal](https://github.com/devture/matrix-corporal) for you. + +Additional details are available in [Setting up Matrix Corporal](docs/configuring-playbook-matrix-corporal.md). + + +# 2018-08-20 + +## Matrix Synapse rate limit control variables + +The following new variables can now be configured to control Matrix Synapse's rate-limiting (default values are shown below). + +```yaml +matrix_synapse_rc_messages_per_second: 0.2 +matrix_synapse_rc_message_burst_count: 10.0 +``` + +## Shared Secret Auth support via matrix-synapse-shared-secret-auth + +The playbook can now install and configure [matrix-synapse-shared-secret-auth](https://github.com/devture/matrix-synapse-shared-secret-auth) for you. + +Additional details are available in [Setting up the Shared Secret Auth password provider module](docs/configuring-playbook-shared-secret-auth.md). + + +# 2018-08-17 + +## REST auth support via matrix-synapse-rest-auth + +The playbook can now install and configure [matrix-synapse-rest-auth](https://github.com/kamax-io/matrix-synapse-rest-auth) for you. + +Additional details are available in [Setting up the REST authentication password provider module](docs/configuring-playbook-rest-auth.md). + + +## Compression improvements + +Shifted Matrix Synapse compression from happening in the Matrix Synapse, +to happening in the nginx proxy that's in front of it. + +Additionally, `riot-web` also gets compressed now (in the nginx proxy), +which drops the initial page load's size from 5.31MB to 1.86MB. + + +## Disabling some unnecessary Synapse services + +The following services are not necessary, so they have been disabled: +- on the federation port (8448): the `client` service +- on the http port (8008, exposed over 443): the old Angular `webclient` and the `federation` service + +Federation runs only on the federation port (8448) now. +The Client APIs run only on the http port (8008) now. + + +# 2018-08-15 + +## mxisd Identity Server support + +The playbook now sets up an [mxisd](https://github.com/kamax-io/mxisd) Identity Server for you by default. +Additional details are available in [Adjusting mxisd Identity Server configuration](docs/configuring-playbook-mxisd.md). + + +# 2018-08-14 + +## Email-sending support + +The playbook now configures an email-sending service (postfix) by default. +Additional details are available in [Adjusting email-sending settings](docs/configuring-playbook-email.md). + +With this, Matrix Synapse is able to send email notifications for missed messages, etc. + + +# 2018-08-08 + + +## (BC Break) Renaming playbook variables + +The following playbook variables were renamed: + +- from `matrix_max_upload_size_mb` to `matrix_synapse_max_upload_size_mb` +- from `matrix_max_log_file_size_mb` to `matrix_synapse_max_log_file_size_mb` +- from `matrix_max_log_files_count` to `matrix_synapse_max_log_files_count` +- from `docker_matrix_image` to `matrix_docker_image_synapse` +- from `docker_nginx_image` to `matrix_docker_image_nginx` +- from `docker_riot_image` to `matrix_docker_image_riot` +- from `docker_goofys_image` to `matrix_docker_image_goofys` +- from `docker_coturn_image` to `matrix_docker_image_coturn` + +If you're overriding any of them in your `vars.yml` file, you'd need to change to the new names. + + +## Renaming Ansible playbook tag + +The command for executing the whole playbook has changed. +The `setup-main` tag got renamed to `setup-all`. + + +## Docker container linking + +Changed the way the Docker containers are linked together. The ones that need to communicate with others operate in a `matrix` network now and not in the default bridge network. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0ad25db4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md index 4be3193e..096c04e3 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,142 @@ -# matrix-docker-ansible-deploy +[![Support room on Matrix](https://img.shields.io/matrix/matrix-docker-ansible-deploy:devture.com.svg?label=%23matrix-docker-ansible-deploy%3Adevture.com&logo=matrix&style=for-the-badge&server_fqdn=matrix.devture.com)](https://matrix.to/#/#matrix-docker-ansible-deploy:devture.com) [![donate](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/s.pantaleev/donate) -Our testing fork of https://github.com/spantaleev/matrix-docker-ansible-deploy +# Matrix (An open network for secure, decentralized communication) server setup using Ansible and Docker -## Getting started +## Purpose -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +This [Ansible](https://www.ansible.com/) playbook is meant to help you run your own [Matrix](http://matrix.org/) homeserver, along with the [various services](#supported-services) related to that. -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! +That is, it lets you join the Matrix network using your own `@:` identifier, all hosted on your own server (see [prerequisites](docs/prerequisites.md)). -## Add your files +We run all services in [Docker](https://www.docker.com/) containers (see [the container images we use](docs/container-images.md)), which lets us have a predictable and up-to-date setup, across multiple supported distros (see [prerequisites](docs/prerequisites.md)) and [architectures](docs/alternative-architectures.md) (x86/amd64 being recommended). -- [ ] [Create](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +[Installation](docs/README.md) (upgrades) and some maintenance tasks are automated using [Ansible](https://www.ansible.com/) (see [our Ansible guide](docs/ansible.md)). -``` -cd existing_repo -git remote add origin https://gitlab.com/GoMatrixHosting/matrix-docker-ansible-deploy.git -git branch -M main -git push -uf origin main -``` -## Integrate with your tools +## Supported services -- [ ] [Set up project integrations](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/user/project/integrations/) +Using this playbook, you can get the following services configured on your server: -## Collaborate with your team +- (optional, default) a [Synapse](https://github.com/matrix-org/synapse) homeserver - storing your data and managing your presence in the [Matrix](http://matrix.org/) network -- [ ] [Invite team members and collaborators](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Automatically merge when pipeline succeeds](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) +- (optional) [Amazon S3](https://aws.amazon.com/s3/) storage for Synapse's content repository (`media_store`) files using [Goofys](https://github.com/kahing/goofys) -## Test and Deploy +- (optional, default) [PostgreSQL](https://www.postgresql.org/) database for Synapse. [Using an external PostgreSQL server](docs/configuring-playbook-external-postgres.md) is also possible. -Use the built-in continuous integration in GitLab. +- (optional, default) a [coturn](https://github.com/coturn/coturn) STUN/TURN server for WebRTC audio/video calls -- [ ] [Get started with GitLab CI/CD](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://docs.gitlab.com/ee/user/application_security/sast/) +- (optional, default) free [Let's Encrypt](https://letsencrypt.org/) SSL certificate, which secures the connection to the Synapse server and the Element web UI -*** +- (optional, default) an [Element](https://app.element.io/) ([formerly Riot](https://element.io/previously-riot)) web UI, which is configured to connect to your own Synapse server by default -# Editing this README +- (optional, default) a [ma1sd](https://github.com/ma1uta/ma1sd) Matrix Identity server -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://gitlab.com/-/experiment/new_project_readme_content:8733cc273e622025d22ff0078c9cffce?https://www.makeareadme.com/) for this template. +- (optional, default) an [Exim](https://www.exim.org/) mail server, through which all Matrix services send outgoing email (can be configured to relay through another SMTP server) -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. +- (optional, default) an [nginx](http://nginx.org/) web server, listening on ports 80 and 443 - standing in front of all the other services. Using your own webserver [is possible](docs/configuring-playbook-own-webserver.md) -## Name -Choose a self-explaining name for your project. +- (optional, advanced) the [matrix-synapse-rest-auth](https://github.com/ma1uta/matrix-synapse-rest-password-provider) REST authentication password provider module -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. +- (optional, advanced) the [matrix-synapse-shared-secret-auth](https://github.com/devture/matrix-synapse-shared-secret-auth) password provider module -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. +- (optional, advanced) the [matrix-synapse-ldap3](https://github.com/matrix-org/matrix-synapse-ldap3) LDAP Auth password provider module + +- (optional, advanced) the [synapse-simple-antispam](https://github.com/t2bot/synapse-simple-antispam) spam checker module + +- (optional, advanced) the [Matrix Corporal](https://github.com/devture/matrix-corporal) reconciliator and gateway for a managed Matrix server + +- (optional) the [mautrix-telegram](https://github.com/tulir/mautrix-telegram) bridge for bridging your Matrix server to [Telegram](https://telegram.org/) + +- (optional) the [mautrix-whatsapp](https://github.com/tulir/mautrix-whatsapp) bridge for bridging your Matrix server to [WhatsApp](https://www.whatsapp.com/) + +- (optional) the [mautrix-facebook](https://github.com/tulir/mautrix-facebook) bridge for bridging your Matrix server to [Facebook](https://facebook.com/) + +- (optional) the [mautrix-hangouts](https://github.com/tulir/mautrix-hangouts) bridge for bridging your Matrix server to [Google Hangouts](https://en.wikipedia.org/wiki/Google_Hangouts) + +- (optional) the [mautrix-instagram](https://github.com/tulir/mautrix-instagram) bridge for bridging your Matrix server to [Instagram](https://instagram.com/) + +- (optional) the [mautrix-signal](https://github.com/tulir/mautrix-signal) bridge for bridging your Matrix server to [Signal](https://www.signal.org/) + +- (optional) the [matrix-appservice-irc](https://github.com/matrix-org/matrix-appservice-irc) bridge for bridging your Matrix server to [IRC](https://wikipedia.org/wiki/Internet_Relay_Chat) + +- (optional) the [matrix-appservice-discord](https://github.com/Half-Shot/matrix-appservice-discord) bridge for bridging your Matrix server to [Discord](https://discordapp.com/) + +- (optional) the [matrix-appservice-slack](https://github.com/matrix-org/matrix-appservice-slack) bridge for bridging your Matrix server to [Slack](https://slack.com/) + +- (optional) the [matrix-appservice-webhooks](https://github.com/turt2live/matrix-appservice-webhooks) bridge for slack compatible webhooks ([ConcourseCI](https://concourse-ci.org/), [Slack](https://slack.com/) etc. pp.) + +- (optional) the [matrix-sms-bridge](https://github.com/benkuly/matrix-sms-bridge) for bridging your Matrix server to SMS - see [docs/configuring-playbook-bridge-matrix-bridge-sms.md](docs/configuring-playbook-bridge-matrix-bridge-sms.md) for setup documentation + +- (optional) the [Heisenbridge](https://github.com/hifi/heisenbridge) for bridging your Matrix server to IRC bouncer-style - see [docs/configuring-playbook-bridge-heisenbridge.md](docs/configuring-playbook-bridge-heisenbridge.md) for setup documentation + +- (optional) the [mx-puppet-skype](https://hub.docker.com/r/sorunome/mx-puppet-skype) for bridging your Matrix server to [Skype](https://www.skype.com) - see [docs/configuring-playbook-bridge-mx-puppet-skype.md](docs/configuring-playbook-bridge-mx-puppet-skype.md) for setup documentation + +- (optional) the [mx-puppet-slack](https://hub.docker.com/r/sorunome/mx-puppet-slack) for bridging your Matrix server to [Slack](https://slack.com) - see [docs/configuring-playbook-bridge-mx-puppet-slack.md](docs/configuring-playbook-bridge-mx-puppet-slack.md) for setup documentation + +- (optional) the [mx-puppet-instagram](https://github.com/Sorunome/mx-puppet-instagram) bridge for Instagram-DMs ([Instagram](https://www.instagram.com/)) - see [docs/configuring-playbook-bridge-mx-puppet-instagram.md](docs/configuring-playbook-bridge-mx-puppet-instagram.md) for setup documentation + +- (optional) the [mx-puppet-twitter](https://github.com/Sorunome/mx-puppet-twitter) bridge for Twitter-DMs ([Twitter](https://twitter.com/)) - see [docs/configuring-playbook-bridge-mx-puppet-twitter.md](docs/configuring-playbook-bridge-mx-puppet-twitter.md) for setup documentation + +- (optional) the [mx-puppet-discord](https://github.com/matrix-discord/mx-puppet-discord) bridge for [Discord](https://discordapp.com/) - see [docs/configuring-playbook-bridge-mx-puppet-discord.md](docs/configuring-playbook-bridge-mx-puppet-discord.md) for setup documentation + +- (optional) the [mx-puppet-groupme](https://gitlab.com/robintown/mx-puppet-groupme) bridge for [GroupMe](https://groupme.com/) - see [docs/configuring-playbook-bridge-mx-puppet-groupme.md](docs/configuring-playbook-bridge-mx-puppet-groupme.md) for setup documentation + +- (optional) the [mx-puppet-steam](https://github.com/icewind1991/mx-puppet-steam) bridge for [Steam](https://steamapp.com/) - see [docs/configuring-playbook-bridge-mx-puppet-steam.md](docs/configuring-playbook-bridge-mx-puppet-steam.md) for setup documentation + +- (optional) [Email2Matrix](https://github.com/devture/email2matrix) for relaying email messages to Matrix rooms - see [docs/configuring-playbook-email2matrix.md](docs/configuring-playbook-email2matrix.md) for setup documentation + +- (optional) [Dimension](https://github.com/turt2live/matrix-dimension), an open source integrations manager for matrix clients - see [docs/configuring-playbook-dimension.md](docs/configuring-playbook-dimension.md) for setup documentation + +- (optional) [Etherpad](https://etherpad.org), an open source collaborative text editor - see [docs/configuring-playbook-etherpad.md](docs/configuring-playbook-etherpad.md) for setup documentation + +- (optional) [Jitsi](https://jitsi.org/), an open source video-conferencing platform - see [docs/configuring-playbook-jitsi.md](docs/configuring-playbook-jitsi.md) for setup documentation + +- (optional) [matrix-reminder-bot](https://github.com/anoadragon453/matrix-reminder-bot) for scheduling one-off & recurring reminders and alarms - see [docs/configuring-playbook-bot-matrix-reminder-bot.md](docs/configuring-playbook-bot-matrix-reminder-bot.md) for setup documentation + +- (optional) [Go-NEB](https://github.com/matrix-org/go-neb) multi functional bot written in Go - see [docs/configuring-playbook-bot-go-neb.md](docs/configuring-playbook-bot-go-neb.md) for setup documentation + +- (optional) [Mjolnir](https://github.com/matrix-org/mjolnir), a moderation tool for Matrix - see [docs/configuring-playbook-bot-mjolnir.md](docs/configuring-playbook-bot-mjolnir.md) for setup documentation + +- (optional) [synapse-admin](https://github.com/Awesome-Technologies/synapse-admin), a web UI tool for administrating users and rooms on your Matrix server - see [docs/configuring-playbook-synapse-admin.md](docs/configuring-playbook-synapse-admin.md) for setup documentation + +- (optional) [matrix-registration](https://github.com/ZerataX/matrix-registration), a simple python application to have a token based matrix registration - see [docs/configuring-playbook-matrix-registration.md](docs/configuring-playbook-matrix-registration.md) for setup documentation + +- (optional) the [Prometheus](https://prometheus.io) time-series database server, the Prometheus [node-exporter](https://prometheus.io/docs/guides/node-exporter/) host metrics exporter, and the [Grafana](https://grafana.com/) web UI - see [Enabling metrics and graphs (Prometheus, Grafana) for your Matrix server](docs/configuring-playbook-prometheus-grafana.md) for setup documentation + +- (optional) the [Sygnal](https://github.com/matrix-org/sygnal) push gateway - see [Setting up the Sygnal push gateway](docs/configuring-playbook-sygnal.md) for setup documentation + +- (optional) the [Hydrogen](https://github.com/vector-im/hydrogen-web) web client - see [docs/configuring-playbook-client-hydrogen.md](docs/configuring-playbook-client-hydrogen.md) for setup documentation + +Basically, this playbook aims to get you up-and-running with all the necessities around Matrix, without you having to do anything else. + +**Note**: the list above is exhaustive. It includes optional or even some advanced components that you will most likely not need. +Sticking with the defaults (which install a subset of the above components) is the best choice, especially for a new installation. +You can always re-run the playbook later to add or remove components. -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. ## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. +To configure and install Matrix on your own server, follow the [README in the docs/ directory](docs/README.md). -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. +## Changes + +This playbook evolves over time, sometimes with backward-incompatible changes. + +When updating the playbook, refer to [the changelog](CHANGELOG.md) to catch up with what's new. + + +## Support -## Contributing -State if you are open to contributions and what your requirements are for accepting them. +- Matrix room: [#matrix-docker-ansible-deploy:devture.com](https://matrix.to/#/#matrix-docker-ansible-deploy:devture.com) -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. +- IRC channel: `#matrix-docker-ansible-deploy` on the [Libera Chat](https://libera.chat/) IRC network (irc.libera.chat:6697) -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. +- GitHub issues: [spantaleev/matrix-docker-ansible-deploy/issues](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues) -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. -## License -For open source projects, say how it is licensed. +## Services by the community -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +- [etke.cc](https://etke.cc) - matrix-docker-ansible-deploy and system stuff "as a service". That service will create your matrix homeserver on your domain and server (doesn't matter if it's cloud provider or on an old laptop in the corner of your room), (optional) maintains it (server's system updates, cleanup, security adjustments, tuning, etc.; matrix homeserver updates & maintenance) and (optional) provide full-featured email service for your domain +- [GoMatrixHosting](https://gomatrixhosting.com) - matrix-docker-ansible-deploy "as a service" with [Ansible AWX](https://github.com/ansible/awx). Members can be assigned a server from DigitalOcean, or they can connect their on-premises server. This AWX system can manage the updates, configuration, import and export, backups, and monitoring on its own. For more information [see our GitLab group](https://gitlab.com/GoMatrixHosting) or come [visit us on Matrix](https://matrix.to/#/#general:gomatrixhosting.com). diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 00000000..360ce153 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,6 @@ +[defaults] +retry_files_enabled = False +stdout_callback = yaml + +[connection] +pipelining = True diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..77c5099d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,39 @@ +# Table of Contents + +- [FAQ](faq.md) - lots of questions and answers. Jump to [Prerequisites](prerequisites.md) to avoid reading too much and to just start a guided installation. + +- [Prerequisites](prerequisites.md) - go here to a guided installation using this Ansible playbook + +- [Configuring your DNS server](configuring-dns.md) + +- [Getting this playbook's source code](getting-the-playbook.md) + +- [Configuring the playbook](configuring-playbook.md) + +- [Installing](installing.md) + +- **Importing data from another server installation** + + - [Importing an existing SQLite database (from another Synapse installation)](importing-synapse-sqlite.md) (optional) + + - [Importing an existing Postgres database (from another installation)](importing-postgres.md) (optional) + + - [Importing `media_store` data files from an existing Synapse installation](importing-synapse-media-store.md) (optional) + +- [Registering users](registering-users.md) + +- [Updating users passwords](updating-users-passwords.md) + +- [Configuring service discovery via .well-known](configuring-well-known.md) + +- [Maintenance / checking if services work](maintenance-checking-services.md) + +- [Maintenance / upgrading services](maintenance-upgrading-services.md) + +- [Maintenance / Synapse](maintenance-synapse.md) + +- [Maintenance / PostgreSQL](maintenance-postgres.md) + +- [Maintenance and Troubleshooting](maintenance-and-troubleshooting.md) + +- [Uninstalling](uninstalling.md) diff --git a/docs/alternative-architectures.md b/docs/alternative-architectures.md new file mode 100644 index 00000000..80749adf --- /dev/null +++ b/docs/alternative-architectures.md @@ -0,0 +1,26 @@ +# Alternative architectures + +As stated in the [Prerequisites](prerequisites.md), currently only `x86_64` is fully supported. However, it is possible to set the target architecture, and some tools can be built on the host or other measures can be used. + +To that end add the following variable to your `vars.yaml` file: + +```yaml +matrix_architecture: +``` + +Currently supported architectures are the following: +- `amd64` (the default) +- `arm64` +- `arm32` + +so for the Raspberry Pi, the following should be in your `vars.yaml` file: + +```yaml +matrix_architecture: "arm32" +``` + +## Implementation details + +For `amd64`, prebuilt container images (see the [container images we use](container-images.md)) are used everywhere, because all images are available for this architecture. + +For other architectures, components which have a prebuilt image make use of it. If the component is not available for the specific architecture, [self-building](self-building.md) will be used. Not all components support self-building though, so your mileage may vary. diff --git a/docs/ansible.md b/docs/ansible.md new file mode 100644 index 00000000..ff513bbf --- /dev/null +++ b/docs/ansible.md @@ -0,0 +1,73 @@ + +# Running this playbook + +This playbook is meant to be run using [Ansible](https://www.ansible.com/). + +Ansible typically runs on your local computer and carries out tasks on a remote server. +If your local computer cannot run Ansible, you can also run Ansible on some server somewhere (including the server you wish to install to). + + +## Supported Ansible versions + +Ansible 2.7.1 or newer is required ([last discussion about Ansible versions](https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/743)). + +Note: Ubuntu 20.04 ships with Ansible 2.9.6 which is a buggy version (see this [bug](https://bugs.launchpad.net/ubuntu/+source/ansible/+bug/1880359)), which can't be used in combination with a host running new systemd (more details in [#517](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/517), [#669](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/669)). If this problem affects you, you can: avoid running Ubuntu 20.04 on your host; run Ansible from another machine targeting your host; or try to upgrade to a newer Ansible version (see below). + + +## Checking your Ansible version + +In most cases, you won't need to worry about the Ansible version. +The playbook will try to detect it and tell you if you're on an unsupported version. + +To manually check which version of Ansible you're on, run: `ansible --version`. + +If you're on an old version of Ansible, you should [upgrade Ansible to a newer version](#upgrading-ansible) or [use Ansible via Docker](#using-ansible-via-docker). + + +## Upgrading Ansible + +Depending on your distribution, you may be able to upgrade Ansible in a few different ways: + +- by using an additional repository (PPA, etc.), which provides newer Ansible versions. See instructions for [CentOS](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-rhel-centos-or-fedora), [Debian](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-debian), or [Ubuntu](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-ubuntu) on the Ansible website. + +- by removing the Ansible package (`yum remove ansible` or `apt-get remove ansible`) and installing via [pip](https://pip.pypa.io/en/stable/installing/) (`pip install ansible`). + +If using the `pip` method, do note that the `ansible-playbook` binary may not be on the `$PATH` (https://linuxconfig.org/linux-path-environment-variable), but in some more special location like `/usr/local/bin/ansible-playbook`. You may need to invoke it using the full path. + + +**Note**: Both of the above methods are a bad way to run system software such as Ansible. +If you find yourself needing to resort to such hacks, please consider reporting a bug to your distribution and/or switching to a sane distribution, which provides up-to-date software. + + +## Using Ansible via Docker + +Alternatively, you can run Ansible on your computer from inside a Docker container (powered by the [devture/ansible](https://hub.docker.com/r/devture/ansible/) Docker image). + +Here's a sample command to get you started (run this from the playbook's directory): + +```bash +docker run -it --rm \ +-w /work \ +-v `pwd`:/work \ +-v $HOME/.ssh/id_rsa:/root/.ssh/id_rsa:ro \ +--entrypoint=/bin/sh \ +docker.io/devture/ansible:2.9.14-r0 +``` + +The above command tries to mount an SSH key (`$HOME/.ssh/id_rsa`) into the container (at `/root/.ssh/id_rsa`). +If your SSH key is at a different path (not in `$HOME/.ssh/id_rsa`), adjust that part. + +Once you execute the above command, you'll be dropped into a `/work` directory inside a Docker container. +The `/work` directory contains the playbook's code. + +You can execute `ansible-playbook` commands as per normal now. + +### If you don't use SSH keys for authentication + +If you don't use SSH keys for authentication, simply remove that whole line (`-v $HOME/.ssh/id_rsa:/root/.ssh/id_rsa:ro`). +To authenticate at your server using a password, you need to add a package. So, when you are in the shell of the ansible docker container (the previously used `docker run -it ...` command), run: +```bash +apk add sshpass +``` +Then, to be asked for the password whenever running an `ansible-playbook` command add `--ask-pass` to the arguments of the command. + diff --git a/docs/configuring-awx-system.md b/docs/configuring-awx-system.md new file mode 100644 index 00000000..87ea228a --- /dev/null +++ b/docs/configuring-awx-system.md @@ -0,0 +1,44 @@ +# Configuring AWX System (optional) + +An AWX setup for managing multiple Matrix servers. + +This section is used in an AWX system that can create and manage multiple [Matrix](http://matrix.org/) servers. You can issue members an AWX login to their own 'organisation', which they can use to manage/configure 1 to N servers. + +Members can be assigned a server from Digitalocean, or they can connect their own on-premises server. This script is free to use in a commercial context with the 'MemberPress Plus' and 'WP Oauth Sever' addons. It can also be run in a non-commercial context. + +The AWX system is arranged into 'members' each with their own 'subscriptions'. After creating a subscription the user enters the 'provision stage' where they defined the URLs they will use, the servers location and whether or not there's already a website at the base domain. They then proceed onto the 'deploy stage' where they can configure their Matrix server. + +This system can manage the updates, configuration, import and export, backups and monitoring on its own. It is an extension of the popular deploy script [spantaleev/matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy). + + +## Other Required Playbooks + +The following repositories allow you to copy and use this setup: + +[Create AWX System](https://gitlab.com/GoMatrixHosting/create-awx-system) - Creates and configures the AWX system for you. + +[Ansible Create Delete Subscription Membership](https://gitlab.com/GoMatrixHosting/ansible-create-delete-subscription-membership) - Used by the AWX system to create memberships and subscriptions. Also includes other administrative playbooks for updates, backups and restoring servers. + +[Ansible Provision Server](https://gitlab.com/GoMatrixHosting/ansible-provision-server) - Used by AWX members to perform initial configuration of their DigitalOcean or On-Premises server. + + +## Testing Fork For This Playbook + +Updates to this section are trailed here: + +[GoMatrixHosting Matrix Docker Ansible Deploy](https://gitlab.com/GoMatrixHosting/gomatrixhosting-matrix-docker-ansible-deploy) + + +## Does I need an AWX setup to use this? How do I configure it? + +Yes, you'll need to configure an AWX instance, the [Create AWX System](https://gitlab.com/GoMatrixHosting/create-awx-system) repository makes it easy to do. Just follow the steps listed in ['/docs/Installation_AWX.md' of that repository](https://gitlab.com/GoMatrixHosting/create-awx-system/-/blob/master/docs/Installation_AWX.md). + +For simpler installation steps you can use to get started with this system, check out our minimal installation guide at ['/doc/Installation_Minimal_AWX.md of that repository'](https://gitlab.com/GoMatrixHosting/create-awx-system/-/blob/master/docs/Installation_Minimal_AWX.md). + + +## Does I need a front-end WordPress site? And a DigitalOcean account? + +You do not need a front-end WordPress site or any of the mentioned WordPress plugins to use this setup. It can be run on it's own in a non-commercial context. + +You also don't need a DigitalOcean account, but this will limit you to only being able to connect 'On-Premises' servers. + diff --git a/docs/configuring-captcha.md b/docs/configuring-captcha.md new file mode 100644 index 00000000..d137cf99 --- /dev/null +++ b/docs/configuring-captcha.md @@ -0,0 +1,23 @@ +(Adapted from the [upstream project](https://github.com/matrix-org/synapse/blob/develop/docs/CAPTCHA_SETUP.md)) + +# Overview +Captcha can be enabled for this home server. This file explains how to do that. +The captcha mechanism used is Google's [ReCaptcha](https://www.google.com/recaptcha/). This requires API keys from Google. + +## Getting keys + +Requires a site/secret key pair from: + + + +Must be a reCAPTCHA **v2** key using the "I'm not a robot" Checkbox option + +## Setting ReCaptcha Keys + +Once registered as above, set the following values: + +```yaml +matrix_synapse_enable_registration_captcha: true +matrix_synapse_recaptcha_public_key: 'YOUR_SITE_KEY' +matrix_synapse_recaptcha_private_key: 'YOUR_SECRET_KEY' +``` diff --git a/docs/configuring-dns.md b/docs/configuring-dns.md new file mode 100644 index 00000000..af2badca --- /dev/null +++ b/docs/configuring-dns.md @@ -0,0 +1,71 @@ +# Configuring your DNS server + +To set up Matrix on your domain, you'd need to do some DNS configuration. + +To use an identifier like `@:`, you don't actually need +to install anything on the actual `` server. + +You do, however need to instruct the Matrix network that Matrix services for `` are delegated +over to `matrix.`. +As we discuss in [Server Delegation](howto-server-delegation.md), there are 2 different ways to set up such delegation: + +- either by serving a `https:///.well-known/matrix/server` file (from the base domain!) +- or by using a `_matrix._tcp` DNS SRV record (don't confuse this with the `_matrix-identity._tcp` SRV record described below) + +This playbook mostly discusses the well-known file method, because it's easier to manage with regard to certificates. +If you decide to go with the alternative method ([Server Delegation via a DNS SRV record (advanced)](howto-server-delegation.md#server-delegation-via-a-dns-srv-record-advanced)), please be aware that the general flow that this playbook guides you through may not match what you need to do. + +## DNS settings for services enabled by default + +| Type | Host | Priority | Weight | Port | Target | +| ----- | ---------------------------- | -------- | ------ | ---- | ---------------------- | +| A | `matrix` | - | - | - | `matrix-server-IP` | +| CNAME | `element` | - | - | - | `matrix.` | + +Be mindful as to how long it will take for the DNS records to propagate. + +If you are using Cloudflare DNS, make sure to disable the proxy and set all records to `DNS only`. Otherwise, fetching certificates will fail. + +## DNS settings for optional services/features + +| Type | Host | Priority | Weight | Port | Target | +| ----- | ---------------------------- | -------- | ------ | ---- | ---------------------- | +| SRV | `_matrix-identity._tcp` | 10 | 0 | 443 | `matrix.` | +| CNAME | `dimension` | - | - | - | `matrix.` | +| CNAME | `jitsi` | - | - | - | `matrix.` | +| CNAME | `stats` | - | - | - | `matrix.` | +| CNAME | `goneb` | - | - | - | `matrix.` | +| CNAME | `sygnal` | - | - | - | `matrix.` | +| CNAME | `hydrogen` | - | - | - | `matrix.` | + +## Subdomains setup + +As the table above illustrates, you need to create 2 subdomains (`matrix.` and `element.`) and point both of them to your new server's IP address (DNS `A` record or `CNAME` record is fine). + +The `element.` subdomain may be necessary, because this playbook installs the [Element](https://github.com/vector-im/element-web) web client for you. +If you'd rather instruct the playbook not to install Element (`matrix_client_element_enabled: false` when [Configuring the playbook](configuring-playbook.md) later), feel free to skip the `element.` DNS record. + +The `dimension.` subdomain may be necessary, because this playbook could install the [Dimension integrations manager](http://dimension.t2bot.io/) for you. Dimension installation is disabled by default, because it's only possible to install it after the other Matrix services are working (see [Setting up Dimension](configuring-playbook-dimension.md) later). If you do not wish to set up Dimension, feel free to skip the `dimension.` DNS record. + +The `jitsi.` subdomain may be necessary, because this playbook could install the [Jitsi video-conferencing platform](https://jitsi.org/) for you. Jitsi installation is disabled by default, because it may be heavy and is not a core required component. To learn how to install it, see our [Jitsi](configuring-playbook-jitsi.md) guide. If you do not wish to set up Jitsi, feel free to skip the `jitsi.` DNS record. + +The `stats.` subdomain may be necessary, because this playbook could install [Grafana](https://grafana.com/) and setup performance metrics for you. Grafana installation is disabled by default, it is not a core required component. To learn how to install it, see our [metrics and graphs guide](configuring-playbook-prometheus-grafana.md). If you do not wish to set up Grafana, feel free to skip the `stats.` DNS record. It is possible to install Prometheus without installing Grafana, this would also not require the `stats.` subdomain. + +The `goneb.` subdomain may be necessary, because this playbook could install the [Go-NEB](https://github.com/matrix-org/go-neb) bot. The installation of Go-NEB is disabled by default, it is not a core required component. To learn how to install it, see our [configuring Go-NEB guide](configuring-playbook-bot-go-neb.md). If you do not wish to set up Go-NEB, feel free to skip the `goneb.` DNS record. + +The `sygnal.` subdomain may be necessary, because this playbook could install the [Sygnal](https://github.com/matrix-org/sygnal) push gateway. The installation of Sygnal is disabled by default, it is not a core required component. To learn how to install it, see our [configuring Sygnal guide](configuring-playbook-sygnal.md). If you do not wish to set up Sygnal (you probably don't, unless you're also developing/building your own Matrix apps), feel free to skip the `sygnal.` DNS record. + +The `hydrogen.` subdomain may be necessary, because this playbook could install the [Hydrogen](https://github.com/vector-im/hydrogen-web) web client. The installation of Hydrogen is disabled by default, it is not a core required component. To learn how to install it, see our [configuring Hydrogen guide](configuring-playbook-client-hydrogen.md). If you do not wish to set up Hydrogen, feel free to skip the `hydrogen.` DNS record. + + +## `_matrix-identity._tcp` SRV record setup + +To make the [ma1sd](https://github.com/ma1uta/ma1sd) Identity Server (which this playbook installs for you) enable its federation features, set up an SRV record that looks like this: +- Name: `_matrix-identity._tcp` (use this text as-is) +- Content: `10 0 443 matrix.` (replace `` with your own) + +This is an optional feature. See [ma1sd's documentation](https://github.com/ma1uta/ma1sd/wiki/mxisd-and-your-privacy#choices-are-never-easy) for information on the privacy implications of setting up this SRV record. + +Note: This `_matrix-identity._tcp` SRV record for the identity server is different from the `_matrix._tcp` that can be used for Synapse delegation. See [howto-server-delegation.md](howto-server-delegation.md) for more information about delegation. + +When you're done with the DNS configuration and ready to proceed, continue with [Getting the playbook](getting-the-playbook.md). diff --git a/docs/configuring-playbook-base-domain-serving.md b/docs/configuring-playbook-base-domain-serving.md new file mode 100644 index 00000000..a5df4ece --- /dev/null +++ b/docs/configuring-playbook-base-domain-serving.md @@ -0,0 +1,52 @@ +# Serving the base domain + +This playbook sets up services on your Matrix server (`matrix.DOMAIN`). +To have this server officially be responsible for Matrix services for the base domain (`DOMAIN`), you need to set up [Server Delegation](howto-server-delegation.md). +This is normally done by [configuring well-known](configuring-well-known.md) files on the base domain. + +People who don't have a separate server to dedicate to the base domain have trouble arranging this. + +Usually, there are 2 options: + +- either get a separate server for the base domain, just for serving the files necessary for [Server Delegation via a well-known file](howto-server-delegation.md#server-delegation-via-a-well-known-file) + +- or, arrange for the Matrix server to serve the base domain. This either involves you [using your own webserver](configuring-playbook-own-webserver.md) or making the integrated webserver (`matrix-nginx-proxy`) serve the base domain for you. + +This documentation page tells you how to do the latter. With some easy changes, we make it possible to serve the base domain from the Matrix server via the integrated webserver (`matrix-nginx-proxy`). + +Just **adjust your DNS records**, so that your base domain is pointed to the Matrix server's IP address (using a DNS `A` record) **and then use the following configuration**: + +```yaml +matrix_nginx_proxy_base_domain_serving_enabled: true +``` + +Doing this, the playbook will: + +- obtain an SSL certificate for the base domain, just like it does for all other domains (see [how we handle SSL certificates](configuring-playbook-ssl-certificates.md)) + +- serve the `/.well-known/matrix/*` files which are necessary for [Federation Server Discovery](configuring-well-known.md#introduction-to-client-server-discovery) (also see [Server Delegation](howto-server-delegation.md)) and [Client-Server discovery](configuring-well-known.md#introduction-to-client-server-discovery) + +- serve a simple homepage at `https://DOMAIN` with content `Hello from DOMAIN` (configurable via the `matrix_nginx_proxy_base_domain_homepage_template` variable). You can also [serve a more complicated static website](#serving-a-static-website-at-the-base-domain). + + +## Serving a static website at the base domain + +By default, when "serving the base domain" is enabled, the playbook hosts a simple `index.html` webpage in `/matrix/nginx-proxy/data/matrix-domain`. +The content of this page is taken from the `matrix_nginx_proxy_base_domain_homepage_template` variable. + +If you'd like to host your own static website (more than a single `index.html` page) at the base domain, you can disable the creation of this default `index.html` page like this: + +```yaml +matrix_nginx_proxy_base_domain_homepage_enabled: false +``` + +With this configuration, Ansible will no longer mess around with the `/matrix/nginx-proxy/data/matrix-domain/index.html` file. + +You are then free to upload any static website files to `/matrix/nginx-proxy/data/matrix-domain` and they will get served at the base domain. + + +## Serving a more complicated website at the base domain + +If you'd like to serve an even more complicated (dynamic) website from the Matrix server, relying on the playbook to serve the base domain is not the best choice. + +Instead, we recommend that you switch to [using your own webserver](configuring-playbook-own-webserver.md) (preferrably nginx). You can then make that webserver host anything you wish, and still easily plug in Matrix services into it. diff --git a/docs/configuring-playbook-bot-go-neb.md b/docs/configuring-playbook-bot-go-neb.md new file mode 100644 index 00000000..ab84e017 --- /dev/null +++ b/docs/configuring-playbook-bot-go-neb.md @@ -0,0 +1,226 @@ +# Setting up Go-NEB (optional) + +The playbook can install and configure [Go-NEB](https://github.com/matrix-org/go-neb) for you. + +Go-NEB is a Matrix bot written in Go. It is the successor to Matrix-NEB, the original Matrix bot written in Python. + +See the project's [documentation](https://github.com/matrix-org/go-neb) to learn what it does and why it might be useful to you. + + +## Registering the bot user + +The playbook does not automatically create users for you. The bot requires at least 1 access token to be able to connect to your homeserver. + +You **need to register the bot user manually** before setting up the bot. + +Choose a strong password for the bot. You can generate a good password with a command like this: `pwgen -s 64 1`. + +You can use the playbook to [register a new user](registering-users.md): + +``` +ansible-playbook -i inventory/hosts setup.yml --extra-vars='username=bot.go-neb password=PASSWORD_FOR_THE_BOT admin=no' --tags=register-user +``` + + +## Getting an access token + +If you use curl, you can get an access token like this: + +``` +curl -X POST --header 'Content-Type: application/json' -d '{ + "identifier": { "type": "m.id.user", "user": "bot.go-neb" }, + "password": "a strong password", + "type": "m.login.password" +}' 'https://matrix.YOURDOMAIN/_matrix/client/r0/login' +``` + +Alternatively, you can use a full-featured client (such as Element) to log in and get the access token from there (note: don't log out from the client as that will invalidate the token), but doing so might lead to decryption problems. That warning comes from [here](https://github.com/matrix-org/go-neb#quick-start). + + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file (adapt to your needs): + +```yaml +matrix_bot_go_neb_enabled: true + +# You need at least 1 client. +# Use the access token you obtained in the step above. +matrix_bot_go_neb_clients: + - UserID: "@goneb:{{ matrix_domain }}" + AccessToken: "MDASDASJDIASDJASDAFGFRGER" + DeviceID: "DEVICE1" + HomeserverURL: "{{ matrix_homeserver_container_url }}" + Sync: true + AutoJoinRooms: true + DisplayName: "Go-NEB!" + AcceptVerificationFromUsers: [":{{ matrix_domain }}"] + + - UserID: "@another_goneb:{{ matrix_domain }}" + AccessToken: "MDASDASJDIASDJASDAFGFRGER" + DeviceID: "DEVICE2" + HomeserverURL: "{{ matrix_homeserver_container_url }}" + Sync: false + AutoJoinRooms: false + DisplayName: "Go-NEB!" + AcceptVerificationFromUsers: ["^@admin:{{ matrix_domain }}"] + +# Optional, for use with the github_cmd, github_webhooks or jira services +matrix_bot_go_neb_realms: + - ID: "github_realm" + Type: "github" + Config: {} # No need for client ID or Secret as Go-NEB isn't generating OAuth URLs + +# Optional. The list of *authenticated* sessions which Go-NEB is aware of. +matrix_bot_go_neb_sessions: + - SessionID: "your_github_session" + RealmID: "github_realm" + UserID: "@YOUR_USER_ID:{{ matrix_domain }}" # This needs to be the username of the person that's allowed to use the !github commands + Config: + # Populate these fields by generating a "Personal Access Token" on github.com + AccessToken: "YOUR_GITHUB_ACCESS_TOKEN" + Scopes: "admin:org_hook,admin:repo_hook,repo,user" + +# The list of services which Go-NEB is aware of. +# Delete or modify this list as appropriate. +# See the docs for /configureService for the full list of options: +# https://matrix-org.github.io/go-neb/pkg/github.com/matrix-org/go-neb/api/index.html#ConfigureServiceRequest +# You need at least 1 service. +matrix_bot_go_neb_services: + - ID: "echo_service" + Type: "echo" + UserID: "@goneb:{{ matrix_domain }}" + Config: {} + +# Can be obtained from https://developers.giphy.com/dashboard/ + - ID: "giphy_service" + Type: "giphy" + UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client + Config: + api_key: "qwg4672vsuyfsfe" + use_downsized: false + +# This service has been dead for over a year :/ + - ID: "guggy_service" + Type: "guggy" + UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client + Config: + api_key: "2356saaqfhgfe" + +# API Key via https://developers.google.com/custom-search/v1/introduction +# CX via http://www.google.com/cse/manage/all +# https://stackoverflow.com/questions/6562125/getting-a-cx-id-for-custom-search-google-api-python +# 'Search the entire web' and 'Image search' enabled for best results + - ID: "google_service" + Type: "google" + UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client + Config: + api_key: "AIzaSyA4FD39m9" + cx: "AIASDFWSRRtrtr" + +# Get a key via https://api.imgur.com/oauth2/addclient +# Select "oauth2 without callback url" + - ID: "imgur_service" + Type: "imgur" + UserID: "@imgur:{{ matrix_domain }}" # requires a Syncing client + Config: + client_id: "AIzaSyA4FD39m9" + client_secret: "somesecret" + + - ID: "wikipedia_service" + Type: "wikipedia" + UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client + Config: + + - ID: "rss_service" + Type: "rssbot" + UserID: "@another_goneb:{{ matrix_domain }}" + Config: + feeds: + "http://lorem-rss.herokuapp.com/feed?unit=second&interval=60": + rooms: ["!qmElAGdFYCHoCJuaNt:{{ matrix_domain }}"] + must_include: + author: + - author1 + description: + - lorem + - ipsum + must_not_include: + title: + - Lorem + - Ipsum + + - ID: "github_cmd_service" + Type: "github" + UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client + Config: + RealmID: "github_realm" + + # Make sure your BASE_URL can be accessed by Github! + - ID: "github_webhook_service" + Type: "github-webhook" + UserID: "@another_goneb:{{ matrix_domain }}" + Config: + RealmID: "github_realm" + ClientUserID: "@YOUR_USER_ID:{{ matrix_domain }}" # needs to be an authenticated user so Go-NEB can create webhooks. Check the UserID field in the github_realm in matrix_bot_go_neb_sessions. + Rooms: + "!someroom:id": + Repos: + "matrix-org/synapse": + Events: ["push", "issues"] + "matrix-org/dendron": + Events: ["pull_request"] + "!anotherroom:id": + Repos: + "matrix-org/synapse": + Events: ["push", "issues"] + "matrix-org/dendron": + Events: ["pull_request"] + + - ID: "slackapi_service" + Type: "slackapi" + UserID: "@slackapi:{{ matrix_domain }}" + Config: + Hooks: + "hook1": + RoomID: "!someroom:id" + MessageType: "m.text" # default is m.text + + - ID: "alertmanager_service" + Type: "alertmanager" + UserID: "@alertmanager:{{ matrix_domain }}" + Config: + # This is for information purposes only. It should point to Go-NEB path as follows: + # `/services/hooks/` + # Where in this case "service ID" is "alertmanager_service" + # Make sure your BASE_URL can be accessed by the Alertmanager instance! + webhook_url: "http://localhost/services/hooks/YWxlcnRtYW5hZ2VyX3NlcnZpY2U" + # Each room will get the notification with the alert rendered with the given template + rooms: + "!someroomid:domain.tld": + text_template: "{{range .Alerts -}} [{{ .Status }}] {{index .Labels \"alertname\" }}: {{index .Annotations \"description\"}} {{ end -}}" + html_template: "{{range .Alerts -}} {{ $severity := index .Labels \"severity\" }} {{ if eq .Status \"firing\" }} {{ if eq $severity \"critical\"}} [FIRING - CRITICAL] {{ else if eq $severity \"warning\"}} [FIRING - WARNING] {{ else }} [FIRING - {{ $severity }}] {{ end }} {{ else }} [RESOLVED] {{ end }} {{ index .Labels \"alertname\"}} : {{ index .Annotations \"description\"}} source
{{end -}}" + msg_type: "m.text" # Must be either `m.text` or `m.notice` +``` + + +## Installing + +Don't forget to add `goneb.` to DNS as described in [Configuring DNS](configuring-dns.md) before running the playbook. + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +To use the bot, invite it to any existing Matrix room (`/invite @whatever_you_chose:DOMAIN` where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain, make sure you have permission from the room owner if that's not you). + +Basic usage is like this: `!echo hi` or `!imgur puppies` or `!giphy matrix` + +If you enabled the github_cmd service you can get the supported commands via `!github help` + +You can also refer to the upstream [Documentation](https://github.com/matrix-org/go-neb). diff --git a/docs/configuring-playbook-bot-matrix-reminder-bot.md b/docs/configuring-playbook-bot-matrix-reminder-bot.md new file mode 100644 index 00000000..c3c8e8bb --- /dev/null +++ b/docs/configuring-playbook-bot-matrix-reminder-bot.md @@ -0,0 +1,59 @@ +# Setting up matrix-reminder-bot (optional) + +The playbook can install and configure [matrix-reminder-bot](https://github.com/anoadragon453/matrix-reminder-bot) for you. + +It's a bot you can use to **schedule one-off & recurring reminders and alarms**. + +See the project's [documentation](https://github.com/anoadragon453/matrix-reminder-bot#usage) to learn what it does and why it might be useful to you. + + +## Registering the bot user + +By default, the playbook will set up the bot with a username like this: `@bot.matrix-reminder-bot:DOMAIN`. + +(to use a different username, adjust the `matrix_bot_matrix_reminder_bot_matrix_user_id_localpart` variable). + +You **need to register the bot user manually** before setting up the bot. You can use the playbook to [register a new user](registering-users.md): + +``` +ansible-playbook -i inventory/hosts setup.yml --extra-vars='username=bot.matrix-reminder-bot password=PASSWORD_FOR_THE_BOT admin=no' --tags=register-user +``` + +Choose a strong password for the bot. You can generate a good password with a command like this: `pwgen -s 64 1`. + + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file: + +```yaml +matrix_bot_matrix_reminder_bot_enabled: true + +# Adjust this to whatever password you chose when registering the bot user +matrix_bot_matrix_reminder_bot_matrix_user_password: PASSWORD_FOR_THE_BOT + +# Adjust this to your timezone +matrix_bot_matrix_reminder_bot_reminders_timezone: Europe/London +``` + + +## Installing + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +To use the bot, start a chat with `@bot.matrix-reminder-bot:DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). + +You can also add the bot to any existing Matrix room (`/invite @bot.matrix-reminder-bot:DOMAIN`). + +Basic usage is like this: `!remindme in 2 minutes; This is a test` + +Send `!help commands` to the room to see the bot's help menu for additional commands. + +You can also refer to the upstream [Usage documentation](https://github.com/anoadragon453/matrix-reminder-bot#usage). diff --git a/docs/configuring-playbook-bot-mjolnir.md b/docs/configuring-playbook-bot-mjolnir.md new file mode 100644 index 00000000..5ddb2ad3 --- /dev/null +++ b/docs/configuring-playbook-bot-mjolnir.md @@ -0,0 +1,132 @@ +# Setting up Mjolnir (optional) + +The playbook can install and configure the [Mjolnir](https://github.com/matrix-org/mjolnir) moderation bot for you. + +See the project's [documentation](https://github.com/matrix-org/mjolnir) to learn what it does and why it might be useful to you. + + +## 1. Register the bot account + +The playbook does not automatically create users for you. The bot requires an access token to be able to connect to your homeserver. + +You **need to register the bot user manually** before setting up the bot. + +Choose a strong password for the bot. You can generate a good password with a command like this: `pwgen -s 64 1`. + +You can use the playbook to [register a new user](registering-users.md): + +``` +ansible-playbook -i inventory/hosts setup.yml --extra-vars='username=bot.mjolnir password=PASSWORD_FOR_THE_BOT admin=no' --tags=register-user +``` + +If you would like Mjolnir to be able to deactivate users, move aliases, shutdown rooms, etc then it must be a server admin so you need to change `admin=no` to `admin=yes` in the command above. + + +## 2. Get an access token + +If you use curl, you can get an access token like this: + +``` +curl -X POST --header 'Content-Type: application/json' -d '{ + "identifier": { "type": "m.id.user", "user": "bot.mjolnir" }, + "password": "PASSWORD_FOR_THE_BOT", + "type": "m.login.password" +}' 'https://matrix.DOMAIN/_matrix/client/r0/login' +``` + +Alternatively, you can use a full-featured client (such as Element) to log in and get the access token from there (note: don't log out from the client as that will invalidate the token). + + +## 3. Make sure the account is free from rate limiting + +You will need to prevent Synapse from rate limiting the bot's account. This is not an optional step. If you do not do this step Mjolnir will crash. [Currently there is no Synapse config option for this](https://github.com/matrix-org/synapse/issues/6286) so you have to manually edit the Synapse database. Manually editing the Synapse database is rarely a good idea but in this case it is required. Please ask for help if you are uncomfortable with these steps. + +1. Copy the statement below into a text editor. + + ``` + INSERT INTO ratelimit_override VALUES ('@bot.mjolnir:DOMAIN', 0, 0); + ``` + +1. Change the username (`@bot.mjolnir:DOMAIN`) to the username you used when you registered the bot's account. You must change `DOMAIN` to your server's domain. + +1. Get a database terminal by following these steps: [maintenance-postgres.md#getting-a-database-terminal](maintenance-postgres.md#getting-a-database-terminal) + +1. Connect to Synapse's database by typing `\connect synapse` into the database terminal + +1. Paste in the `INSERT INTO` command that you edited and press enter. + +You can run `SELECT * FROM ratelimit_override;` to see if it worked. If the output looks like this: + +``` + user_id | messages_per_second | burst_count +-----------------------+---------------------+------------- + @bot.mjolnir:raim.ist | 0 | 0` +``` +then you did it correctly. + + +## 4. Create a management room + +Using your own account, create a new invite only room that you will use to manage the bot. This is the room where you will see the status of the bot and where you will send commands to the bot, such as the command to ban a user from another room. Anyone in this room can control the bot so it is important that you only invite trusted users to this room. The room must be unencrypted since the playbook does not support installing Pantalaimon yet. + +Once you have created the room you need to copy the room ID so you can tell the bot to use that room. In Element you can do this by going to the room's settings, clicking Advanced, and then coping the internal room ID. The room ID will look something like `!QvgVuKq0ha8glOLGMG:DOMAIN`. + +Finally invite the `@bot.mjolnir:DOMAIN` account you created earlier into the room. + + +## 5. Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file (adapt to your needs): + +You must replace `ACCESS_TOKEN_FROM_STEP_2_GOES_HERE` and `ROOM_ID_FROM_STEP_4_GOES_HERE` with the your own values. + +```yaml +matrix_bot_mjolnir_enabled: true + +matrix_bot_mjolnir_access_token: "ACCESS_TOKEN_FROM_STEP_2_GOES_HERE" + +matrix_bot_mjolnir_management_room: "ROOM_ID_FROM_STEP_4_GOES_HERE" +``` + +## 6. Adding mjolnir synapse antispam module (optional) + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file (adapt to your needs): + + +```yaml +matrix_synapse_ext_spam_checker_mjolnir_antispam_enabled: true +matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_invites: true +matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_messages: false +matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_usernames: false +matrix_synapse_ext_spam_checker_mjolnir_antispam_config_ban_lists: [] +``` + + +## 7. Installing + +After configuring the playbook, run the [installation](installing.md) command: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +You can refer to the upstream [documentation](https://github.com/matrix-org/mjolnir) for additional ways to use and configure mjolnir. Check out their [quickstart guide](https://github.com/matrix-org/mjolnir#quickstart-guide) for some basic commands you can give to the bot. + +You can configure additional options by adding the `matrix_bot_mjolnir_configuration_extension_yaml` variable to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file. + +For example to change mjolnir's `recordIgnoredInvites` option to `true` you would add the following to your `vars.yml` file. + +```yaml +matrix_bot_mjolnir_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_bot_mjolnir_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_bot_mjolnir_configuration_yaml`. + recordIgnoredInvites: true +``` diff --git a/docs/configuring-playbook-bridge-appservice-discord.md b/docs/configuring-playbook-bridge-appservice-discord.md new file mode 100644 index 00000000..82a2edc2 --- /dev/null +++ b/docs/configuring-playbook-bridge-appservice-discord.md @@ -0,0 +1,53 @@ +# Setting up Appservice Discord (optional) + +**Note**: bridging to [Discord](https://discordapp.com/) can also happen via the [mx-puppet-discord](configuring-playbook-bridge-mx-puppet-discord.md) bridge supported by the playbook. + +The playbook can install and configure [matrix-appservice-discord](https://github.com/Half-Shot/matrix-appservice-discord) for you. + +See the project's [documentation](https://github.com/Half-Shot/matrix-appservice-discord/blob/master/README.md) to learn what it does and why it might be useful to you. + + +## Setup Instructions + +Instructions loosely based on [this](https://github.com/Half-Shot/matrix-appservice-discord#setting-up). + +1. Create a Discord Application [here](https://discordapp.com/developers/applications). +2. Retrieve Client ID. +3. Create a bot from the Bot tab and retrieve the Bot token. +4. Enable the bridge with the following configuration in your `vars.yml` file: + +```yaml +matrix_appservice_discord_enabled: true +matrix_appservice_discord_client_id: "YOUR DISCORD APP CLIENT ID" +matrix_appservice_discord_bot_token: "YOUR DISCORD APP BOT TOKEN" +``` + +5. If you've already installed Matrix services using the playbook before, you'll need to re-run it (`--tags=setup-all,start`). If not, proceed with [configuring other playbook services](configuring-playbook.md) and then with [Installing](installing.md). Get back to this guide once ready. +6. Retrieve Discord invite link from the `{{ matrix_appservice_discord_config_path }}/invite_link` file on the server (this defaults to `/matrix/appservice-discord/config/invite_link`). You need to peek at the file on the server via SSH, etc., because it's not available via HTTP(S). +7. Invite the Bot to Discord servers you wish to bridge. Administrator permission is recommended. +8. Room addresses follow this syntax: `#_discord_guildid_channelid`. You can easily find the guild and channel ids by logging into Discord in a browser and opening the desired channel. The URL will have this format: `discordapp.com/channels/guild_id/channel_id`. Once you have figured out the appropriate room addrss, you can join by doing `/join #_discord_guildid_channelid` in your Matrix client. + +Other configuration options are available via the `matrix_appservice_discord_configuration_extension_yaml` variable. + + +## Getting Administrator access in a room + +By default, you won't have Administrator access in rooms created by the bridge. + +To [adjust room access privileges](#adjusting-room-access-privileges) or do various other things (change the room name subsequently, etc.), you'd wish to become an Administrator. + +There's the Discord bridge's guide for [setting privileges on bridge managed rooms](https://github.com/Half-Shot/matrix-appservice-discord/blob/master/docs/howto.md#set-privileges-on-bridge-managed-rooms). To do the same with our container setup, run the following command on the server: + +```sh +docker exec -it matrix-appservice-discord \ +/bin/sh -c 'cp /cfg/registration.yaml /tmp/discord-registration.yaml && cd /tmp && node /build/tools/adminme.js -c /cfg/config.yaml -m "!ROOM_ID:SERVER" -u "@USER:SERVER" -p 100' +``` + + +## Adjusting room access privileges + +All rooms created by the bridge are **listed publicly** in your server's directory and **joinable by everyone** by default. + +To get more control of them, [make yourself a room Administrator](#getting-administrator-access-in-a-room) first. + +You can then unlist the room from the directory and change the join rules. diff --git a/docs/configuring-playbook-bridge-appservice-irc.md b/docs/configuring-playbook-bridge-appservice-irc.md new file mode 100644 index 00000000..93e0fb36 --- /dev/null +++ b/docs/configuring-playbook-bridge-appservice-irc.md @@ -0,0 +1,61 @@ +# Setting up Appservice IRC (optional) + +**Note**: bridging to [IRC](https://en.wikipedia.org/wiki/Internet_Relay_Chat) can also happen via the [Heisenbridge](configuring-playbook-bridge-heisenbridge.md) bridge supported by the playbook. + +The playbook can install and configure the [matrix-appservice-irc](https://github.com/matrix-org/matrix-appservice-irc) bridge for you. + +See the project's [documentation](https://github.com/matrix-org/matrix-appservice-irc/blob/master/HOWTO.md) to learn what it does and why it might be useful to you. + +You'll need to use the following playbook configuration: + +```yaml +matrix_appservice_irc_enabled: true + +matrix_appservice_irc_ircService_servers: + irc.example.com: + name: "ExampleNet" + port: 6697 + ssl: true + sasl: false + allowExpiredCerts: false + sendConnectionMessages: true + botConfig: + enabled: true + nick: "MatrixBot" + joinChannelsIfNoUsers: true + privateMessages: + enabled: true + federate: true + dynamicChannels: + enabled: true + createAlias: true + published: true + joinRule: public + groupId: +myircnetwork:localhost + federate: true + aliasTemplate: "#irc_$CHANNEL" + membershipLists: + enabled: false + floodDelayMs: 10000 + global: + ircToMatrix: + initial: false + incremental: false + matrixToIrc: + initial: false + incremental: false + matrixClients: + userTemplate: "@irc_$NICK" + displayName: "$NICK (IRC)" + joinAttempts: -1 + ircClients: + nickTemplate: "$DISPLAY[m]" + allowNickChanges: true + maxClients: 30 + idleTimeout: 10800 + reconnectIntervalMs: 5000 + concurrentReconnectLimit: 50 + lineLimit: 3 +``` + +You then need to start a chat with `@irc_bot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). diff --git a/docs/configuring-playbook-bridge-appservice-slack.md b/docs/configuring-playbook-bridge-appservice-slack.md new file mode 100644 index 00000000..7d45d34d --- /dev/null +++ b/docs/configuring-playbook-bridge-appservice-slack.md @@ -0,0 +1,112 @@ +# Setting up Appservice Slack (optional) + +**Note**: bridging to [Slack](https://slack.com) can also happen via the [mx-puppet-slack](configuring-playbook-bridge-mx-puppet-slack.md) bridge supported by the playbook. + +The playbook can install and configure [matrix-appservice-slack](https://github.com/matrix-org/matrix-appservice-slack) for you. + +See the project's [documentation](https://github.com/matrix-org/matrix-appservice-slack/blob/master/README.md) to learn what it does and why it might be useful to you. + +## Setup Instructions: + +loosely based on [this](https://github.com/matrix-org/matrix-appservice-slack#Setup) + +1. Create a new Matrix room to act as the administration control room. Note its internal room ID. This can +be done in Riot by making a message, opening the options for that message and choosing "view source". The +room ID will be displayed near the top. +2. Enable the bridge with the following configuration in your `vars.yml` file: + +```yaml +matrix_appservice_slack_enabled: true +matrix_appservice_slack_control_room_id: "Your matrix admin room id" +``` + +3. If you've already installed Matrix services using the playbook before, you'll need to re-run it (`--tags=setup-all,start`). If not, proceed with [configuring other playbook services](configuring-playbook.md) and then with [Installing](installing.md). Get back to this guide once ready. +4. Invite the bridge bot user into the admin room: + +``` + /invite @slackbot:MY.DOMAIN +``` + +Note that the bot's domain is your server's domain **without the `matrix.` prefix.** + +5. Create a Classic Slack App [here](https://api.slack.com/apps?new_classic_app=1). + + Name the app "matrixbot" (or anything else you'll remember). + + Select the team/workspace this app will belong to. + + Click on bot users and add a new bot user. We will use this account to bridge the the rooms. + +6. Click on Event Subscriptions and enable them and use the request url `https://matrix.DOMAIN/appservice-slack`. Then add the following events and save: + + Bot User Events: + + - team_domain_change + - message.channels + - message.groups (if you want to bridge private channels) + - reaction_added + - reaction_removed + +7. Click on OAuth & Permissions and add the following scopes: + + - chat:write:bot + - users:read + - reactions:write + + If you want to bridge files, also add the following: + + - files:write:user + + Note: In order to make Slack files visible to matrix users, this bridge will make Slack files visible to anyone with the url (including files in private channels). This is different than the current behavior in Slack, which only allows authenticated access to media posted in private channels. See MSC701 for details. + +8. Click on Install App and Install App to Workspace. Note the access tokens shown. You will need the Bot User OAuth Access Token and if you want to bridge files, the OAuth Access Token whenever you link a room. + +9. For each channel you would like to bridge, perform the following steps: + + * Create a Matrix room in the usual manner for your client. Take a note of its Matrix room ID - it will look something like !aBcDeF:example.com. + + * Invite the bot user to both the Slack and Matrix channels you would like to bridge using `/invite @matrixbot` for slack and `/invite @slackbot:MY.DOMAIN` for matrix. + + * Determine the "channel ID" that Slack uses to identify the channel. You can see it when you open a given Slack channel in a browser. The URL reads like this: `https://app.slack.com/client/XXX//details/`. + + * Issue a link command in the administration control room with these collected values as arguments: + + with file bridging: + ``` + link --channel_id CHANNELID --room !the-matrix:room.id --slack_bot_token xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxx --slack_user_token xoxp-xxxxxxxx-xxxxxxxxx-xxxxxxxx-xxxxxxxx + ``` + without file bridging: + ``` + link --channel_id CHANNELID --room !the-matrix:room.id --slack_bot_token xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxx + ``` + These arguments can be shortened to single-letter forms: + ``` + link -I CHANNELID -R !the-matrix:room.id -t xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxx + ``` + +Other configuration options are available via the `matrix_appservice_slack_configuration_extension_yaml` variable. + +10. Unlinking + + Channels can be unlinked again like this: + ``` + unlink --room !the-matrix:room.id + ``` + + Unlinking doesn't only disconnect the bridge, but also makes the slackbot leave the bridged matrix room. So in case you want to re-link later, don't forget to re-invite the slackbot into this room again. + +## Troubleshooting + +* as always, check the logs: +`journalctl -fu matrix-appservice-slack` + +* linking: "Room is now pending-name" +This typically means that you haven't used the correct slack channel id. Unlink the room and recheck 'Determine the "channel ID"' from above. + +* Messages work from M to S, but not the other way around +Check you logs, if they say something like + +`WARN SlackEventHandler Ignoring message from unrecognised slack channel id : %s (%s) ` + +then unlink your room, reinvite the bot and re-link it again. This may particularly hit you, if you tried to unsuccessfully link +your room multiple times without unlinking it after each failed attempt. diff --git a/docs/configuring-playbook-bridge-appservice-webhooks.md b/docs/configuring-playbook-bridge-appservice-webhooks.md new file mode 100644 index 00000000..3654bfa4 --- /dev/null +++ b/docs/configuring-playbook-bridge-appservice-webhooks.md @@ -0,0 +1,63 @@ +# Setting up Appservice Webhooks (optional) + +The playbook can install and configure [matrix-appservice-webhooks](https://github.com/turt2live/matrix-appservice-webhooks) for you. + +This bridge provides support for Slack-compatible webhooks. + +Setup Instructions: + +loosely based on [this](https://github.com/turt2live/matrix-appservice-webhooks/blob/master/README.md) + +1. All you basically need is to adjust your `inventory/host_vars/matrix./vars.yml`: + +```yaml +matrix_appservice_webhooks_enabled: true +matrix_appservice_webhooks_api_secret: '' +``` + +2. In case you want to change the verbosity of logging via `journalctl -fu matrix-appservice-webhooks.service` +you can adjust this in `inventory/host_vars/matrix./vars.yml` as well. + +*Note*: default value is: `info` and availabe log levels are : `info`, `verbose` + +```yaml +matrix_appservice_webhooks_log_level: '' +``` + +3. If you've already installed Matrix services using the playbook before, you'll need to re-run it (`--tags=setup-all,start`). If not, proceed with [configuring other playbook services](configuring-playbook.md) and then with [Installing](installing.md). Get back to this guide once ready. + +4. If you're using the [Dimension Integration Manager](configuring-playbook-dimension.md), you can configure the Webhooks bridge by opening the Dimension integration manager -> Settings -> Bridges and selecting edit action for "Webhook Bridge". Press "Add self-hosted Bridge" button and populate "Provisioning URL" & "Shared Secret" values from `/matrix/appservice-webhooks/config/config.yaml` file's homeserver URL value and provisioning secret value, respectively. + +5. Invite the bridge bot user to your room: + + - either with `/invite @_webhook:` (*Note*: Make sure you have administration permissions in your room) + + - or simply add the bridge bot to a private channel (personal channels imply you being an administrator) + +6. Send a message to the bridge bot in order to receive a private message including the webhook link. +``` +!webhook +``` + +7. The JSON body for posting messages will have to look like this: +```json +{ + "text": "Hello world!", + "format": "plain", + "displayName": "My Cool Webhook", + "avatar_url": "http://i.imgur.com/IDOBtEJ.png" +} +``` + +You can test this via curl like so: + +``` +curl --header "Content-Type: application/json" \ +--data '{ +"text": "Hello world!", +"format": "plain", +"displayName": "My Cool Webhook", +"avatar_url": "http://i.imgur.com/IDOBtEJ.png" +}' \ + +``` diff --git a/docs/configuring-playbook-bridge-heisenbridge.md b/docs/configuring-playbook-bridge-heisenbridge.md new file mode 100644 index 00000000..2c1b438f --- /dev/null +++ b/docs/configuring-playbook-bridge-heisenbridge.md @@ -0,0 +1,38 @@ +# Setting up Heisenbridge (optional) + +**Note**: bridging to [IRC](https://en.wikipedia.org/wiki/Internet_Relay_Chat) can also happen via the [matrix-appservice-irc](configuring-playbook-bridge-appservice-irc.md) bridge supported by the playbook. + +The playbook can install and configure [Heisenbridge](https://github.com/hifi/heisenbridge) - the bouncer-style [IRC](https://en.wikipedia.org/wiki/Internet_Relay_Chat) bridge for you. + +See the project's [README](https://github.com/hifi/heisenbridge/blob/master/README.md) to learn what it does and why it might be useful to you. You can also take a look at [this demonstration video](https://www.youtube.com/watch?v=nQk1Bp4tk4I). + +## Configuration + +Below are the common configuration options that you may want to set, exhaustive list is in [the bridge's defaults var file](../roles/matrix-bridge-heisenbridge/defaults/main.yml). + +At a minimum, you only need to enable the bridge to get it up and running (`inventory/host_vars/matrix.DOMAIN/vars.yml`): + +```yaml +matrix_heisenbridge_enabled: true + +# set owner (optional) +matrix_heisenbridge_owner: "@you:your-homeserver" + +# to enable identd on host port 113/TCP (optional) +matrix_heisenbridge_identd_enabled: true +``` + +That's it! A registration file is automatically generated during the setup phase. + +Setting the owner is optional as the first local user to DM `@heisenbridge:your-homeserver` will be made the owner. +If you are not using a local user you must set it as otherwise you can't DM it at all. + +## Usage + +After the bridge is successfully running just DM `@heisenbridge:your-homeserver` to start setting it up. +Help is available for all commands with the `-h` switch. +If the bridge ignores you and a DM is not accepted then the owner setting may be wrong. + +You can also learn the basics by watching [this demonstration video](https://www.youtube.com/watch?v=nQk1Bp4tk4I). + +If you encounter issues or feel lost you can join the project room at [#heisenbridge:vi.fi](https://matrix.to/#/#heisenbridge:vi.fi) for help. diff --git a/docs/configuring-playbook-bridge-matrix-bridge-sms.md b/docs/configuring-playbook-bridge-matrix-bridge-sms.md new file mode 100644 index 00000000..86a95ab2 --- /dev/null +++ b/docs/configuring-playbook-bridge-matrix-bridge-sms.md @@ -0,0 +1,37 @@ +# Setting up matrix-sms-bridge (optional) + +The playbook can install and configure [matrix-sms-bridge](https://github.com/benkuly/matrix-sms-bridge) for you. + +See the project page to learn what it does and why it might be useful to you. + +**The bridge uses [android-sms-gateway-server](https://github.com/RebekkaMa/android-sms-gateway-server). You need to configure it first.** + +To enable the bridge just use the following +playbook configuration: + + +```yaml +matrix_sms_bridge_enabled: true + +# (optional but recommended) a room id to a default room +matrix_sms_bridge_default_room: "" + +# (optional but recommended) configure your server location +matrix_sms_bridge_default_region: DE +matrix_sms_bridge_default_timezone: Europe/Berlin + +# Settings to connect to android-sms-gateway-server +matrix_sms_bridge_provider_android_baseurl: https://192.168.24.24:9090 +matrix_sms_bridge_provider_android_username: admin +matrix_sms_bridge_provider_android_password: supeSecretPassword + +# (optional) if your android-sms-gateway-server uses a self signed vertificate, the bridge needs a "truststore". This can be the certificate itself. +matrix_sms_bridge_provider_android_truststore_local_path: android-sms-gateway-server.p12 +matrix_sms_bridge_provider_android_truststore_password: 123 + +``` + + +## Usage + +Read the [user guide](https://github.com/benkuly/matrix-sms-bridge/blob/master/README.md#user-guide) to see how this bridge works. diff --git a/docs/configuring-playbook-bridge-mautrix-facebook.md b/docs/configuring-playbook-bridge-mautrix-facebook.md new file mode 100644 index 00000000..d07873ae --- /dev/null +++ b/docs/configuring-playbook-bridge-mautrix-facebook.md @@ -0,0 +1,118 @@ +# Setting up Mautrix Facebook (optional) + +The playbook can install and configure [mautrix-facebook](https://github.com/tulir/mautrix-facebook) for you. + +See the project's [documentation](https://github.com/tulir/mautrix-facebook/blob/master/ROADMAP.md) to learn what it does and why it might be useful to you. + +```yaml +matrix_mautrix_facebook_enabled: true +``` + +There are some additional things you may wish to configure about the bridge before you continue. + +Encryption support is off by default. If you would like to enable encryption, add the following to your `vars.yml` file: +```yaml +matrix_mautrix_facebook_configuration_extension_yaml: | + bridge: + encryption: + allow: true + default: true +``` + +If you would like to be able to administrate the bridge from your account it can be configured like this: +```yaml +matrix_mautrix_facebook_configuration_extension_yaml: | + bridge: + permissions: + '@YOUR_USERNAME:YOUR_DOMAIN': admin +``` + +You may wish to look at `roles/matrix-bridge-mautrix-facebook/templates/config.yaml.j2` to find other things you would like to configure. + + +## Set up Double Puppeting + +If you'd like to use [Double Puppeting](https://docs.mau.fi/bridges/general/double-puppeting.html) (hint: you most likely do), you have 2 ways of going about it. + +### Method 1: automatically, by enabling Shared Secret Auth + +The bridge will automatically perform Double Puppeting if you enable [Shared Secret Auth](configuring-playbook-shared-secret-auth.md) for this playbook. + +This is the recommended way of setting up Double Puppeting, as it's easier to accomplish, works for all your users automatically, and has less of a chance of breaking in the future. + +### Method 2: manually, by asking each user to provide a working access token + +**Note**: This method for enabling Double Puppeting can be configured only after you've already set up bridging (see [Usage](#usage)). + +When using this method, **each user** that wishes to enable Double Puppeting needs to follow the following steps: + +- retrieve a Matrix access token for yourself. You can use the following command: + +``` +curl \ +--data '{"identifier": {"type": "m.id.user", "user": "YOUR_MATRIX_USERNAME" }, "password": "YOUR_MATRIX_PASSWORD", "type": "m.login.password", "device_id": "Mautrix-Facebook", "initial_device_display_name": "Mautrix-Facebook"}' \ +https://matrix.DOMAIN/_matrix/client/r0/login +``` + +- send the access token to the bot. Example: `login-matrix MATRIX_ACCESS_TOKEN_HERE` + +- make sure you don't log out the `Mautrix-Facebook` device some time in the future, as that would break the Double Puppeting feature + + +## Usage + +You then need to start a chat with `@facebookbot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). + +Send `login YOUR_FACEBOOK_EMAIL_ADDRESS` to the bridge bot to enable bridging for your Facebook Messenger account. You can learn more here about authentication from the bridge's [official documentation on Authentication](https://docs.mau.fi/bridges/python/facebook/authentication.html). + +If you run into trouble, check the [Troubleshooting](#troubleshooting) section below. + +After successfully enabling bridging, you may wish to [set up Double Puppeting](#set-up-double-puppeting), if you haven't already done so. + + +## Set up community-grouping + +This is an **optional feature** that you may wish to enable. + +The Facebook bridge can create a Matrix community for you, which would contain all your chats and contacts. + +For this to work, the bridge's bot needs to have permissions to create communities (also referred to as groups). +Since the bot is a non-admin user, you need to enable such group-creation for non-privileged users in [Synapse's settings](configuring-playbook-synapse.md). + +Here's an example configuration: + +```yaml +matrix_synapse_configuration_extension_yaml: | + enable_group_creation: true + group_creation_prefix: "unofficial/" + +matrix_mautrix_facebook_configuration_extension_yaml: | + bridge: + community_template: "unofficial/facebook_{localpart}={server}" +``` + +Once the bridge is restarted, it would create a community and invite you to it. You need to accept the community invitation manually. +If you don't see all your contacts, you may wish to send a `sync` message to the bot. + + +## Troubleshooting + +### Facebook rejecting login attempts and forcing you to change password + +If your Matrix server is in a wildly different location than where you usually use your Facebook account from, the bridge's login attempts may be outright rejected by Facebook. Along with that, Facebook may even force you to change the account's password. + +If you happen to run into this problem while [setting up bridging](#usage), try to first get a successful session up by logging in to Facebook through the Matrix server's IP address. + +The easiest way to do this may be to use [sshuttle](https://sshuttle.readthedocs.io/) to proxy your traffic through the Matrix server. + +Example command for proxying your traffic through the Matrix server: + +``` +sshuttle -r root@matrix.DOMAIN:22 0/0 +``` + +Once connected, you should be able to verify that you're browsing the web through the Matrix server's IP by checking [icanhazip](https://icanhazip.com/). + +Then proceed to log in to [Facebook/Messenger](https://www.facebook.com/). + +Once logged in, proceed to [set up bridging](#usage). diff --git a/docs/configuring-playbook-bridge-mautrix-hangouts.md b/docs/configuring-playbook-bridge-mautrix-hangouts.md new file mode 100644 index 00000000..a74b1f11 --- /dev/null +++ b/docs/configuring-playbook-bridge-mautrix-hangouts.md @@ -0,0 +1,58 @@ +# Setting up Mautrix Hangouts (optional) + +The playbook can install and configure [mautrix-hangouts](https://github.com/tulir/mautrix-hangouts) for you. + +See the project's [documentation](https://github.com/tulir/mautrix-hangouts/wiki#usage) to learn what it does and why it might be useful to you. + +To enable the [Google Hangouts](https://hangouts.google.com/) bridge just use the following playbook configuration: + + +```yaml +matrix_mautrix_hangouts_enabled: true +``` + + +## Set up Double Puppeting + +If you'd like to use [Double Puppeting](https://github.com/tulir/mautrix-hangouts/wiki/Authentication#double-puppeting) (hint: you most likely do), you have 2 ways of going about it. + +### Method 1: automatically, by enabling Shared Secret Auth + +The bridge will automatically perform Double Puppeting if you enable [Shared Secret Auth](configuring-playbook-shared-secret-auth.md) for this playbook. + +This is the recommended way of setting up Double Puppeting, as it's easier to accomplish, works for all your users automatically, and has less of a chance of breaking in the future. + + +### Method 2: manually, by asking each user to provide a working access token + +**Note**: This method for enabling Double Puppeting can be configured only after you've already set up bridging (see [Usage](#usage)). + +When using this method, **each user** that wishes to enable Double Puppeting needs to follow the following steps: + +- retrieve a Matrix access token for yourself. You can use the following command: + +``` +curl \ +--data '{"identifier": {"type": "m.id.user", "user": "YOUR_MATRIX_USERNAME" }, "password": "YOUR_MATRIX_PASSWORD", "type": "m.login.password", "device_id": "Mautrix-Hangouts", "initial_device_display_name": "Mautrix-Hangouts"}' \ +https://matrix.DOMAIN/_matrix/client/r0/login +``` + +- send the access token to the bot. Example: `login-matrix MATRIX_ACCESS_TOKEN_HERE` + +- make sure you don't log out the `Mautrix-Hangouts` device some time in the future, as that would break the Double Puppeting feature + + +## Usage + +Once the bot is enabled you need to start a chat with `Hangouts bridge bot` with handle `@hangoutsbot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). + +Send `login` to the bridge bot to receive a link to the portal from which you can enable the bridging. Open the link sent by the bot and follow the instructions. + +Automatic login may not work. If it does not, reload the page and select the "Manual login" checkbox before starting. Manual login involves logging into your Google account normally and then manually getting the OAuth token from browser cookies with developer tools. + +Once logged in, recent chats should show up as new conversations automatically. Other chats will get portals as you receive messages. + +You can learn more about authentication from the bridge's [official documentation on Authentication](https://github.com/tulir/mautrix-hangouts/wiki/Authentication). + +After successfully enabling bridging, you may wish to [set up Double Puppeting](#set-up-double-puppeting), if you haven't already done so. + diff --git a/docs/configuring-playbook-bridge-mautrix-instagram.md b/docs/configuring-playbook-bridge-mautrix-instagram.md new file mode 100644 index 00000000..7cdbc7a8 --- /dev/null +++ b/docs/configuring-playbook-bridge-mautrix-instagram.md @@ -0,0 +1,17 @@ +# Setting up Mautrix Instagram (optional) + +The playbook can install and configure [mautrix-instagram](https://github.com/tulir/mautrix-instagram) for you. + +See the project's [documentation](https://docs.mau.fi/bridges/python/instagram/index.html) to learn what it does and why it might be useful to you. + +```yaml +matrix_mautrix_instagram_enabled: true +``` + +## Usage + +You then need to start a chat with `@instagrambot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). + +Send `login YOUR_INSTAGRAM_EMAIL_ADDRESS YOUR_INSTAGRAM_PASSWORD` to the bridge bot to enable bridging for your instagram/Messenger account. + +You can learn more here about authentication from the bridge's [official documentation on Authentication](https://docs.mau.fi/bridges/python/instagram/authentication.html). diff --git a/docs/configuring-playbook-bridge-mautrix-signal.md b/docs/configuring-playbook-bridge-mautrix-signal.md new file mode 100644 index 00000000..6d3c4dfb --- /dev/null +++ b/docs/configuring-playbook-bridge-mautrix-signal.md @@ -0,0 +1,46 @@ +# Setting up Mautrix Signal (optional) + +The playbook can install and configure [mautrix-signal](https://github.com/tulir/mautrix-signal) for you. + +See the project's [documentation](https://github.com/tulir/mautrix-signal/wiki) to learn what it does and why it might be useful to you. + +**Note/Prerequisite**: If you're running with the Postgres database server integrated by the playbook (which is the default), you don't need to do anything special and can easily proceed with installing. However, if you're [using an external Postgres server](configuring-playbook-external-postgres.md), you'd need to manually prepare a Postgres database for this bridge and adjust the variables related to that (`matrix_mautrix_signal_database_*`). + +Use the following playbook configuration: + +```yaml +matrix_mautrix_signal_enabled: true +``` + +## Set up Double Puppeting + +If you'd like to use [Double Puppeting](https://github.com/tulir/mautrix-signal/wiki/Authentication#double-puppeting) (hint: you most likely do), you have 2 ways of going about it. + +### Method 1: automatically, by enabling Shared Secret Auth + +The bridge will automatically perform Double Puppeting if you enable [Shared Secret Auth](configuring-playbook-shared-secret-auth.md) for this playbook. + +This is the recommended way of setting up Double Puppeting, as it's easier to accomplish, works for all your users automatically, and has less of a chance of breaking in the future. + +### Method 2: manually, by asking each user to provide a working access token + +**Note**: This method for enabling Double Puppeting can be configured only after you've already set up bridging (see [Usage](#usage)). + +When using this method, **each user** that wishes to enable Double Puppeting needs to follow the following steps: + +- retrieve a Matrix access token for yourself. You can use the following command: + +``` +curl \ +--data '{"identifier": {"type": "m.id.user", "user": "YOUR_MATRIX_USERNAME" }, "password": "YOUR_MATRIX_PASSWORD", "type": "m.login.password", "device_id": "Mautrix-Signal", "initial_device_display_name": "Mautrix-Signal"}' \ +https://matrix.DOMAIN/_matrix/client/r0/login +``` + +- send the access token to the bot. Example: `login-matrix MATRIX_ACCESS_TOKEN_HERE` + +- make sure you don't log out the `Mautrix-Signal` device some time in the future, as that would break the Double Puppeting feature + + +## Usage + +You then need to start a chat with `@signalbot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). diff --git a/docs/configuring-playbook-bridge-mautrix-telegram.md b/docs/configuring-playbook-bridge-mautrix-telegram.md new file mode 100644 index 00000000..bfdc6fc5 --- /dev/null +++ b/docs/configuring-playbook-bridge-mautrix-telegram.md @@ -0,0 +1,52 @@ +# Setting up Mautrix Telegram (optional) + +The playbook can install and configure [mautrix-telegram](https://github.com/tulir/mautrix-telegram) for you. + +See the project's [documentation](https://github.com/tulir/mautrix-telegram/wiki#usage) to learn what it does and why it might be useful to you. + +You'll need to obtain API keys from [https://my.telegram.org/apps](https://my.telegram.org/apps) and then use the following playbook configuration: + +```yaml +matrix_mautrix_telegram_enabled: true +matrix_mautrix_telegram_api_id: YOUR_TELEGRAM_APP_ID +matrix_mautrix_telegram_api_hash: YOUR_TELEGRAM_API_HASH +``` + +## Set up Double Puppeting + +If you'd like to use [Double Puppeting](https://github.com/tulir/mautrix-telegram/wiki/Authentication#replacing-telegram-accounts-matrix-puppet-with-matrix-account) (hint: you most likely do), you have 2 ways of going about it. + +### Method 1: automatically, by enabling Shared Secret Auth + +The bridge will automatically perform Double Puppeting if you enable [Shared Secret Auth](configuring-playbook-shared-secret-auth.md) for this playbook. + +This is the recommended way of setting up Double Puppeting, as it's easier to accomplish, works for all your users automatically, and has less of a chance of breaking in the future. + +### Method 2: manually, by asking each user to provide a working access token + +**Note**: This method for enabling Double Puppeting can be configured only after you've already set up bridging. + +When using this method, **each user** that wishes to enable Double Puppeting needs to follow the following steps: + +- retrieve a Matrix access token for yourself. You can use the following command: + +``` +curl \ +--data '{"identifier": {"type": "m.id.user", "user": "YOUR_MATRIX_USERNAME" }, "password": "YOUR_MATRIX_PASSWORD", "type": "m.login.password", "device_id": "Mautrix-Telegram", "initial_device_display_name": "Mautrix-Telegram"}' \ +https://matrix.DOMAIN/_matrix/client/r0/login +``` + +- send `login-matrix` to the bot and follow instructions about how to send the access token to it + +- make sure you don't log out the `Mautrix-Telegram` device some time in the future, as that would break the Double Puppeting feature + + +## Usage + +You then need to start a chat with `@telegrambot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). + +If you want to use the relay-bot feature ([relay bot documentation](https://github.com/tulir/mautrix-telegram/wiki/Relay-bot)), which allows anonymous user to chat with telegram users, use the following additional playbook configuration: + +```yaml +matrix_mautrix_telegram_bot_token: YOUR_TELEGRAM_BOT_TOKEN +``` diff --git a/docs/configuring-playbook-bridge-mautrix-whatsapp.md b/docs/configuring-playbook-bridge-mautrix-whatsapp.md new file mode 100644 index 00000000..78ae2863 --- /dev/null +++ b/docs/configuring-playbook-bridge-mautrix-whatsapp.md @@ -0,0 +1,45 @@ +# Setting up Mautrix Whatsapp (optional) + +The playbook can install and configure [mautrix-whatsapp](https://github.com/tulir/mautrix-whatsapp) for you. + +See the project's [documentation](https://github.com/tulir/mautrix-whatsapp/wiki) to learn what it does and why it might be useful to you. + +Use the following playbook configuration: + +```yaml +matrix_mautrix_whatsapp_enabled: true +``` + + +## Set up Double Puppeting + +If you'd like to use [Double Puppeting](https://github.com/tulir/mautrix-whatsapp/wiki/Authentication#replacing-whatsapp-accounts-matrix-puppet-with-matrix-account) (hint: you most likely do), you have 2 ways of going about it. + +### Method 1: automatically, by enabling Shared Secret Auth + +The bridge will automatically perform Double Puppeting if you enable [Shared Secret Auth](configuring-playbook-shared-secret-auth.md) for this playbook. + +This is the recommended way of setting up Double Puppeting, as it's easier to accomplish, works for all your users automatically, and has less of a chance of breaking in the future. + +### Method 2: manually, by asking each user to provide a working access token + +**Note**: This method for enabling Double Puppeting can be configured only after you've already set up bridging (see [Usage](#usage)). + +When using this method, **each user** that wishes to enable Double Puppeting needs to follow the following steps: + +- retrieve a Matrix access token for yourself. You can use the following command: + +``` +curl \ +--data '{"identifier": {"type": "m.id.user", "user": "YOUR_MATRIX_USERNAME" }, "password": "YOUR_MATRIX_PASSWORD", "type": "m.login.password", "device_id": "Mautrix-Whatsapp", "initial_device_display_name": "Mautrix-Whatsapp"}' \ +https://matrix.DOMAIN/_matrix/client/r0/login +``` + +- send the access token to the bot. Example: `login-matrix MATRIX_ACCESS_TOKEN_HERE` + +- make sure you don't log out the `Mautrix-Whatsapp` device some time in the future, as that would break the Double Puppeting feature + + +## Usage + +You then need to start a chat with `@whatsappbot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base domain, not the `matrix.` domain). diff --git a/docs/configuring-playbook-bridge-mx-puppet-discord.md b/docs/configuring-playbook-bridge-mx-puppet-discord.md new file mode 100644 index 00000000..1a48f05b --- /dev/null +++ b/docs/configuring-playbook-bridge-mx-puppet-discord.md @@ -0,0 +1,34 @@ +# Setting up MX Puppet Discord (optional) + +**Note**: bridging to [Discord](https://discordapp.com/) can also happen via the [matrix-appservice-discord](configuring-playbook-bridge-appservice-discord.md) bridge supported by the playbook. + +The playbook can install and configure +[mx-puppet-discord](https://github.com/matrix-discord/mx-puppet-discord) for you. + +See the project page to learn what it does and why it might be useful to you. + +To enable the [Discord](https://discordapp.com/) bridge just use the following +playbook configuration: + + +```yaml +matrix_mx_puppet_discord_enabled: true +``` + + +## Usage + +Once the bot is enabled you need to start a chat with `Discord Puppet Bridge` with +the handle `@_discordpuppet_bot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base +domain, not the `matrix.` domain). + +Three authentication methods are available, Legacy Token, OAuth and xoxc token. +See mx-puppet-discord [documentation](https://github.com/matrix-discord/mx-puppet-discord) +for more information about how to configure the bridge. + +Once logged in, send `list` to the bot user to list the available rooms. + +Clicking rooms in the list will result in you receiving an invitation to the +bridged room. + +Also send `help` to the bot to see the commands available. diff --git a/docs/configuring-playbook-bridge-mx-puppet-groupme.md b/docs/configuring-playbook-bridge-mx-puppet-groupme.md new file mode 100644 index 00000000..2f0eda19 --- /dev/null +++ b/docs/configuring-playbook-bridge-mx-puppet-groupme.md @@ -0,0 +1,36 @@ +# Setting up MX Puppet GroupMe (optional) + +The playbook can install and configure +[mx-puppet-groupme](https://gitlab.com/robintown/mx-puppet-groupme) for you. + +See the project page to learn what it does and why it might be useful to you. + +To enable the [GroupMe](https://groupme.com/) bridge just use the following +playbook configuration: + + +```yaml +matrix_mx_puppet_groupme_enabled: true +``` + + +## Usage + +Once the bot is enabled you need to start a chat with `GroupMe Puppet Bridge` with +the handle `@_groupmepuppet_bot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base +domain, not the `matrix.` domain). + +One authentication method is available. + +To link your GroupMe account, go to [dev.groupme.com](https://dev.groupme.com/), sign in, and select "Access Token" from the top menu. Copy the token and message the bridge with: + +``` +link +``` + +Once logged in, send `listrooms` to the bot user to list the available rooms. + +Clicking rooms in the list will result in you receiving an invitation to the +bridged room. + +Also send `help` to the bot to see the commands available. diff --git a/docs/configuring-playbook-bridge-mx-puppet-instagram.md b/docs/configuring-playbook-bridge-mx-puppet-instagram.md new file mode 100644 index 00000000..3dad9844 --- /dev/null +++ b/docs/configuring-playbook-bridge-mx-puppet-instagram.md @@ -0,0 +1,36 @@ +# Setting up mx-puppet-instagram (optional) + +The playbook can install and configure +[mx-puppet-instagram](https://github.com/Sorunome/mx-puppet-instagram) for you. + +This allows you to bridge Instagram DirectMessages into Matrix. + +To enable the [Instagram](https://www.instagram.com/) bridge just use the following +playbook configuration: + + +```yaml +matrix_mx_puppet_instagram_enabled: true +``` + + +## Usage + +Once the bot is enabled, you need to start a chat with `Instagram Puppet Bridge` with +the handle `@_instagrampuppet_bot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base +domain, not the `matrix.` domain). + +Send `link ` to the bridge bot to link your instagram account. + +The `list` commands shows which accounts are linked and which `puppetId` is associated. + +For double-puppeting, you probably want to issue these commands: + +- `settype $puppetId puppet` to enable puppeting for the link (instead of relaying) +- `setautoinvite $puppetId 1` to automatically invite you to chats +- `setmatrixtoken $accessToken` to set the access token to enable puppeting from the other side (the "double" in double puppeting) + +If you are linking only one Instagram account, your `$puppetId` is probably 1, but use the `list` command find out. + +The `help` command shows which commands are available, though at the time of writing, not every command is fully implemented. + diff --git a/docs/configuring-playbook-bridge-mx-puppet-skype.md b/docs/configuring-playbook-bridge-mx-puppet-skype.md new file mode 100644 index 00000000..cca5e305 --- /dev/null +++ b/docs/configuring-playbook-bridge-mx-puppet-skype.md @@ -0,0 +1,30 @@ +# Setting up MX Puppet Skype (optional) + +The playbook can install and configure +[mx-puppet-skype](https://github.com/Sorunome/mx-puppet-skype) for you. + +See the project page to learn what it does and why it might be useful to you. + +To enable the [Skype](https://www.skype.com/) bridge just use the following +playbook configuration: + + +```yaml +matrix_mx_puppet_skype_enabled: true +``` + + +## Usage + +Once the bot is enabled you need to start a chat with `Skype Puppet Bridge` with +the handle `@_skypepuppet_bot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base +domain, not the `matrix.` domain). + +Send `link ` to the bridge bot to link your skype account. + +Once logged in, send `list` to the bot user to list the available rooms. + +Clicking rooms in the list will result in you receiving an invitation to the +bridged room. + +Also send `help` to the bot to see the commands available. diff --git a/docs/configuring-playbook-bridge-mx-puppet-slack.md b/docs/configuring-playbook-bridge-mx-puppet-slack.md new file mode 100644 index 00000000..e7d8dba2 --- /dev/null +++ b/docs/configuring-playbook-bridge-mx-puppet-slack.md @@ -0,0 +1,34 @@ +# Setting up MX Puppet Slack (optional) + +**Note**: bridging to [Slack](https://slack.com) can also happen via the [matrix-appservice-slack](configuring-playbook-bridge-appservice-slack.md) bridge supported by the playbook. + +The playbook can install and configure +[mx-puppet-slack](https://github.com/Sorunome/mx-puppet-slack) for you. + +See the project page to learn what it does and why it might be useful to you. + +To enable the [Slack](https://slack.com/) bridge just use the following +playbook configuration: + + +```yaml +matrix_mx_puppet_slack_enabled: true +``` + + +## Usage + +Once the bot is enabled you need to start a chat with `Slack Puppet Bridge` with +the handle `@_slackpuppet_bot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base +domain, not the `matrix.` domain). + +Three authentication methods are available, Legacy Token, OAuth and xoxc token. +See mx-puppet-slack [documentation](https://github.com/Sorunome/mx-puppet-slack) +for more information about how to configure the bridge. + +Once logged in, send `list` to the bot user to list the available rooms. + +Clicking rooms in the list will result in you receiving an invitation to the +bridged room. + +Also send `help` to the bot to see the commands available. diff --git a/docs/configuring-playbook-bridge-mx-puppet-steam.md b/docs/configuring-playbook-bridge-mx-puppet-steam.md new file mode 100644 index 00000000..5681c3d1 --- /dev/null +++ b/docs/configuring-playbook-bridge-mx-puppet-steam.md @@ -0,0 +1,32 @@ +# Setting up MX Puppet Steam (optional) + +The playbook can install and configure +[mx-puppet-steam](https://github.com/icewind1991/mx-puppet-steam) for you. + +See the project page to learn what it does and why it might be useful to you. + +To enable the [Steam](https://steampowered.com/) bridge just use the following +playbook configuration: + + +```yaml +matrix_mx_puppet_steam_enabled: true +``` + + +## Usage + +Once the bot is enabled you need to start a chat with `Steam Puppet Bridge` with +the handle `@_steampuppet_bot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base +domain, not the `matrix.` domain). + +Three authentication methods are available, Legacy Token, OAuth and xoxc token. +See mx-puppet-steam [documentation](https://github.com/icewind1991/mx-puppet-steam) +for more information about how to configure the bridge. + +Once logged in, send `list` to the bot user to list the available rooms. + +Clicking rooms in the list will result in you receiving an invitation to the +bridged room. + +Also send `help` to the bot to see the commands available. diff --git a/docs/configuring-playbook-bridge-mx-puppet-twitter.md b/docs/configuring-playbook-bridge-mx-puppet-twitter.md new file mode 100644 index 00000000..dd2c9c12 --- /dev/null +++ b/docs/configuring-playbook-bridge-mx-puppet-twitter.md @@ -0,0 +1,34 @@ +# Setting up MX Puppet Twitter (optional) + +The playbook can install and configure +[mx-puppet-twitter](https://github.com/Sorunome/mx-puppet-twitter) for you. + +See the project page to learn what it does and why it might be useful to you. + +To enable the [Twitter](https://twitter.com) bridge, make an app on [developer.twitter.com](https://developer.twitter.com/en/apps) +and fill out the following playbook configuration. + +```yaml +matrix_mx_puppet_twitter_enabled: true +matrix_mx_puppet_twitter_consumer_key: '' +matrix_mx_puppet_twitter_consumer_secret: '' +matrix_mx_puppet_twitter_access_token: '' +matrix_mx_puppet_twitter_access_token_secret: '' +matrix_mx_puppet_twitter_environment: '' +``` + + +## Usage + +Once the bot is enabled you need to start a chat with `Twitter Puppet Bridge` with +the handle `@_twitterpuppet_bot:YOUR_DOMAIN` (where `YOUR_DOMAIN` is your base +domain, not the `matrix.` domain). + +To log in, use `link` and click the link. + +Once logged in, send `list` to the bot user to list the available rooms. + +Clicking rooms in the list will result in you receiving an invitation to the +bridged room. + +Also send `help` to the bot to see the commands available. diff --git a/docs/configuring-playbook-client-element.md b/docs/configuring-playbook-client-element.md new file mode 100644 index 00000000..45299c55 --- /dev/null +++ b/docs/configuring-playbook-client-element.md @@ -0,0 +1,41 @@ +# Configuring Element (optional) + +By default, this playbook installs the [Element](https://github.com/vector-im/element-web) Matrix client web application. +If that's okay, you can skip this document. + + +## Disabling Element + +If you'd like for the playbook to not install Element (or to uninstall it if it was previously installed), you can disable it in your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_client_element_enabled: false +``` + + +## Configuring Element settings + +The playbook provides some customization variables you could use to change Element's settings. + +Their defaults are defined in [`roles/matrix-client-element/defaults/main.yml`](../roles/matrix-client-element/defaults/main.yml) and they ultimately end up in the generated `/matrix/element/config.json` file (on the server). This file is generated from the [`roles/matrix-client-element/templates/config.json.j2`](../roles/matrix-client-element/templates/config.json.j2) template. + +**If there's an existing variable** which controls a setting you wish to change, you can simply define that variable in your configuration file (`inventory/host_vars/matrix./vars.yml`) and [re-run the playbook](installing.md) to apply the changes. + +Alternatively, **if there is no pre-defined variable** for an Element setting you wish to change: + +- you can either **request a variable to be created** (or you can submit such a contribution yourself). Keep in mind that it's **probably not a good idea** to create variables for each one of Element's various settings that rarely get used. + +- or, you can **extend and override the default configuration** ([`config.json.j2`](../roles/matrix-client-element/templates/config.json.j2)) by making use of the `matrix_client_element_configuration_extension_json_` variable. You can find information about this in [`roles/matrix-client-element/defaults/main.yml`](../roles/matrix-client-element/defaults/main.yml). + +- or, if extending the configuration is still not powerful enough for your needs, you can **override the configuration completely** using `matrix_client_element_configuration_default` (or `matrix_client_element_configuration`). You can find information about this in [`roles/matrix-client-element/defaults/main.yml`](../roles/matrix-client-element/defaults/main.yml). + + +## Themes + +To change the look of Element, you can define your own themes manually by using the `matrix_client_element__settingDefaults_custom_themes` setting. + +Or better yet, you can automatically pull it all themes provided by the [aaronraimist/element-themes](https://github.com/aaronraimist/element-themes) project by simply flipping a flag (`matrix_client_element_themes_enabled: true`). + +If you make your own theme, we encourage you to submit it to the **aaronraimist/element-themes** project, so that the whole community could easily enjoy it. + +Note that for a custom theme to work well, all Element instances that you use must have the same theme installed. diff --git a/docs/configuring-playbook-client-hydrogen.md b/docs/configuring-playbook-client-hydrogen.md new file mode 100644 index 00000000..4b05240b --- /dev/null +++ b/docs/configuring-playbook-client-hydrogen.md @@ -0,0 +1,21 @@ +# Configuring Hydrogen (optional) + +This playbook can install the [Hydrogen](https://github.com/vector-im/hydrogen-web) Matrix web client for you. +Hydrogen is a lightweight web client that supports mobile and legacy web browsers. +Hydrogen can be installed alongside or instead of Element. + +If you'd like Hydrogen to be installed, add the following to your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_client_hydrogen_enabled: true +``` + +You will also need to add a DNS record so that Hydrogen can be accessed. +By default Hydrogen will use https://hydrogen.DOMAIN so you will need to create an CNAME record +for `hydrogen`. See [Configuring DNS](configuring-dns.md). + +If you would like to use a different domain, add the following to your configuration file (changing it to use your preferred domain): + +```yaml + matrix_server_fqn_hydrogen: "helium.{{ matrix_domain }}" +``` diff --git a/docs/configuring-playbook-dimension.md b/docs/configuring-playbook-dimension.md new file mode 100644 index 00000000..d5f0a9e6 --- /dev/null +++ b/docs/configuring-playbook-dimension.md @@ -0,0 +1,97 @@ +# Setting up Dimension (optional) + +**[Dimension](https://dimension.t2bot.io) can only be installed after Matrix services are installed and running.** +If you're just installing Matrix services for the first time, please continue with the [Configuration](configuring-playbook.md) / [Installation](installing.md) flow and come back here later. + +**Note**: enabling Dimension, means that the `openid` API endpoints will be exposed on the Matrix Federation port (usually `8448`), even if [federation](configuring-playbook-federation.md) is disabled. It's something to be aware of, especially in terms of firewall whitelisting (make sure port `8448` is accessible). + + +## Prerequisites + +This playbook now supports running [Dimension](https://dimension.t2bot.io) in both a federated and an [unfederated](https://github.com/turt2live/matrix-dimension/blob/master/docs/unfederated.md) environment. This is handled automatically based on the value of `matrix_synapse_federation_enabled`. + +Other important prerequisite is the `dimension.` DNS record being set up correctly. See [Configuring your DNS server](configuring-dns.md) on how to set up DNS record correctly. + + +## Enable + +[Dimension integrations manager](https://dimension.t2bot.io) installation is disabled by default. You can enable it in your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_dimension_enabled: true +``` + + +## Define admin users + +These users can modify the integrations this Dimension supports. Admin interface is accessible by opening Dimension in Element and clicking the settings icon. +Add this to your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_dimension_admins: + - "@user1:{{ matrix_domain }}" + - "@user2:{{ matrix_domain }}" +``` + + +## Access token + +We recommend that you create a dedicated Matrix user for Dimension (`dimension` is a good username). +Follow our [Registering users](registering-users.md) guide to learn how to register **a regular (non-admin) user**. + +You are required to specify an access token (belonging to this new user) for Dimension to work. +To get an access token for the Dimension user, you can follow one of two options: + +*Through an interactive login*: + +1. In a private browsing session (incognito window), open Element. +2. Log in with the `dimension` user and its password. +1. Set the display name and avatar, if required. +2. In the settings page choose "Help & About", scroll down to the bottom and click `Access Token: `. +3. Copy the highlighted text to your configuration. +4. Close the private browsing session. **Do not log out**. Logging out will invalidate the token, making it not work. + +*With CURL* + +``` +curl -X POST --header 'Content-Type: application/json' -d '{ + "identifier": { "type": "m.id.user", "user": "YourDimensionUsername" }, + "password": "YourDimensionPassword", + "type": "m.login.password" +}' 'https://matrix.YOURDOMAIN/_matrix/client/r0/login' +``` +*Change the "YourDimensionUser/Pass" URL accordigly* + +**Access tokens are sensitive information. Do not include them in any bug reports, messages, or logs. Do not share the access token with anyone.** + +Add access token to your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_dimension_access_token: "YOUR ACCESS TOKEN HERE" +``` + +For more information on how to acquire an access token, visit [https://t2bot.io/docs/access_tokens](https://t2bot.io/docs/access_tokens). + + +## Installation + +After these variables have been set, please run the following command to re-run setup and to restart Dimension: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Jitsi domain + +By default Dimension will use [jitsi.riot.im](https://jitsi.riot.im/) as the `conferenceDomain` of [Jitsi](https://jitsi.org/) audio/video conference widgets. For users running [a self-hosted Jitsi instance](./configuring-playbook-jitsi.md), you will likely want the widget to use your own Jitsi instance. Currently there is no way to configure this via the playbook, see [this issue](https://github.com/turt2live/matrix-dimension/issues/345) for details. + +In the interim until the above limitation is resolved, an admin user needs to configure the domain via the admin ui once dimension is running. In Element, go to *Manage Integrations* → *Settings* → *Widgets* → *Jitsi Conference Settings* and set *Jitsi Domain* and *Jitsi Script URL* appropriately. + + +## Additional features + +To use a more custom configuration, you can define a `matrix_dimension_configuration_extension_yaml` string variable and put your configuration in it. +To learn more about how to do this, refer to the information about `matrix_dimension_configuration_extension_yaml` in the [default variables file](../roles/matrix-dimension/defaults/main.yml) of the Dimension component. + +You can find all configuration options on [GitHub page of Dimension project](https://github.com/turt2live/matrix-dimension/blob/master/config/default.yaml). diff --git a/docs/configuring-playbook-dynamic-dns.md b/docs/configuring-playbook-dynamic-dns.md new file mode 100644 index 00000000..bc58c273 --- /dev/null +++ b/docs/configuring-playbook-dynamic-dns.md @@ -0,0 +1,27 @@ +# Dynamic DNS + +## Setup + +Most cloud providers / ISPs will charge you extra for a static IP address. If you're +not hosting a highly reliable homeserver you can workaround this via dynamic DNS. To +set this up, you'll need to get the username/password from your DNS provider. For +google domains, this process is described [here](https://support.google.com/domains/answer/6147083). +After you've gotten the proper credentials you can add the following config to your `inventory/host_vars/matrix.DOMAIN/vars.yml`: + +```yaml +matrix_dynamic_dns_enabled: true + +matrix_dynamic_dns_domain_configurations: + - provider: domains.google.com + protocol: dyndn2 + username: XXXXXXXXXXXXXXXX + password: XXXXXXXXXXXXXXXX + domain: "{{ matrix_domain }}" +``` + + +## Additional Reading + +Additional resources: + +- https://matrix.org/docs/guides/free-small-matrix-server diff --git a/docs/configuring-playbook-email.md b/docs/configuring-playbook-email.md new file mode 100644 index 00000000..d9721a4e --- /dev/null +++ b/docs/configuring-playbook-email.md @@ -0,0 +1,55 @@ +# Adjusting email-sending settings (optional) + +By default, this playbook sets up an [Exim](https://www.exim.org/) email server through which all Matrix services send emails. + +The email server would attempt to deliver emails directly to their final destination. +This may or may not work, depending on your domain configuration (SPF settings, etc.) + +By default, emails are sent from `matrix@` (as specified by the `matrix_mailer_sender_address` playbook variable). + +**Note**: If you are using a Google Cloud instance, [port 25 is always blocked](https://cloud.google.com/compute/docs/tutorials/sending-mail/), so you need to relay email through another SMTP server as described below. + + +## Firewall settings + +No matter whether you send email directly (the default) or you relay email through another host (see how below), you'll probably need to allow outgoing traffic for TCP ports 25/587 (depending on configuration). + + +## Relaying email through another SMTP server + +If you'd like to relay email through another SMTP server, feel free to redefine a few playbook variables. +Example: + +```yaml +matrix_mailer_sender_address: "another.sender@example.com" +matrix_mailer_relay_use: true +matrix_mailer_relay_host_name: "mail.example.com" +matrix_mailer_relay_host_port: 587 +matrix_mailer_relay_auth: true +matrix_mailer_relay_auth_username: "another.sender@example.com" +matrix_mailer_relay_auth_password: "some-password" +``` + +**Note**: only the secure submission protocol (using `STARTTLS`, usually on port `587`) is supported. **SMTPS** (encrypted SMTP, usually on port `465`) **is not supported**. + + +### Configuations for sending emails using Sendgrid +An easy and free SMTP service to set up is [Sendgrid](https://sendgrid.com/), the free tier allows for up to 100 emails per day to be sent. In the settings below you can provide any email for `matrix_mailer_sender_address`. + +The only other thing you need to change is the `matrix_mailer_relay_auth_password`, which you can generate at https://app.sendgrid.com/settings/api_keys. The API key password looks something like `SG.955oW1mLSfwds7i9Yd6IA5Q.q8GTaB8q9kGDzasegdG6u95fQ-6zkdwrPP8bOeuI`. + +Note that the `matrix_mailer_relay_auth_username` is literally the string `apikey`, it's always the same for Sendgrid. + +```yaml +matrix_mailer_sender_address: "arbitrary@email.com" +matrix_mailer_relay_use: true +matrix_mailer_relay_host_name: "smtp.sendgrid.net" +matrix_mailer_relay_host_port: 587 +matrix_mailer_relay_auth: true +matrix_mailer_relay_auth_username: "apikey" +matrix_mailer_relay_auth_password: "" +``` + +## Troubleshooting + +If you're having trouble with email not being delivered, it may be useful to inspect the mailer logs: `journalctl -f -u matrix-mailer`. diff --git a/docs/configuring-playbook-email2matrix.md b/docs/configuring-playbook-email2matrix.md new file mode 100644 index 00000000..510a9dcc --- /dev/null +++ b/docs/configuring-playbook-email2matrix.md @@ -0,0 +1,80 @@ +# Setting up Email2Matrix (optional) + +The playbook can install and configure [email2matrix](https://github.com/devture/email2matrix) for you. + +See the project's [documentation](https://github.com/devture/email2matrix/blob/master/docs/README.md) to learn what it does and why it might be useful to you. + + +## Preparation + +### Port availability + +Ensure that port 25 is available on your Matrix server and open in your firewall. +If you have `postfix` or some other email server software installed, you may need to manually remove it first (unless you need it, of course). + +If you really need to run an email server on the Matrix machine for other purposes, it may be possible to run Email2Matrix on another port (with a configuration like `matrix_email2matrix_smtp_host_bind_port: "127.0.0.01:2525"`) and have your other email server relay messages there. +For details about using Email2Matrix alongside [Postfix](http://www.postfix.org/), see [here](https://github.com/devture/email2matrix/blob/master/docs/setup_with_postfix.md). + +### Creating a user + +Before enabling Email2Matrix, you'd most likely wish to create a dedicated user (or more) that would be sending messages on the Matrix side. +Refer to [Registering users](registering-users.md) for ways to do that. A regular (non-admin) user works best. + +### Creating a shared room + +After creating a sender user, you should create one or more Matrix rooms that you share with that user. +It doesn't matter who creates and owns the rooms and who joins later (you or the sender user). + +What matters is that both you and the sender user are part of the same room and that the sender user has enough privileges in the room to be able to send messages there. +Inviting additional people to the room is okay too. + +Take note of each room's room id (different clients show the room id in a different place). +You'll need the room id when doing [Configuration](#configuration) below. + + +### Obtaining an access token for the sender user + +In order for the sender user created above to be able to send messages to the room, we'll need to obtain an access token for it. + +To do this, you can execute a command like this: + +``` +curl \ +--data '{"identifier": {"type": "m.id.user", "user": "email2matrix" }, "password": "MATRIX_PASSWORD_FOR_THE_USER", "type": "m.login.password", "device_id": "Email2Matrix", "initial_device_display_name": "Email2Matrix"}' \ +https://matrix.DOMAIN/_matrix/client/r0/login +``` + +Take note of the `access_token` value. You'll need the access token when doing [Configuration](#configuration) below. + + +## Configuration + +After doing the preparation steps above, adjust your `inventory/host_vars/matrix.DOMAIN/vars.yml` configuration like this: + +```yaml +matrix_email2matrix_enabled: true + +matrix_email2matrix_matrix_mappings: + - MailboxName: "my-mailbox" + MatrixRoomId: "!someRoom:DOMAIN" + MatrixHomeserverUrl: "https://matrix.DOMAIN" + MatrixUserId: "@email2matrix:DOMAIN" + MatrixAccessToken: "ACCESS_TOKEN_GOES_HERE" + IgnoreSubject: false + IgnoreBody: false + SkipMarkdown: false + + - MailboxName: "my-mailbox2" + MatrixRoomId: "!anotherRoom:DOMAIN" + MatrixHomeserverUrl: "https://matrix.DOMAIN" + MatrixUserId: "@email2matrix:DOMAIN" + MatrixAccessToken: "ACCESS_TOKEN_GOES_HERE" + IgnoreSubject: true + IgnoreBody: false + SkipMarkdown: true +``` + +You can also set `MatrixHomeserverUrl` to `http://matrix-synapse:8008`, instead of the public `https://matrix.DOMAIN`. +However, that's more likely to break in the future if you switch to another server implementation than Synapse. + +Re-run the playbook (`--tags=setup-email2matrix,start`) and try sending an email to `my-mailbox@matrix.DOMAIN`. diff --git a/docs/configuring-playbook-etherpad.md b/docs/configuring-playbook-etherpad.md new file mode 100644 index 00000000..e5533e71 --- /dev/null +++ b/docs/configuring-playbook-etherpad.md @@ -0,0 +1,31 @@ +# Setting up Etherpad (optional) + +[Etherpad](https://etherpad.org) is is an open source collaborative text editor that can be embedded in a Matrix chat room using the [Dimension integrations manager](https://dimension.t2bot.io) + +When enabled together with the Jitsi audio/video conferencing system (see [our docs on Jitsi](configuring-playbook-jitsi.md)), it will be made available as an option during the conferences. + +## Prerequisites + +For the self-hosted Etherpad instance to be available to your users, you must first enable and configure the **Dimension integrations manager** as described in [the playbook documentation](configuring-playbook-dimension.md) + +## Installing + +[Etherpad](https://etherpad.org) installation is disabled by default. You can enable it in your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_etherpad_enabled: true +``` + +## Set Dimension default to the self-hosted Etherpad + +The Dimension administrator users can configure the default URL template. The Dimension configuration menu can be accessed with the sprocket icon as you begin to add a widget to a room in Element. There you will find the Etherpad Widget Configuration action beneath the _Widgets_ tab. Replace `scalar.vector.im` with your own Dimension domain. + +### Removing the integrated Etherpad chat + +If you wish to disable the Etherpad chat button, you can do it by appending `?showChat=false` to the end of the pad URL, or the template. +Example: `https://dimension./etherpad/p/$roomId_$padName?showChat=false` + +## Known issues + +If your Etherpad widget fails to load, this might be due to Dimension generating a Pad name so long, the Etherpad app rejects it. +`$roomId_$padName` can end up being longer than 50 characters. You can avoid having this problem by altering the template so it only contains the three word random identifier `$padName`. diff --git a/docs/configuring-playbook-external-postgres.md b/docs/configuring-playbook-external-postgres.md new file mode 100644 index 00000000..0becc8ff --- /dev/null +++ b/docs/configuring-playbook-external-postgres.md @@ -0,0 +1,25 @@ +# Using an external PostgreSQL server (optional) + +By default, this playbook would set up a PostgreSQL database server on your machine, running in a Docker container. +If that's alright, you can skip this. + +If you'd like to use an external PostgreSQL server that you manage, you can edit your configuration file (`inventory/host_vars/matrix./vars.yml`). + +It should be something like this: + +```yaml +matrix_postgres_enabled: false + +# Rewire Synapse to use your external Postgres server +matrix_synapse_database_host: "your-postgres-server-hostname" +matrix_synapse_database_user: "your-postgres-server-username" +matrix_synapse_database_password: "your-postgres-server-password" +matrix_synapse_database_database: "your-postgres-server-database-name" +``` + +The database (as specified in `matrix_synapse_database_database`) must exist and be accessible with the given credentials. +It must be empty or contain a valid Synapse database. If empty, Synapse would populate it the first time it runs. + +**Note**: the external server that you specify in `matrix_synapse_database_host` must be accessible from within the `matrix-synapse` Docker container (and possibly other containers too). This means that it either needs to be a publicly accessible hostname or that it's a hostname on the same Docker network where all containers installed by this playbook run (a network called `matrix` by default). Using a local PostgreSQL instance on the host (running on the same machine, but not in a container) is not possible. + +The connection to your external Postgres server **will not be SSL encrypted**, as [we don't support that yet](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/89). diff --git a/docs/configuring-playbook-federation.md b/docs/configuring-playbook-federation.md new file mode 100644 index 00000000..2e6410ec --- /dev/null +++ b/docs/configuring-playbook-federation.md @@ -0,0 +1,49 @@ +# Controlling Matrix federation (optional) + +By default, your server federates with the whole Matrix network. +That is, people on your server can communicate with people on any other Matrix server. + + +## Federating only with select servers + +To make your server only federate with servers of your choosing, add this to your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_synapse_federation_domain_whitelist: +- example.com +- another.com +``` + +If you wish to disable federation, you can do that with an empty list (`[]`), or better yet by completely disabling federation (see below). + + +## Exposing the room directory over federation + +By default, your server's public rooms directory is not exposed to other servers via federation. + +If you wish to expose it, add this to your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_synapse_allow_public_rooms_over_federation: true +``` + + +## Disabling federation + +To completely disable federation, isolating your server from the rest of the Matrix network, add this to your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_synapse_federation_enabled: false +``` + +With that, your server's users will only be able to talk among themselves, but not to anyone who is on another server. + +**Disabling federation does not necessarily disable the federation port** (`8448`). Services like [Dimension](configuring-playbook-dimension.md) and [ma1sd](configuring-playbook-ma1sd.md) normally rely on `openid` APIs exposed on that port. Even if you disable federation and only if necessary, we may still be exposing the federation port and serving the `openid` APIs there. To override this and completely disable Synapse's federation port use: + +```yaml +# This stops the federation port on the Synapse side (normally `matrix-synapse:8048` on the container network). +matrix_synapse_federation_port_enabled: false + +# This removes the `8448` virtual host from the matrix-nginx-proxy reverse-proxy server. +matrix_nginx_proxy_proxy_matrix_federation_api_enabled: false +``` diff --git a/docs/configuring-playbook-jitsi.md b/docs/configuring-playbook-jitsi.md new file mode 100644 index 00000000..ec3ab416 --- /dev/null +++ b/docs/configuring-playbook-jitsi.md @@ -0,0 +1,168 @@ +# Jitsi + +The playbook can install the [Jitsi](https://jitsi.org/) video-conferencing platform and integrate it with [Element](configuring-playbook-client-element.md). + +Jitsi installation is **not enabled by default**, because it's not a core component of Matrix services. + +The setup done by the playbook is very similar to [docker-jitsi-meet](https://github.com/jitsi/docker-jitsi-meet). You can refer to the documentation there for many of the options here. + + +## Prerequisites + +Before installing Jitsi, make sure you've created the `jitsi.DOMAIN` DNS record. See [Configuring DNS](configuring-dns.md). + +You may also need to open the following ports to your server: + +- `4443/tcp` - RTP media fallback over TCP +- `10000/udp` - RTP media over UDP. Depending on your firewall/NAT setup, incoming RTP packets on port `10000` may have the external IP of your firewall as destination address, due to the usage of STUN in JVB (see [`matrix_jitsi_jvb_stun_servers`](../roles/matrix-jitsi/defaults/main.yml)). + + +## Installation + +Add this to your `inventory/host_vars/matrix.DOMAIN/vars.yml` configuration: + +```yaml +matrix_jitsi_enabled: true + +# Run `bash inventory/scripts/jitsi-generate-passwords.sh` to generate these passwords, +# or define your own strong passwords manually. +matrix_jitsi_jicofo_component_secret: "" +matrix_jitsi_jicofo_auth_password: "" +matrix_jitsi_jvb_auth_password: "" +matrix_jitsi_jibri_recorder_password: "" +matrix_jitsi_jibri_xmpp_password: "" +``` + + +## (Optional) Configure Jitsi authentication and guests mode + +By default the Jitsi Meet instance does not require any kind of login and is open to use for anyone without registration. + +If you're fine with such an open Jitsi instance, please skip to [Apply changes](#apply-changes). + +If you would like to control who is allowed to open meetings on your new Jitsi instance, then please follow this step to enable Jitsi's authentication and guests mode. With authentication enabled, all meeting rooms have to be opened by a registered user, after which guests are free to join. If a registered host is not yet present, guests are put on hold in individual waiting rooms. + +Add these two lines to your `inventory/host_vars/matrix.DOMAIN/vars.yml` configuration: + +```yaml +matrix_jitsi_enable_auth: true +matrix_jitsi_enable_guests: true +``` + +### (Optional) LDAP authentication + +The default authentication mode of Jitsi is `internal`, however LDAP is also supported. An example LDAP configuration could be: + +```yaml +matrix_jitsi_enable_auth: true +matrix_jitsi_auth_type: ldap +matrix_jitsi_ldap_url: "ldap://ldap.DOMAIN" +matrix_jitsi_ldap_base: "OU=People,DC=DOMAIN +#matrix_jitsi_ldap_binddn: "" +#matrix_jitsi_ldap_bindpw: "" +matrix_jitsi_ldap_filter: "uid=%u" +matrix_jitsi_ldap_auth_method: "bind" +matrix_jitsi_ldap_version: "3" +matrix_jitsi_ldap_use_tls: true +matrix_jitsi_ldap_tls_ciphers: "" +matrix_jitsi_ldap_tls_check_peer: true +matrix_jitsi_ldap_tls_cacert_file: "/etc/ssl/certs/ca-certificates.crt" +matrix_jitsi_ldap_tls_cacert_dir: "/etc/ssl/certs" +matrix_jitsi_ldap_start_tls: false +``` + +For more information refer to the [docker-jitsi-meet](https://github.com/jitsi/docker-jitsi-meet#authentication-using-ldap) and the [saslauthd `LDAP_SASLAUTHD`](https://github.com/winlibs/cyrus-sasl/blob/master/saslauthd/LDAP_SASLAUTHD) documentation. + + +## (Optional) Making your Jitsi server work on a LAN + +By default the Jitsi Meet instance does not work with a client in LAN (Local Area Network), even if others are connected from WAN. There are no video and audio. In the case of WAN to WAN everything is ok. + +The reason is the Jitsi VideoBridge git to LAN client the IP address of the docker image instead of the host. The [documentation](https://github.com/jitsi/docker-jitsi-meet#running-behind-nat-or-on-a-lan-environment) of Jitsi in docker suggest to add `DOCKER_HOST_ADDRESS` in enviornment variable to make it work. + +Here is how to do it in the playbook. + +Add these two lines to your `inventory/host_vars/matrix.DOMAIN/vars.yml` configuration: + +```yaml +matrix_jitsi_jvb_container_extra_arguments: + - '--env "DOCKER_HOST_ADDRESS="' +``` + +## (Optional) Fine tune Jitsi + +Sample **additional** `inventory/host_vars/matrix.DOMAIN/vars.yml` configuration to save up resources (explained below): + +```yaml +matrix_jitsi_web_custom_config_extension: | + config.enableLayerSuspension = true; + + config.disableAudioLevels = true; + + // Limit the number of video feeds forwarded to each client + config.channelLastN = 4; + +matrix_jitsi_web_config_resolution_width_ideal_and_max: 480 +matrix_jitsi_web_config_resolution_height_ideal_and_max: 240 +``` + +You may want to **suspend unused video layers** until they are requested again, to save up resources on both server and clients. +Read more on this feature [here](https://jitsi.org/blog/new-off-stage-layer-suppression-feature/) +For this add this line to your `inventory/host_vars/matrix.DOMAIN/vars.yml` configuration: + +You may wish to **disable audio levels** to avoid excessive refresh of the client-side page and decrease the CPU consumption involved. + +You may want to **limit the number of video feeds forwarded to each client**, to save up resources on both server and clients. As clients’ bandwidth and CPU may not bear the load, use this setting to avoid lag and crashes. +This feature is found by default in other webconference applications such as Office 365 Teams (limit is set to 4). +Read how it works [here](https://github.com/jitsi/jitsi-videobridge/blob/master/doc/last-n.md) and performance evaluation on this [study](https://jitsi.org/wp-content/uploads/2016/12/nossdav2015lastn.pdf). + +You may want to **limit the maximum video resolution**, to save up resources on both server and clients. + + +## Apply changes + +Then re-run the playbook: `ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start` + +## Required if configuring Jitsi with internal authentication: register new users + +Until this gets integrated into the playbook, we need to register new users / meeting hosts for Jitsi manually. +Please SSH into your matrix host machine and execute the following command targeting the `matrix-jitsi-prosody` container: + +```bash +docker exec matrix-jitsi-prosody prosodyctl --config /config/prosody.cfg.lua register meet.jitsi +``` + +Run this command for each user you would like to create, replacing `` and `` accordingly. After you've finished, please exit the host. + +**If you get an error** like this: "Error: Account creation/modification not supported.", it's likely that you had previously installed Jitsi without auth/guest support. In such a case, you should look into [Rebuilding your Jitsi installation](#rebuilding-your-jitsi-installation). + + +## Usage + +You can use the self-hosted Jitsi server in multiple ways: + +- **by adding a widget to a room via Element** (the one configured by the playbook at `https://element.DOMAIN`). Just start a voice or a video call in a room containing more than 2 members and that would create a Jitsi widget which utilizes your self-hosted Jitsi server. + +- **by adding a widget to a room via the Dimension Integration Manager**. You'll have to point the widget to your own Jitsi server manually. See our [Dimension](./configuring-playbook-dimension.md) documentation page for more details. Naturally, Dimension would need to be installed first (the playbook doesn't install it by default). + +- **directly (without any Matrix integration)**. Just go to `https://jitsi.DOMAIN` + +**Note**: Element apps on mobile devices currently [don't support joining meetings on a self-hosted Jitsi server](https://github.com/vector-im/riot-web/blob/601816862f7d84ac47547891bd53effa73d32957/docs/jitsi.md#mobile-app-support). + + +## Troubleshooting + +### Rebuilding your Jitsi installation + +**If you ever run into any trouble** or **if you change configuration (`matrix_jitsi_*` variables) too much**, we urge you to rebuild your Jitsi setup. + +We normally don't require such manual intervention for other services, but Jitsi services generate a lot of configuration files on their own. + +These files are not all managed by Ansible (at least not yet), so you may sometimes need to delete them all and start fresh. + +To rebuild your Jitsi configuration: + +- SSH into the server and do this: + - stop all Jitsi services (`systemctl stop matrix-jitsi-*`). + - remove all Jitsi configuration & data (`rm -rf /matrix/jitsi`) +- ask Ansible to set up Jitsi anew and restart services (`ansible-playbook -i inventory/hosts setup.yml --tags=setup-jitsi,start`) diff --git a/docs/configuring-playbook-ldap-auth.md b/docs/configuring-playbook-ldap-auth.md new file mode 100644 index 00000000..5144323a --- /dev/null +++ b/docs/configuring-playbook-ldap-auth.md @@ -0,0 +1,32 @@ +# Setting up the LDAP authentication password provider module (optional, advanced) + +The playbook can install and configure the [matrix-synapse-ldap3](https://github.com/matrix-org/matrix-synapse-ldap3) LDAP Auth password provider for you. + +See that project's documentation to learn what it does and why it might be useful to you. + +If you decide that you'd like to let this playbook install it for you, you need some configuration like this: + +```yaml +matrix_synapse_ext_password_provider_ldap_enabled: true +matrix_synapse_ext_password_provider_ldap_uri: "ldap://ldap.mydomain.tld:389" +matrix_synapse_ext_password_provider_ldap_start_tls: true +matrix_synapse_ext_password_provider_ldap_base: "ou=users,dc=example,dc=com" +matrix_synapse_ext_password_provider_ldap_attributes_uid: "uid" +matrix_synapse_ext_password_provider_ldap_attributes_mail: "mail" +matrix_synapse_ext_password_provider_ldap_attributes_name: "cn" +matrix_synapse_ext_password_provider_ldap_bind_dn: "" +matrix_synapse_ext_password_provider_ldap_bind_password: "" +matrix_synapse_ext_password_provider_ldap_filter: "" +``` + + +## Authenticating only using a password provider + +If you wish for users to **authenticate only against configured password providers** (like this one), **without consulting Synapse's local database**, feel free to disable it: + +```yaml +matrix_synapse_password_config_localdb_enabled: false +``` + +## Using ma1sd Identity Server for authentication +If you wish to use the ma1sd Identity Server for LDAP authentication instead of [matrix-synapse-ldap3](https://github.com/matrix-org/matrix-synapse-ldap3) consult [Adjusting ma1sd Identity Server configuration](configuring-playbook-ma1sd.md#authentication). diff --git a/docs/configuring-playbook-ma1sd.md b/docs/configuring-playbook-ma1sd.md new file mode 100644 index 00000000..70c507cb --- /dev/null +++ b/docs/configuring-playbook-ma1sd.md @@ -0,0 +1,135 @@ +# Adjusting ma1sd Identity Server configuration (optional) + +By default, this playbook configures an [ma1sd](https://github.com/ma1uta/ma1sd) Identity Server for you. + +This server is private by default, potentially at the expense of user discoverability. + +*ma1sd is a fork of [mxisd](https://github.com/kamax-io/mxisd) which was pronounced end of life 2019-06-21.* + +**Note**: enabling ma1sd (which is also the default), means that the `openid` API endpoints will be exposed on the Matrix Federation port (usually `8448`), even if [federation](configuring-playbook-federation.md) is disabled. It's something to be aware of, especially in terms of firewall whitelisting (make sure port `8448` is accessible). + + +## Disabling ma1sd + +ma1sd, being an Identity Server, is not strictly needed. It is only used for 3PIDs (3rd party identifiers like E-mail and phone numbers) and some [enhanced features](https://github.com/ma1uta/ma1sd/#features). + +If you'd like for the playbook to not install ma1sd (or to uninstall it if it was previously installed), you can disable it in your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_ma1sd_enabled: false +``` + +## Matrix.org lookup forwarding + +To ensure maximum discovery, you can make your identity server also forward lookups to the central matrix.org Identity server (at the cost of potentially leaking all your contacts information). + +Enabling this is discouraged and you'd better [learn more](https://github.com/ma1uta/ma1sd/blob/master/docs/features/identity.md#lookups) before proceeding. + +Enabling matrix.org forwarding can happen with the following configuration: + +```yaml +matrix_ma1sd_matrixorg_forwarding_enabled: true +``` + + +## Customizing email templates + +If you'd like to change the default email templates used by ma1sd, take a look at the `matrix_ma1sd_threepid_medium_email_custom_` variables +(in the `roles/matrix-ma1sd/defaults/main.yml` file. + + +## ma1sd-controlled Registration + +To use the [Registration](https://github.com/ma1uta/ma1sd/blob/master/docs/features/registration.md) feature of ma1sd, you can make use of the following variables: + +- `matrix_synapse_enable_registration` - to enable user-initiated registration in Synapse + +- `matrix_synapse_enable_registration_captcha` - to validate registering users using reCAPTCHA, as described in the [enabling reCAPTCHA](configuring_captcha.md) documentation. + +- `matrix_synapse_registrations_require_3pid` - to control the types of 3pid (`'email'`, `'msisdn'`) required by the Synapse server for registering + +- variables prefixed with `matrix_nginx_proxy_proxy_matrix_3pid_registration_` (e.g. `matrix_nginx_proxy_proxy_matrix_3pid_registration_enabled`) - to configure the integrated nginx webserver to send registration requests to ma1sd (instead of Synapse), so it can apply its additional functionality + +- `matrix_ma1sd_configuration_extension_yaml` - to configure ma1sd as required. See the [Registration feature's docs](https://github.com/ma1uta/ma1sd/blob/master/docs/features/registration.md) for inspiration. Also see the [Additional features](#additional-features) section below to learn more about how to use `matrix_ma1sd_configuration_extension_yaml`. + +**Note**: For this to work, either the homeserver needs to [federate](configuring-playbook-federation.md) or the `openid` APIs need to exposed on the federation port. When federation is disabled and ma1sd is enabled, we automatically expose the `openid` APIs (only!) on the federation port. Make sure the federation port (usually `https://matrix.DOMAIN:8448`) is whitelisted in your firewall (even if you don't actually use/need federation). + + +## Authentication + +[Authentication](https://github.com/ma1uta/ma1sd/blob/master/docs/features/authentication.md) provides the possibility to use your own [Identity Stores](https://github.com/ma1uta/ma1sd/blob/master/docs/stores/README.md) (for example LDAP) to authenticate users on your Homeserver. The following configuration can be used to authenticate against an LDAP server: + +```yaml +matrix_synapse_ext_password_provider_rest_auth_enabled: true + +# matrix-ma1sd is the hostname of the ma1sd Docker container +matrix_synapse_ext_password_provider_rest_auth_endpoint: "http://matrix-ma1sd:8090" + +matrix_ma1sd_configuration_extension_yaml: | + ldap: + enabled: true + connection: + host: ldapHostnameOrIp + tls: false + port: 389 + baseDNs: ['OU=Users,DC=example,DC=org'] + bindDn: CN=My ma1sd User,OU=Users,DC=example,DC=org + bindPassword: TheUserPassword +``` + +## Additional features + +What this playbook configures for your is some bare minimum Identity Server functionality, so that you won't need to rely on external 3rd party services. + +A few variables can be toggled in this playbook to alter the ma1sd configuration that gets generated. + +Still, ma1sd can do much more. +You can refer to the [ma1sd website](https://github.com/ma1uta/ma1sd) for more details and configuration options. + +To use a more custom configuration, you can define a `matrix_ma1sd_configuration_extension_yaml` string variable +and put your configuration in it. +To learn more about how to do this, refer to the information about `matrix_ma1sd_configuration_extension_yaml` in the [default variables file](../roles/matrix-ma1sd/defaults/main.yml) of the ma1sd component. + +## Example: SMS verification + +If your use case requires mobile verification, it is quite simple to integrate ma1sd with [Twilio](https://www.twilio.com/), an online telephony services gateway. Their prices are reasonable for low-volume projects and integration can be done with the following configuration: + +```yaml +matrix_ma1sd_configuration_extension_yaml: | + threepid: + medium: + msisdn: + connectors: + twilio: + account_sid: '' + auth_token: '' + number: '+' +``` + +## Example: Open Registration for every Domain + +If you want to open registration for any domain, you have to setup the allowed domains with ma1sd's `blacklist` and `whitelist`. The default behavior when neither the `blacklist`, nor the `whitelist` match, is to allow registration. Beware: you can't block toplevel domains (aka `.xy`) because the internal architecture of ma1sd doesn't allow that. + +```yaml +matrix_ma1sd_configuration_extension_yaml: | + register: + policy: + allowed: true + threepid: + email: + domain: + blacklist: ~ + whitelist: ~ +``` + +## Troubleshooting + +If email address validation emails sent by ma1sd are not reaching you, you should look into [Adjusting email-sending settings](configuring-playbook-email.md). + +If you'd like additional logging information, temporarily enable verbose logging for ma1sd. + +Example configuration (`inventory/host_vars/matrix.DOMAIN/vars.yml`): + +```yaml +matrix_ma1sd_verbose_logging: true +``` diff --git a/docs/configuring-playbook-matrix-corporal.md b/docs/configuring-playbook-matrix-corporal.md new file mode 100644 index 00000000..fb12e94a --- /dev/null +++ b/docs/configuring-playbook-matrix-corporal.md @@ -0,0 +1,92 @@ +# Setting up Matrix Corporal (optional, advanced) + +------------------------------------- + +**WARNING**: This is an advanced feature! It requires prior experience with Matrix and a specific need for using [Matrix Corporal](https://github.com/devture/matrix-corporal). If you're unsure whether you have such a need, you most likely don't. + +------------------------------------- + +The playbook can install and configure [matrix-corporal](https://github.com/devture/matrix-corporal) for you. + +In short, it's a sort of automation and firewalling service, which is helpful if you're instaling Matrix services in a controlled corporate environment. +See that project's documentation to learn what it does and why it might be useful to you. + +If you decide that you'd like to let this playbook install it for you, you'd need to also: +- (required) [set up the Shared Secret Auth password provider module](configuring-playbook-shared-secret-auth.md) +- (optional, but encouraged) [set up the REST authentication password provider module](configuring-playbook-rest-auth.md) + + +## Playbook configuration + +You would then need some configuration like this: + +```yaml +# The Shared Secret Auth password provider module is required for Corporal to work. +# See configuring-playbook-shared-secret-auth.md +matrix_synapse_ext_password_provider_shared_secret_auth_enabled: true +matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret: YOUR_SHARED_SECRET_GOES_HERE + +# When matrix-corporal is acting as the primary authentication provider, +# you need to set up the REST authentication password provider module +# to make Interactive User Authentication work. +# This is necessary for certain user actions (like E2EE, device management, etc). +# +# See configuring-playbook-rest-auth.md +matrix_synapse_ext_password_provider_rest_auth_enabled: true +matrix_synapse_ext_password_provider_rest_auth_endpoint: "http://matrix-corporal:41080/_matrix/corporal" + +matrix_corporal_enabled: true + +matrix_corporal_policy_provider_config: | + { + "Type": "http", + "Uri": "https://intranet.example.com/matrix/policy", + "AuthorizationBearerToken": "SOME_SECRET", + "CachePath": "/var/cache/matrix-corporal/last-policy.json", + "ReloadIntervalSeconds": 1800, + "TimeoutMilliseconds": 300 + } + +# If you also want to enable Matrix Corporal's HTTP API.. +matrix_corporal_http_api_enabled: true +matrix_corporal_http_api_auth_token: "AUTH_TOKEN_HERE" + +# If you need to change matrix-corporal's user id from the default (matrix-corporal). +# In any case, you need to make sure this Matrix user is created on your server. +matrix_corporal_corporal_user_id_local_part: "matrix-corporal" + +# Because Corporal peridoically performs lots of user logins from the same IP, +# you may need raise Synapse's ratelimits. +# The values below are just an example. Tweak to your use-case (number of users, etc.) +matrix_synapse_rc_login: + address: + per_second: 50 + burst_count: 300 + account: + per_second: 0.17 + burst_count: 3 + failed_attempts: + per_second: 0.17 + burst_count: 3 +``` + +Matrix Corporal operates with a specific Matrix user on your server. +By default, it's `matrix-corporal` (controllable by the `matrix_corporal_reconciliation_user_id_local_part` setting, see above). +No matter what Matrix user id you configure to run it with, make sure that: + +- the Matrix Corporal user is created by [registering it](registering-users.md). Use a password you remember, as you'll need to log in from time to time to create or join rooms + +- the Matrix Corporal user is joined and has Admin/Moderator-level access to any rooms you want it to manage + + +## Matrix Corporal files + +The following local filesystem paths are mounted in the `matrix-corporal` container and can be used in your configuration (or policy): + +- `/matrix/corporal/config` is mounted at `/etc/matrix-corporal` (read-only) + +- `/matrix/corporal/var` is mounted at `/var/matrix-corporal` (read and write) + +- `/matrix/corporal/cache` is mounted at `/var/cache/matrix-corporal` (read and write) + +As an example: you can create your own configuration files in `/matrix/corporal/config` and they will appear in `/etc/matrix-corporal` in the Docker container. Your configuration (stuff in `matrix_corporal_policy_provider_config`) needs to refer to these files via the local container paths - `/etc/matrix-corporal` (read-only), `/var/matrix-corporal` (read and write), `/var/cache/matrix-corporal` (read and write). diff --git a/docs/configuring-playbook-matrix-registration.md b/docs/configuring-playbook-matrix-registration.md new file mode 100644 index 00000000..b0240d3f --- /dev/null +++ b/docs/configuring-playbook-matrix-registration.md @@ -0,0 +1,66 @@ +# Setting up matrix-registration (optional) + +The playbook can install and configure [matrix-registration](https://github.com/ZerataX/matrix-registration) for you. + +> matrix-registration is a simple python application to have a token based matrix registration. + +Use matrix-registration to **create unique registration links**, which people can use to register on your Matrix server. It allows you to **keep your server's registration closed (private)**, but still allow certain people (these having a special link) to register a user account. + +**matrix-registration** provides 2 things: + +- **an API for creating registration tokens** (unique registration links). This API can be used via `curl` or via the playbook (see [Usage](#usage) below) + +- **a user registration page**, where people can use these registration tokens. By default, exposed at `https://matrix.DOMAIN/matrix-registration` + + +## Installing + +Adjust your playbook configuration (your `inventory/host_vars/matrix.DOMAIN/vars.yml` file): + +```yaml +matrix_registration_enabled: true + +# Generate a strong secret using: `pwgen -s 64 1`. +matrix_registration_admin_secret: "ENTER_SOME_SECRET_HERE" +``` + +Then, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +**matrix-registration** gets exposed at `https://matrix.DOMAIN/matrix-registration` + +It provides various [APIs](https://github.com/ZerataX/matrix-registration/wiki/api) - for creating registration tokens, listing tokens, disabling tokens, etc. To make use of all of its capabilities, consider using `curl`. + +We make the most common APIs easy to use via the playbook (see below). + + +### Creating registration tokens + +To **create a new user registration token (link)**, use this command: + +```bash +ansible-playbook -i inventory/hosts setup.yml \ +--tags=generate-matrix-registration-token \ +--extra-vars="one_time=yes ex_date=2021-12-31" +``` + +The above command creates and returns a **one-time use** token, which **expires** on the 31st of December 2021. +Adjust the `one_time` and `ex_date` variables as you see fit. + +Share the unique registration link (generated by the command above) with users to let them register on your Matrix server. + + +### Listing registration tokens + +To **list the existing user registration tokens**, use this command: + +```bash +ansible-playbook -i inventory/hosts setup.yml \ +--tags=list-matrix-registration-tokens +``` diff --git a/docs/configuring-playbook-nginx.md b/docs/configuring-playbook-nginx.md new file mode 100644 index 00000000..c8500b37 --- /dev/null +++ b/docs/configuring-playbook-nginx.md @@ -0,0 +1,84 @@ +# Configure Nginx (optional, advanced) + +By default, this playbook installs its own nginx webserver (in a Docker container) which listens on ports 80 and 443. +If that's alright, you can skip this. + + +## Using Nginx status + +This will serve a statuspage to the hosting machine only. Useful for monitoring software like [longview](https://www.linode.com/docs/platform/longview/longview-app-for-nginx/) + +```yaml +matrix_nginx_proxy_proxy_matrix_nginx_status_enabled: true +``` + +This will serve the status page under the following addresses: +- `http://matrix.DOMAIN/nginx_status` (using HTTP) +- `https://matrix.DOMAIN/nginx_status` (using HTTPS) + +By default, if ```matrix_nginx_proxy_nginx_status_enabled``` is enabled, access to the status page would be allowed from the local IP address of the server. If you wish to allow access from other IP addresses, you can provide them as a list: + +```yaml +matrix_nginx_proxy_proxy_matrix_nginx_status_allowed_addresses: +- 8.8.8.8 +- 1.1.1.1 +``` + +## Adjusting SSL in your server + +You can adjust how the SSL is served by the nginx server using the `matrix_nginx_proxy_ssl_preset` variable. We support a few presets, based on the Mozilla Server Side TLS +Recommended configurations. These presets influence the TLS Protocol, the SSL Cipher Suites and the `ssl_prefer_server_ciphers` variable of nginx. +Possible values are: + +- `"modern"` - For Modern clients that support TLS 1.3, with no need for backwards compatibility +- `"intermediate"` (**default**) - Recommended configuration for a general-purpose server +- `"old"` - Services accessed by very old clients or libraries, such as Internet Explorer 8 (Windows XP), Java 6, or OpenSSL 0.9.8 + +**Be really carefull when setting it to `"modern"`**. This could break comunication with other Matrix servers, limiting your federation posibilities. + +Besides changing the preset (`matrix_nginx_proxy_ssl_preset`), you can also directly override these 3 variables: + +- `matrix_nginx_proxy_ssl_protocols`: for specifying the supported TLS protocols. +- `matrix_nginx_proxy_ssl_prefer_server_ciphers`: for specifying if the server or the client choice when negotiating the cipher. It can set to `on` or `off`. +- `matrix_nginx_proxy_ssl_ciphers`: for specifying the SSL Cipher suites used by nginx. + +For more information about these variables, check the `roles/matrix-nginx-proxy/defaults/main.yml` file. + +## Synapse + OpenID Connect for Single-Sign-On + +If you want to use OpenID Connect as an SSO provider (as per the [Synapse OpenID docs](https://github.com/matrix-org/synapse/blob/develop/docs/openid.md)), you need to use the following configuration (in your `vars.yml` file) to instruct nginx to forward `/_synapse/oidc` to Synapse: + +```yaml +matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_oidc_api_enabled: true +``` + +## Disable Nginx access logs + +This will disable the access logging for nginx. + +```yaml +matrix_nginx_proxy_access_log_enabled: false +``` + +## Additional configuration + +This playbook also allows for additional configuration to be applied to the nginx server. + +If you want this playbook to obtain and renew certificates for other domains, then you can set the `matrix_ssl_additional_domains_to_obtain_certificates_for` variable (as mentioned in the [Obtaining SSL certificates for additional domains](configuring-playbook-ssl-certificates.md#obtaining-ssl-certificates-for-additional-domains) documentation as well). Make sure that you have set the DNS configuration for the domains you want to include to point at your server. + +```yaml +matrix_ssl_additional_domains_to_obtain_certificates_for: + - domain.one.example + - domain.two.example +``` + +You can include additional nginx configuration by setting the `matrix_nginx_proxy_proxy_http_additional_server_configuration_blocks` variable. + +```yaml +matrix_nginx_proxy_proxy_http_additional_server_configuration_blocks: + - | + # These lines will be included in the nginx configuration. + # This is at the top level of the file, so you will need to define all of the `server { ... }` blocks. + - | + # For advanced use, have a look at the template files in `roles/matrix-nginx-proxy/templates/nginx/conf.d` +``` diff --git a/docs/configuring-playbook-own-webserver.md b/docs/configuring-playbook-own-webserver.md new file mode 100644 index 00000000..8a827d0d --- /dev/null +++ b/docs/configuring-playbook-own-webserver.md @@ -0,0 +1,232 @@ +# Using your own webserver, instead of this playbook's nginx proxy (optional, advanced) + +By default, this playbook installs its own nginx webserver (in a Docker container) which listens on ports 80 and 443. +If that's alright, you can skip this. + +If you don't want this playbook's nginx webserver to take over your server's 80/443 ports like that, +and you'd like to use your own webserver (be it nginx, Apache, Varnish Cache, etc.), you can. + +There are **2 ways you can go about it**, if you'd like to use your own webserver: + +- [Method 1: Disabling the integrated nginx reverse-proxy webserver](#method-1-disabling-the-integrated-nginx-reverse-proxy-webserver) + +- [Method 2: Fronting the integrated nginx reverse-proxy webserver with another reverse-proxy](#method-2-fronting-the-integrated-nginx-reverse-proxy-webserver-with-another-reverse-proxy) + + +## Method 1: Disabling the integrated nginx reverse-proxy webserver + +This method is about completely disabling the integrated nginx reverse-proxy webserver and replicating its behavior using another webserver. +For an alternative, make sure to check Method #2 as well. + +### Preparation + +No matter which external webserver you decide to go with, you'll need to: + +1) Make sure your web server user (something like `http`, `apache`, `www-data`, `nginx`) is part of the `matrix` group. You should run something like this: `usermod -a -G matrix nginx`. This allows your webserver user to access files owned by the `matrix` group. When using an external nginx webserver, this allows it to read configuration files from `/matrix/nginx-proxy/conf.d`. When using another server, it would make other files, such as `/matrix/static-files/.well-known`, accessible to it. + +2) Edit your configuration file (`inventory/host_vars/matrix./vars.yml`) to disable the integrated nginx server: + +```yaml +matrix_nginx_proxy_enabled: false +``` + +3) **If you'll manage SSL certificates by yourself**, edit your configuration file (`inventory/host_vars/matrix./vars.yml`) to disable SSL certificate retrieval: + +```yaml +matrix_ssl_retrieval_method: none +``` + +**Note**: During [installation](installing.md), unless you've disabled SSL certificate management (`matrix_ssl_retrieval_method: none`), the playbook would need 80 to be available, in order to retrieve SSL certificates. **Please manually stop your other webserver while installing**. You can start it back up afterwards. + + +### Using your own external nginx webserver + +Once you've followed the [Preparation](#preparation) guide above, it's time to set up your external nginx server. + +Even with `matrix_nginx_proxy_enabled: false`, the playbook still generates some helpful files for you in `/matrix/nginx-proxy/conf.d`. +Those configuration files are adapted for use with an external web server (one not running in the container network). + +You can most likely directly use the config files installed by this playbook at: `/matrix/nginx-proxy/conf.d`. Just include them in your own `nginx.conf` like this: `include /matrix/nginx-proxy/conf.d/*.conf;` + +Note that if your nginx version is old, it might not like our default choice of SSL protocols (particularly the fact that the brand new `TLSv1.3` protocol is enabled). You can override the protocol list by redefining the `matrix_nginx_proxy_ssl_protocols` variable. Example: + +```yaml +# Custom protocol list (removing `TLSv1.3`) to suit your nginx version. +matrix_nginx_proxy_ssl_protocols: "TLSv1.2" +``` + +If you are experiencing issues, try updating to a newer version of Nginx. As a data point in May 2021 a user reported that Nginx 1.14.2 was not working for them. They were getting errors about socket leaks. Updating to Nginx 1.19 fixed their issue. + + +### Using your own external Apache webserver + +Once you've followed the [Preparation](#preparation) guide above, you can take a look at the [examples/apache](../examples/apache) directory for a sample configuration. + +### Using your own external caddy webserver + +After following the [Preparation](#preparation) guide above, you can take a look at the [examples/caddy](../examples/caddy) directory for a sample configuration. + +### Using your own HAproxy reverse proxy +After following the [Preparation](#preparation) guide above, you can take a look at the [examples/haproxy](../examples/haproxy) directory for a sample configuration. In this case HAproxy is used as a reverse proxy and a simple Nginx container is used to serve statically `.well-known` files. + +### Using another external webserver + +Feel free to look at the [examples/apache](../examples/apache) directory, or the [template files in the matrix-nginx-proxy role](../roles/matrix-nginx-proxy/templates/conf.d/). + + +## Method 2: Fronting the integrated nginx reverse-proxy webserver with another reverse-proxy + +This method is about leaving the integrated nginx reverse-proxy webserver be, but making it not get in the way (using up important ports, trying to retrieve SSL certificates, etc.). + +If you wish to use another webserver, the integrated nginx reverse-proxy webserver usually gets in the way because it attempts to fetch SSL certificates and binds to ports 80, 443 and 8448 (if Matrix Federation is enabled). + +You can disable such behavior and make the integrated nginx reverse-proxy webserver only serve traffic locally (or over a local network). + +You would need some configuration like this: + +```yaml +# Do not retrieve SSL certificates. This shall be managed by another webserver or other means. +matrix_ssl_retrieval_method: none + +# Do not try to serve HTTPS, since we have no SSL certificates. +# Disabling this also means services will be served on the HTTP port +# (`matrix_nginx_proxy_container_http_host_bind_port`). +matrix_nginx_proxy_https_enabled: false + +# Do not listen for HTTP on port 80 globally (default), listen on the loopback interface. +# If you'd like, you can make it use the local network as well and reverse-proxy from another local machine. +matrix_nginx_proxy_container_http_host_bind_port: '127.0.0.1:81' + +# Likewise, expose the Matrix Federation port on the loopback interface. +# Since `matrix_nginx_proxy_https_enabled` is set to `false`, this federation port will serve HTTP traffic. +# If you'd like, you can make it use the local network as well and reverse-proxy from another local machine. +# +# You'd most likely need to expose it publicly on port 8448 (8449 was chosen for the local port to prevent overlap). +matrix_nginx_proxy_container_federation_host_bind_port: '127.0.0.1:8449' + +# Coturn relies on SSL certificates that have already been obtained. +# Since we don't obtain any certificates (`matrix_ssl_retrieval_method: none` above), it won't work by default. +# An alternative is to tweak some of: `matrix_coturn_tls_enabled`, `matrix_coturn_tls_cert_path` and `matrix_coturn_tls_key_path`. +matrix_coturn_enabled: false +``` + +With this, nginx would still be in use, but it would not bother with anything SSL related or with taking up public ports. + +All services would be served locally on `127.0.0.1:81` and `127.0.0.1:8449` (as per the example configuration above). + +You can then set up another reverse-proxy server on ports 80/443/8448 for all of the expected domains and make traffic go to these local ports. +The expected domains vary depending on the services you have enabled (`matrix.DOMAIN` for sure; `element.DOMAIN`, `dimension.DOMAIN` and `jitsi.DOMAIN` are optional). + +### Sample configuration for running behind Traefik 2.0 + +Below is a sample configuration for using this playbook with a [Traefik](https://traefik.io/) 2.0 reverse proxy. + +```yaml +# Disable generation and retrieval of SSL certs +matrix_ssl_retrieval_method: none + +# Configure Nginx to only use plain HTTP +matrix_nginx_proxy_https_enabled: false + +# Don't bind any HTTP or federation port to the host +# (Traefik will proxy directly into the containers) +matrix_nginx_proxy_container_http_host_bind_port: '' +matrix_nginx_proxy_container_federation_host_bind_port: '' + +# Disable Coturn because it needs SSL certs +# (Clients can, though exposing IP address, use Matrix.org TURN) +matrix_coturn_enabled: false + +# All containers need to be on the same Docker network as Traefik +# (This network should already exist and Traefik should be using this network) +matrix_docker_network: 'traefik' + +matrix_nginx_proxy_container_extra_arguments: + # May be unnecessary depending on Traefik config, but can't hurt + - '--label "traefik.enable=true"' + + # The Nginx proxy container will receive traffic from these subdomains + - '--label "traefik.http.routers.matrix-nginx-proxy.rule=Host(`{{ matrix_server_fqn_matrix }}`,`{{ matrix_server_fqn_element }}`,`{{ matrix_server_fqn_dimension }}`,`{{ matrix_server_fqn_jitsi }}`)"' + + # (The 'web-secure' entrypoint must bind to port 443 in Traefik config) + - '--label "traefik.http.routers.matrix-nginx-proxy.entrypoints=web-secure"' + + # (The 'default' certificate resolver must be defined in Traefik config) + - '--label "traefik.http.routers.matrix-nginx-proxy.tls.certResolver=default"' + + # The Nginx proxy container uses port 8080 internally + - '--label "traefik.http.services.matrix-nginx-proxy.loadbalancer.server.port=8080"' + +matrix_synapse_container_extra_arguments: + # May be unnecessary depending on Traefik config, but can't hurt + - '--label "traefik.enable=true"' + + # The Synapse container will receive traffic from this subdomain + - '--label "traefik.http.routers.matrix-synapse.rule=Host(`{{ matrix_server_fqn_matrix }}`)"' + + # (The 'synapse' entrypoint must bind to port 8448 in Traefik config) + - '--label "traefik.http.routers.matrix-synapse.entrypoints=synapse"' + + # (The 'default' certificate resolver must be defined in Traefik config) + - '--label "traefik.http.routers.matrix-synapse.tls.certResolver=default"' + + # The Synapse container uses port 8048 internally + - '--label "traefik.http.services.matrix-synapse.loadbalancer.server.port=8048"' +``` + +This method uses labels attached to the Nginx and Synapse containers to provide the Traefik Docker provider with the information it needs to proxy `matrix.DOMAIN`, `element.DOMAIN`, `dimension.DOMAIN` and `jitsi.DOMAIN`. Some [static configuration](https://docs.traefik.io/v2.0/reference/static-configuration/file/) is required in Traefik; namely, having endpoints on ports 443 and 8448 and having a certificate resolver. + +Note that this configuration on its own does **not** redirect traffic on port 80 (plain HTTP) to port 443 for HTTPS, which may cause some issues, since the built-in Nginx proxy usually does this. If you are not already doing this in Traefik, it can be added to Traefik in a [file provider](https://docs.traefik.io/v2.0/providers/file/) as follows: + +```toml +[http] + [http.routers] + [http.routers.redirect-http] + entrypoints = ["web"] # The 'web' entrypoint must bind to port 80 + rule = "HostRegexp(`{host:.+}`)" # Change if you don't want to redirect all hosts to HTTPS + service = "dummy" # Unused, but all routers need services (for now) + middlewares = ["https"] + [http.services] + [http.services.dummy.loadbalancer] + [[http.services.dummy.loadbalancer.servers]] + url = "localhost" + [http.middlewares] + [http.middlewares.https.redirectscheme] + scheme = "https" + permanent = true +``` + +You can use the following `docker-compose.yml` as example to launch Traefik. + +```yaml +version: "3.3" + +services: + + traefik: + image: "traefik:v2.3" + restart: always + container_name: "traefik" + networks: + - traefik + command: + - "--api.insecure=true" + - "--providers.docker=true" + - "--providers.docker.network=traefik" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web-secure.address=:443" + - "--entrypoints.synapse.address=:8448" + - "--certificatesresolvers.default.acme.tlschallenge=true" + - "--certificatesresolvers.default.acme.email=YOUR EMAIL" + - "--certificatesresolvers.default.acme.storage=/letsencrypt/acme.json" + ports: + - "443:443" + - "8448:8448" + volumes: + - "./letsencrypt:/letsencrypt" + - "/var/run/docker.sock:/var/run/docker.sock:ro" + +networks: + traefik: + external: true +``` diff --git a/docs/configuring-playbook-postgres-backup.md b/docs/configuring-playbook-postgres-backup.md new file mode 100644 index 00000000..2d878c11 --- /dev/null +++ b/docs/configuring-playbook-postgres-backup.md @@ -0,0 +1,32 @@ +# Setting up postgres backup (optional) + +The playbook can install and configure [docker-postgres-backup-local](https://github.com/prodrigestivill/docker-postgres-backup-local) for you. + +## Adjusting the playbook configuration + +Minimal working configuration (`inventory/host_vars/matrix.DOMAIN/vars.yml`) to enable Postgres backup: + +```yaml +matrix_postgres_backup_enabled: true +``` + +Refer to the table below for additional configuration variables and their default values. + + +| Name | Default value | Description | +| :-------------------------------- | :--------------------------- | :--------------------------------------------------------------- | +|`matrix_postgres_backup_enabled`|`false`|Set to true to use [docker-postgres-backup-local](https://github.com/prodrigestivill/docker-postgres-backup-local) to create automatic database backups| +|`matrix_postgres_backup_schedule`| `'@daily'` |Cron-schedule specifying the interval between postgres backups.| +|`matrix_postgres_backup_keep_days`|`7`|Number of daily backups to keep| +|`matrix_postgres_backup_keep_weeks`|`4`|Number of weekly backups to keep| +|`matrix_postgres_backup_keep_months`|`12`|Number of monthly backups to keep| +|`matrix_postgres_backup_path` | `"{{ matrix_base_data_path }}/postgres-backup"` | Storagepath for the database backups| + + +## Installing + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` diff --git a/docs/configuring-playbook-prometheus-grafana.md b/docs/configuring-playbook-prometheus-grafana.md new file mode 100644 index 00000000..dc1b7b4c --- /dev/null +++ b/docs/configuring-playbook-prometheus-grafana.md @@ -0,0 +1,68 @@ +# Enabling metrics and graphs for your Matrix server (optional) + +It can be useful to have some (visual) insight into the performance of your homeserver. + +You can enable this with the following settings in your configuration file (`inventory/host_vars/matrix./vars.yml`): + +Remember to add `stats.` to DNS as described in [Configuring DNS](configuring-dns.md) before running the playbook. + +```yaml +matrix_prometheus_enabled: true + +matrix_prometheus_node_exporter_enabled: true + +matrix_grafana_enabled: true + +matrix_grafana_anonymous_access: false + +# This has no relation to your Matrix user id. It can be any username you'd like. +# Changing the username subsequently won't work. +matrix_grafana_default_admin_user: "some_username_chosen_by_you" + +# Changing the password subsequently won't work. +matrix_grafana_default_admin_password: "some_strong_password_chosen_by_you" +``` + +By default, a [Grafana](https://grafana.com/) web user-interface will be available at `https://stats.`. + + +## What does it do? + +Name | Description +-----|---------- +`matrix_prometheus_enabled`|[Prometheus](https://prometheus.io) is a time series database. It holds all the data we're going to talk about. +`matrix_prometheus_node_exporter_enabled`|[Node Exporter](https://prometheus.io/docs/guides/node-exporter/) is an addon of sorts to Prometheus that collects generic system information such as CPU, memory, filesystem, and even system temperatures +`matrix_grafana_enabled`|[Grafana](https://grafana.com/) is the visual component. It shows (on the `stats.` subdomain) the dashboards with the graphs that we're interested in +`matrix_grafana_anonymous_access`|By default you need to log in to see graphs. If you want to publicly share your graphs (e.g. when asking for help in [`#synapse:matrix.org`](https://matrix.to/#/#synapse:matrix.org?via=matrix.org&via=privacytools.io&via=mozilla.org)) you'll want to enable this option. +`matrix_grafana_default_admin_user`
`matrix_grafana_default_admin_password`|By default Grafana creates a user with `admin` as the username and password. If you feel this is insecure and you want to change it beforehand, you can do that here + + +## Security and privacy + +Metrics and resulting graphs can contain a lot of information. This includes system specs but also usage patterns. This applies especially to small personal/family scale homeservers. Someone might be able to figure out when you wake up and go to sleep by looking at the graphs over time. Think about this before enabling anonymous access. And you should really not forget to change your Grafana password. + +Most of our docker containers run with limited system access, but the `prometheus-node-exporter` has access to the host network stack and (readonly) root filesystem. This is required to report on them. If you don't like that, you can set `matrix_prometheus_node_exporter_enabled: false` (which is actually the default). You will still get Synapse metrics with this container disabled. Both of the dashboards will always be enabled, so you can still look at historical data after disabling either source. + + +## Collecting metrics to an external Prometheus server + +If you wish, you could expose homeserver metrics without enabling (installing) Prometheus and Grafana via the playbook. This may be useful for hooking Matrix services to an external Prometheus/Grafana installation. + +To do this, you may be interested in the following variables: + +Name | Description +-----|---------- +`matrix_synapse_metrics_enabled`|Set this to `true` to make Synapse expose metrics (locally, on the container network) +`matrix_nginx_proxy_proxy_synapse_metrics`|Set this to `true` to make matrix-nginx-proxy expose the Synapse metrics at `https://matrix.DOMAIN/_synapse/metrics` +`matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled`|Set this to `true` to password-protect (using HTTP Basic Auth) `https://matrix.DOMAIN/_synapse/metrics` (the username is always `prometheus`, the password is defined in `matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_key`) +`matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_key`|Set this to a password to use for HTTP Basic Auth for protecting `https://matrix.DOMAIN/_synapse/metrics` (the username is always `prometheus` - it's not configurable) +`matrix_server_fqn_grafana`|Use this variable to override the domain at which the Grafana web user-interface is at (defaults to `stats.DOMAIN`). + + +## More information + +- [Understanding Synapse Performance Issues Through Grafana Graphs](https://github.com/matrix-org/synapse/wiki/Understanding-Synapse-Performance-Issues-Through-Grafana-Graphs) at the Synapse Github Wiki +- [The Prometheus scraping rules](https://github.com/matrix-org/synapse/tree/master/contrib/prometheus) (we use v2) +- [The Synapse Grafana dashboard](https://github.com/matrix-org/synapse/tree/master/contrib/grafana) +- [The Node Exporter dashboard](https://github.com/rfrail3/grafana-dashboards) (for generic non-synapse performance graphs) + diff --git a/docs/configuring-playbook-prometheus-postgres.md b/docs/configuring-playbook-prometheus-postgres.md new file mode 100644 index 00000000..34407aae --- /dev/null +++ b/docs/configuring-playbook-prometheus-postgres.md @@ -0,0 +1,29 @@ +# Enabling metrics and graphs for Postgres (optional) + +Expanding on the metrics exposed by the [synapse exporter and the node exporter](configuring-playbook-prometheus-grafana.md), the playbook enables the [postgres exporter](https://github.com/prometheus-community/postgres_exporter) that exposes more detailed information about what's happening on your postgres database. + +You can enable this with the following settings in your configuration file (`inventory/host_vars/matrix./vars.yml`): + + +```yaml +matrix_prometheus_postgres_exporter_enabled: true + +# the role creates a postgres user as credential. You can configure these if required: +matrix_prometheus_postgres_exporter_database_username: 'matrix_prometheus_postgres_exporter' +matrix_prometheus_postgres_exporter_database_password: 'some-password' + +``` + +## What does it do? + +Name | Description +-----|---------- +`matrix_prometheus_postgres_exporter_enabled`|Enable the postgres prometheus exporter. This sets up the docker container, connects it to the database and adds a 'job' to the prometheus config which tells prometheus about this new exporter. The default is 'false' +`matrix_prometheus_postgres_exporter_database_username`| The 'username' for the user that the exporter uses to connect to the database. The default is 'matrix_prometheus_postgres_exporter' +`matrix_prometheus_postgres_exporter_database_password`| The 'password' for the user that the exporter uses to connect to the database. + + +## More information + +- [The PostgresSQL dashboard](https://grafana.com/grafana/dashboards/9628) (generic postgres dashboard) + diff --git a/docs/configuring-playbook-rest-auth.md b/docs/configuring-playbook-rest-auth.md new file mode 100644 index 00000000..23644c9e --- /dev/null +++ b/docs/configuring-playbook-rest-auth.md @@ -0,0 +1,24 @@ +# Setting up the REST authentication password provider module (optional, advanced) + +The playbook can install and configure [matrix-synapse-rest-auth](https://github.com/ma1uta/matrix-synapse-rest-password-provider) for you. + +See that project's documentation to learn what it does and why it might be useful to you. + +If you decide that you'd like to let this playbook install it for you, you need some configuration like this: + +```yaml +matrix_synapse_ext_password_provider_rest_auth_enabled: true +matrix_synapse_ext_password_provider_rest_auth_endpoint: "http://matrix-ma1sd:8090" +matrix_synapse_ext_password_provider_rest_auth_registration_enforce_lowercase: false +matrix_synapse_ext_password_provider_rest_auth_registration_profile_name_autofill: true +matrix_synapse_ext_password_provider_rest_auth_login_profile_name_autofill: false +``` + + +## Authenticating only using a password provider + +If you wish for users to **authenticate only against configured password providers** (like this one), **without consulting Synapse's local database**, feel free to disable it: + +```yaml +matrix_synapse_password_config_localdb_enabled: false +``` diff --git a/docs/configuring-playbook-riot-web.md b/docs/configuring-playbook-riot-web.md new file mode 100644 index 00000000..08370e07 --- /dev/null +++ b/docs/configuring-playbook-riot-web.md @@ -0,0 +1,39 @@ +# Configuring Riot-web (optional) + +By default, this playbook **used to install** the [Riot-web](https://github.com/vector-im/riot-web) Matrix client web application. + +Riot has since been [renamed to Element](https://element.io/blog/welcome-to-element/). + +- to learn more about Element and its configuration, see our dedicated [Configuring Element](configuring-playbook-client-element.md) documentation page +- to learn how to migrate from Riot to Element, see [Migrating to Element](#migrating-to-element) below + + +## Migrating to Element + +### Migrating your custom settings + +If you have custom `matrix_riot_web_` variables in your `inventory/host_vars/matrix.DOMAIN/vars.yml` file, you'll need to rename them (`matrix_riot_web_` -> `matrix_client_element_`). + +Some other playbook variables (but not all) with `riot` in their name are also renamed. The playbook checks and warns if you are using the old name for some commonly used ones. + + +### Domain migration + +We used to set up Riot at the `riot.DOMAIN` domain. The playbook now sets up Element at `element.DOMAIN` by default. + +There are a few options for handling this: + +- (**avoiding changes** - using the old `riot.DOMAIN` domain and avoiding DNS changes) -- to keep using `riot.DOMAIN` instead of `element.DOMAIN`, override the domain at which the playbook serves Element: `matrix_server_fqn_element: "riot.{{ matrix_domain }}"` + +- (**embracing changes** - using only `element.DOMAIN`) - set up the `element.DOMAIN` DNS record (see [Configuring DNS](configuring-dns.md)). You can drop the `riot.DOMAIN` in this case. If so, you may also wish to remove old SSL certificates (`rm -rf /matrix/ssl/config/live/riot.DOMAIN`) and renewal configuration (`rm -f /matrix/ssl/config/renewal/riot.DOMAIN.conf`), so that `certbot` would stop trying to renew them. + +- (**embracing changes and transitioning smoothly** - using both `element.DOMAIN` and `riot.DOMAIN`) - to serve Element at the new domain (`element.DOMAIN`) and to also have `riot.DOMAIN` redirect there - set up the `element.DOMAIN` DNS record (see [Configuring DNS](configuring-dns.md)) and enable Riot to Element redirection (`matrix_nginx_proxy_proxy_riot_compat_redirect_enabled: true`). + + +### Re-running the playbook + +As always, after making the necessary DNS and configuration adjustments, re-run the playbook to apply the changes: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` diff --git a/docs/configuring-playbook-s3.md b/docs/configuring-playbook-s3.md new file mode 100644 index 00000000..9132ff71 --- /dev/null +++ b/docs/configuring-playbook-s3.md @@ -0,0 +1,187 @@ +# Storing Matrix media files on Amazon S3 (optional) + +By default, this playbook configures your server to store Synapse's content repository (`media_store`) files on the local filesystem. +If that's alright, you can skip this. + +If you'd like to store Synapse's content repository (`media_store`) files on Amazon S3 (or other S3-compatible service), +you can let this playbook configure [Goofys](https://github.com/kahing/goofys) for you. + +Using a Goofys-backed media store works, but performance may not be ideal. If possible, try to use a region which is close to your Matrix server. + +If you'd like to move your locally-stored media store data to Amazon S3 (or another S3-compatible object store), we also provide some migration instructions below. + + +## Amazon S3 + +You'll need an Amazon S3 bucket and some IAM user credentials (access key + secret key) with full write access to the bucket. Example security policy: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Stmt1400105486000", + "Effect": "Allow", + "Action": [ + "s3:*" + ], + "Resource": [ + "arn:aws:s3:::your-bucket-name", + "arn:aws:s3:::your-bucket-name/*" + ] + } + ] +} +``` + +You then need to enable S3 support in your configuration file (`inventory/host_vars/matrix./vars.yml`). +It would be something like this: + +```yaml +matrix_s3_media_store_enabled: true +matrix_s3_media_store_bucket_name: "your-bucket-name" +matrix_s3_media_store_aws_access_key: "access-key-goes-here" +matrix_s3_media_store_aws_secret_key: "secret-key-goes-here" +matrix_s3_media_store_region: "eu-central-1" +``` + + +## Using other S3-compatible object stores + +You can use any S3-compatible object store by **additionally** configuring these variables: + +```yaml +matrix_s3_media_store_custom_endpoint_enabled: true +# Example: "https://storage.googleapis.com" +matrix_s3_media_store_custom_endpoint: "your-custom-endpoint" +``` + +### Backblaze B2 + +To use [Backblaze B2](https://www.backblaze.com/b2/cloud-storage.html): + +- create a new **private** bucket through its user interface (you can call it something like `matrix-DOMAIN-media-store`) +- note the **Endpoint** for your bucket (something like `s3.us-west-002.backblazeb2.com`) +- adjust its lifecycle rules to use the following **custom** rules: + - File Path: *empty value* + - Days Till Hide: *empty value* + - Days Till Delete: `1` +- go to [App Keys](https://secure.backblaze.com/app_keys.htm) and use the **Add a New Application Key** to create a new one + - restrict it to the previously created bucket (e.g. `matrix-DOMAIN-media-store`) + - give it *Read & Write* access + +Copy the `keyID` and `applicationKey`. + +You need the following *additional* playbook configuration (on top of what you see above): + +```yaml +matrix_s3_media_store_bucket_name: "YOUR_BUCKET_NAME_GOES_HERE" +matrix_s3_media_store_aws_access_key: "YOUR_keyID_GOES_HERE" +matrix_s3_media_store_aws_secret_key: "YOUR_applicationKey_GOES_HERE" +matrix_s3_media_store_custom_endpoint_enabled: true +matrix_s3_media_store_custom_endpoint: "https://s3.us-west-002.backblazeb2.com" # this may be different for your bucket +``` + +If you have local media store files and wish to migrate to Backblaze B2 subsequently, follow our [migration guide to Backblaze B2](#migrating-to-backblaze-b2) below instead of applying this configuration as-is. + + +## Migrating from local filesystem storage to S3 + +It's a good idea to [make a complete server backup](faq.md#how-do-i-backup-the-data-on-my-server) before migrating your local media store to an S3-backed one. + +Follow one of the guides below for a migration path from a locally-stored media store to one stored on S3-compatible storage: + +- [Migrating to any S3-compatible storage (universal, but likely slow)](#migrating-to-any-s3-compatible-storage-universal-but-likely-slow) +- [Migrating to Backblaze B2](#migrating-to-backblaze-b2) + +### Migrating to any S3-compatible storage (universal, but likely slow) + +It's a good idea to [make a complete server backup](faq.md#how-do-i-backup-the-data-on-my-server) before doing this. + +1. Proceed with the steps below without stopping Matrix services + +2. Start by adding the base S3 configuration in your `vars.yml` file (seen above, may be different depending on the S3 provider of your choice) + +3. In addition to the base configuration you see above, add this to your `vars.yml` file: + +```yaml +matrix_s3_media_store_path: /matrix/s3-media-store +``` + +This enables S3 support, but mounts the S3 storage bucket to `/matrix/s3-media-store` without hooking it to your homeserver yet. Your homeserver will still continue using your local filesystem for its media store. + +5. Run the playbook to apply the changes: `ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start` + +6. Do an **initial sync of your files** by running this **on the server** (it may take a very long time): + +```sh +sudo -u matrix -- rsync --size-only --ignore-existing -avr /matrix/synapse/storage/media-store/. /matrix/s3-media-store/. +``` + +You may need to install `rsync` manually. + +7. Stop all Matrix services (`ansible-playbook -i inventory/hosts setup.yml --tags=stop`) + +8. Start the S3 service by running this **on the server**: `systemctl start matrix-goofys` + +9. Sync the files again by re-running the `rsync` command you see in step #6 + +10. Stop the S3 service by running this **on the server**: `systemctl stop matrix-goofys` + +11. Get the old media store out of the way by running this command on the server: + +```sh +mv /matrix/synapse/storage/media-store /matrix/synapse/storage/media-store-local-backup +``` + +12. Remove the `matrix_s3_media_store_path` configuration from your `vars.yml` file (undoing step #3 above) + +13. Run the playbook: `ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start` + +14. You're done! Verify that loading existing (old) media files works and that you can upload new ones. + +15. When confident that it all works, get rid of the local media store directory: `rm -rf /matrix/synapse/storage/media-store-local-backup` + + +### Migrating to Backblaze B2 + +It's a good idea to [make a complete server backup](faq.md#how-do-i-backup-the-data-on-my-server) before doing this. + +1. While all Matrix services are running, run the following command on the server: + +(you need to adjust the 3 `--env` line below with your own data) + +```sh +docker run -it --rm -w /work \ +--env='B2_KEY_ID=YOUR_KEY_GOES_HERE' \ +--env='B2_KEY_SECRET=YOUR_SECRET_GOES_HERE' \ +--env='B2_BUCKET_NAME=YOUR_BUCKET_NAME_GOES_HERE' \ +-v /matrix/synapse/storage/media-store/:/work \ +--entrypoint=/bin/sh \ +docker.io/tianon/backblaze-b2:2.1.0 \ +-c 'b2 authorize-account $B2_KEY_ID $B2_KEY_SECRET > /dev/null && b2 sync /work/ b2://$B2_BUCKET_NAME' +``` + +This is some initial file sync, which may take a very long time. + +2. Stop all Matrix services (`ansible-playbook -i inventory/hosts setup.yml --tags=stop`) + +3. Run the command from step #1 again. + +Doing this will sync any new files that may have been created locally in the meantime. + +Now that Matrix services aren't running, we're sure to get Backblaze B2 and your local media store fully in sync. + +4. Get the old media store out of the way by running this command on the server: + +```sh +mv /matrix/synapse/storage/media-store /matrix/synapse/storage/media-store-local-backup +``` + +5. Put the [Backblaze B2 settings seen above](#backblaze-b2) in your `vars.yml` file + +6. Run the playbook: `ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start` + +7. You're done! Verify that loading existing (old) media files works and that you can upload new ones. + +8. When confident that it all works, get rid of the local media store directory: `rm -rf /matrix/synapse/storage/media-store-local-backup` diff --git a/docs/configuring-playbook-shared-secret-auth.md b/docs/configuring-playbook-shared-secret-auth.md new file mode 100644 index 00000000..21d1c332 --- /dev/null +++ b/docs/configuring-playbook-shared-secret-auth.md @@ -0,0 +1,23 @@ +# Setting up the Shared Secret Auth password provider module (optional, advanced) + +The playbook can install and configure [matrix-synapse-shared-secret-auth](https://github.com/devture/matrix-synapse-shared-secret-auth) for you. + +See that project's documentation to learn what it does and why it might be useful to you. + +If you decide that you'd like to let this playbook install it for you, you need some configuration (`inventory/host_vars/matrix./vars.yml`) like this: + +```yaml +matrix_synapse_ext_password_provider_shared_secret_auth_enabled: true +matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret: YOUR_SHARED_SECRET_GOES_HERE +``` + +You can generate a strong shared secret with a command like this: `pwgen -s 64 1` + + +## Authenticating only using a password provider + +If you wish for users to **authenticate only against configured password providers** (like this one), **without consulting Synapse's local database**, feel free to disable it: + +```yaml +matrix_synapse_password_config_localdb_enabled: false +``` diff --git a/docs/configuring-playbook-ssl-certificates.md b/docs/configuring-playbook-ssl-certificates.md new file mode 100644 index 00000000..2e288cc8 --- /dev/null +++ b/docs/configuring-playbook-ssl-certificates.md @@ -0,0 +1,97 @@ +# Adjusting SSL certificate retrieval (optional, advanced) + +By default, this playbook retrieves and auto-renews free SSL certificates from [Let's Encrypt](https://letsencrypt.org/) for the domains it needs (`matrix.` and possibly `element.`) + +Those certificates are used when configuring the nginx reverse proxy installed by this playbook. +They can also be used for configuring [your own webserver](configuring-playbook-own-webserver.md), in case you're not using the integrated nginx server provided by the playbook. + +If you need to retrieve certificates for other domains (e.g. your base domain) or more control over certificate retrieval, read below. + +Things discussed in this document: + +- [Using self-signed SSL certificates](#using-self-signed-ssl-certificates), if you can't use Let's Encrypt or just need a test setup + +- [Using your own SSL certificates](#using-your-own-ssl-certificates), if you don't want to or can't use Let's Encrypt certificates, but are still interested in using the integrated nginx reverse proxy server + +- [Not bothering with SSL certificates](#not-bothering-with-ssl-certificates), if you're using [your own webserver](configuring-playbook-own-webserver.md) and would rather this playbook leaves SSL certificate management to you + +- [Obtaining SSL certificates for additional domains](#obtaining-ssl-certificates-for-additional-domains), if you'd like to host additional domains on the Matrix server and would like the playbook to help you obtain and renew certificates for those domains automatically + + +## Using self-signed SSL certificates + +For private deployments (not publicly accessible from the internet), you may not be able to use Let's Encrypt certificates. + +If self-signed certificates are alright with you, you can ask the playbook to generate such for you with the following configuration: + +```yaml +matrix_ssl_retrieval_method: self-signed +``` + +If you get a `Cannot reach homeserver` error in Element, you will have to visit `https://matrix.` in your browser and agree to the certificate exception before you can login. + + +## Using your own SSL certificates + +If you'd like to manage SSL certificates by yourself and have the playbook use your certificate files, you can use the following configuration: + +```yaml +matrix_ssl_retrieval_method: manually-managed +``` + +With such a configuration, the playbook would expect you to drop the SSL certificate files in the directory specified by `matrix_ssl_config_dir_path` (`/matrix/ssl/config` by default) obeying the following hierarchy: + +- `/live//fullchain.pem` +- `/live//privkey.pem` +- `/live//chain.pem` + +where `` refers to the domains that you need (usually `matrix.` and `element.`). + + +## Not bothering with SSL certificates + +If you're [using an external web server](configuring-playbook-own-webserver.md) which is not nginx, or you would otherwise want to manage its certificates without this playbook getting in the way, you can completely disable SSL certificate management with the following configuration: + +```yaml +matrix_ssl_retrieval_method: none +``` + +With such a configuration, no certificates will be retrieved at all. You're free to manage them however you want. + + +## Obtaining SSL certificates for additional domains + +The playbook tries to be smart about the certificates it will obtain for you. + +By default, it obtains certificates for: +- `matrix.` (`matrix_server_fqn_matrix`) +- possibly for `element.`, unless you have disabled the [Element client component](configuring-playbook-client-element.md) using `matrix_client_element_enabled: false` +- possibly for `riot.`, if you have explicitly enabled Riot to Element redirection (for background compatibility) using `matrix_nginx_proxy_proxy_riot_compat_redirect_enabled: true` +- possibly for `dimension.`, if you have explicitly [set up Dimension](configuring-playbook-dimension.md). +- possibly for `jitsi.`, if you have explicitly [set up Jitsi](configuring-playbook-jitsi.md). +- possibly for your base domain (``), if you have explicitly configured [Serving the base domain](configuring-playbook-base-domain-serving.md) + +If you are hosting other domains on the Matrix machine, you can make the playbook obtain and renew certificates for those other domains too. +To do that, simply define your own custom configuration like this: + +```yaml +# In this example, we retrieve 2 extra certificates, +# one for the base domain (in the `matrix_domain` variable) and one for a hardcoded domain. +# Adding any other additional domains (hosted on the same machine) is possible. +matrix_ssl_additional_domains_to_obtain_certificates_for: + - '{{ matrix_domain }}' + - 'another.domain.example.com' +``` + +After redefining `matrix_ssl_domains_to_obtain_certificates_for`, to actually obtain certificates you should: + +- make sure the web server occupying port 80 is stopped. If you are using matrix-nginx-proxy server (which is the default for this playbook), you need to stop it temporarily by running `systemctl stop matrix-nginx-proxy` on the server. + +- re-run the SSL part of the playbook and restart all services: `ansible-playbook -i inventory/hosts setup.yml --tags=setup-ssl,start` + +The certificate files would be made available in `/matrix/ssl/config/live//...`. + +For automated certificate renewal to work, each port `80` vhost for each domain you are obtaining certificates for needs to forward requests for `/.well-known/acme-challenge` to the certbot container we use for renewal. + +See how this is configured for the `matrix.` subdomain in `/matrix/nginx-proxy/conf.d/matrix-synapse.conf` +Don't be alarmed if the above configuration file says port `8080`, instead of port `80`. It's due to port mapping due to our use of containers. diff --git a/docs/configuring-playbook-sygnal.md b/docs/configuring-playbook-sygnal.md new file mode 100644 index 00000000..3eb626eb --- /dev/null +++ b/docs/configuring-playbook-sygnal.md @@ -0,0 +1,71 @@ +# Setting up Sygnal (optional) + +The playbook can install and configure the [Sygnal](https://github.com/matrix-org/sygnal) push gateway for you. + +See the project's [documentation](https://github.com/matrix-org/sygnal) to learn what it does and why it might be useful to you. + +**Note**: most people don't need to install their own gateway. As Sygnal's [Notes for application developers](https://github.com/matrix-org/sygnal/blob/master/docs/applications.md) documentation says: + +> It is not feasible to allow end-users to configure their own Sygnal instance, because the Sygnal instance needs the appropriate FCM or APNs secrets that belong to the application. + +This optional playbook component is only useful to people who develop/build their own Matrix client applications themselves. + + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file (adapt to your needs): + +```yaml +matrix_sygnal_enabled: true + +# You need at least 1 app defined. +# The configuration below is incomplete. Read more below. +matrix_sygnal_apps: + com.example.myapp.ios: + type: apns + keyfile: /data/my_key.p8 + # .. more configuration .. + com.example.myapp.android: + type: gcm + api_key: your_api_key_for_gcm + # .. more configuration .. + +matrix_aux_file_definitions: + - dest: "{{ matrix_sygnal_data_path }}/my_key.p8" + content: | + some + content + here + mode: '0600' + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" +``` + +For a more complete example of available fields and values they can take, see `roles/matrix-sygnal/templates/sygnal.yaml.j2` (or the [upstream `sygnal.yaml.sample` configuration file](https://github.com/matrix-org/sygnal/blob/master/sygnal.yaml.sample)). + +Configuring [GCM/FCM](https://firebase.google.com/docs/cloud-messaging/) is easier, as it only requires that you provide some config values. + +To configure [APNS](https://developer.apple.com/notifications/) (Apple Push Notification Service), you'd need to provide one or more certificate files. +To do that, the above example configuration: + +- makes use of the `matrix-aux` role (and its `matrix_aux_file_definitions` variable) to make the playbook install files into `/matrix/sygnal/data` (the `matrix_sygnal_data_path` variable). See `roles/matrix-aux/defaults/main.yml` for usage examples. It also makes sure the files are owned by `matrix:matrix`, so that Sygnal can read them. Of course, you can also install these files manually yourself, if you'd rather not use `matrix-aux`. + +- references these files in the Sygnal configuration (`matrix_sygnal_apps`) using a path like `/data/..` (the `/matrix/sygnal/data` directory on the host system is mounted into the `/data` directory inside the container) + + +## Installing + +Don't forget to add `sygnal.` to DNS as described in [Configuring DNS](configuring-dns.md) before running the playbook. + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +To make use of your Sygnal installation, you'd need to build your own Matrix client application, which uses the same API keys (for [GCM/FCM](https://firebase.google.com/docs/cloud-messaging/)) and certificates (for [APNS](https://developer.apple.com/notifications/)) and is also pointed to `https://sygnal.DOMAIN` as the configured push server. + +Refer to Sygnal's [Notes for application developers](https://github.com/matrix-org/sygnal/blob/master/docs/applications.md) document. diff --git a/docs/configuring-playbook-synapse-admin.md b/docs/configuring-playbook-synapse-admin.md new file mode 100644 index 00000000..68d70305 --- /dev/null +++ b/docs/configuring-playbook-synapse-admin.md @@ -0,0 +1,64 @@ +# Setting up Synapse Admin (optional) + +The playbook can install and configure [synapse-admin](https://github.com/Awesome-Technologies/synapse-admin) for you. + +It's a web UI tool you can use to **administrate users and rooms on your Matrix server**. + +See the project's [documentation](https://github.com/Awesome-Technologies/synapse-admin) to learn what it does and why it might be useful to you. + + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file: + +```yaml +matrix_synapse_admin_enabled: true +``` + +**Note**: Synapse Admin requires Synapse's [Admin APIs](https://github.com/matrix-org/synapse/tree/master/docs/admin_api) to function. Access to them is restricted with a valid access token, so exposing them publicly should not be a real security concern. Still, for additional security, we normally leave them unexposed, following [official Synapse reverse-proxying recommendations](https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.md#synapse-administration-endpoints). Because Synapse Admin needs these APIs to function, when installing Synapse Admin, we **automatically** exposes them publicly for you (equivalent to `matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_admin_api_enabled: true`). + + +## Installing + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +After installation, Synapse Admin will be accessible at: `https://matrix.DOMAIN/synapse-admin/` + +To use Synapse Admin, you need to have [registered at least one administrator account](registering-users.md) on your server. + +The Homeserver URL to use on Synapse Admin's login page is: `https://matrix.DOMAIN` + +### Sample configuration for running behind Traefik 2.0 + +Below is a sample configuration for using this playbook with a [Traefik](https://traefik.io/) 2.0 reverse proxy. + +This an extension to Traefik config sample in [own-webserver-documentation](./configuring-playbook-own-webserver.md). + +```yaml +# Don't bind any HTTP or federation port to the host +# (Traefik will proxy directly into the containers) +matrix_synapse_admin_container_http_host_bind_port: "" + +matrix_synapse_admin_container_extra_arguments: + # May be unnecessary depending on Traefik config, but can't hurt + - '--label "traefik.enable=true"' + + # The Synapse Admin container will only receive traffic from this subdomain and path + - '--label "traefik.http.routers.matrix-synapse-admin.rule=(Host(`{{ matrix_server_fqn_matrix }}`) && Path(`{{matrix_synapse_admin_public_endpoint}}`))"' + + # (Define your entrypoint) + - '--label "traefik.http.routers.matrix-synapse-admin.entrypoints=web-secure"' + + # (The 'default' certificate resolver must be defined in Traefik config) + - '--label "traefik.http.routers.matrix-synapse-admin.tls.certResolver=default"' + + # The Synapse Admin container uses port 80 by default + - '--label "traefik.http.services.matrix-synapse-admin.loadbalancer.server.port=80"' +``` diff --git a/docs/configuring-playbook-synapse-simple-antispam.md b/docs/configuring-playbook-synapse-simple-antispam.md new file mode 100644 index 00000000..4c365a56 --- /dev/null +++ b/docs/configuring-playbook-synapse-simple-antispam.md @@ -0,0 +1,16 @@ +# Setting up Synapse Simple Antispam (optional, advanced) + +The playbook can install and configure [synapse-simple-antispam](https://github.com/t2bot/synapse-simple-antispam) for you. + +See that project's documentation to learn what it does and why it might be useful to you. +In short, it lets you fight invite-spam by automatically blocking invitiations from a list of servers specified by you (blacklisting). + +If you decide that you'd like to let this playbook install it for you, you need some configuration like this: + +```yaml +matrix_synapse_ext_spam_checker_synapse_simple_antispam_enabled: true + +matrix_synapse_ext_spam_checker_synapse_simple_antispam_config_blocked_homeservers: +- example.com +- another.com +``` diff --git a/docs/configuring-playbook-synapse.md b/docs/configuring-playbook-synapse.md new file mode 100644 index 00000000..50860a17 --- /dev/null +++ b/docs/configuring-playbook-synapse.md @@ -0,0 +1,76 @@ +# Configuring Synapse (optional) + +By default, this playbook configures the [Synapse](https://github.com/matrix-org/synapse) Matrix server, so that it works for the general case. +If that's enough for you, you can skip this document. + +The playbook provides lots of customization variables you could use to change Synapse's settings. + +Their defaults are defined in [`roles/matrix-synapse/defaults/main.yml`](../roles/matrix-synapse/defaults/main.yml) and they ultimately end up in the generated `/matrix/synapse/config/homeserver.yaml` file (on the server). This file is generated from the [`roles/matrix-synapse/templates/synapse/homeserver.yaml.j2`](../roles/matrix-synapse/templates/synapse/homeserver.yaml.j2) template. + +**If there's an existing variable** which controls a setting you wish to change, you can simply define that variable in your configuration file (`inventory/host_vars/matrix./vars.yml`) and [re-run the playbook](installing.md) to apply the changes. + +Alternatively, **if there is no pre-defined variable** for a Synapse setting you wish to change: + +- you can either **request a variable to be created** (or you can submit such a contribution yourself). Keep in mind that it's **probably not a good idea** to create variables for each one of Synapse's various settings that rarely get used. + +- or, you can **extend and override the default configuration** ([`homeserver.yaml.j2`](../roles/matrix-synapse/templates/synapse/homeserver.yaml.j2)) by making use of the `matrix_synapse_configuration_extension_yaml` variable. You can find information about this in [`roles/matrix-synapse/defaults/main.yml`](../roles/matrix-synapse/defaults/main.yml). + +- or, if extending the configuration is still not powerful enough for your needs, you can **override the configuration completely** using `matrix_synapse_configuration` (or `matrix_synapse_configuration_yaml`). You can find information about this in [`roles/matrix-synapse/defaults/main.yml`](../roles/matrix-synapse/defaults/main.yml). + + +## Load balancing with workers + +To have Synapse gracefully handle thousands of users, worker support should be enabled. It factors out some homeserver tasks and spreads the load of incoming client and server-to-server traffic between multiple processes. More information can be found in the [official Synapse workers documentation](https://github.com/matrix-org/synapse/blob/master/docs/workers.md). + +To enable Synapse worker support, update your `inventory/host_vars/matrix.DOMAIN/vars.yml` file: + +```yaml +matrix_synapse_workers_enabled: true +``` + +We support a few configuration presets (`matrix_synapse_workers_preset: one-of-each` being the default configuration): +- `little-federation-helper` - a very minimal worker configuration to improve federation performance +- `one-of-each` - one worker of each supported type + +If you'd like more customization power, you can start with one of the presets and tweak various `matrix_synapse_workers_*_count` variables manually. + +If you increase worker counts too much, you may need to increase the maximum number of Postgres connections too (example): + +```yaml +matrix_postgres_process_extra_arguments: [ + "-c 'max_connections=200'" +] +``` + +If you're using the default setup (the `matrix-nginx-proxy` webserver being enabled) or you're using your own `nginx` server (which imports the configuration files generated by the playbook), you're good to go. If you use some other webserver, you may need to tweak your reverse-proxy setup manually to forward traffic to the various workers. + +In case any problems occur, make sure to have a look at the [list of synapse issues about workers](https://github.com/matrix-org/synapse/issues?q=workers+in%3Atitle) and your `journalctl --unit 'matrix-*'`. + + +## Synapse Admin + +Certain Synapse administration tasks (managing users and rooms, etc.) can be performed via a web user-interace, if you install [Synapse Admin](configuring-playbook-synapse-admin.md). + + +## Synapse + OpenID Connect for Single-Sign-On + +If you'd like to use OpenID Connect authentication with Synapse, you'll need some additional reverse-proxy configuration (see [our nginx reverse-proxy doc page](configuring-playbook-nginx.md#synapse-openid-connect-for-single-sign-on)). + +In case you encounter errors regarding the parsing of the variables, you can try to add `{% raw %}` and `{% endraw %}` blocks around them. For example ; + +``` + - idp_id: keycloak + idp_name: "Keycloak" + issuer: "https://url.ix/auth/realms/x" + client_id: "matrix" + client_secret: "{{ vault_synapse_keycloak }}" + scopes: ["openid", "profile"] + authorization_endpoint: "https://url.ix/auth/realms/x/protocol/openid-connect/auth" + token_endpoint: "https://url.ix/auth/realms/x/protocol/openid-connect/token" + userinfo_endpoint: "https://url.ix/auth/realms/x/protocol/openid-connect/userinfo" + user_mapping_provider: + config: + display_name_template: "{% raw %}{{ user.given_name }}{% endraw %} {% raw %}{{ user.family_name }}{% endraw %}" + email_template: "{% raw %}{{ user.email }}{% endraw %}" +``` + diff --git a/docs/configuring-playbook-telemetry.md b/docs/configuring-playbook-telemetry.md new file mode 100644 index 00000000..da583838 --- /dev/null +++ b/docs/configuring-playbook-telemetry.md @@ -0,0 +1,46 @@ +# Enabling Telemetry for your Matrix server (optional) + +By default, this playbook configures your Matrix homeserver to not send any telemetry data anywhere. + +The [matrix.org](https://matrix.org) team would really appreciate it if you could help the project out by reporting +anonymized usage statistics from your homeserver. Only very [basic aggregate +data](#usage-statistics-being-submitted) (e.g. number of users) will be reported, but it helps track the +growth of the Matrix community, and helps to make Matrix a success. + + +## Enabling Telemetry + +If you'd like to **help by enabling submission of general usage statistics** for your homeserver, add this to your configuration file (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_synapse_report_stats: true +``` + + +## Usage statistics being submitted + +If statistics reporting is enabled, the information that gets submitted to the matrix.org team [according to the source code](https://github.com/matrix-org/synapse/blob/master/synapse/app/homeserver.py) is: + +- your homeserver's domain name + +- uptime of the homeserver program + +- [Python](https://www.python.org/) version powering your homeserver + +- total number of users on your home server (including bridged users) + +- total number of native Matrix users on your home server + +- total number of rooms on your homeserver + +- total number of daily active users on your homeserver + +- total number of daily active rooms on your homeserver + +- total number of messages sent per day + +- cache setting information + +- CPU and memory statistics for the homeserver program + +- database engine type and version diff --git a/docs/configuring-playbook-turn.md b/docs/configuring-playbook-turn.md new file mode 100644 index 00000000..c1d777a8 --- /dev/null +++ b/docs/configuring-playbook-turn.md @@ -0,0 +1,42 @@ +# TURN server + +The playbook installs a [Coturn](https://github.com/coturn/coturn) TURN server by default, so that clients can make audio/video calls even from [NAT](https://en.wikipedia.org/wiki/Network_address_translation)-ed networks. + +By default, the Synapse chat server is configured, so that it points to the Coturn TURN server installed by the playbook. + + +## Disabling Coturn + +If, for some reason, you'd like to prevent the playbook from installing Coturn, you can use the following configuration: + +```yaml +matrix_coturn_enabled: false +``` + +In that case, Synapse would not point to any Coturn servers and audio/video call functionality may fail. + + +## Using your own external Coturn server + +If you'd like to use another TURN server (be it Coturn or some other one), you can configure the playbook like this: + +```yaml +# Disable integrated Coturn server +matrix_coturn_enabled: false + +# Point Synapse to your other Coturn server +matrix_synapse_turn_uris: +- turns:HOSTNAME_OR_IP?transport=udp +- turns:HOSTNAME_OR_IP?transport=tcp +- turn:HOSTNAME_OR_IP?transport=udp +- turn:HOSTNAME_OR_IP?transport=tcp +``` + +If you have or want to enable [Jitsi](configuring-playbook-jitsi.md), you might want to enable the TURN server there too. +If you do not do it, Jitsi will fall back to an upstream service. + +```yaml +matrix_jitsi_web_stun_servers: +- stun:HOSTNAME_OR_IP:PORT +``` +You can put multiple host/port combinations if you like. diff --git a/docs/configuring-playbook.md b/docs/configuring-playbook.md new file mode 100644 index 00000000..fae66dca --- /dev/null +++ b/docs/configuring-playbook.md @@ -0,0 +1,145 @@ +# Configuring the Ansible playbook + +To configure the playbook, you need to have done the following things: + +- have a server where Matrix services will run +- [configured your DNS records](configuring-dns.md) +- [retrieved the playbook's source code](getting-the-playbook.md) to your computer + +You can then follow these steps inside the playbook directory: + +1. create a directory to hold your configuration (`mkdir inventory/host_vars/matrix.`) + +1. copy the sample configuration file (`cp examples/vars.yml inventory/host_vars/matrix./vars.yml`) + +1. edit the configuration file (`inventory/host_vars/matrix./vars.yml`) to your liking. You may also take a look at the various `roles/ROLE_NAME_HERE/defaults/main.yml` files and see if there's something you'd like to copy over and override in your `vars.yml` configuration file. + +1. copy the sample inventory hosts file (`cp examples/hosts inventory/hosts`) + +1. edit the inventory hosts file (`inventory/hosts`) to your liking + + +For a basic Matrix installation, that's all you need. +For a more custom setup, see the [Other configuration options](#other-configuration-options) below. + +When you're done with all the configuration you'd like to do, continue with [Installing](installing.md). + + +## Other configuration options + +### Additional useful services + +- [Setting up the Dimension Integration Manager](configuring-playbook-dimension.md) (optional, but recommended; after [installing](installing.md)) + +- [Setting up the Jitsi video-conferencing platform](configuring-playbook-jitsi.md) (optional) + +- [Setting up Dynamic DNS](configuring-playbook-dynamic-dns.md) (optional) + +- [Enabling metrics and graphs (Prometheus, Grafana) for your Matrix server](configuring-playbook-prometheus-grafana.md) (optional) + +### Core service adjustments + +- [Configuring Synapse](configuring-playbook-synapse.md) (optional) + +- [Configuring Element](configuring-playbook-client-element.md) (optional) + +- [Storing Matrix media files on Amazon S3](configuring-playbook-s3.md) (optional) + +- [Using an external PostgreSQL server](configuring-playbook-external-postgres.md) (optional) + +- [Adjusting ma1sd Identity Server configuration](configuring-playbook-ma1sd.md) (optional) + +- [Adjusting SSL certificate retrieval](configuring-playbook-ssl-certificates.md) (optional, advanced) + +- [Serving your base domain using this playbook's nginx server](configuring-playbook-base-domain-serving.md) (optional) + +- [Configure Nginx (optional, advanced)](configuring-playbook-nginx.md) (optional, advanced) + +- [Using your own webserver, instead of this playbook's nginx proxy](configuring-playbook-own-webserver.md) (optional, advanced) + +- [Adjusting TURN server configuration](configuring-playbook-turn.md) (optional, advanced) + + +### Server connectivity + +- [Enabling Telemetry for your Matrix server](configuring-playbook-telemetry.md) (optional) + +- [Controlling Matrix federation](configuring-playbook-federation.md) (optional) + +- [Adjusting email-sending settings](configuring-playbook-email.md) (optional) + +- [Setting up Hydrogen](configuring-playbook-client-hydrogen.md) - a new lightweight matrix client with legacy and mobile browser support (optional) + + +### Authentication and user-related + +- [Setting up Synapse Admin](configuring-playbook-synapse-admin.md) (optional) + +- [Setting up matrix-registration](configuring-playbook-matrix-registration.md) (optional) + +- [Setting up the REST authentication password provider module](configuring-playbook-rest-auth.md) (optional, advanced) + +- [Setting up the Shared Secret Auth password provider module](configuring-playbook-shared-secret-auth.md) (optional, advanced) + +- [Setting up the LDAP password provider module](configuring-playbook-ldap-auth.md) (optional, advanced) + +- [Setting up Synapse Simple Antispam](configuring-playbook-synapse-simple-antispam.md) (optional, advanced) + +- [Setting up Matrix Corporal](configuring-playbook-matrix-corporal.md) (optional, advanced) + + +### Bridging other networks + +- [Setting up Mautrix Telegram bridging](configuring-playbook-bridge-mautrix-telegram.md) (optional) + +- [Setting up Mautrix Whatsapp bridging](configuring-playbook-bridge-mautrix-whatsapp.md) (optional) + +- [Setting up Mautrix Facebook bridging](configuring-playbook-bridge-mautrix-facebook.md) (optional) + +- [Setting up Mautrix Hangouts bridging](configuring-playbook-bridge-mautrix-hangouts.md) (optional) + +- [Setting up Mautrix Instagram bridging](configuring-playbook-bridge-mautrix-instagram.md) (optional) + +- [Setting up Mautrix Signal bridging](configuring-playbook-bridge-mautrix-signal.md) (optional) + +- [Setting up Appservice IRC bridging](configuring-playbook-bridge-appservice-irc.md) (optional) + +- [Setting up Appservice Discord bridging](configuring-playbook-bridge-appservice-discord.md) (optional) + +- [Setting up Appservice Slack bridging](configuring-playbook-bridge-appservice-slack.md) (optional) + +- [Setting up Appservice Webhooks bridging](configuring-playbook-bridge-appservice-webhooks.md) (optional) + +- [Setting up MX Puppet Skype bridging](configuring-playbook-bridge-mx-puppet-skype.md) (optional) + +- [Setting up MX Puppet Slack bridging](configuring-playbook-bridge-mx-puppet-slack.md) (optional) + +- [Setting up MX Puppet Instagram bridging](configuring-playbook-bridge-mx-puppet-instagram.md) (optional) + +- [Setting up MX Puppet Twitter bridging](configuring-playbook-bridge-mx-puppet-twitter.md) (optional) + +- [Setting up MX Puppet Discord bridging](configuring-playbook-bridge-mx-puppet-discord.md) (optional) + +- [Setting up MX Puppet GroupMe bridging](configuring-playbook-bridge-mx-puppet-groupme.md) (optional) + +- [Setting up MX Puppet Steam bridging](configuring-playbook-bridge-mx-puppet-steam.md) (optional) + +- [Setting up Email2Matrix](configuring-playbook-email2matrix.md) (optional) + +- [Setting up Matrix SMS bridging](configuring-playbook-bridge-matrix-bridge-sms.md) (optional) + +- [Setting up Heisenbridge bouncer-style IRC bridging](configuring-playbook-bridge-heisenbridge.md) (optional) + + +### Bots + +- [Setting up matrix-reminder-bot](configuring-playbook-bot-matrix-reminder-bot.md) - a bot to remind you about stuff (optional) + +- [Setting up Go-NEB](configuring-playbook-bot-go-neb.md) - an extensible multifunctional bot (optional) + +- [Setting up Mjolnir](configuring-playbook-bot-mjolnir.md) - a moderation tool/bot (optional) + + +### Other specialized services + +- [Setting up the Sygnal push gateway](configuring-playbook-sygnal.md) (optional) diff --git a/docs/configuring-well-known.md b/docs/configuring-well-known.md new file mode 100644 index 00000000..27a4001c --- /dev/null +++ b/docs/configuring-well-known.md @@ -0,0 +1,171 @@ +# Configuring Service Discovery via .well-known + +Service discovery is a way for the Matrix network to discover where a Matrix server is. + +There are 2 types of well-known service discovery that Matrix makes use of: + +- (important) **Federation Server discovery** (`/.well-known/matrix/server`) -- assists other servers in the Matrix network with finding your server. Without a proper configuration, your server will effectively not be part of the Matrix network. Learn more in [Introduction to Federation Server Discovery](#introduction-to-federation-server-discovery) + +- (not that important) **Client Server discovery** (`/.well-known/matrix/client`) -- assists programs that you use to connect to your server (e.g. Element), so that they can make it more convenient for you by automatically configuring the "Homeserver URL" and "Identity Server URL" addresses. Learn more in [Introduction to Client Server Discovery](#introduction-to-client-server-discovery) + + +## Introduction to Federation Server Discovery + +All services created by this playbook are meant to be installed on their own server (such as `matrix.`). + +As [per the Server-Server specification](https://matrix.org/docs/spec/server_server/r0.1.0.html#server-discovery), to use a Matrix user identifier like `@:` while hosting services on a subdomain like `matrix.`, the Matrix network needs to be instructed of such delegation/redirection. + +Server delegation can be configured using DNS SRV records or by setting up a `/.well-known/matrix/server` file on the base domain (``). + +Both methods have their place and will continue to do so. You only need to use just one of these delegation methods. +For simplicity reasons, our setup advocates for the `/.well-known/matrix/server` method and guides you into using that. + +To learn how to set up `/.well-known/matrix/server`, read the Installing section below. + + +## Introduction to Client Server Discovery + +Client Server Service discovery lets various client programs which support it, to receive a full user id (e.g. `@username:example.com`) and determine where the Matrix server is automatically (e.g. `https://matrix.example.com`). + +This lets you (and your users) easily connect to your Matrix server without having to customize connection URLs. When using client programs that support it, you won't need to point them to `https://matrix.example.com` in Custom Server options manually anymore. The connection URL would be discovered automatically from your full username. + +As [per the Client-Server specification](https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery) Matrix does Client Server service discovery using a `/.well-known/matrix/client` file hosted on the base domain (e.g. `example.com`). + +However, this playbook installs your Matrix server on another domain (e.g. `matrix.example.com`) and not on the base domain (e.g. `example.com`), so it takes a little extra manual effort to set up the file. + +To learn how to set it up, read the Installing section below. + + +## Installing well-known files on the base domain's server + +To implement the two service discovery mechanisms, your base domain's server (e.g. `example.com`) needs to run an HTTPS-capable webserver. + +If you don't have a server for your base domain at all, you can use the Matrix server for this. +See [Serving the base domain](configuring-playbook-base-domain-serving.md) to learn how the playbook can help you set it up. +If you decide to go this route, you don't need to read ahead in this document. When **Serving the base domain**, the playbook takes care to serve the appropriate well-known files automatically. + +If you're managing the base domain by yourself somehow, you'll need to set up serving of some `/.well-known/matrix/*` files from it via HTTPS. + +To make things easy for you to set up, this playbook generates and hosts 2 well-known files on the Matrix domain's server (e.g. `https://matrix.example.com/.well-known/matrix/server` and `https://matrix.example.com/.well-known/matrix/client`), even though this is the wrong place to host them. + +You have 3 options when it comes to installing the files on the base domain's server: + + +### (Option 1): **Copying the files manually** to your base domain's server + +**Hint**: Option 2 and 3 (below) are generally a better way to do this. Make sure to go with them, if possible. + +All you need to do is: + +- copy `/.well-known/matrix/server` and `/.well-known/matrix/client` from the Matrix server (e.g. `matrix.example.com`) to your base domain's server (`example.com`). You can find these files in the `/matrix/static-files/.well-known/matrix` directory on the Matrix server. They are also accessible on URLs like this: `https://matrix.example.com/.well-known/matrix/server` (same for `client`). + +- set up the server at your base domain (e.g. `example.com`) so that it adds an extra HTTP header when serving the `/.well-known/matrix/client` file. [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), the `Access-Control-Allow-Origin` header should be set with a value of `*`. If you don't do this step, web-based Matrix clients (like Element) may fail to work. Setting up headers for the `/.well-known/matrix/server` file is not necessary, as this file is only consumed by non-browsers, which don't care about CORS. + +This is relatively easy to do and possibly your only choice if you can only host static files from the base domain's server. +It is, however, **a little fragile**, as future updates performed by this playbook may regenerate the well-known files and you may need to notice that and copy them over again. + + +### (Option 2): **Serving the base domain** from the Matrix server via the playbook + +If you don't need the base domain (e.g. `example.com`) for anything else (hosting a website, etc.), you can point it to the Matrix server's IP address and tell the playbook to configure it. + +This is the easiest way to set up well-known serving -- letting the playbook handle the whole base domain for you (including SSL certificates, etc.). However, if you need to use the base domain for other things (such as hosting some website, etc.), going with Option 1 or Option 3 might be more suitable. + +See [Serving the base domain](configuring-playbook-base-domain-serving.md) to learn how the playbook can help you set it up. + + +### (Option 3): **Setting up reverse-proxying** of the well-known files from the base domain's server to the Matrix server + +This option is less fragile and generally better. + +On the base domain's server (e.g. `example.com`), you can set up reverse-proxying, so that any access for the `/.well-known/matrix` location prefix is forwarded to the Matrix domain's server (e.g. `matrix.example.com`). + +With this method, you **don't need** to add special HTTP headers for [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) reasons (like `Access-Control-Allow-Origin`), because your Matrix server (where requests ultimately go) will be configured by this playbook correctly. + +**For nginx**, it would be something like this: + +```nginx +# This is your HTTPS-enabled server for DOMAIN. +server { + server_name DOMAIN; + + location /.well-known/matrix { + proxy_pass https://matrix.DOMAIN/.well-known/matrix; + proxy_set_header X-Forwarded-For $remote_addr; + } + + # other configuration +} +``` + +**For Apache**, it would be something like this: + +```apache + + ServerName DOMAIN + + SSLProxyEngine on + + ProxyPass "https://matrix.DOMAIN/.well-known/matrix" + + + # other configuration + +``` + +**For Caddy 2**, it would be something like this: + +```caddy +reverse_proxy /.well-known/matrix/* https://matrix.DOMAIN { + header_up Host {http.reverse_proxy.upstream.hostport} +} +``` + +**For Caddy 1**, it would be something like this: + +```caddy +proxy /.well-known/matrix/ https://matrix.DOMAIN { + header_upstream Host {http.reverse_proxy.upstream.hostport} +} +``` + +**For HAProxy**, it would be something like this: + +```haproxy +frontend www-https + # Select a Challenge for Matrix federation redirect + acl matrix-acl path_beg /.well-known/matrix/ + # Use the challenge backend if the challenge is set + use_backend matrix-backend if matrix-acl +backend matrix-backend + # Redirects the .well-known matrix to the matrix server for federation. + http-request set-header Host matrix.example.com + server matrix matrix.example.com:80 + # Map url path as ProxyPass does + reqirep ^(GET|POST|HEAD)\ /.well-known/matrix/(.*) \1\ /\2 + # Rewrite redirects as ProxyPassReverse does + acl response-is-redirect res.hdr(Location) -m found + rsprep ^Location:\ (http|https)://matrix.example.com\/(.*) Location:\ \1://matrix.example.com/.well-known/matrix/\2 if response-is-redirect +``` + +**For Netlify**, it would be something like this: + +``` +# In the _redirects file in the website's root +/.well-known/matrix/* https://matrix.DOMAIN/.well-known/matrix/:splat 200! +``` + +Make sure to: + +- **replace `DOMAIN`** in the server configuration with your actual domain name +- and: to **do this for the HTTPS-enabled server block**, as that's where Matrix expects the file to be + + +## Confirming it works + +No matter which method you've used to set up the well-known files, if you've done it correctly you should be able to see a JSON file at both of these URLs: + +- `https:///.well-known/matrix/server` +- `https:///.well-known/matrix/client` + +You can also check if everything is configured correctly, by [checking if services work](maintenance-checking-services.md). diff --git a/docs/container-images.md b/docs/container-images.md new file mode 100644 index 00000000..f2914488 --- /dev/null +++ b/docs/container-images.md @@ -0,0 +1,103 @@ +# Container Images used by the playbook + +This page summarizes the container ([Docker](https://www.docker.com/)) images used by the playbook when setting up your server. + +We try to stick to official images (provided by their respective projects) as much as possible. + + +## Container images used by default + +These services are enabled and used by default, but you can turn them off, if you wish. + +- [matrixdotorg/synapse](https://hub.docker.com/r/matrixdotorg/synapse/) - the official [Synapse](https://github.com/matrix-org/synapse) Matrix homeserver (optional) + +- [coturn/coturn](https://hub.docker.com/r/coturn/coturn/) - the [Coturn](https://github.com/coturn/coturn) STUN/TURN server (optional) + +- [vectorim/element-web](https://hub.docker.com/r/vectorim/element-web/) - the [Element](https://element.io/) web client (optional) + +- [ma1uta/ma1sd](https://hub.docker.com/r/ma1uta/ma1sd/) - the [ma1sd](https://github.com/ma1uta/ma1sd) Matrix Identity server (optional) + +- [postgres](https://hub.docker.com/_/postgres/) - the [Postgres](https://www.postgresql.org/) database server (optional) + +- [devture/exim-relay](https://hub.docker.com/r/devture/exim-relay/) - the [Exim](https://www.exim.org/) email server (optional) + +- [nginx](https://hub.docker.com/_/nginx/) - the [nginx](http://nginx.org/) web server (optional) + +- [certbot/certbot](https://hub.docker.com/r/certbot/certbot/) - the [certbot](https://certbot.eff.org/) tool for obtaining SSL certificates from [Let's Encrypt](https://letsencrypt.org/) (optional) + + +## Optional other container images we may use + +These services are not part of our default installation, but can be enabled by [configuring the playbook](configuring-playbook.md) (either before the initial installation or any time later): + +- [ewoutp/goofys](https://hub.docker.com/r/ewoutp/goofys/) - the [Goofys](https://github.com/kahing/goofys) Amazon [S3](https://aws.amazon.com/s3/) file-system-mounting program (optional) + +- [etherpad/etherpad](https://hub.docker.com/r/etherpad/etherpad/) - the [Etherpad](https://etherpad.org) realtime collaborative text editor that can be used in a Jitsi audio/video call or integrated as a widget into Matrix chat rooms via the Dimension integration manager (optional) + +- [devture/email2matrix](https://hub.docker.com/r/devture/email2matrix/) - the [Email2Matrix](https://github.com/devture/email2matrix) email server, which can relay email messages to Matrix rooms (optional) + +- [devture/matrix-corporal](https://hub.docker.com/r/devture/matrix-corporal/) - [Matrix Corporal](https://github.com/devture/matrix-corporal): reconciliator and gateway for a managed Matrix server (optional) + +- [zeratax/matrix-registration](https://hub.docker.com/r/devture/zeratax-matrix-registration/) - [matrix-registration](https://github.com/ZerataX/matrix-registration): a simple python application to have a token based matrix registration (optional) + +- [tulir/mautrix-telegram](https://mau.dev/tulir/mautrix-telegram/container_registry) - the [mautrix-telegram](https://github.com/tulir/mautrix-telegram) bridge to [Telegram](https://telegram.org/) (optional) + +- [tulir/mautrix-whatsapp](https://mau.dev/tulir/mautrix-whatsapp/container_registry) - the [mautrix-whatsapp](https://github.com/tulir/mautrix-whatsapp) bridge to [Whatsapp](https://www.whatsapp.com/) (optional) + +- [tulir/mautrix-facebook](https://mau.dev/tulir/mautrix-facebook/container_registry) - the [mautrix-facebook](https://github.com/tulir/mautrix-facebook) bridge to [Facebook](https://facebook.com/) (optional) + +- [tulir/mautrix-hangouts](https://mau.dev/tulir/mautrix-hangouts/container_registry) - the [mautrix-hangouts](https://github.com/tulir/mautrix-hangouts) bridge to [Google Hangouts](https://en.wikipedia.org/wiki/Google_Hangouts) (optional) + +- [tulir/mautrix-instagram](https://mau.dev/tulir/mautrix-instagram/container_registry) - the [mautrix-instagram](https://github.com/tulir/mautrix-instagram) bridge to [Instagram](https://instagram.com/) (optional) + +- [tulir/mautrix-signal](https://mau.dev/tulir/mautrix-signal/container_registry) - the [mautrix-signal](https://github.com/tulir/mautrix-signal) bridge to [Signal](https://www.signal.org/) (optional) + +- [matrixdotorg/matrix-appservice-irc](https://hub.docker.com/r/matrixdotorg/matrix-appservice-irc) - the [matrix-appservice-irc](https://github.com/matrix-org/matrix-appservice-irc) bridge to [IRC](https://wikipedia.org/wiki/Internet_Relay_Chat) (optional) + +- [halfshot/matrix-appservice-discord](https://hub.docker.com/r/halfshot/matrix-appservice-discord) - the [matrix-appservice-discord](https://github.com/Half-Shot/matrix-appservice-discord) bridge to [Discord](https://discordapp.com/) (optional) + +- [cadair/matrix-appservice-slack](https://hub.docker.com/r/cadair/matrix-appservice-slack) - the [matrix-appservice-slack](https://github.com/matrix-org/matrix-appservice-slack) bridge to [Slack](https://slack.com/) (optional) + +- [turt2live/matrix-appservice-webhooks](https://hub.docker.com/r/turt2live/matrix-appservice-webhooks) - the [Appservice Webhooks](https://github.com/turt2live/matrix-appservice-webhooks) bridge (optional) + +- [folivonet/matrix-sms-bridge](https://hub.docker.com/repository/docker/folivonet/matrix-sms-bridge) - the [matrix-sms-bridge](https://github.com/benkuly/matrix-sms-bridge) (optional) + +- [sorunome/mx-puppet-skype](https://hub.docker.com/r/sorunome/mx-puppet-skype) - the [mx-puppet-skype](https://github.com/Sorunome/mx-puppet-skype) bridge to [Skype](https://www.skype.com) (optional) + +- [sorunome/mx-puppet-slack](https://hub.docker.com/r/sorunome/mx-puppet-slack) - the [mx-puppet-slack](https://github.com/Sorunome/mx-puppet-slack) bridge to [Slack](https://slack.com) (optional) + +- [sorunome/mx-puppet-instagram](https://hub.docker.com/r/sorunome/mx-puppet-instagram) - the [mx-puppet-instagram](https://github.com/Sorunome/mx-puppet-instagram) bridge to [Instagram](https://www.instagram.com) (optional) + +- [sorunome/mx-puppet-twitter](https://hub.docker.com/r/sorunome/mx-puppet-twitter) - the [mx-puppet-twitter](https://github.com/Sorunome/mx-puppet-twitter) bridge to [Twitter](https://twitter.com) (optional) + +- [sorunome/mx-puppet-discord](https://hub.docker.com/r/sorunome/mx-puppet-discord) - the [mx-puppet-discord](https://github.com/matrix-discord/mx-puppet-discord) bridge to [Discord](https://discordapp.com) (optional) + +- [xangelix/mx-puppet-groupme](https://hub.docker.com/r/xangelix/mx-puppet-groupme) - the [mx-puppet-groupme](https://gitlab.com/robintown/mx-puppet-groupme) bridge to [GroupMe](https://groupme.com/) (optional) + +- [icewind1991/mx-puppet-steam](https://hub.docker.com/r/icewind1991/mx-puppet-steam) - the [mx-puppet-steam](https://github.com/icewind1991/mx-puppet-steam) bridge to [Steam](https://steampowered.com) (optional) + +- [turt2live/matrix-dimension](https://hub.docker.com/r/turt2live/matrix-dimension) - the [Dimension](https://dimension.t2bot.io/) integrations manager (optional) + +- [jitsi/web](https://hub.docker.com/r/jitsi/web) - the [Jitsi](https://jitsi.org/) web UI (optional) + +- [jitsi/jicofo](https://hub.docker.com/r/jitsi/jicofo) - the [Jitsi](https://jitsi.org/) Focus component (optional) + +- [jitsi/prosody](https://hub.docker.com/r/jitsi/prosody) - the [Jitsi](https://jitsi.org/) Prosody XMPP server component (optional) + +- [jitsi/jvb](https://hub.docker.com/r/jitsi/jvb) - the [Jitsi](https://jitsi.org/) Video Bridge component (optional) + +- [anoa/matrix-reminder-bot](https://hub.docker.com/r/anoa/matrix-reminder-bot) - the [matrix-reminder-bot](https://github.com/anoadragon453/matrix-reminder-bot) bot for one-off & recurring reminders and alarms (optional) + +- [matrixdotorg/go-neb](https://hub.docker.com/r/matrixdotorg/go-neb) - the [Go-NEB](https://github.com/matrix-org/go-neb) bot (optional) + +- [matrixdotorg/mjolnir](https://hub.docker.com/r/matrixdotorg/mjolnir) - the [mjolnir](https://github.com/matrix-org/mjolnir) moderation bot (optional) + +- [awesometechnologies/synapse-admin](https://hub.docker.com/r/awesometechnologies/synapse-admin) - the [synapse-admin](https://github.com/Awesome-Technologies/synapse-admin) web UI tool for administrating users and rooms on your Matrix server (optional) + +- [prom/prometheus](https://hub.docker.com/r/prom/prometheus/) - [Prometheus](https://github.com/prometheus/prometheus/) is a systems and service monitoring system + +- [prom/node-exporter](https://hub.docker.com/r/prom/node-exporter/) - [Prometheus Node Exporter](https://github.com/prometheus/node_exporter/) is an addon for Prometheus that gathers standard system metrics + +- [grafana/grafana](https://hub.docker.com/r/grafana/grafana/) - [Grafana](https://github.com/grafana/grafana/) is a graphing tool that works well with the above two images. Our playbook also adds two dashboards for [Synapse](https://github.com/matrix-org/synapse/tree/master/contrib/grafana) and [Node Exporter](https://github.com/rfrail3/grafana-dashboards) + +- [matrixdotorg/sygnal](https://hub.docker.com/r/matrixdotorg/sygnal/) - [Sygnal](https://github.com/matrix-org/sygnal) is a reference Push Gateway for Matrix diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 00000000..6c9eedc2 --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,475 @@ +# Frequently Asked Questions + +This documentation page tries to answer various Frequently Asked Questions about all things [Matrix](https://matrix.org/), with a focus on this [Ansible](https://www.ansible.com/) playbook ([What is Ansible? How does it work?](#what-is-ansible-how-does-it-work)). + +This FAQ page does not intend to replace the [matrix.org FAQ](https://matrix.org/faq/) (please see that one too). + +We've only started this FAQ recently, so it's still somewhat empty. + +Also, we encourage you to not dig yourself into a hole by reading way too much. When you've heard enough, proceed to [Prerequisites](prerequisites.md) to get guided into installing Matrix. + + +## Introductory + +## Where do I find more questions and answers about Matrix? + +This is a Frequently Asked Questions page focused on this [Ansible](https://www.ansible.com/) playbook ([What is Ansible? How does it work?](#what-is-ansible-how-does-it-work)) for deploying a [Matrix](https://matrix.org/) server. + +For a lot more generic questions and answers, see the [matrix.org FAQ](https://matrix.org/faq/). + +## What is Matrix? What is Element? What is Synapse? Why are you confusing me with so many terms? + +[Matrix](https://matrix.org/) is a new type of realtime communication (chat) network, the closest analogy to which is probably "email". + +You don't just use the "email" protocols (SMTP, POP3, IMAP) directly though. There's a *server* somewhere which stores your data (`@gmail.com`, `@yahoo.com`, `@hotmail.com`, `@your-company.com`) and you access it by using these "email" protocols via some *client* program (Outlook, Thunderbird, some website, etc). + +In the world of the Matrix chat protocol, there are various client programs. The first and currently most full-featured one is called [Element](https://element.io/) (used to be called Riot.im and Vector.im in the past). There are [many other clients](https://matrix.org/clients/). You can switch clients as much as you want until you find the one that is right for you on a given platform (you may use Element on your desktop, but Fluffychat on your phone, etc). + +Matrix is also like email due to the fact that there are many servers around the world which can all talk to each other (you can send email from `@gmail.com` addresses to `@yahoo.com` and `@hotmail.com` addresses). It's the same with Matrix (`@bob:his-domain.com` can talk to `@alice:her-domain.org`). + +If someone else is hosting your Matrix server (you being `@user:matrix.org` or some other public server like this), all you need is a Matrix client program, like Element. + +If you'd like to host your own server (you being `@user:your-own-domain.com`), you'd need to set up a Matrix server program, like Synapse. + +In short: + +- Matrix is the protocol - a set of rules about how the chat network operates +- Element is a client program you can use to participate on the Matrix chat network via some server (yours or someone else's). There are also [many other client programs](https://matrix.org/clients/). +- Synapse is a server program you can use to host your very own Matrix server. + +This FAQ here mostly focuses on installing various Matrix services using the Ansible automation tool. You can learn much more about Matrix in the [matrix.org FAQ](https://matrix.org/faq/). + +## People I wish to talk to are not on Matrix. Can I talk to them? + +You most likely can. Besides Matrix-native chats, Matrix also supports the concept of "bridging", which allows you to plug other networks into it. + +This Ansible playbook can help you install [tens of bridges for various networks](configuring-playbook.md#bridging-other-networks). + +Besides setting up your own bridges (preferable), you can also use some [public bridges hosted by others](https://publiclist.anchel.nl/#bridges). + +## How do I get started with Matrix? + +One of [Matrix](https://matrix.org/)'s distinguishing strengths (compared to other chat networks) is its decentralized nature. There's not just one entity (company, organization) controlling the servers. Rather there's thousands of servers operated by different people - one server being insecure, slow or disrespective toward its users does not affect the rest of the network. To participate in that decentralization in its fullest, consider hosting your own server or using some public server other than the largest/default one (`matrix.org`). + +There are 3 ways to get into Martix, depending on your technical ability and needs: + +- **using the existing default server** - the easiest way is to use an existing server. The largest public Matrix server is `matrix.org` and it's configured as a default server in clients such as [Element](https://element.io) and many others. Just use Element on the browser via that link (or download the Element app on a smartphone), create an account and start chatting. + +- **using some other server** - instead of using the largest public server (`matrix.org`), you can use another public one. Here's a [list of public Matrix servers](https://publiclist.anchel.nl/) to choose from. Again, you download [Element](https://element.io) or [some other client](https://matrix.org/clients/) of your choosing and adjust the homeserver URL during login. + +- **using your own server** - running your own server puts you in ultimate control of your data. It also lets you have your own user identifiers (e.g. `@bob:your-domain.com`). See [How do I set up my own Matrix server](#how-do-i-set-up-my-own-matrix-server). + +### How do I set up my own Matrix server? + +Normally, you'd first choose the [Matrix](https://matrix.org/) server software you'd like to run. At the time of this writing (January/2021), there's only one fully-featured server program, so there's only one reasonable choice. That's [Synapse](https://github.com/matrix-org/synapse). + +There are [many guides about installing Synapse](https://matrix.org/docs/guides/#installing-synapse). Using this Ansible playbook is just one way of doing it. + +Naturally, we're biased, so our usual recommendation is to go with this [Ansible](https://www.ansible.com/) playbook, instead of installing Synapse (and many many other things around it) manually. +To get started with the playbook, start at the [Prerequisites](prerequisites.md) page. + +### What is Ansible? How does it work? + +[Ansible](https://www.ansible.com/) is an automation program. This "playbook" is a collection of tasks/scripts that will set up a [Matrix](https://matrix.org/) server for you, so you don't have to perform these tasks manually. + +We have written these automated tasks for you and all you need to do is execute them using the Ansible program. + +You can install Ansible and this playbook code repository on your own computer and tell it to install Matrix services at the server living at `matrix.DOMAIN`. We recommend installing Ansible on your own computer. + +Alternatively, you can download Ansible and the playbook itself directly on the `matrix.DOMAIN` server. + +To learn more, see our [dedicated Ansible documentation page](ansible.md). + +### Why use this playbook and not install Synapse and other things manually? + +There are various guides telling you how easy it is to install [Synapse](https://github.com/matrix-org/synapse). + +Reading the documentation of this Ansible playbook, you may also be thinking: + +> I don't know what [Ansible](https://www.ansible.com/) is. I don't know what [Docker](https://www.docker.com/) is. This looks more complicated. + +.. so you may be leaning toward [installing Synapse manually](https://github.com/matrix-org/synapse/blob/master/INSTALL.md). + +The problem with a manual installation is: + +- Synapse is written in Python. If not packaged for your distribution, you'd need to install various Python modules, etc., and keep them updated. +- Synapse requires a [Postgres](https://www.postgresql.org/) database (it can run on SQLite, but that's very much discouraged). So you'd need to install Postgres as well. +- you may also need a reverse-proxy server in front of it (nginx, Apache), so you'd need to be familiar with that +- SSL is required, so you'd need to obtain Let's Encrypt (or other free or non-free) certificates for one or more domain names. You'd need to be familiar with [certbot](https://certbot.eff.org/) (when using Let's Encrypt) or similar software. +- for each additional component you'd like to add (client like [Element](https://element.io), bridge to some other chat network, Integration Manager (sitckers, other services), Identity Manager, etc.), you'll need to spend extra time installing and wiring it with the rest of the system in a way that works. +- you'll likely get slower updates for all of these components, depending on your distro packaging or your own time and ability + +The playbook, on the other hand, installs a bunch of components for you by default, obtains SSL certificates for you, etc. If you'd like, you can enable various bridges and other services with very little effort. All the components are wired to work together. + +All services run in Docker containers (most being officially provided by each component's developers), so we're not at the mercy of distro packaging. + +### Why use this playbook and not just use the Docker image directly? + +Reasons are similar to the reasons for not installing manually. + +Besides Synapse, you'd need other things - a Postgres database, likely the [Element](https://element.io) client, etc., etc. + +Using the playbook, you get all these components in a way that works well together out of the box. + +### What's different about this Ansible playbook compared to [EMnify/matrix-synapse-auto-deploy](https://github.com/EMnify/matrix-synapse-auto-deploy)? + +This is similar to the [EMnify/matrix-synapse-auto-deploy](https://github.com/EMnify/matrix-synapse-auto-deploy) Ansible deployment, but: + +- this one is a complete Ansible playbook (instead of just a role), so it's **easier to run** - especially for folks not familiar with Ansible + +- this one installs and hooks together **a lot more Matrix-related services** for you (see above) + +- this one **can be executed more than once** without causing trouble + +- works on various distros: **CentOS** (7.0+), Debian-based distributions (**Debian** 9/Stretch+, **Ubuntu** 16.04+), **Archlinux** + +- this one installs everything in a single directory (`/matrix` by default) and **doesn't "contaminate" your server** with files all over the place + +- this one **doesn't necessarily take over** ports 80 and 443. By default, it sets up nginx for you there, but you can also [use your own webserver](configuring-playbook-own-webserver.md) + +- this one **runs everything in Docker containers**, so it's likely more predictable and less fragile (see [Docker images used by this playbook](container-images.md)) + +- this one retrieves and automatically renews free [Let's Encrypt](https://letsencrypt.org/) **SSL certificates** for you + +- this one optionally can store the `media_store` content repository files on [Amazon S3](https://aws.amazon.com/s3/) (but defaults to storing files on the server's filesystem) + +- this one optionally **allows you to use an external PostgreSQL server** for Synapse's database (but defaults to running one in a container) + +- helps you **import data from a previous installation** (so you can migrate your manual virtualenv/Docker setup to a more managed one) + +- this one is actually **maintained** + +## Server-related + +### What kind of server do I need to install Matrix using this Ansible playbook? + +We list our server requirements in [Prerequisites](prerequisites.md). + +### Why not run Matrix on Kubernetes? + +There's no reason not to run Matrix on [Kubernetes](https://kubernetes.io/). + +However, that's overly complicated for thousands of us who just want to run a single small (and sometimes not so small) Matrix server, either using "cloud" servers or even a [Raspberry Pi](https://www.raspberrypi.org/) at home. + +For us, a Kubernetes-based setup which requires a cluster of multiple computers and is more technically-involved is a no-go. + +There are others working on automating a Matrix-on-Kubernetes setup, such as this [Helm](https://helm.sh/) chart: https://github.com/dacruz21/matrix-chart. + +### Why don't you use Podman instead of Docker? + +We like the philosophy of a daemonless container runtime, but [Podman](https://podman.io) is just not ready for our use case yet. + +Learn more about our past experiences/attempts to give Podman a chance, by reading [this issue](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/520). + +In short, `alias podman=docker` is a lie (for us). + +### Why use Docker? + +[Docker](https://www.docker.com/) is one of our 2 hard dependencies (the other one being [systemd](https://systemd.io/)). + +It lets us run services in an isolated manner and independently of the (usually old) packages available for distributions. + +It also lets us have a unified setup which runs the same across various supported distros (see them on [Prerequisites](prerequisites.md)). + +### Is Docker a hard requirement? + +Yes. See [Why don't you use Podman instead of Docker?](#why-dont-you-use-podman-instead-of-docker) for why we're not using another container runtime. + +All of our services run in containers. It's how we achieve predictability and also how we support tens of different services across lots of distros. + +The only thing we need on the distro is systemd and Python (we install Docker ourselves, unless you ask us not to). + +### Why don't you use docker-compose? + +Instead of using [docker-compose](https://docs.docker.com/compose/), we prefer installing systemd services and scheduling those independently. + +There are people who have worked on turning this setup into a docker-compose-based one. See these experiments [here](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/64#issuecomment-603164625). + +### Can I run this on a distro without systemd? + +No. [systemd](https://systemd.io/) is one of our 2 hard dependencies (the other one being [Docker](https://www.docker.com/)). + +### Can I install this on a Raspberry Pi? + +Yes, you can. See our [Alternative Architectures](alternative-architectures.md) documentation page. + +Whether a Raspberry Pi has enough power to give you a good experience is another question. It depends on your use case. + +Also see: [What kind of server specs do I need?](#what-kind-of-server-specs-do-i-need). + +### What kind of server specs do I need? + +This largely depends on your use case. It's not so much the number of users that you plan to host, but rather the number of large rooms they will join. + +Federated rooms with lots of history and containing hundreds of other servers are very heavy CPU-wise and memory-wise. + +You can probably use a 1 CPU + 1GB memory server to host hundreds of local users just fine, but as soon as one of them joins a federated room like `#matrix:matrix.org` (Matrix HQ) or some IRC-bridged room (say `##linux`), your server will get the need for a lot more power (at least 2GB RAM, etc). + +Running Matrix on a server with 1GB of memory is possible (especially if you disable some not-so-important services). See [How do I optimize this setup for a low-power server?](#how-do-i-optimize-this-setup-for-a-low-power-server). + +**We recommend starting with a server having at least 2GB of memory** and even then using it sparingly. If you know for sure you'll be joining various large rooms, etc., then going for 4GB of memory or more is a good idea. + +Besides the regular Matrix stuff, we also support things like video-conferencing using [Jitsi](configuring-playbook-jitsi.md) and other additional services which (when installed) may use up a lot of memory. Things do add up. Besides the Synapse Matrix server, Jitsi is especially notorious for consuming a lot of resources. If you plan on running Jitsi, we recommend a server with at least 2GB of memory (preferrably more). See our [Jitsi documentation page](configuring-playbook-jitsi.md) to learn how to optimize its memory/CPU usage. + +### Can I run this in an LXC container? + +If your distro runs within an [LXC container](https://linuxcontainers.org/), you may hit [this issue](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/703). It can be worked around, if absolutely necessary, but we suggest that you avoid running from within an LXC container. + + +## Configuration + +### Why install my server at matrix.DOMAIN and not at the base DOMAIN? + +It's the same with email servers. Your email address is likely `name@company.com`, not `name@mail.company.com`, even though it's `mail.company.com` that is really handling your data for `@company.com` email to work. + +Using a separate domain name is easier to manage (although it's a little hard to get right at first) and keeps your Matrix server isolated from your website (if you have one), from your email server (if you have one), etc. + +We allow `matrix.DOMAIN` to be the Matrix server handling Matrix stuff for `DOMAIN` by [Server Delegation](howto-server-delegation.md). During the installation procedure, we recommend that you set up server delegation using the [.well-known](configuring-well-known.md) method. + +If you'd really like to install Matrix services directly on the base domain, see [How do I install on matrix.DOMAIN without involving the base DOMAIN?](#how-do-i-install-on-matrixdomain-without-involving-the-base-domain). + +### I don't control anything on the base domain and can't set up delegation to matrix.DOMAIN. What do I do? + +If you're not in control of your base domain (or the server handling it) at all, you can take a look at [How do I install on matrix.DOMAIN without involving the base DOMAIN?](#how-do-i-install-on-matrixdomain-without-involving-the-base-domain) + +### I can't set up HTTPS on the base domain. How will I get Matrix federating? + +If you really can't obtain an HTTPS certificate for your base domain, you can take a look at [How do I install on matrix.DOMAIN without involving the base DOMAIN?](#how-do-i-install-on-matrixdomain-without-involving-the-base-domain) + +### How do I install on matrix.DOMAIN without involving the base DOMAIN? + +This Ansible playbook guides you into installing a server for `DOMAIN` (user identifiers are like this: `@user:DOMAIN`), while the server is at `matrix.DOMAIN`. + +We allow `matrix.DOMAIN` to be the Matrix server handling Matrix stuff for `DOMAIN` by [Server Delegation](howto-server-delegation.md). During the installation procedure, we recommend that you set up server delegation using the [.well-known](configuring-well-known.md) method. + +If you're fine with uglier identifiers (`@user:matrix.DOMAIN`, which is the equivalent of having an email address like `bob@mail.company.com`, instead of just `bob@company.com`), you can do that as well using the following configuration in your `vars.yml` file: + +```yaml +# This is what your identifiers are like (e.g. `@bob:matrix.YOUR_BASE_DOMAIN`). +matrix_domain: "matrix.YOUR_BASE_DOMAIN" + +# This is where Matrix services +matrix_server_fqn_matrix: "matrix.YOUR_BASE_DOMAIN" + +# This is where you access the Element web UI from (if enabled via `matrix_client_element_enabled: true`; enabled by default). +# This and the Matrix FQN (see above) are expected to be on the same server. +# +# Feel free to use `element.matrix.YOUR_BASE_DOMAIN`, if you'd prefer that. +matrix_server_fqn_element: "element.YOUR_BASE_DOMAIN" + +# This is where you access Dimension (if enabled via `matrix_dimension_enabled: true`; NOT enabled by default). +# +# Feel free to use `dimension.matrix.YOUR_BASE_DOMAIN`, if you'd prefer that. +matrix_server_fqn_dimension: "dimension.YOUR_BASE_DOMAIN" + +# This is where you access Jitsi (if enabled via `matrix_jitsi_enabled: true`; NOT enabled by default). +# +# Feel free to use `jitsi.matrix.YOUR_BASE_DOMAIN`, if you'd prefer that. +matrix_server_fqn_jitsi: "jitsi.YOUR_BASE_DOMAIN" +``` + +### I don't use the base domain for anything. How am I supposed to set up Server Delegation for Matrix services? + +If you don't use your base domain for anything, then it's hard for you to "serve files over HTTPS" on it -- something we ask you to do for the [.well-known](configuring-well-known.md) setup (needed for [Server Delegation](howto-server-delegation.md)). + +Luckily, the playbook can set up your Matrix server (at `matrix.DOMAIN`) to also handle traffic for the base domain (`DOMAIN`). + +See [Serving the base domain](configuring-playbook-base-domain-serving.md). + +### How do I optimize this setup for a low-power server? + +You can disable some not-so-important services to save on memory. + +```yaml +# An identity server is not a must. +matrix_ma1sd_enabled: false + +# Disabling this will prevent email-notifications and other such things from working. +matrix_mailer_enabled: false + +# You can also disable this to save more RAM, +# at the expense of audio/video calls being unreliable. +matrix_coturn_enabled: false + +# This makes Synapse not keep track of who is online/offline. +# +# Keeping track of this and announcing such online-status in federated rooms with +# hundreds of servers inside is insanely heavy (https://github.com/matrix-org/synapse/issues/3971). +# +# If your server does not federate with hundreds of others, enabling this doesn't hurt much. +matrix_synapse_presence_enabled: false +``` + +You can also consider implementing a restriction on room complexity, in order to prevent users from joining very heavy rooms: + +```yaml +matrix_synapse_configuration_extension_yaml: | + limit_remote_rooms: + enabled: true + complexity: 1.0 # this limits joining complex (~large) rooms, can be + # increased, but larger values can require more RAM +``` + +If you've installed [Jitsi](configuring-playbook-jitsi.md) (not installed by default), there are additional optimizations listed on its documentation page that you can perform. + +### I already have Docker on my server. Can you stop installing Docker via the playbook? + +Yes, we can stop installing Docker ourselves. Just use this in your `vars.yml` file: + +```yaml +matrix_docker_installation_enabled: true +``` + +### I run another webserver on the same server where I wish to install Matrix. What now? + +By default, we install a webserver for you (nginx), but you can also use [your own webserver](configuring-playbook-own-webserver.md). + +### How is the effective configuration determined? + +Configuration variables are defined in multiple places in this playbook and are considered in this order: + +- there are defaults coming from each role's defaults file (`role/matrix*/defaults/main.yml`). These variable values aim to be good defaults for when the role is used standalone (outside of this collection of roles, also called playbook). + +- then, there are overrides in `group_vars/matrix_servers`, which aim to adjust these "standalone role defaults" to something which better fits the playbook in its entirety. + +- finally, there's your `inventory/host_vars/matrix.DOMAIN/vars.yml` file, which is the ultimate override + +### What configuration variables are available? + +You can discover the variables you can override in each role (`role/matrix*/defaults/main.yml`). + +As described in [How is the effective configuration determined?](#how-is-the-effective-configuration-determined), these role-defaults may be overriden by values defined in `group_vars/matrix_servers`. + +Refer to both of these for inspiration. Still, as mentioned in [Configuring the playbook](configuring-playbook.md), you're only ever supposed to edit your own `inventory/host_vars/matrix.DOMAIN/vars.yml` file and nothing else inside the playbook (unless you're meaning to contribute new features). + +### I'd like to adjust some configuration which doesn't have a corresponding variable. How do I do it? + +The playbook doesn't aim to expose all configuration settings for all services using variables. +Doing so would amount to hundreds of variables that we have to create and maintain. + +Instead, we only try to make some important basics configurable using dedicated variables you can see in each role. +See [What configuration variables are available?](#what-configuration-variables-are-available). + +Besides that, each role (component) aims to provide a `matrix_SOME_COMPONENT_configuration_extension_yaml` (or `matrix_SOME_COMPONENT_configuration_extension_json`) variable, which can be used to override the configuration. + +Check each role's `role/matrix*/defaults/main.yml` for the corresponding variable and an example for how use it. + + +## Installation + +### How do I run the installation? + +See [Installing](installing.md) to learn how to use Ansible to install Matrix services. + +Of course, don't just jump straight to Installing. Rather, start at [Prerequisites](prerequisites.md) and get guided from there (into [setting up DNS](configuring-dns.md), [configuring the playbook](configuring-playbook.md), etc). + +### I installed Synapse some other way. Can I migrate such a setup to the playbook? + +Yes, you can. + +You generally need to do a playbook installation (start at the [Prerequisites](prerequisites.md) page), followed by importing your existing data into it. + +This Ansible playbook guides you into installing a server for `DOMAIN` (user identifiers are like this: `@user:DOMAIN`), while the server is at `matrix.DOMAIN`. If your existing setup has a server name (`server_name` configuration setting in Synapse's `homeserver.yaml` file) other than the base `DOMAIN`, you may need to tweak some additional variables. This FAQ entry may be of use if you're dealing with a more complicated setup - [How do I install on matrix.DOMAIN without involving the base DOMAIN?](#how-do-i-install-on-matrixdomain-without-involving-the-base-domain) + +After configuring the playbook and installing and **before starting** services (done with `ansible-playbook ... --tags=start`) you'd import [your SQLite](importing-synapse-sqlite.md) (or [Postgres](importing-postgres.md)) database and also [import your media store](importing-synapse-media-store.md). + +### I've downloaded Ansible and the playbook on the server. It can't connect using SSH. + +If you're using the playbook directly on the server, then Ansible doesn't need to connect using SSH. + +It can perform a local connection instead. Just set `ansible_connection=local` at the end of the server line in `inventory/hosts` and re-run the playbook. + +If you're running Ansible from within a container (one of the possibilities we list on our [dedicated Ansible documentation page](ansible.md)), then using `ansible_connection=local` is not possible. + + +## Troubleshooting + +### I get "Error response from daemon: configured logging driver does not support reading" when I do `docker logs matrix-synapse`. + +See [How can I see the logs?](#how-can-i-see-the-logs). + +### How can I see the logs? + +We utilize [systemd/journald](https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html#Description) for logging. + +To see logs for Synapse, run `journalctl -fu matrix-synapse.service`. You may wish to see the [manual page for journalctl](https://www.commandlinux.com/man-page/man1/journalctl.1.html). + +Available service names can be seen by doing `ls /etc/systemd/system/matrix*.service` on the server. + +Some services also log to files in `/matrix/*/data/..`, but we're slowly moving away from that. + +We also disable Docker logging, so you can't use `docker logs matrix-*` either. We do this to prevent useless double (or even triple) logging and to avoid having to rotate log files. + +We just simply delegate logging to journald and it takes care of persistence and expiring old data. + +Also see: [How long do systemd/journald logs persist for?](#how-long-do-systemdjournald-logs-persist-for) + +### How long do systemd/journald logs persist for? + +On some distros, the journald logs are just in-memory and not persisted to disk. + +Consult (and feel free to adjust) your distro's journald logging configuration in `/etc/systemd/journald.conf`. + +To enable persistence and put some limits on how large the journal log files can become, adjust your configuration like this: + +```ini +[Journal] +RuntimeMaxUse=200M +SystemMaxUse=1G +RateLimitInterval=0 +RateLimitBurst=0 +Storage=persistent +``` + + +## Maintenance + +### Do I need to do anything to keep my Matrix server updated? + +Yes. We don't update anything for you automatically. + +See our [documentation page about upgrading services](maintenance-upgrading-services.md). + +### How do I move my existing installation to another (VM) server? + +If you have an existing installation done using this Ansible playbook, you can easily migrate that to another server using [our dedicated server migration guide](maintenance-migrating.md). + +If your previous installation is done in some other way (not using this Ansible playbook), see [I installed Synapse some other way. Can I migrate such a setup to the playbook?](#i-installed-synapse-some-other-way-can-i-migrate-such-a-setup-to-the-playbook). + +### How do I back up the data on my server? + +We haven't documented this properly yet, but the general advice is to: + +- back up Postgres by making a database dump. See [Backing up PostgreSQL](maintenance-postgres.md#backing-up-postgresql) + +- back up all `/matrix` files, except for `/matrix/postgres/data` (you already have a dump) and `/matrix/postgres/data-auto-upgrade-backup` (this directory may exist and contain your old data if you've [performed a major Postgres upgrade](maintenance-postgres.md#upgrading-postgresql)). + +You can later restore these roughly like this: + +- restore the `/matrix` directory and files on the new server manually +- run the playbook again (see [Installing](installing.md)), but **don't** start services yet (**don't run** `... --tags=start`). This step will fix any file permission mismatches and will also set up additional software (Docker, etc.) and files on the server (systemd service, etc.). +- perform a Postgres database import (see [Importing Postgres](importing-postgres.md)) to restore your database backup +- start services (see [Starting the services](installing.md#starting-the-services)) + +If your server's IP address has changed, you may need to [set up DNS](configuring-dns.md) again. + +### What is this `/matrix/postgres/data-auto-upgrade-backup` directory that is taking up so much space? + +When you [perform a major Postgres upgrade](maintenance-postgres.md#upgrading-postgresql), we save the the old data files in `/matrix/postgres/data-auto-upgrade-backup`, just so you could easily restore them should something have gone wrong. + +After verifying that everything still works after the Postgres upgrade, you can safely delete `/matrix/postgres/data-auto-upgrade-backup` + +### How do I debug or force SSL certificate renewal? + +SSL certificate renewal normally happens automatically via [systemd timers](https://wiki.archlinux.org/index.php/Systemd/Timers). + +If you're having trouble with SSL certificate renewal, you can inspect the renewal logs using: + +- `journalctl -fu matrix-ssl-lets-encrypt-certificates-renew.service` +- *or* by looking at the log files in `/matrix/ssl/log/` + +To trigger renewal, run: `systemctl start matrix-ssl-lets-encrypt-certificates-renew.service`. You can then take a look at the logs again. + +If you're using the integrated webserver (`matrix-nginx-proxy`), you can reload it manually like this: `systemctl reload matrix-nginx-proxy`. Reloading also happens periodically via a systemd timer. + +If you're [using your own webserver](configuring-playbook-own-webserver.md) instead of the integrated one (`matrix-nginx-proxy`) you may also need to reload/restart it, to make it pick up the renewed SSL certificate files. diff --git a/docs/getting-the-playbook.md b/docs/getting-the-playbook.md new file mode 100644 index 00000000..2541da0d --- /dev/null +++ b/docs/getting-the-playbook.md @@ -0,0 +1,41 @@ +# Getting the playbook + +This Ansible playbook is meant to be executed on your own computer (not the Matrix server). + +In special cases (if your computer cannot run Ansible, etc.) you may put the playbook on the server as well. + +You can retrieve the playbook's source code by: + +- [Using git to get the playbook](#using-git-to-get-the-playbook) (recommended) + +- [Downloading the playbook as a ZIP archive](#downloading-the-playbook-as-a-zip-archive) (not recommended) + + +## Using git to get the playbook + +We recommend using the [git](https://git-scm.com/) tool to get the playbook's source code, because it lets you easily keep up to date in the future when [Maintaining services](maintenance-upgrading-services.md). + +Once you've installed git on your computer, you can go to any directory of your choosing and run the following command to retrieve the playbook's source code: + +```bash +git clone https://github.com/spantaleev/matrix-docker-ansible-deploy.git +``` + +This will create a new `matrix-docker-ansible-deploy` directory. +You're supposed to execute all other installation commands inside that directory. + + +## Downloading the playbook as a ZIP archive + +Alternatively, you can download the playbook as a ZIP archive. +This is not recommended, as it's not easy to keep up to date with future updates. We suggest you [use git](#using-git-to-get-the-playbook) instead. + +The latest version is always at the following URL: https://github.com/spantaleev/matrix-docker-ansible-deploy/archive/master.zip + +You can extract this archive anywhere. You'll get a directory called `matrix-docker-ansible-deploy-master`. +You're supposed to execute all other installation commands inside that directory. + + +--------------------------------------------- + +No matter which method you've used to download the playbook, you can proceed by [Configuring the playbook](configuring-playbook.md). diff --git a/docs/howto-server-delegation.md b/docs/howto-server-delegation.md new file mode 100644 index 00000000..e9ca5bde --- /dev/null +++ b/docs/howto-server-delegation.md @@ -0,0 +1,132 @@ +# Server Delegation + +To have a server on a subdomain (e.g. `matrix.`) handle Matrix federation traffic for the base domain (``), we need to instruct the Matrix network of such a delegation. + +By default, this playbook guides you into setting up [Server Delegation via a well-known file](#server-delegation-via-a-well-known-file). +However, that method may have some downsides that are not to your liking. Hence this guide about alternative ways to set up Server Delegation. + +It is a complicated matter, so unless you are affected by the [Downsides of well-known-based Server Delegation](#downsides-of-well-known-based-server-delegation), we suggest you stay on the simple/default path. + + +## Server Delegation via a well-known file + +Serving a `/.well-known/matrix/server` file from the base domain is the most straightforward way to set up server delegation, but it suffers from some problems that we list in [Downsides of well-known-based Server Delegation](#downsides-of-well-known-based-server-delegation). + +As we already mention in [Configuring DNS](configuring-dns.md) and [Configuring Service Discovery via .well-known](configuring-well-known.md), +this playbook already properly guides you into setting up such delegation by means of a `/.well-known/matrix/server` file served from the base domain (``). + +If this is okay with you, feel free to not read ahead. + + +### Downsides of well-known-based Server Delegation + +Server Delegation by means of a `/.well-known/matrix/server` file is the most straightforward, but suffers from the following downsides: + +- you need to have a working HTTPS server for the base domain (``). If you don't have any server for the base domain at all, you can easily solve it by making the playbook [serve the base domain from the Matrix server](configuring-playbook-base-domain-serving.md). + +- any downtime on the base domain (``) or network trouble between the matrix subdomain (`matrix.`) and the base `` may cause Matrix Federation outages. As the [Server-Server spec says](https://matrix.org/docs/spec/server_server/r0.1.0.html#server-discovery): + +> Errors are recommended to be cached for up to an hour, and servers are encouraged to exponentially back off for repeated failures. + +**For most people, this is a reasonable tradeoff** given that it's easy and straightforward to set up. We recommend you stay on this path. + +Otherwise, you can decide to go against the default for this playbook, and instead set up [Server Delegation via a DNS SRV record (advanced)](#server-delegation-via-a-dns-srv-record-advanced) (much more complicated). + + +## Server Delegation via a DNS SRV record (advanced) + +**NOTE**: doing Server Delegation via a DNS SRV record is a more **advanced** way to do it and is not the default for this playbook. This is usually **much more complicated** to set up, so **we don't recommend it**. If you're not an experience sysadmin, you'd better stay away from this. + +As per the [Server-Server spec](https://matrix.org/docs/spec/server_server/r0.1.0.html#server-discovery), it's possible to do Server Delegation using only a SRV record (without a `/.well-known/matrix/server` file). + +This prevents you from suffering the [Downsides of well-known-based Server Delegation](#downsides-of-well-known-based-server-delegation). + +To use DNS SRV record validation, you need to: + +- ensure that `/.well-known/matrix/server` is **not served** from the base domain, as that would interfere with DNS SRV record Server Delegation. To make the playbook **not** generate and serve the file, use the following configuration: `matrix_well_known_matrix_server_enabled: false`. + +- ensure that you have a `_matrix._tcp` DNS SRV record for your base domain (``) with a value of `10 0 8448 matrix.` + +- ensure that you are serving the Matrix Federation API (tcp/8448) with a certificate for `` (not `matrix.`!). Getting this certificate to the `matrix.` server may be complicated. The playbook's automatic SSL obtaining/renewal flow will likely not work and you'll need to copy certificates around manually. See below. + + +### Obtaining certificates + +How you can obtain a valid certificate for `` on the `matrix.` server is up to you. + +If `` and `matrix.` are hosted on the same machine, you can let the playbook obtain the certificate for you, by following our [Obtaining SSL certificates for additional domains](configuring-playbook-ssl-certificates.md#obtaining-ssl-certificates-for-additional-domains) guide. + +If `` and `matrix.` are not hosted on the same machine, you can copy over the certificate files manually. +Don't forget that they may get renewed once in a while, so you may also have to transfer them periodically. How often you do that is up to you, as long as the certificate files don't expire. + + +### Serving the Federation API with your certificates + +Regardless of which method for obtaining certificates you've used, once you've managed to get certificates for your base domain onto the `matrix.` machine you can put them to use. + +Based on your setup, you have different ways to go about it: + +- [Serving the Federation API with your certificates and matrix-nginx-proxy](#serving-the-federation-api-with-your-certificates-and-matrix-nginx-proxy) + +- [Serving the Federation API with your certificates and another webserver](#serving-the-federation-api-with-your-certificates-and-another-webserver) + +- [Serving the Federation API with your certificates and Synapse handling Federation](#serving-the-federation-api-with-your-certificates-and-synapse-handling-federation) + + +### Serving the Federation API with your certificates and matrix-nginx-proxy + +**If you are using matrix-nginx-proxy**, a reverse-proxy webserver used by default in this playbook, you only need to override the certificates used for the Matrix Federation API. You can do that using: + +```yaml +# Adjust paths below to point to your certificate. +# +# NOTE: these are in-container paths. `/matrix/ssl` on the host is mounted into the container +# at the same path (`/matrix/ssl`) by default, so if that's the path you need, it would be seamless. +matrix_nginx_proxy_proxy_matrix_federation_api_ssl_certificate: /matrix/ssl/config/live//fullchain.pem +matrix_nginx_proxy_proxy_matrix_federation_api_ssl_certificate_key: /matrix/ssl/config/live//privkey.pem +``` + +If your files are not in `/matrix/ssl` but in some other location, you would need to mount them into the container: + +```yaml +matrix_nginx_proxy_container_extra_arguments: + - "--mount type=bind,src=/some/path/on/the/host,dst=/some/path/inside/the/container,ro" +``` + +You then refer to them (for `matrix_nginx_proxy_proxy_matrix_federation_api_ssl_certificate` and `matrix_nginx_proxy_proxy_matrix_federation_api_ssl_certificate_key`) by using `/some/path/inside/the/container`. + +Make sure to reload matrix-nginx-proxy once in a while (`systemctl reload matrix-nginx-proxy`), so that newer certificates can kick in. +Reloading doesn't cause any downtime. + + +### Serving the Federation API with your certificates and another webserver + +**If you are NOT using matrix-nginx-proxy**, but rather some other webserver, you can set up reverse-proxying for the `tcp/8448` port by yourself. +Make sure to use the proper certificates for `` (not for `matrix.`) when serving the `tcp/8448` port. + +Proxying needs to happen to `127.0.0.1:8048` (unencrypted Synapse federation listener). + +Make sure to reload/restart your webserver once in a while, so that newer certificates can kick in. + + +### Serving the Federation API with your certificates and Synapse handling Federation + +**Alternatively**, if you are **NOT using matrix-nginx-proxy** and **would rather not use your own webserver for Federation traffic**, you can let Synapse handle Federation by itself. + +To do that, make sure the certificate files are mounted into the Synapse container: + +```yaml +matrix_synapse_container_extra_arguments: + - "--mount type=bind,src=/some/path/on/the/host,dst=/some/path/inside/the/container,ro" +``` + +You can then tell Synapse to serve Federation traffic over TLS on `tcp/8448`: + +```yaml +matrix_synapse_tls_federation_listener_enabled: true +matrix_synapse_tls_certificate_path: /some/path/inside/the/container/certificate.crt +matrix_synapse_tls_private_key_path: /some/path/inside/the/container/private.key +``` + +Make sure to reload Synapse once in a while (`systemctl reload matrix-synapse`), so that newer certificates can kick in. +Reloading doesn't cause any downtime. diff --git a/docs/importing-postgres.md b/docs/importing-postgres.md new file mode 100644 index 00000000..84347b57 --- /dev/null +++ b/docs/importing-postgres.md @@ -0,0 +1,84 @@ +# Importing an existing Postgres database from another installation (optional) + +Run this if you'd like to import your database from a previous installation. +(don't forget to import your Synapse `media_store` files as well - see [the importing-synape-media-store guide](importing-synapse-media-store.md)). + + +## Prerequisites + +For this to work, **the database name in Postgres must match** what this playbook uses. +This playbook uses a Postgres database name of `synapse` by default (controlled by the `matrix_synapse_database_database` variable). +If your database name differs, be sure to change `matrix_synapse_database_database` to your desired name and to re-run the playbook before proceeding. + +The playbook supports importing Postgres dump files in **text** (e.g. `pg_dump > dump.sql`) or **gzipped** formats (e.g. `pg_dump | gzip -c > dump.sql.gz`). + +Importing multiple databases (as dumped by `pg_dumpall`) is also supported. + +Before doing the actual import, **you need to upload your Postgres dump file to the server** (any path is okay). + + +## Importing + +To import, run this command (make sure to replace `` with a file path on your server): + +```sh +ansible-playbook -i inventory/hosts setup.yml \ +--extra-vars='server_path_postgres_dump=' \ +--tags=import-postgres +``` + +We specify the `synapse` database as the default import database. If your dump is a single-database dump (`pg_dump`), then we need to tell it where to go to. If you're redefining `matrix_synapse_database_database` to something other than `synapse`, please adjust it here too. For database dumps spanning multiple databases (`pg_dumpall`), you can remove the `postgres_default_import_database` definition (but it doesn't hurt to keep it too). + +**Note**: `` must be a file path to a Postgres dump file on the server (not on your local machine!). + + +## Troubleshooting + +A table ownership issue can occur if you are importing from a Synapse installation which was both: + + - migrated from SQLite to Postgres, and + - used a username other than 'synapse' + +In this case you may run into the following error during the import task: + +``` +"ERROR: role \"synapse_user\" does not exist" +``` + +where `synapse_user` is the database username from the previous Synapse installation. + +This can be verified by examining the dump for ALTER TABLE statements which set OWNER TO that username: + +```Shell +$ grep "ALTER TABLE" homeserver.sql" +ALTER TABLE public.access_tokens OWNER TO synapse_user; +ALTER TABLE public.account_data OWNER TO synapse_user; +ALTER TABLE public.account_data_max_stream_id OWNER TO synapse_user; +ALTER TABLE public.account_validity OWNER TO synapse_user; +ALTER TABLE public.application_services_state OWNER TO synapse_user; +... +``` + +It can be worked around by changing the username to `synapse`, for example by using `sed`: + +```Shell +$ sed -i "s/synapse_user/synapse/g" homeserver.sql" +``` + +This uses sed to perform an 'in-place' (`-i`) replacement globally (`/g`), searching for `synapse user` and replacing with `synapse` (`s/synapse_user/synapse`). If your database username was different, change `synapse_user` to that username instead. + +Note that if the previous import failed with an error it may have made changes which are incompatible with re-running the import task right away; if you do so it may fail with an error such as: + +``` +ERROR: relation \"access_tokens\" already exists +``` + +In this case you can use the command suggested in the import task to clear the database before retrying the import: + +```Shell +# systemctl stop matrix-postgres +# rm -rf /matrix/postgres/data/* +# systemctl start matrix-postgres +``` + +Once the database is clear and the ownership of the tables has been fixed in the SQL file, the import task should succeed. diff --git a/docs/importing-synapse-media-store.md b/docs/importing-synapse-media-store.md new file mode 100644 index 00000000..0ba7bacb --- /dev/null +++ b/docs/importing-synapse-media-store.md @@ -0,0 +1,22 @@ +# Importing `media_store` data files from an existing Synapse installation (optional) + +Run this if you'd like to import your `media_store` files from a previous installation of Synapse. + + +## Prerequisites + +Before doing the actual data restore, **you need to upload your media store directory to the server** (any path is okay). + +If you are [Storing Matrix media files on Amazon S3](configuring-playbook-s3.md) (optional), restoring with this tool is not possible right now. +As an alternative, you can perform a manual restore using the [AWS CLI tool](https://aws.amazon.com/cli/) (e.g. `aws s3 sync /path/to/server/media_store/. s3://name-of-bucket/`) + +**Note for Mac users**: Due to case-sensitivity issues on certain Mac filesystems (HFS or HFS+), filename corruption may occur if you copy a `media_store` directory to your Mac. If you're transferring a `media_store` directory between 2 servers, make sure you do it directly (from server to server with a tool such as [rsync](https://rsync.samba.org/)), and not by downloading the files to your Mac. + + +## Importing + +Run this command (make sure to replace `` with a path on your server): + + ansible-playbook -i inventory/hosts setup.yml --extra-vars='server_path_media_store=' --tags=import-synapse-media-store + +**Note**: `` must be a file path to a `media_store` directory on the server (not on your local machine!). diff --git a/docs/importing-synapse-sqlite.md b/docs/importing-synapse-sqlite.md new file mode 100644 index 00000000..aade2226 --- /dev/null +++ b/docs/importing-synapse-sqlite.md @@ -0,0 +1,26 @@ +# Importing an existing SQLite database from another Synapse installation (optional) + +Run this if you'd like to import your database from a previous default installation of Synapse. +(don't forget to import your `media_store` files as well - see [the importing-synapse-media-store guide](importing-synapse-media-store.md)). + +While this playbook always sets up PostgreSQL, by default a Synapse installation would run +using an SQLite database. + +If you have such a Synapse setup and wish to migrate it here (and over to PostgreSQL), this command is for you. + + +## Prerequisites + +Before doing the actual import, **you need to upload your SQLite database file to the server** (any path is okay). + + +## Importing + +Run this command (make sure to replace `` with a file path on your server): + + ansible-playbook -i inventory/hosts setup.yml --extra-vars='server_path_homeserver_db=' --tags=import-synapse-sqlite-db + +**Notes**: + +- `` must be a file path to a `homeserver.db` **file on the server** (not on your local machine!). +- if the SQLite database is from an older version of Synapse, the **importing procedure may run migrations on it to bring it up to date**. That is, your SQLite database file may get modified and become unusable with your older Synapse version. Keeping a copy of the original is probably wise. diff --git a/docs/installing.md b/docs/installing.md new file mode 100644 index 00000000..0e9dadd1 --- /dev/null +++ b/docs/installing.md @@ -0,0 +1,54 @@ +# Installing + +If you've [configured your DNS](configuring-dns.md) and have [configured the playbook](configuring-playbook.md), you can start the installation procedure. + +Run this as-is to set up a server: + +```bash +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all +``` + +**Note**: if you don't use SSH keys for authentication, but rather a regular password, you may need to add `--ask-pass` to the above (and all other) Ansible commands. + +**Note**: if you **do** use SSH keys for authentication, **and** use a non-root user to *become* root (sudo), you may need to add `-K` (`--ask-become-pass`) to the above (and all other) Ansible commands. + +The above command **doesn't start any services just yet** (another step does this later - below). + +Feel free to **re-run this setup command any time** you think something is off with the server configuration. + + +## Things you might want to do after installing + +After installing, but before starting the services, you may want to do additional things like: + +- [Importing an existing SQLite database (from another Synapse installation)](importing-synapse-sqlite.md) (optional) + +- [Importing an existing Postgres database (from another installation)](importing-postgres.md) (optional) + +- [Importing `media_store` data files from an existing Synapse installation](importing-synapse-media-store.md) (optional) + + +## Starting the services + +When you're ready to start the Matrix services (and set them up to auto-start in the future): + +```bash +ansible-playbook -i inventory/hosts setup.yml --tags=start +``` + +Now that services are running, you need to **finalize the installation process** (required for federation to work!) by [Configuring Service Discovery via .well-known](configuring-well-known.md) + + +## Things to do next + +If you have started services and **finalized the installation process** (required for federation to work!) by [Configuring Service Discovery via .well-known](configuring-well-known.md), you can: + +- [check if services work](maintenance-checking-services.md) +- or [create your first Matrix user account](registering-users.md) +- or [set up additional services](configuring-playbook.md#other-configuration-options) (bridges to other chat networks, bots, etc.) +- or learn how to [upgrade services when new versions are released](maintenance-upgrading-services.md) +- or learn how to [maintain your server](faq.md#maintenance) +- or join some Matrix rooms: + * via the *Explore rooms* feature in Element or some other client, or by discovering them using this [matrix-static list](https://view.matrix.org). Note: joining large rooms may overload small servers. + * or come say Hi in our support room - [#matrix-docker-ansible-deploy:devture.com](https://matrix.to/#/#matrix-docker-ansible-deploy:devture.com). You might learn something or get to help someone else new to Matrix hosting. +- or help make this playbook better by contributing (code, documentation, or [coffee/beer](https://liberapay.com/s.pantaleev/donate)) diff --git a/docs/maintenance-and-troubleshooting.md b/docs/maintenance-and-troubleshooting.md new file mode 100644 index 00000000..ae90fba2 --- /dev/null +++ b/docs/maintenance-and-troubleshooting.md @@ -0,0 +1,49 @@ +# Maintenance and Troubleshooting + +## How to see the current status of your services + +You can check the status of your services by using `systemctl status`. Example: +``` +sudo systemctl status matrix-nginx-proxy + +● matrix-nginx-proxy.service - Matrix nginx proxy server + Loaded: loaded (/etc/systemd/system/matrix-nginx-proxy.service; enabled; vendor preset: enabled) + Active: active (running) since Wed 2018-11-14 19:38:35 UTC; 49min ago +``` + +You can see the logs by using journalctl. Example: +``` +sudo journalctl -fu matrix-synapse +``` + + +## Increasing Synapse logging + +Because the [Synapse](https://github.com/matrix-org/synapse) Matrix server is originally very chatty when it comes to logging, we intentionally reduce its [logging level](https://docs.python.org/3/library/logging.html#logging-levels) from `INFO` to `WARNING`. + +If you'd like to debug an issue or [report a Synapse bug](https://github.com/matrix-org/synapse/issues/new/choose) to the developers, it'd be better if you temporarily increasing the logging level to `INFO`. + +Example configuration (`inventory/host_vars/matrix.DOMAIN/vars.yml`): + +```yaml +matrix_synapse_log_level: "INFO" +matrix_synapse_storage_sql_log_level: "INFO" +matrix_synapse_root_log_level: "INFO" +``` + +Re-run the playbook after making these configuration changes. + +## Remove unused Docker data + +You can free some disk space from Docker, see [docker system prune](https://docs.docker.com/engine/reference/commandline/system_prune/) for more information. +```bash +ansible-playbook -i inventory/hosts setup.yml --tags=run-docker-prune +``` + +## Postgres + +See the dedicated [PostgreSQL Maintenance](maintenance-postgres.md) documentation page. + +## Ma1sd + +See the dedicated [Adjusting ma1sd Identity Server configuration](configuring-playbook-ma1sd.md) documentation page. diff --git a/docs/maintenance-checking-services.md b/docs/maintenance-checking-services.md new file mode 100644 index 00000000..151a5778 --- /dev/null +++ b/docs/maintenance-checking-services.md @@ -0,0 +1,13 @@ +# Checking if services work + +This playbook can perform a check to ensure that you've configured things correctly and that services are running. + +To perform the check, run: + +```bash +ansible-playbook -i inventory/hosts setup.yml --tags=self-check +``` + +If it's all green, everything is probably running correctly. + +Besides this self-check, you can also check your server using the [Federation Tester](https://federationtester.matrix.org/). diff --git a/docs/maintenance-migrating.md b/docs/maintenance-migrating.md new file mode 100644 index 00000000..fd593691 --- /dev/null +++ b/docs/maintenance-migrating.md @@ -0,0 +1,14 @@ +> **Note**: This migration guide is applicable if you migrate from one server to another server having the same CPU architecture (e.g. both servers being `amd64`). +> +> If you're trying to migrate between different architectures (e.g. `amd64` --> `arm64`), simply copying the complete `/matrix` directory is not possible as it would move the raw PostgreSQL data between different architectures. In this specific case, you can use the guide below as a reference, but you would also need to dump the database on your current server and import it properly on the new server. See our [Backing up PostgreSQL](maintenance-postgres.md#backing-up-postgresql) docs for help with PostgreSQL backup/restore. + +# Migrating to new server + +1. Prepare by lowering DNS TTL for your domains (`matrix.DOMAIN`, etc.), so that DNS record changes (step 4 below) would happen faster, leading to less downtime +2. Stop all services on the old server and make sure they won't be starting again. Execute this on the old server: `systemctl disable --now matrix*` +3. Copy directory `/matrix` from the old server to the new server. Make sure to preserve ownership and permissions (use `cp -p` or `rsync -ar`)! +4. Make sure your DNS records are adjusted to point to the new server's IP address +5. Remove old server from the `inventory/hosts` file and add new server. +6. Run `ansible-playbook -i inventory/hosts setup.yml --tags=setup-system-user`. This will create the `matrix` user and group on the new server +7. Because the `matrix` user and group are created dynamically on each server, the user/group id may differ between the old and new server. We suggest that you adjust ownership of `/matrix` files manually by running this on the new server: `chown -R matrix:matrix /matrix`. +8. Run `ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start` to finish the installation and start all services diff --git a/docs/maintenance-postgres.md b/docs/maintenance-postgres.md new file mode 100644 index 00000000..52d2d9ee --- /dev/null +++ b/docs/maintenance-postgres.md @@ -0,0 +1,159 @@ +# PostgreSQL maintenance + +This document shows you how to perform various maintenance tasks related to the Postgres database server used by Matrix. + +Table of contents: + +- [Getting a database terminal](#getting-a-database-terminal), for when you wish to execute SQL queries + +- [Vacuuming PostgreSQL](#vacuuming-postgresql), for when you wish to run a Postgres [VACUUM](https://www.postgresql.org/docs/current/sql-vacuum.html) (optimizing disk space) + +- [Backing up PostgreSQL](#backing-up-postgresql), for when you wish to make a backup + +- [Upgrading PostgreSQL](#upgrading-postgresql), for upgrading to new major versions of PostgreSQL. Such **manual upgrades are sometimes required**. + +- [Tuning PostgreSQL](#tuning-postgresql) to make it run faster + +## Getting a database terminal + +You can use the `/usr/local/bin/matrix-postgres-cli` tool to get interactive terminal access ([psql](https://www.postgresql.org/docs/11/app-psql.html)) to the PostgreSQL server. + +If you are using an [external Postgres server](configuring-playbook-external-postgres.md), the above tool will not be available. + +By default, this tool puts you in the `matrix` database, which contains nothing. + +To see the available databases, run `\list` (or just `\l`). + +To change to another database (for example `synapse`), run `\connect synapse` (or just `\c synapse`). + +You can then proceed to write queries. Example: `SELECT COUNT(*) FROM users;` + +**Be careful**. Modifying the database directly (especially as services are running) is dangerous and may lead to irreversible database corruption. +When in doubt, consider [making a backup](#backing-up-postgresql). + + +## Vacuuming PostgreSQL + +Deleting lots data from Postgres does not make it release disk space, until you perform a `VACUUM` operation. + +To perform a `FULL` Postgres [VACUUM](https://www.postgresql.org/docs/current/sql-vacuum.html), run the playbook with `--tags=run-postgres-vacuum`. + +Example: + +```bash +ansible-playbook -i inventory/hosts setup.yml --tags=run-postgres-vacuum,start +``` + +**Note**: this will automatically stop Synapse temporarily and restart it later. You'll also need plenty of available disk space in your Postgres data directory (usually `/matrix/postgres/data`). + + +## Backing up PostgreSQL + +To automatically make Postgres database backups on a fixed schedule, see [Setting up postgres backup](configuring-playbook-postgres-backup.md). + +To make a one off back up of the current PostgreSQL database, make sure it's running and then execute a command like this on the server: + +```bash +/usr/bin/docker exec \ +--env-file=/matrix/postgres/env-postgres-psql \ +matrix-postgres \ +/usr/local/bin/pg_dumpall -h matrix-postgres \ +| gzip -c \ +> /matrix/postgres.sql.gz +``` + +If you are using an [external Postgres server](configuring-playbook-external-postgres.md), the above command will not work, because neither the credentials file (`/matrix/postgres/env-postgres-psql`), nor the `matrix-postgres` container is available. + +Restoring a backup made this way can be done by [importing it](importing-postgres.md). + + +## Upgrading PostgreSQL + +Unless you are using an [external Postgres server](configuring-playbook-external-postgres.md), this playbook initially installs Postgres for you. + +Once installed, the playbook attempts to preserve the Postgres version it starts with. +This is because newer Postgres versions cannot start with data generated by older Postgres versions. + +Upgrades must be performed manually. + +This playbook can upgrade your existing Postgres setup with the following command: + + ansible-playbook -i inventory/hosts setup.yml --tags=upgrade-postgres + +**The old Postgres data directory is backed up** automatically, by renaming it to `/matrix/postgres/data-auto-upgrade-backup`. +To rename to a different path, pass some extra flags to the command above, like this: `--extra-vars="postgres_auto_upgrade_backup_data_path=/another/disk/matrix-postgres-before-upgrade"` + +The auto-upgrade-backup directory stays around forever, until you **manually decide to delete it**. + +As part of the upgrade, the database is dumped to `/tmp`, an upgraded and empty Postgres server is started, and then the dump is restored into the new server. +To use a different directory for the dump, pass some extra flags to the command above, like this: `--extra-vars="postgres_dump_dir=/directory/to/dump/here"` + +To save disk space in `/tmp`, the dump file is gzipped on the fly at the expense of CPU usage. +If you have plenty of space in `/tmp` and would rather avoid gzipping, you can explicitly pass a dump filename which doesn't end in `.gz`. +Example: `--extra-vars="postgres_dump_name=matrix-postgres-dump.sql"` + +**All databases, roles, etc. on the Postgres server are migrated**. + + +## Tuning PostgreSQL + +PostgreSQL can be tuned to make it run faster. This is done by passing extra arguments to Postgres with the `matrix_postgres_process_extra_arguments` variable. You should use a website like https://pgtune.leopard.in.ua/ or information from https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server to determine what Postgres settings you should change. + +**Note**: the configuration generator at https://pgtune.leopard.in.ua/ adds spaces around the `=` sign, which is invalid. You'll need to remove it manually (`max_connections = 300` -> `max_connections=300`) + +### Here are some examples: + +These are not recommended values and they may not work well for you. This is just to give you an idea of some of the options that can be set. If you are an experienced PostgreSQL admin feel free to update this documentation with better examples. + +Here is an example config for a small 2 core server with 4GB of RAM and SSD storage: +``` +matrix_postgres_process_extra_arguments: [ + "-c shared_buffers=128MB", + "-c effective_cache_size=2304MB", + "-c effective_io_concurrency=100", + "-c random_page_cost=2.0", + "-c min_wal_size=500MB", +] +``` + +Here is an example config for a 4 core server with 8GB of RAM on a Virtual Private Server (VPS); the paramters have been configured using https://pgtune.leopard.in.ua with the following setup: PostgreSQL version 12, OS Type: Linux, DB Type: Mixed type of application, Data Storage: SSD storage: +``` +matrix_postgres_process_extra_arguments: [ + "-c max_connections=100", + "-c shared_buffers=2GB", + "-c effective_cache_size=6GB", + "-c maintenance_work_mem=512MB", + "-c checkpoint_completion_target=0.9", + "-c wal_buffers=16MB", + "-c default_statistics_target=100", + "-c random_page_cost=1.1", + "-c effective_io_concurrency=200", + "-c work_mem=5242kB", + "-c min_wal_size=1GB", + "-c max_wal_size=4GB", + "-c max_worker_processes=4", + "-c max_parallel_workers_per_gather=2", + "-c max_parallel_workers=4", + "-c max_parallel_maintenance_workers=2", +] +``` + +Here is an example config for a large 6 core server with 24GB of RAM: +``` +matrix_postgres_process_extra_arguments: [ + "-c max_connections=40", + "-c shared_buffers=1536MB", + "-c checkpoint_completion_target=0.7", + "-c wal_buffers=16MB", + "-c default_statistics_target=100", + "-c random_page_cost=1.1", + "-c effective_io_concurrency=100", + "-c work_mem=2621kB", + "-c min_wal_size=1GB", + "-c max_wal_size=4GB", + "-c max_worker_processes=6", + "-c max_parallel_workers_per_gather=3", + "-c max_parallel_workers=6", + "-c max_parallel_maintenance_workers=3", +] +``` diff --git a/docs/maintenance-synapse.md b/docs/maintenance-synapse.md new file mode 100644 index 00000000..9727f450 --- /dev/null +++ b/docs/maintenance-synapse.md @@ -0,0 +1,84 @@ +# Synapse maintenance + +This document shows you how to perform various maintenance tasks related to the Synapse chat server. + +Table of contents: + +- [Purging old data with the Purge History API](#purging-old-data-with-the-purge-history-api), for when you wish to delete in-use (but old) data from the Synapse database + +- [Compressing state with rust-synapse-compress-state](#compressing-state-with-rust-synapse-compress-state) + +- [Browse and manipulate the database](#browse-and-manipulate-the-database), for when you really need to take matters into your own hands + +- [Make Synapse faster](#make-synapse-faster) + +## Purging old data with the Purge History API + +You can use the **[Purge History API](https://github.com/matrix-org/synapse/blob/master/docs/admin_api/purge_history_api.md)** to delete old messages on a per-room basis. **This is destructive** (especially for non-federated rooms), because it means **people will no longer have access to history past a certain point**. + +To make use of this API, **you'll need an admin access token** first. You can find your access token in the setting of some clients (like Element). +Alternatively, you can log in and obtain a new access token like this: + +``` +curl \ +--data '{"identifier": {"type": "m.id.user", "user": "YOUR_MATRIX_USERNAME" }, "password": "YOUR_MATRIX_PASSWORD", "type": "m.login.password", "device_id": "Synapse-Purge-History-API"}' \ +https://matrix.DOMAIN/_matrix/client/r0/login +``` + +Synapse's Admin API is not exposed to the internet by default. To expose it you will need to add `matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_admin_api_enabled: true` to your `vars.yml` file. + +Follow the [Purge History API](https://github.com/matrix-org/synapse/blob/master/docs/admin_api/purge_history_api.md) documentation page for the actual purging instructions. + +After deleting data, you may wish to run a [`FULL` Postgres `VACUUM`](./maintenance-postgres.md#vacuuming-postgresql). + + +## Compressing state with rust-synapse-compress-state + +[rust-synapse-compress-state](https://github.com/matrix-org/rust-synapse-compress-state) can be used to optimize some `_state` tables used by Synapse. If your server participates in large rooms this is the most effective way to reduce the size of your database. + +This tool should be safe to use (even when Synapse is running), but it's always a good idea to [make Postgres backups](./maintenance-postgres.md#backing-up-postgresql) first. + +To ask the playbook to run rust-synapse-compress-state, execute: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=rust-synapse-compress-state +``` + +By default, all rooms with more than `100000` state group rows will be compressed. +If you need to adjust this, pass: `--extra-vars='matrix_synapse_rust_synapse_compress_state_min_state_groups_required=SOME_NUMBER_HERE'` to the command above. + +After state compression, you may wish to run a [`FULL` Postgres `VACUUM`](./maintenance-postgres.md#vacuuming-postgresql). + + +## Browse and manipulate the database + +When the [Synapse Admin API](https://github.com/matrix-org/synapse/tree/master/docs/admin_api) and the other tools do not provide a more convenient way, having a look at synapse's postgresql database can satisfy a lot of admins' needs. + +Editing the database manually is not recommended or supported by the Synapse developers. If you are going to do so you should [make a database backup](./maintenance-postgres.md#backing-up-postgresql). + +First, set up an SSH tunnel to your matrix server (skip if it is your local machine): + +``` +# you may replace 1799 with an arbitrary port unbound on both machines +ssh -L 1799:localhost:1799 matrix.DOMAIN +``` + +Then start up an ephemeral [adminer](https://www.adminer.org/) container on the Matrix server, connecting it to the `matrix` network and linking the postgresql container: + +``` +docker run --rm --publish 1799:8080 --link matrix-postgres --net matrix adminer +``` + +You should then be able to browse the adminer database administration GUI at http://localhost:1799/ after entering your DB credentials (found in the `host_vars` or on the server in `{{matrix_synapse_config_dir_path}}/homeserver.yaml` under `database.args`) + +⚠️ Be **very careful** with this, there is **no undo** for impromptu DB operations. + +## Make Synapse faster + +Synapse's presence feature which tracks which users are online and which are offline can use a lot of processing power. You can disable presence by adding `matrix_synapse_presence_enabled: false` to your `vars.yml` file. + +Tuning Synapse's cache factor can help reduce RAM usage. [See the upstream documentation](https://github.com/matrix-org/synapse#help-synapse-is-slow-and-eats-all-my-ram-cpu) for more information on what value to set the cache factor to. Use the variable `matrix_synapse_caches_global_factor` to set the cache factor. + +Tuning your PostgreSQL database will also make Synapse run significantly faster. See [maintenance-postgres.md##tuning-postgresql](maintenance-postgres.md##tuning-postgresql). + +See also [How do I optimize this setup for a low-power server?](faq.md#how-do-i-optimize-this-setup-for-a-low-power-server). diff --git a/docs/maintenance-upgrading-services.md b/docs/maintenance-upgrading-services.md new file mode 100644 index 00000000..fe289a86 --- /dev/null +++ b/docs/maintenance-upgrading-services.md @@ -0,0 +1,17 @@ +# Upgrading the Matrix services + +This playbook not only installs the various Matrix services for you, but can also upgrade them as new versions are made available. + +If you want to be notified when new versions of Synapse are released, you should join the Synapse Homeowners room: [#homeowners:matrix.org](https://matrix.to/#/#homeowners:matrix.org). + +To upgrade services: + +- update your playbook directory (`git pull`), so you'd obtain everything new we've done + +- take a look at [the changelog](../CHANGELOG.md) to see if there have been any backward-incompatible changes that you need to take care of + +- re-run the [playbook setup](installing.md): `ansible-playbook -i inventory/hosts setup.yml --tags=setup-all` + +- restart the services: `ansible-playbook -i inventory/hosts setup.yml --tags=start` + +**Note**: major version upgrades to the internal PostgreSQL database are not done automatically. To upgrade it, refer to the [upgrading PostgreSQL guide](maintenance-postgres.md#upgrading-postgresql). diff --git a/docs/prerequisites.md b/docs/prerequisites.md new file mode 100644 index 00000000..39b42ef1 --- /dev/null +++ b/docs/prerequisites.md @@ -0,0 +1,39 @@ +# Prerequisites + +To install Matrix services using this Ansible playbook, you need: + +- (Recommended) An **x86** server ([What kind of server specs do I need?](faq.md#what-kind-of-server-specs-do-i-need)) running one of these operating systems: + - **CentOS** (7 only for now; [8 is not yet supported](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/300)) + - **Debian** (9/Stretch or newer) + - **Ubuntu** (16.04 or newer, although [20.04 may be problematic](ansible.md#supported-ansible-versions)) + - **Archlinux** + +Generally, newer is better. We only strive to support released stable versions of distributions, not betas or pre-releases. This playbook can take over your whole server or co-exist with other services that you have there. + +This playbook somewhat supports running on non-`amd64` architectures like ARM. See [Alternative Architectures](alternative-architectures.md). + +If your distro runs within an [LXC container](https://linuxcontainers.org/), you may hit [this issue](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/703). It can be worked around, if absolutely necessary, but we suggest that you avoid running from within an LXC container. + +- `root` access to your server (or a user capable of elevating to `root` via `sudo`). + +- [Python](https://www.python.org/) being installed on the server. Most distributions install Python by default, but some don't (e.g. Ubuntu 18.04) and require manual installation (something like `apt-get install python3`). On some distros, Ansible may incorrectly [detect the Python version](https://docs.ansible.com/ansible/latest/reference_appendices/interpreter_discovery.html) (2 vs 3) and you may need to explicitly specify the interpreter path in `inventory/hosts` during installation (e.g. `ansible_python_interpreter=/usr/bin/python3`) + +- The [Ansible](http://ansible.com/) program being installed on your own computer. It's used to run this playbook and configures your server for you. Take a look at [our guide about Ansible](ansible.md) for more information, as well as [version requirements](ansible.md#supported-ansible-versions) and alternative ways to run Ansible. + +- An HTTPS-capable web server at the base domain name (``) which is capable of serving static files. Unless you decide to [Serve the base domain from the Matrix server](configuring-playbook-base-domain-serving.md) or alternatively, to use DNS SRV records for [Server Delegation](howto-server-delegation.md). + +- Properly configured DNS records for `` (details in [Configuring DNS](configuring-dns.md)). + +- Some TCP/UDP ports open. This playbook configures the server's internal firewall for you. In most cases, you don't need to do anything special. But **if your server is running behind another firewall**, you'd need to open these ports: + + - `80/tcp`: HTTP webserver + - `443/tcp`: HTTPS webserver + - `3478/tcp`: TURN over TCP (used by Coturn) + - `3478/udp`: TURN over UDP (used by Coturn) + - `5349/tcp`: TURN over TCP (used by Coturn) + - `5349/udp`: TURN over UDP (used by Coturn) + - `8448/tcp`: Matrix Federation API HTTPS webserver. In some cases, this **may necessary even with federation disabled**. Integration Servers (like Dimension) and Identity Servers (like ma1sd) may need to access `openid` APIs on the federation port. + - the range `49152-49172/udp`: TURN over UDP + - potentially some other ports, depending on the additional (non-default) services that you enable in the **configuring the playbook** step (later on). Consult each service's documentation page in `docs/` for that. + +When ready to proceed, continue with [Configuring DNS](configuring-dns.md). diff --git a/docs/registering-users.md b/docs/registering-users.md new file mode 100644 index 00000000..90c065bf --- /dev/null +++ b/docs/registering-users.md @@ -0,0 +1,70 @@ +# Registering users + +This documentation page tells you how to create user account on your Matrix server. + +Table of contents: + +- [Registering users](#registering-users) + - [Registering users manually](#registering-users-manually) + - [Managing users via a Web UI](#managing-users-via-a-web-ui) + - [Letting certain users register on your private server](#letting-certain-users-register-on-your-private-server) + - [Enabling public user registration](#enabling-public-user-registration) + - [Adding/Removing Administrator privileges to an existing user](#addingremoving-administrator-privileges-to-an-existing-user) + + +## Registering users manually + +You can do it via this Ansible playbook (make sure to edit the `` and `` part below): + +``` +ansible-playbook -i inventory/hosts setup.yml --extra-vars='username= password= admin=' --tags=register-user +``` + +**or** using the command-line after **SSH**-ing to your server (requires that [all services have been started](#starting-the-services)): + +``` +/usr/local/bin/matrix-synapse-register-user +``` + +**Note**: `` is just a plain username (like `john`), not your full `@:` identifier. + +**You can then log in with that user** via the Element service that this playbook has created for you at a URL like this: `https://element./`. + +----- + +If you've just installed Matrix, **to finalize the installation process**, it's best if you proceed to [Configuring service discovery via .well-known](configuring-well-known.md) + + +## Managing users via a Web UI + +To manage users more easily (via a web user-interace), you can install [Synapse Admin](configuring-playbook-synapse-admin.md). + + +## Letting certain users register on your private server + +If you'd rather **keep your server private** (public registration closed, as is the default), and **let certain people create accounts by themselves** (instead of creating user accounts manually like this), consider installing and making use of [matrix-registration](configuring-playbook-matrix-registration.md). + + +## Enabling public user registration + +To **open up user registration publicly** (usually **not recommended**), consider using the following configuration: + +```yaml +matrix_synapse_enable_registration: true +``` + +and running the [installation](installing.md) procedure once again. + +If you're opening up registrations publicly like this, you might also wish to [configure CAPTCHA protection](configuring-captcha.md). + + +## Adding/Removing Administrator privileges to an existing user + +The script `/usr/local/bin/matrix-change-user-admin-status` may be used to change a user's admin privileges. + +* log on to your server with ssh +* execute with the username and 0/1 (0 = non-admin | 1 = admin) + +``` +/usr/local/bin/matrix-change-user-admin-status <0/1> +``` diff --git a/docs/self-building.md b/docs/self-building.md new file mode 100644 index 00000000..61f05c48 --- /dev/null +++ b/docs/self-building.md @@ -0,0 +1,36 @@ +# Self-building + +**Caution: self-building does not have to be used on its own. See the [Alternative Architectures](alternative-architectures.md) page.** + +The playbook supports self-building of various components, which don't have a container image for your architecture (see the [container images we use](container-images.md)). For `amd64`, self-building is not required. + +For other architectures (e.g. `arm32`, `arm64`), ready-made container images are used when available. If there's no ready-made image for a specific component and said component supports self-building, an image will be built on the host. Building images like this takes more time and resources (some build tools need to get installed by the playbook to assist building). + +To make use of self-building, you don't need to do anything besides change your architecture variable (e.g. `matrix_architecture: arm64`). If a component has an image for the specified architecture, the playbook will use it directly. If not, it will build the image on the server itself. + +Note that **not all components support self-building yet**. + +List of roles where self-building the Docker image is currently possible: +- `matrix-synapse` +- `matrix-synapse-admin` +- `matrix-client-element` +- `matrix-client-hydrogen` +- `matrix-registration` +- `matrix-coturn` +- `matrix-corporal` +- `matrix-ma1sd` +- `matrix-mailer` +- `matrix-bridge-appservice-irc` +- `matrix-bridge-appservice-slack` +- `matrix-bridge-mautrix-facebook` +- `matrix-bridge-mautrix-hangouts` +- `matrix-bridge-mautrix-telegram` +- `matrix-bridge-mautrix-signal` +- `matrix-bridge-mx-puppet-skype` +- `matrix-bot-mjolnir` +- `matrix-bot-matrix-reminder-bot` +- `matrix-email2matrix` + +Adding self-building support to other roles is welcome. Feel free to contribute! + +If you'd like **to force self-building** even if an image is available for your architecture, look into the `matrix_*_self_build` variables provided by individual roles. diff --git a/docs/uninstalling.md b/docs/uninstalling.md new file mode 100644 index 00000000..73af01d9 --- /dev/null +++ b/docs/uninstalling.md @@ -0,0 +1,38 @@ +# Uninstalling + +**Warnings**: + +- If your server federates with others, make sure to **leave any federated rooms before nuking your Matrix server's data**. Otherwise, the next time you set up a Matrix server for this domain (regardless of the installation method you use), you'll encounter trouble federating. + +- If you have some trouble with your installation, you can just [re-run the playbook](installing.md) and it will try to set things up again. **Uninstalling and then installing anew rarely solves anything**. + + +----------------- + + +## Uninstalling using a script + +Installing places a `/usr/local/bin/matrix-remove-all` script on the server. + +You can run it to to have it uninstall things for you automatically (see below). **Use with caution!** + + +## Uninstalling manually + +If you prefer to uninstall manually, run these commands (most are meant to be executed on the Matrix server itself): + +- ensure all Matrix services are stopped: `ansible-playbook -i inventory/hosts setup.yml --tags=stop` (if you can't get Ansible working to run this command, you can run `systemctl stop 'matrix*'` manually on the server) + +- delete the Matrix-related systemd `.service` and `.timer` files (`rm -f /etc/systemd/system/matrix*.{service,timer}`) and reload systemd (`systemctl daemon-reload`) + +- delete some helper scripts (`rm -f /usr/local/bin/matrix*`) + +- delete some cached Docker images (`docker system prune -a`) or just delete them all (`docker rmi $(docker images -aq)`) + +- delete the Docker networks: `docker network rm matrix matrix-coturn` (might have been deleted already if you ran the `docker system prune` command) + +- uninstall Docker itself, if necessary + +- delete the `/matrix` directory (`rm -rf /matrix`) + + diff --git a/docs/updating-users-passwords.md b/docs/updating-users-passwords.md new file mode 100644 index 00000000..7d2f2832 --- /dev/null +++ b/docs/updating-users-passwords.md @@ -0,0 +1,45 @@ +# Updating users passwords + +## Option 1 (if you are using the default matrix-postgres container): + +You can reset a user's password via the Ansible playbook (make sure to edit the `` and `` part below): + +``` +ansible-playbook -i inventory/hosts setup.yml --extra-vars='username= password=' --tags=update-user-password +``` + +**Note**: `` is just a plain username (like `john`), not your full `@:` identifier. + +**You can then log in with that user** via the Element service that this playbook has created for you at a URL like this: `https://element./`. + + +## Option 2 (if you are using an external Postgres server): + +You can manually generate the password hash by using the command-line after **SSH**-ing to your server (requires that [all services have been started](installing.md#starting-the-services)): + +``` +docker exec -it matrix-synapse /usr/local/bin/hash_password -c /data/homeserver.yaml +``` + +and then connecting to the postgres server and executing: + +``` +UPDATE users SET password_hash = '' WHERE name = '@someone:server.com' +``` + +where `` is the hash returned by the docker command above. + + +## Option 3: + +Use the Synapse User Admin API as described here: https://github.com/matrix-org/synapse/blob/master/docs/admin_api/user_admin_api.rst#reset-password + +This requires an access token from a server admin account. *This method will also log the user out of all of their clients while the other options do not.* + +If you didn't make your account a server admin when you created it, you can use the `/usr/local/bin/matrix-change-user-admin-status` script as described in [registering-users.md](registering-users.md). + +### Example: +To set @user:domain.com's password to `correct_horse_battery_staple` you could use this curl command: +``` +curl -XPOST -d '{ "new_password": "correct_horse_battery_staple" }' "https://matrix./_matrix/client/r0/admin/reset_password/@user:domain.com?access_token=MDA...this_is_my_access_token +``` diff --git a/examples/apache/README.md b/examples/apache/README.md new file mode 100644 index 00000000..92d7d932 --- /dev/null +++ b/examples/apache/README.md @@ -0,0 +1,17 @@ +# Apache reverse-proxy + +This directory contains sample files that show you how to do reverse-proxying using Apache. + +This is for when you wish to have your own Apache webserver sitting in front of Matrix services installed by this playbook. +See the [Using your own webserver, instead of this playbook's nginx proxy](../../docs/configuring-playbook-own-webserver.md) documentation page. + +To use your own Apache reverse-proxy, you first need to disable the integrated nginx server. +You do that with the following custom configuration (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_nginx_proxy_enabled: false +``` + +You can then use the configuration files from this directory as an example for how to configure your Apache server. + +**NOTE**: this is just an example and may not be entirely accurate. It may also not cover other use cases (enabling various services or bridges requires additional reverse-proxying configuration). diff --git a/examples/apache/matrix-client-element.conf b/examples/apache/matrix-client-element.conf new file mode 100644 index 00000000..f2b347fe --- /dev/null +++ b/examples/apache/matrix-client-element.conf @@ -0,0 +1,41 @@ +# This is a sample file demonstrating how to set up reverse-proxy for element.DOMAIN. +# If you're not using Element (`matrix_client_element_enabled: false`), you won't need this. + + + ServerName element.DOMAIN + + ProxyVia On + + # Map /.well-known/acme-challenge to the certbot server + # If you manage SSL certificates by yourself, this will differ. + + ProxyPreserveHost On + ProxyPass http://127.0.0.1:2402/.well-known/acme-challenge + + + Redirect permanent / https://element.DOMAIN/ + + + + ServerName element.DOMAIN + + SSLEngine On + + # If you manage SSL certificates by yourself, these paths will differ. + SSLCertificateFile /matrix/ssl/config/live/element.DOMAIN/fullchain.pem + SSLCertificateKeyFile /matrix/ssl/config/live/element.DOMAIN/privkey.pem + + SSLProxyEngine on + SSLProxyProtocol +TLSv1.2 +TLSv1.3 + SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH + + ProxyPreserveHost On + ProxyRequests Off + ProxyVia On + + ProxyPass / http://127.0.0.1:8765/ + ProxyPassReverse / http://127.0.0.1:8765/ + + ErrorLog ${APACHE_LOG_DIR}/element.DOMAIN-error.log + CustomLog ${APACHE_LOG_DIR}/element.DOMAIN-access.log combined + diff --git a/examples/apache/matrix-dimension.conf b/examples/apache/matrix-dimension.conf new file mode 100644 index 00000000..f334690e --- /dev/null +++ b/examples/apache/matrix-dimension.conf @@ -0,0 +1,41 @@ +# This is a sample file demonstrating how to set up reverse-proxy for dimension.DOMAIN. +# If you're not using Dimension (`matrix_dimension_enabled: false`, which is also the default), you won't need this. + + + ServerName dimension.DOMAIN + + ProxyVia On + + # Map /.well-known/acme-challenge to the certbot server + # If you manage SSL certificates by yourself, this will differ. + + ProxyPreserveHost On + ProxyPass http://127.0.0.1:2402/.well-known/acme-challenge + + + Redirect permanent / https://dimension.DOMAIN/ + + + + ServerName dimension.DOMAIN + + SSLEngine On + + # If you manage SSL certificates by yourself, these paths will differ. + SSLCertificateFile /matrix/ssl/config/live/dimension.DOMAIN/fullchain.pem + SSLCertificateKeyFile /matrix/ssl/config/live/dimension.DOMAIN/privkey.pem + + SSLProxyEngine on + SSLProxyProtocol +TLSv1.2 +TLSv1.3 + SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH + + ProxyPreserveHost On + ProxyRequests Off + ProxyVia On + + ProxyPass / http://127.0.0.1:8184/ + ProxyPassReverse / http://127.0.0.1:8184/ + + ErrorLog ${APACHE_LOG_DIR}/dimension.DOMAIN-error.log + CustomLog ${APACHE_LOG_DIR}/dimension.DOMAIN-access.log combined + diff --git a/examples/apache/matrix-synapse.conf b/examples/apache/matrix-synapse.conf new file mode 100644 index 00000000..2c7b3dde --- /dev/null +++ b/examples/apache/matrix-synapse.conf @@ -0,0 +1,134 @@ +# This is a sample file demonstrating how to set up reverse-proxy for matrix.DOMAIN + + + ServerName matrix.DOMAIN + + ProxyVia On + + # Map /.well-known/acme-challenge to the certbot server + # If you manage SSL certificates by yourself, this will differ. + + ProxyPreserveHost On + ProxyPass http://127.0.0.1:2402/.well-known/acme-challenge + + + Redirect permanent / https://matrix.DOMAIN/ + + +# Client-Server API + + ServerName matrix.DOMAIN + + SSLEngine On + + # If you manage SSL certificates by yourself, these paths will differ. + SSLCertificateFile /matrix/ssl/config/live/matrix.DOMAIN/fullchain.pem + SSLCertificateKeyFile /matrix/ssl/config/live/matrix.DOMAIN/privkey.pem + + SSLProxyEngine on + SSLProxyProtocol +TLSv1.2 +TLSv1.3 + SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH + + ProxyPreserveHost On + ProxyRequests Off + ProxyVia On + RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME} + + # Keep some URIs free for different proxy/location + ProxyPassMatch ^/.well-known/matrix/client ! + ProxyPassMatch ^/.well-known/matrix/server ! + ProxyPassMatch ^/_matrix/identity ! + ProxyPassMatch ^/_matrix/client/r0/user_directory/search ! + + # Proxy all remaining traffic to Synapse + AllowEncodedSlashes NoDecode + ProxyPass /_matrix http://127.0.0.1:8008/_matrix retry=0 nocanon + ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix + ProxyPass /_synapse/client http://127.0.0.1:8008/_synapse/client retry=0 nocanon + ProxyPassReverse /_synapse/client http://127.0.0.1:8008/_synapse/client + + # Proxy Admin API (necessary for Synapse-Admin) + # ProxyPass /_synapse/admin http://127.0.0.1:8008/_synapse/admin retry=0 nocanon + # ProxyPassReverse /_synapse/admin http://127.0.0.1:8008/_synapse/admin + + # Proxy Synapse-Admin + # ProxyPass /synapse-admin http://127.0.0.1:8766 retry=0 nocanon + # ProxyPassReverse /synapse-admin http://127.0.0.1:8766 + + # Map /.well-known/matrix/client for client discovery + Alias /.well-known/matrix/client /matrix/static-files/.well-known/matrix/client + + Require all granted + + + Header always set Content-Type "application/json" + Header always set Access-Control-Allow-Origin "*" + + # Map /.well-known/matrix/server for server discovery + Alias /.well-known/matrix/server /matrix/static-files/.well-known/matrix/server + + Require all granted + + + Header always set Content-Type "application/json" + + + AllowOverride All + # Apache 2.4: + Require all granted + # Or for Apache 2.2: + #order allow,deny + + + # Map /_matrix/identity to the identity server + + ProxyPass http://127.0.0.1:8090/_matrix/identity nocanon + + + # Map /_matrix/client/r0/user_directory/search to the identity server + + ProxyPass http://127.0.0.1:8090/_matrix/client/r0/user_directory/search nocanon + + + ErrorLog ${APACHE_LOG_DIR}/matrix.DOMAIN-error.log + CustomLog ${APACHE_LOG_DIR}/matrix.DOMAIN-access.log combined + + +# Server-Server (federation) API +# Use this apache reverse proxy template to enable matrix server-to-server federation traffic +# Be sure that network traffic on port 8448 is possible +# +# You can check your federation config at https://federationtester.matrix.org/ +# Enter there your base DOMAIN address, NOT your matrix.DOMAIN address, ex. https://DOMAIN +# +# In this example we use all services on the same machine (127.0.0.1) but you can do this with different machines. +# If you do so be sure to reach the destinated IPADRESS and the correspondending port. Check this with netstat, nmap or your favourite tool. +Listen 8448 + + ServerName matrix.DOMAIN + + SSLEngine On + + # If you manage SSL certificates by yourself, these paths will differ. + SSLCertificateFile /matrix/ssl/config/live/matrix.DOMAIN/fullchain.pem + SSLCertificateKeyFile /matrix/ssl/config/live/matrix.DOMAIN/privkey.pem + + SSLProxyEngine on + SSLProxyProtocol +TLSv1.2 +TLSv1.3 + SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH + + ProxyPreserveHost On + ProxyRequests Off + ProxyVia On + RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME} + + # Proxy all remaining traffic to the Synapse port + # Beware: In this example the local traffic goes to the local synapse server at 127.0.0.1 + # Of course you can use another IPADRESS in case of using other synapse servers in your network + AllowEncodedSlashes NoDecode + ProxyPass /_matrix http://127.0.0.1:8048/_matrix retry=0 nocanon + ProxyPassReverse /_matrix http://127.0.0.1:8048/_matrix + + ErrorLog ${APACHE_LOG_DIR}/matrix.DOMAIN-error.log + CustomLog ${APACHE_LOG_DIR}/matrix.DOMAIN-access.log combined + diff --git a/examples/caddy/matrix-client-element b/examples/caddy/matrix-client-element new file mode 100644 index 00000000..2dc6ba34 --- /dev/null +++ b/examples/caddy/matrix-client-element @@ -0,0 +1,8 @@ +https://element.DOMAIN { + # These might differ if you are supplying your own certificates + tls /matrix/ssl/config/live/element.DOMAIN/fullchain.pem /matrix/ssl/config/live/element.DOMAIN/privkey.pem + + proxy / http://127.0.0.1:8765 { + transparent + } +} diff --git a/examples/caddy/matrix-dimension b/examples/caddy/matrix-dimension new file mode 100644 index 00000000..74d08e59 --- /dev/null +++ b/examples/caddy/matrix-dimension @@ -0,0 +1,9 @@ +https://dimension.DOMAIN { + # These might differ if you are supplying your own certificates + # If you wish to use Caddy's built-in Let's Encrypt support, you can also supply an email address here + tls /matrix/ssl/config/live/dimension.DOMAIN/fullchain.pem /matrix/ssl/config/live/dimension.DOMAIN/privkey.pem + + proxy / http://127.0.0.1:8184/ { + transparent + } +} diff --git a/examples/caddy/matrix-synapse b/examples/caddy/matrix-synapse new file mode 100644 index 00000000..4202c759 --- /dev/null +++ b/examples/caddy/matrix-synapse @@ -0,0 +1,31 @@ +https://matrix.DOMAIN { + # If you use your own certificates, your path may differ + # If you wish to use Caddy's built-in Let's Encrypt support, you can also supply an email address here + tls /matrix/ssl/config/live/matrix.DOMAIN/fullchain.pem /matrix/ssl/config/live/matrix.DOMAIN/privkey.pem + + root /matrix/static-files + + header { + Access-Control-Allow-Origin * + Strict-Transport-Security "mag=age=31536000;" + X-Frame-Options "DENY" + X-XSS-Protection "1; mode=block" + } + + # Identity server traffic + proxy /_matrix/identity matrix-msisd:8090 { + transparent + } + proxy /_matrix/client/r0/user_directory/search matrix-msisd:8090 { + transparent + } + + # Synapse Client<>Server API + proxy /_matrix matrix-synapse:8008 { + transparent + except /_matrix/identity/ /_matrix/client/r0/user_directory/search + } + proxy /_synapse/client matrix-synapse:8008 { + transparent + } +} diff --git a/examples/caddy/matrix-util b/examples/caddy/matrix-util new file mode 100644 index 00000000..191f7758 --- /dev/null +++ b/examples/caddy/matrix-util @@ -0,0 +1,7 @@ +:80 { + # Redirect ACME-Challenge traffic to port 2402 + proxy /.well-known/acme-challenge http://127.0.0.1:2402 + + # Redirect all other traffic to HTTPS + redir / https://{host}{uri} 301 +} diff --git a/examples/caddy2/Caddyfile b/examples/caddy2/Caddyfile new file mode 100644 index 00000000..c6d2841b --- /dev/null +++ b/examples/caddy2/Caddyfile @@ -0,0 +1,203 @@ +matrix.DOMAIN.tld { + + # creates letsencrypt certificate + # tls your@email.com + + @identity { + path /_matrix/identity/* + } + + @noidentity { + not path /_matrix/identity/* + } + + @search { + path /_matrix/client/r0/user_directory/search/* + } + + @nosearch { + not path /_matrix/client/r0/user_directory/search/* + } + + @static { + path /matrix/static-files/* + } + + @nostatic { + not path /matrix/static-files/* + } + + header { + # Enable HTTP Strict Transport Security (HSTS) to force clients to always connect via HTTPS + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + # Enable cross-site filter (XSS) and tell browser to block detected attacks + X-XSS-Protection "1; mode=block" + # Prevent some browsers from MIME-sniffing a response away from the declared Content-Type + X-Content-Type-Options "nosniff" + # Disallow the site to be rendered within a frame (clickjacking protection) + X-Frame-Options "DENY" + # X-Robots-Tag + X-Robots-Tag "noindex, noarchive, nofollow" + } + + # Cache + header @static { + # Cache + Cache-Control "public, max-age=31536000" + defer + } + + # identity + handle @identity { + reverse_proxy localhost:8090 { + header_up X-Forwarded-Port {http.request.port} + header_up X-Forwarded-Proto {http.request.scheme} + header_up X-Forwarded-TlsProto {tls_protocol} + header_up X-Forwarded-TlsCipher {tls_cipher} + header_up X-Forwarded-HttpsProto {proto} + } + } + + # search + handle @search { + reverse_proxy localhost:8090 { + header_up X-Forwarded-Port {http.request.port} + header_up X-Forwarded-Proto {http.request.scheme} + header_up X-Forwarded-TlsProto {tls_protocol} + header_up X-Forwarded-TlsCipher {tls_cipher} + header_up X-Forwarded-HttpsProto {proto} + } + } + + handle { + encode zstd gzip + + reverse_proxy localhost:8008 { + header_up X-Forwarded-Port {http.request.port} + header_up X-Forwarded-Proto {http.request.scheme} + header_up X-Forwarded-TlsProto {tls_protocol} + header_up X-Forwarded-TlsCipher {tls_cipher} + header_up X-Forwarded-HttpsProto {proto} + } + } +} + +matrix.DOMAIN.tld:8448 { + handle { + encode zstd gzip + + reverse_proxy 127.0.0.1:8048 { + header_up X-Forwarded-Port {http.request.port} + header_up X-Forwarded-Proto {http.request.scheme} + header_up X-Forwarded-TlsProto {tls_protocol} + header_up X-Forwarded-TlsCipher {tls_cipher} + header_up X-Forwarded-HttpsProto {proto} + } + } +} + +element.DOMAIN.tld { + + # creates letsencrypt certificate + # tls your@email.com + + header { + # Enable HTTP Strict Transport Security (HSTS) to force clients to always connect via HTTPS + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + # Enable cross-site filter (XSS) and tell browser to block detected attacks + X-XSS-Protection "1; mode=block" + # Prevent some browsers from MIME-sniffing a response away from the declared Content-Type + X-Content-Type-Options "nosniff" + # Disallow the site to be rendered within a frame (clickjacking protection) + X-Frame-Options "DENY" + # X-Robots-Tag + X-Robots-Tag "noindex, noarchive, nofollow" + } + + handle { + encode zstd gzip + + reverse_proxy localhost:8765 { + header_up X-Forwarded-Port {http.request.port} + header_up X-Forwarded-Proto {http.request.scheme} + header_up X-Forwarded-TlsProto {tls_protocol} + header_up X-Forwarded-TlsCipher {tls_cipher} + header_up X-Forwarded-HttpsProto {proto} + } +} + +#dimension.DOMAIN.tld { +# +# # creates letsencrypt certificate +# # tls your@email.com +# +# header { +# # Enable HTTP Strict Transport Security (HSTS) to force clients to always connect via HTTPS +# Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" +# # Enable cross-site filter (XSS) and tell browser to block detected attacks +# X-XSS-Protection "1; mode=block" +# # Prevent some browsers from MIME-sniffing a response away from the declared Content-Type +# X-Content-Type-Options "nosniff" +# # Disallow the site to be rendered within a frame (clickjacking protection) +# X-Frame-Options "DENY" +# # X-Robots-Tag +# X-Robots-Tag "noindex, noarchive, nofollow" +# } +# +# handle { +# encode zstd gzip +# +# reverse_proxy localhost:8184 { +# header_up X-Forwarded-Port {http.request.port} +# header_up X-Forwarded-Proto {http.request.scheme} +# header_up X-Forwarded-TlsProto {tls_protocol} +# header_up X-Forwarded-TlsCipher {tls_cipher} +# header_up X-Forwarded-HttpsProto {proto} +# } +# } +#} + + +#jitsi.DOMAIN.tld { +# +# creates letsencrypt certificate +# tls your@email.com +# +# header { +# # Enable HTTP Strict Transport Security (HSTS) to force clients to always connect via HTTPS +# Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" +# +# # Enable cross-site filter (XSS) and tell browser to block detected attacks +# X-XSS-Protection "1; mode=block" +# +# # Prevent some browsers from MIME-sniffing a response away from the declared Content-Type +# X-Content-Type-Options "nosniff" +# +# # Disallow the site to be rendered within a frame (clickjacking protection) +# X-Frame-Options "SAMEORIGIN" +# +# # Disable some features +# Feature-Policy "accelerometer 'none';ambient-light-sensor 'none'; autoplay 'none';camera 'none';encrypted-media 'none';focus-without-user-activation 'none'; geolocation 'none';gyroscope #'none';magnetometer 'none';microphone 'none';midi 'none';payment 'none';picture-in-picture 'none'; speaker 'none';sync-xhr 'none';usb 'none';vr 'none'" +# +# # Referer +# Referrer-Policy "no-referrer" +# +# # X-Robots-Tag +# X-Robots-Tag "none" +# +# # Remove Server header +# -Server +# } +# +# handle { +# encode zstd gzip +# +# reverse_proxy 127.0.0.1:13080 { +# header_up X-Forwarded-Port {http.request.port} +# header_up X-Forwarded-Proto {http.request.scheme} +# header_up X-Forwarded-TlsProto {tls_protocol} +# header_up X-Forwarded-TlsCipher {tls_cipher} +# header_up X-Forwarded-HttpsProto {proto} +# } +# } +#} diff --git a/examples/caddy2/README.md b/examples/caddy2/README.md new file mode 100644 index 00000000..fe8d5763 --- /dev/null +++ b/examples/caddy2/README.md @@ -0,0 +1,12 @@ +# Caddyfile + +This directory contains sample files that show you how to do reverse-proxying using Caddy2. + +## Config + +| Variable | Function | +| ------------------ | -------- | +| tls your@email.com | Specify an email address for your [ACME account](https://caddyserver.com/docs/caddyfile/directives/tls) (but if only one email is used for all sites, we recommend the email [global option](https://caddyserver.com/docs/caddyfile/options) instead) | +| tls | To enable [tls](https://caddyserver.com/docs/caddyfile/directives/tls) support uncomment the lines for tls | +| Dimension | To enable Dimension support uncomment the lines for Dimension and set your data | +| Jitsi | To enable Jitsi support uncomment the lines for Jitsi and set your data | diff --git a/examples/haproxy/Dockerfile b/examples/haproxy/Dockerfile new file mode 100644 index 00000000..8c7db989 --- /dev/null +++ b/examples/haproxy/Dockerfile @@ -0,0 +1,12 @@ +# Pull nginx base image +FROM nginx:latest + +# Expost port 80 +EXPOSE 80 + +# Copy custom configuration file from the current directory +COPY nginx.conf /etc/nginx/nginx.conf + +# Start up nginx server +CMD ["nginx"] + diff --git a/examples/haproxy/README.md b/examples/haproxy/README.md new file mode 100644 index 00000000..c1d20b0f --- /dev/null +++ b/examples/haproxy/README.md @@ -0,0 +1,26 @@ +# HAproxy reverse-proxy + +This directory contains sample files that show you how to do reverse-proxying using HAproxy. + +This is for when you wish to have your own HAproxy instance sitting in front of Matrix services installed by this playbook. +See the [Using your own webserver, instead of this playbook's nginx proxy](../../docs/configuring-playbook-own-webserver.md) documentation page. + +To use your own HAproxy reverse-proxy, you first need to disable the integrated Nginx server. +You do that with the following custom configuration (`inventory/host_vars/matrix./vars.yml`): + +```yaml +matrix_nginx_proxy_enabled: false +``` + +You can then use the configuration files from this directory as an example for how to configure your HAproxy reverse proxy. + +**NOTE**: this is just an example and may not be entirely accurate. It may also not cover other use cases or performance needs. + +### Configuration + +HAproxy, unlike Apache, Nginx and others, does not provide you with a webserver to serve static files (i.e., `/.well-known/` directory). For this reason, in this folder you can find an example on how to use HAproxy together with a simple Nginx container whose only task is to serve those files. + +* Build the Docker image. `docker build -t local/nginx .` +* Start the container. `docker-compose up -d`. Note that if you want to run Nginx on a different port, you will have to change the port both in the `docker-compose.yml` and in `haproxy.cfg`. +* If you don't want to use a wildcard certificate, you will need to modify the corresponding line in the HTTPS frontent and add the paths of all the specific certificates (as for the commented example in `haproxy.cfg`). +* Start HAproxy with the proposed configuration. diff --git a/examples/haproxy/docker-compose.yml b/examples/haproxy/docker-compose.yml new file mode 100644 index 00000000..9177161d --- /dev/null +++ b/examples/haproxy/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3' +services: + nginx: + image: local/nginx + ports: + - 40888:80 + volumes: + - /matrix/static-files:/var/www/:ro diff --git a/examples/haproxy/haproxy.cfg b/examples/haproxy/haproxy.cfg new file mode 100644 index 00000000..c7fbf967 --- /dev/null +++ b/examples/haproxy/haproxy.cfg @@ -0,0 +1,97 @@ +global + log /dev/log local0 + log /dev/log local1 notice + chroot /var/lib/haproxy + stats socket /run/haproxy/admin.sock mode 660 level admin + stats timeout 30s + user haproxy + group haproxy + daemon + # Default SSL material locations + ca-base /etc/ssl/certs + crt-base /etc/ssl/private + # Default ciphers to use on SSL-enabled listening sockets. + # For more information, see ciphers(1SSL). This list is from: + # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ + ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS + ssl-default-bind-options no-sslv3 + +defaults + log global + mode http + option httplog + option dontlognull + option forwardfor + option redispatch + timeout connect 5000 + timeout client 50000 + timeout server 50000 + errorfile 400 /etc/haproxy/errors/400.http + errorfile 403 /etc/haproxy/errors/403.http + errorfile 408 /etc/haproxy/errors/408.http + errorfile 500 /etc/haproxy/errors/500.http + errorfile 502 /etc/haproxy/errors/502.http + errorfile 503 /etc/haproxy/errors/503.http + errorfile 504 /etc/haproxy/errors/504.http + +frontend https-frontend + bind *:80 + # HAproxy wants the full chain and the private key in one file. For Letsencrypt manually generated certs (e.g., wildcard certs) you can use + # cat /etc/letsencrypt/live/example.com/fullchain.pem /etc/letsencrypt/live/example.com/privkey.pem > /etc/haproxy/certs/star-example.com.pem + bind *:443 ssl crt /etc/haproxy/certs/star-example.com.pem + #bind *:443 ssl crt /etc/haproxy/certs/element.example.com.pem /etc/haproxy/certs/matrix.example.com.pem + reqadd X-Forwarded-Proto:\ https + option httplog + option http-server-close + # + # Matrix + # + # matrix.example.com + acl matrix_domain hdr_dom(host) -i matrix.example.com + acl static_files path -i -m beg /.well-known/matrix + use_backend nginx-static if static_files + # /_matrix/identity and /_matrix/client/r0/user_directory/search + acl matrix_identity path -i -m beg /_matrix/identity + acl matrix_search path -i -m beg /_matrix/client/r0/user_directory/search + # Send to :8090 + use_backend matrix-supporting if matrix_identity or matrix_search + # /_matrix and /_synapse/admin + acl matrix_path path -i -m beg /_matrix + acl synapse_admin path -i -m beg /_synapse/admin + # Send to :8008 + use_backend matrix-main if matrix_path or synapse_admin + # element.example.com + acl element_domain hdr_dom(host) -i element.example.com + # Send to 8765 + use_backend element if element_domain + # If nothing else match, just send to default matrix backend + use_backend matrix-main if matrix_domain + #default_backend matrix-main + +frontend matrix-federation + bind *:8448 ssl crt /etc/haproxy/certs/star-example.com.pem + reqadd X-Forwarded-Proto:\ https + option httplog + option http-server-close + default_backend synapse + +backend matrix-supporting + server matrix-supporting 127.0.0.1:8090 check + +backend matrix-main + server matrix-main 127.0.0.1:8008 check + +backend synapse + server synapse 127.0.0.1:8048 check + +backend nginx-static + capture request header origin len 128 + http-response add-header Access-Control-Allow-Origin * + rspadd Access-Control-Allow-Methods:\ GET,\ HEAD,\ OPTIONS,\ POST,\ PUT if { capture.req.hdr(0) -m found } + rspadd Access-Control-Allow-Credentials:\ true if { capture.req.hdr(0) -m found } + rspadd Access-Control-Allow-Headers:\ Origin,\ Accept,\ X-Requested-With,\ Content-Type,\ Access-Control-Request-Method,\ Access-Control-Request-Headers,\ Authorization if { capture.req.hdr(0) -m found } + server nginx 127.0.0.1:40888 check + +backend element + server element 127.0.0.1:8765 check + diff --git a/examples/haproxy/nginx.conf b/examples/haproxy/nginx.conf new file mode 100644 index 00000000..912e17c6 --- /dev/null +++ b/examples/haproxy/nginx.conf @@ -0,0 +1,15 @@ +worker_processes auto; +daemon off; + +events { + worker_connections 1024; +} + +http { + server_tokens off; + server { + listen 80; + index index.html; + root /var/www; + } +} diff --git a/examples/hosts b/examples/hosts new file mode 100644 index 00000000..ba08107b --- /dev/null +++ b/examples/hosts @@ -0,0 +1,19 @@ +# We explicitly ask for your server's external IP address, because the same value is used for configuring Coturn. +# If you'd rather use a local IP here, make sure to set up `matrix_coturn_turn_external_ip_address`. +# +# To connect using a non-root user (and elevate to root with sudo later), +# replace `ansible_ssh_user=root` with something like this: `ansible_ssh_user=username become=true become_user=root` +# +# For improved Ansible performance, SSH pipelining is enabled by default in `ansible.cfg`. +# If this causes SSH connection troubles, disable it by adding `ansible_ssh_pipelining=False` +# to the host line below or by adding `ansible_ssh_pipelining: False` to your variables file. +# +# If you're running this Ansible playbook on the same server as the one you're installing to, +# consider adding an additional `ansible_connection=local` argument to the host line below. +# +# Ansible may fail to discover which Python interpreter to use on the host for some distros (like Ubuntu 20.04). +# You may sometimes need to explicitly add the argument `ansible_python_interpreter=/usr/bin/python3` +# to the host line below. + +[matrix_servers] +matrix. ansible_host= ansible_ssh_user=root diff --git a/examples/vars.yml b/examples/vars.yml new file mode 100644 index 00000000..f79e5e3c --- /dev/null +++ b/examples/vars.yml @@ -0,0 +1,35 @@ +# The bare domain name which represents your Matrix identity. +# Matrix user ids for your server will be of the form (`@user:`). +# +# Note: this playbook does not touch the server referenced here. +# Installation happens on another server ("matrix."). +# +# If you've deployed using the wrong domain, you'll have to run the Uninstalling step, +# because you can't change the Domain after deployment. +# +# Example value: example.com +matrix_domain: YOUR_BARE_DOMAIN_NAME_HERE + +# This is something which is provided to Let's Encrypt when retrieving SSL certificates for domains. +# +# In case SSL renewal fails at some point, you'll also get an email notification there. +# +# If you decide to use another method for managing SSL certifites (different than the default Let's Encrypt), +# you won't be required to define this variable (see `docs/configuring-playbook-ssl-certificates.md`). +# +# Example value: someone@example.com +matrix_ssl_lets_encrypt_support_email: '' + +# A shared secret (between Coturn and Synapse) used for authentication. +# You can put any string here, but generating a strong one is preferred (e.g. `pwgen -s 64 1`). +matrix_coturn_turn_static_auth_secret: '' + +# A secret used to protect access keys issued by the server. +# You can put any string here, but generating a strong one is preferred (e.g. `pwgen -s 64 1`). +matrix_synapse_macaroon_secret_key: '' + +# A Postgres password to use for the superuser Postgres user (called `matrix` by default). +# +# The playbook creates additional Postgres users and databases (one for each enabled service) +# using this superuser account. +matrix_postgres_connection_password: '' diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers new file mode 100755 index 00000000..a4e00d41 --- /dev/null +++ b/group_vars/matrix_servers @@ -0,0 +1,1914 @@ +--- + +# This variables file wires together the various components (roles) used by the playbook. +# +# Roles used by playbook are pretty minimal and kept independent of one another as much as possible. +# To deliver a turnkey fully-featured Matrix server, this playbook needs +# to connect them all together. It does so by overriding role variables. +# +# You can also override ANY variable (seen here or in any given role), +# by re-defining it in your own configuration file (`inventory/host_vars/matrix.`). + +matrix_container_global_registry_prefix: "docker.io/" + +###################################################################### +# +# matrix-base +# +###################################################################### + +matrix_identity_server_url: "{{ ('https://' + matrix_server_fqn_matrix) if matrix_ma1sd_enabled else None }}" + +# If Synapse workers are enabled and matrix-nginx-proxy is disabled, certain APIs may not work over 'http://matrix-synapse:8008'. +# This is because we explicitly disable them for the main Synapse process. +matrix_homeserver_container_url: "{{ 'http://matrix-nginx-proxy:12080' if matrix_nginx_proxy_enabled else 'http://matrix-synapse:8008' }}" + +###################################################################### +# +# /matrix-base +# +###################################################################### + + +###################################################################### +# +# matrix-awx +# +###################################################################### + +# We don't enable AWX support by default. +matrix_awx_enabled: false + +matrix_nginx_proxy_data_path: "{{ '/chroot/website' if (matrix_awx_enabled and not matrix_nginx_proxy_base_domain_homepage_enabled) else (matrix_nginx_proxy_base_path + '/data') }}" +matrix_nginx_proxy_data_path_in_container: "{{ '/nginx-data/matrix-domain' if (matrix_awx_enabled and not matrix_nginx_proxy_base_domain_homepage_enabled) else '/nginx-data' }}" +matrix_nginx_proxy_data_path_extension: "{{ '' if (matrix_awx_enabled and not matrix_nginx_proxy_base_domain_homepage_enabled) else '/matrix-domain' }}" +matrix_nginx_proxy_base_domain_create_directory: "{{ false if matrix_awx_enabled else true }}" + +###################################################################### +# +# /matrix-awx +# +###################################################################### + + +###################################################################### +# +# matrix-bridge-appservice-discord +# +###################################################################### + +# We don't enable bridges by default. +matrix_appservice_discord_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach matrix-appservice-discord over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# matrix-appservice-discord's client-server port to the local host. +matrix_appservice_discord_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:9005' }}" + +# If the homeserver disables presence, it's likely better (less wasteful) to also disable presence on the bridge side. +matrix_appservice_discord_bridge_disablePresence: "{{ not matrix_synapse_presence_enabled }}" + +matrix_appservice_discord_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_appservice_discord_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'discord.as.token') | to_uuid }}" + +matrix_appservice_discord_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'discord.hs.token') | to_uuid }}" + +# We only make this use Postgres if our own Postgres server is enabled. +# It's only then (for now) that we can automatically create the necessary database and user for this service. +matrix_appservice_discord_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_appservice_discord_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'as.discord.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-appservice-discord +# +###################################################################### + + +###################################################################### +# +# matrix-appservice-webhooks +# +###################################################################### + +# We don't enable bridges by default. +matrix_appservice_webhooks_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach matrix-appservice-webhooks over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# matrix-appservice-webhooks' client-server port to the local host. +matrix_appservice_webhooks_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else ('127.0.0.1:' ~ matrix_appservice_webhooks_matrix_port) }}" + +matrix_appservice_webhooks_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'webhook.as.token') | to_uuid }}" + +matrix_appservice_webhooks_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'webhook.hs.token') | to_uuid }}" + +matrix_appservice_webhooks_id_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'webhook.id.token') | to_uuid }}" + +matrix_appservice_webhooks_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +###################################################################### +# +# /matrix-appservice-webhooks +# +###################################################################### + + +###################################################################### +# +# matrix-appservice-slack +# +###################################################################### + +# We don't enable bridges by default. +matrix_appservice_slack_enabled: false + +matrix_appservice_slack_container_self_build: "{{ matrix_architecture != 'amd64' }}" + +# Normally, matrix-nginx-proxy is enabled and nginx can reach matrix-appservice-slack over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# matrix-appservice-slack's client-server port to the local host. +matrix_appservice_slack_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else ('127.0.0.1:' ~ matrix_appservice_slack_slack_port) }}" + +matrix_appservice_slack_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'slack.as.token') | to_uuid }}" + +matrix_appservice_slack_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'slack.hs.token') | to_uuid }}" + +matrix_appservice_slack_id_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'slack.id.token') | to_uuid }}" + +matrix_appservice_slack_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_appservice_slack_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'nedb' }}" +matrix_appservice_slack_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'as.slack.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-appservice-slack +# +###################################################################### + +###################################################################### +# +# matrix-bridge-appservice-irc +# +###################################################################### + +# We don't enable bridges by default. +matrix_appservice_irc_enabled: false + +matrix_appservice_irc_container_self_build: "{{ matrix_architecture != 'amd64' }}" + +# Normally, matrix-nginx-proxy is enabled and nginx can reach matrix-appservice-irc over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# matrix-appservice-irc's client-server port to the local host. +matrix_appservice_irc_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:9999' }}" + +# The IRC bridge docs say that if homeserver presence is disabled, it's better to also disable +# IRC bridge presence, for performance reasons. +matrix_appservice_irc_homeserver_enablePresence: "{{ matrix_synapse_presence_enabled }}" + +matrix_appservice_irc_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_appservice_irc_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'irc.as.token') | to_uuid }}" + +matrix_appservice_irc_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'irc.hs.token') | to_uuid }}" + +matrix_appservice_irc_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'nedb' }}" +matrix_appservice_irc_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'as.irc.db') | to_uuid }}" + + +###################################################################### +# +# /matrix-bridge-appservice-irc +# +###################################################################### + + +###################################################################### +# +# matrix-bridge-mautrix-facebook +# +###################################################################### + +# We don't enable bridges by default. +matrix_mautrix_facebook_enabled: false + +matrix_mautrix_facebook_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" + +matrix_mautrix_facebook_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mautrix_facebook_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'fb.as.token') | to_uuid }}" + +matrix_mautrix_facebook_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'fb.hs.token') | to_uuid }}" + +matrix_mautrix_facebook_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +matrix_mautrix_facebook_bridge_presence: "{{ matrix_synapse_presence_enabled if matrix_synapse_enabled else true }}" + +# We'd like to force-set people with external Postgres to SQLite, so the bridge role can complain +# and point them to a migration path. +matrix_mautrix_facebook_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mautrix_facebook_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mau.fb.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mautrix-facebook +# +###################################################################### + + +###################################################################### +# +# matrix-bridge-mautrix-hangouts +# +###################################################################### + +# We don't enable bridges by default. +matrix_mautrix_hangouts_enabled: false + +matrix_mautrix_hangouts_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" + +matrix_mautrix_hangouts_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mautrix_hangouts_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'ho.as.token') | to_uuid }}" + +matrix_mautrix_hangouts_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'ho.hs.token') | to_uuid }}" + +matrix_mautrix_hangouts_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:9007' }}" + +matrix_mautrix_hangouts_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mautrix_hangouts_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mautrix_hangouts_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mau.hangouts.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mautrix-hangouts +# +###################################################################### + + +###################################################################### +# +# matrix-bridge-mautrix-instagram +# +###################################################################### + +# We don't enable bridges by default. +matrix_mautrix_instagram_enabled: false + +matrix_mautrix_instagram_container_image_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" + +matrix_mautrix_instagram_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mautrix_instagram_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'ig.as.token') | to_uuid }}" + +matrix_mautrix_instagram_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'ig.hs.token') | to_uuid }}" + +matrix_mautrix_instagram_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +matrix_mautrix_instagram_bridge_presence: "{{ matrix_synapse_presence_enabled if matrix_synapse_enabled else true }}" + +# We'd like to force-set people with external Postgres to SQLite, so the bridge role can complain +# and point them to a migration path. +matrix_mautrix_instagram_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mautrix_instagram_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mau.ig.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mautrix-instagram +# +###################################################################### + + +###################################################################### +# +# matrix-bridge-mautrix-signal +# +###################################################################### + +# We don't enable bridges by default. +matrix_mautrix_signal_enabled: false + +matrix_mautrix_signal_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + + + ['matrix-mautrix-signal-daemon.service'] + }} + +matrix_mautrix_signal_homeserver_domain: '{{ matrix_domain }}' + +matrix_mautrix_signal_homeserver_address: "{{ matrix_homeserver_container_url }}" + +matrix_mautrix_signal_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'si.hs.token') | to_uuid }}" + +matrix_mautrix_signal_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'si.as.token') | to_uuid }}" + +matrix_mautrix_signal_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +matrix_mautrix_signal_database_engine: 'postgres' +matrix_mautrix_signal_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mau.signal.db') | to_uuid }}" + +matrix_mautrix_signal_container_self_build: "{{ matrix_architecture not in ['amd64', 'arm64'] }}" +matrix_mautrix_signal_daemon_container_self_build: "{{ matrix_architecture != 'amd64' }}" + +###################################################################### +# +# /matrix-bridge-mautrix-signal +# +###################################################################### + +###################################################################### +# +# matrix-bridge-mautrix-telegram +# +###################################################################### + +# We don't enable bridges by default. +matrix_mautrix_telegram_enabled: false + +# Images are multi-arch (amd64 and arm64, but not arm32). +matrix_mautrix_telegram_container_self_build: "{{ matrix_architecture not in ['arm64', 'amd64'] }}" +matrix_telegram_lottieconverter_container_self_build: "{{ matrix_architecture not in ['arm64', 'amd64'] }}" +matrix_telegram_lottieconverter_container_self_build_mask_arch: "{{ matrix_architecture != 'amd64' }}" + +matrix_mautrix_telegram_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mautrix_telegram_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'telegr.as.token') | to_uuid }}" + +matrix_mautrix_telegram_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'telegr.hs.token') | to_uuid }}" + +matrix_mautrix_telegram_public_endpoint: "/{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'telegram') | to_uuid }}" + +matrix_mautrix_telegram_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:9006' }}" + +matrix_mautrix_telegram_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mautrix_telegram_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mautrix_telegram_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mau.telegram.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mautrix-telegram +# +###################################################################### + +###################################################################### +# +# matrix-bridge-mautrix-whatsapp +# +###################################################################### + +# We don't enable bridges by default. +matrix_mautrix_whatsapp_enabled: false + +matrix_mautrix_whatsapp_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mautrix_whatsapp_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'whats.as.token') | to_uuid }}" + +matrix_mautrix_whatsapp_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'whats.hs.token') | to_uuid }}" + +matrix_mautrix_whatsapp_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mautrix_whatsapp_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mautrix_whatsapp_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mauwhatsapp.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mautrix-whatsapp +# +###################################################################### + +###################################################################### +# +# matrix-sms-bridge +# +###################################################################### + +# We don't enable bridges by default. +matrix_sms_bridge_enabled: false + +matrix_sms_bridge_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_sms_bridge_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'sms.as.token') | to_uuid }}" + +matrix_sms_bridge_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'sms.hs.token') | to_uuid }}" + +###################################################################### +# +# /matrix-sms-bridge +# +###################################################################### + +###################################################################### +# +# matrix-bridge-heisenbridge +# +###################################################################### + +# We don't enable bridges by default. +matrix_heisenbridge_enabled: false + +matrix_heisenbridge_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'heisen.as.tok') | to_uuid }}" + +matrix_heisenbridge_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'heisen.hs.tok') | to_uuid }}" + +matrix_heisenbridge_systemd_wanted_services_list: | + {{ + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +###################################################################### +# +# /matrix-bridge-heisenbridge +# +###################################################################### + +###################################################################### +# +# matrix-bridge-mx-puppet-skype +# +###################################################################### + +# We don't enable bridges by default. +matrix_mx_puppet_skype_enabled: false + +matrix_mx_puppet_skype_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_mx_puppet_skype_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mx_puppet_skype_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'skype.as.tok') | to_uuid }}" + +matrix_mx_puppet_skype_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'skype.hs.tok') | to_uuid }}" + +matrix_mx_puppet_skype_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mx_puppet_skype_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mx_puppet_skype_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxpup.skype.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mx-puppet-skype +# +###################################################################### + + +###################################################################### +# +# matrix-bridge-mx-puppet-slack +# +###################################################################### + +# We don't enable bridges by default. +matrix_mx_puppet_slack_enabled: false + +matrix_mx_puppet_slack_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_mx_puppet_slack_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mx_puppet_slack_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxslk.as.tok') | to_uuid }}" + +matrix_mx_puppet_slack_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxslk.hs.tok') | to_uuid }}" + +matrix_mx_puppet_slack_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mx_puppet_slack_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mx_puppet_slack_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxpup.slack.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mx-puppet-slack +# +###################################################################### + +###################################################################### +# +# matrix-bridge-mx-puppet-twitter +# +###################################################################### + +# We don't enable bridges by default. +matrix_mx_puppet_twitter_enabled: false + +matrix_mx_puppet_twitter_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_mx_puppet_twitter_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mx_puppet_twitter_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxtwt.as.tok') | to_uuid }}" + +matrix_mx_puppet_twitter_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxtwt.hs.tok') | to_uuid }}" + +matrix_mx_puppet_twitter_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +matrix_mx_puppet_twitter_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else ('127.0.0.1:' ~ matrix_mx_puppet_twitter_appservice_port) }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mx_puppet_twitter_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mx_puppet_twitter_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxpup.twitter.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mx-puppet-twitter +# +###################################################################### + + +###################################################################### +# +# matrix-bridge-mx-puppet-instagram +# +###################################################################### + +# We don't enable bridges by default. +matrix_mx_puppet_instagram_enabled: false + +matrix_mx_puppet_instagram_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_mx_puppet_instagram_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mx_puppet_instagram_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxig.as.tok') | to_uuid }}" + +matrix_mx_puppet_instagram_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxig.hs.tok') | to_uuid }}" + +matrix_mx_puppet_instagram_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mx_puppet_instagram_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mx_puppet_instagram_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxpup.ig.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mx-puppet-instagram +# +###################################################################### + +###################################################################### +# +# matrix-bridge-mx-puppet-discord +# +###################################################################### + +# We don't enable bridges by default. +matrix_mx_puppet_discord_enabled: false + +matrix_mx_puppet_discord_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_mx_puppet_discord_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mx_puppet_discord_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxdsc.as.tok') | to_uuid }}" + +matrix_mx_puppet_discord_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxdsc.hs.tok') | to_uuid }}" + +matrix_mx_puppet_discord_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mx_puppet_discord_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mx_puppet_discord_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxpup.dsc.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mx-puppet-discord +# +###################################################################### + +###################################################################### +# +# matrix-bridge-mx-puppet-steam +# +###################################################################### + +# We don't enable bridges by default. +matrix_mx_puppet_steam_enabled: false + +matrix_mx_puppet_steam_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_mx_puppet_steam_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mx_puppet_steam_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxste.as.tok') | to_uuid }}" + +matrix_mx_puppet_steam_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxste.hs.tok') | to_uuid }}" + +matrix_mx_puppet_steam_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mx_puppet_steam_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mx_puppet_steam_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxpup.steam.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mx-puppet-steam +# +###################################################################### + +###################################################################### +# +# matrix-bridge-mx-puppet-groupme +# +###################################################################### + +# We don't enable bridges by default. +matrix_mx_puppet_groupme_enabled: false + +matrix_mx_puppet_groupme_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_mx_puppet_groupme_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_mx_puppet_groupme_appservice_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxgro.as.tok') | to_uuid }}" + +matrix_mx_puppet_groupme_homeserver_token: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxgro.hs.tok') | to_uuid }}" + +matrix_mx_puppet_groupme_login_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret if matrix_synapse_ext_password_provider_shared_secret_auth_enabled else '' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_mx_puppet_groupme_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_mx_puppet_groupme_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mxpup.groupme.db') | to_uuid }}" + +###################################################################### +# +# /matrix-bridge-mx-puppet-groupme +# +###################################################################### + +###################################################################### +# +# matrix-bot-matrix-reminder-bot +# +###################################################################### + +# We don't enable bots by default. +matrix_bot_matrix_reminder_bot_enabled: false + +matrix_bot_matrix_reminder_bot_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_bot_matrix_reminder_bot_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_bot_matrix_reminder_bot_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'reminder.bot.db') | to_uuid }}" +matrix_bot_matrix_reminder_bot_container_self_build: "{{ matrix_architecture != 'amd64' }}" + +###################################################################### +# +# /matrix-bot-matrix-reminder-bot +# +###################################################################### + + +###################################################################### +# +# matrix-bot-go-neb +# +###################################################################### + +# We don't enable bots by default. +matrix_bot_go_neb_enabled: false + +matrix_bot_go_neb_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +matrix_bot_go_neb_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:4050' }}" + +###################################################################### +# +# /matrix-bot-go-neb +# +###################################################################### + + +###################################################################### +# +# matrix-bot-mjolnir +# +###################################################################### + +# We don't enable bots by default. +matrix_bot_mjolnir_enabled: false + +matrix_bot_mjolnir_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_bot_mjolnir_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +###################################################################### +# +# /matrix-bot-mjolnir +# +###################################################################### + + +###################################################################### +# +# matrix-corporal +# +###################################################################### + +matrix_corporal_enabled: false + +matrix_corporal_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" + +# Normally, matrix-nginx-proxy is enabled and nginx can reach matrix-corporal over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# matrix-corporal's web-server ports to the local host. +matrix_corporal_container_http_gateway_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:41080' }}" +matrix_corporal_container_http_api_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:41081' }}" + +matrix_corporal_systemd_required_services_list: | + {{ + (['docker.service']) + + + (['matrix-synapse.service']) + }} + +# This goes to Synapse's vhost +matrix_corporal_matrix_homeserver_api_endpoint: "{{ matrix_homeserver_container_url }}" + +matrix_corporal_matrix_auth_shared_secret: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret }}" + +# This is only useful if there's REST auth provider to make use of it. +matrix_corporal_http_gateway_internal_rest_auth_enabled: "{{ matrix_synapse_ext_password_provider_rest_auth_enabled }}" + +matrix_corporal_matrix_registration_shared_secret: "{{ matrix_synapse_registration_shared_secret }}" + +###################################################################### +# +# /matrix-corporal +# +###################################################################### + + + +###################################################################### +# +# matrix-coturn +# +###################################################################### + +matrix_coturn_enabled: true + +matrix_coturn_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +matrix_coturn_turn_external_ip_address: "{{ ansible_host }}" + +matrix_coturn_tls_enabled: "{{ matrix_ssl_retrieval_method != 'none' }}" +matrix_coturn_tls_cert_path: "{{ matrix_ssl_config_dir_path }}/live/{{ matrix_server_fqn_matrix }}/fullchain.pem" +matrix_coturn_tls_key_path: "{{ matrix_ssl_config_dir_path }}/live/{{ matrix_server_fqn_matrix }}/privkey.pem" +matrix_coturn_container_additional_volumes: | + {{ + ([] if matrix_ssl_retrieval_method == 'none' else [ + { + 'src': matrix_ssl_config_dir_path, + 'dst': matrix_ssl_config_dir_path, + 'options': 'ro', + } + ]) + }} + +###################################################################### +# +# /matrix-coturn +# +###################################################################### + + + +###################################################################### +# +# matrix-dimension +# +###################################################################### + +matrix_dimension_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach Dimension over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# the Dimension HTTP port to the local host. +matrix_dimension_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:8184' }}" + +matrix_integration_manager_rest_url: "{{ matrix_dimension_integrations_rest_url if matrix_dimension_enabled else None }}" +matrix_integration_manager_ui_url: "{{ matrix_dimension_integrations_ui_url if matrix_dimension_enabled else None }}" + +matrix_dimension_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-nginx-proxy.service'] if matrix_nginx_proxy_enabled else []) + }} + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_dimension_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_dimension_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'dimension.db') | to_uuid }}" + +###################################################################### +# +# /matrix-dimension +# +###################################################################### + +###################################################################### +# +# matrix-etherpad +# +###################################################################### + +matrix_etherpad_enabled: false + +matrix_etherpad_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:9001' }}" + +matrix_etherpad_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + }} + +matrix_etherpad_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'etherpad.db') | to_uuid }}" + +###################################################################### +# +# /matrix-etherpad +# +###################################################################### + +###################################################################### +# +# matrix-dynamic-dns +# +###################################################################### + +matrix_dynamic_dns_enabled: false + +###################################################################### +# +# /matrix-dynamic-dns +# +###################################################################### + + + +###################################################################### +# +# matrix-email2matrix +# +###################################################################### + +matrix_email2matrix_enabled: false + +matrix_email2matrix_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" + +###################################################################### +# +# /matrix-email2matrix +# +###################################################################### + + + +###################################################################### +# +# matrix-jitsi +# +###################################################################### + +matrix_jitsi_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach jitsi/web over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# the Jitsi HTTP port to the local host. +matrix_jitsi_web_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:13080' }}" + +matrix_jitsi_jvb_container_colibri_ws_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:13090' }}" + +matrix_jitsi_prosody_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:5280' }}" + +matrix_jitsi_jibri_xmpp_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'jibri') | to_uuid }}" +matrix_jitsi_jicofo_auth_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'jicofo') | to_uuid }}" +matrix_jitsi_jvb_auth_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'jvb') | to_uuid }}" + +matrix_jitsi_web_stun_servers: | + {{ + [ + 'stun:' + matrix_server_fqn_matrix + ':5349', + 'stun:' + matrix_server_fqn_matrix + ':3478', + ] + if matrix_coturn_enabled + else [ 'stun:meet-jit-si-turnrelay.jitsi.net:443'] + }} + +# If the self-hosted Etherpad instance is available, it will also show up in Jitsi conferences, +# unless explicitly disabled by setting `matrix_jitsi_etherpad_enabled` to false. +# Falls back to the scalar.vector.im etherpad in case someone sets `matrix_jitsi_etherpad_enabled` to true, +# while also setting `matrix_etherpad_enabled` to false. +matrix_jitsi_etherpad_enabled: "{{ matrix_etherpad_enabled }}" +matrix_jitsi_etherpad_base: "{{ matrix_etherpad_base_url if matrix_etherpad_enabled else 'https://scalar.vector.im/etherpad' }}" + +###################################################################### +# +# /matrix-jitsi +# +###################################################################### + + + +###################################################################### +# +# matrix-mailer +# +###################################################################### + +# By default, this playbook sets up an exim mailer server (running in a container). +# This is so that Synapse can send email reminders for unread messages. +# Other services (like ma1sd), also use the mailer. +matrix_mailer_enabled: true + +matrix_mailer_container_image_self_build: "{{ matrix_architecture != 'amd64'}}" + +###################################################################### +# +# /matrix-mailer +# +###################################################################### + + + +###################################################################### +# +# matrix-ma1sd +# +###################################################################### + +# By default, this playbook installs the ma1sd identity server on the same domain as Synapse (`matrix_server_fqn_matrix`). +# If you wish to use the public identity servers (matrix.org, vector.im) instead of your own you may wish to disable this. +matrix_ma1sd_enabled: true + +matrix_ma1sd_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" + +# Normally, matrix-nginx-proxy is enabled and nginx can reach ma1sd over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# ma1sd's web-server port. +matrix_ma1sd_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:8090' }}" + +# We enable Synapse integration via its Postgres database by default. +# When using another Identity store, you might wish to disable this and define +# your own configuration in `matrix_ma1sd_configuration_extension_yaml`. +matrix_ma1sd_synapsesql_enabled: true +matrix_ma1sd_synapsesql_type: postgresql +matrix_ma1sd_synapsesql_connection: //{{ matrix_synapse_database_host }}/{{ matrix_synapse_database_database }}?user={{ matrix_synapse_database_user | urlencode() }}&password={{ matrix_synapse_database_password | urlencode() }} + +matrix_ma1sd_dns_overwrite_enabled: true +matrix_ma1sd_dns_overwrite_homeserver_client_name: "{{ matrix_server_fqn_matrix }}" +# The `matrix_ma1sd_dns_overwrite_homeserver_client_value` value when matrix_nginx_proxy_enabled is false covers the general case, +# but may be inaccurate if matrix-corporal is enabled. +matrix_ma1sd_dns_overwrite_homeserver_client_value: "{{ ('http://' + matrix_nginx_proxy_proxy_matrix_client_api_addr_with_container) if matrix_nginx_proxy_enabled else matrix_homeserver_container_url }}" + +# By default, we send mail through the `matrix-mailer` service. +matrix_ma1sd_threepid_medium_email_identity_from: "{{ matrix_mailer_sender_address }}" +matrix_ma1sd_threepid_medium_email_connectors_smtp_host: "matrix-mailer" +matrix_ma1sd_threepid_medium_email_connectors_smtp_port: 8025 +matrix_ma1sd_threepid_medium_email_connectors_smtp_tls: 0 + +matrix_ma1sd_self_check_validate_certificates: "{{ false if matrix_ssl_retrieval_method == 'self-signed' else true }}" + +matrix_ma1sd_systemd_required_services_list: | + {{ + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + }} + +matrix_ma1sd_systemd_wanted_services_list: | + {{ + (['matrix-corporal.service'] if matrix_corporal_enabled else ['matrix-synapse.service']) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-mailer.service'] if matrix_mailer_enabled else []) + }} + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_ma1sd_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_ma1sd_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'ma1sd.db') | to_uuid }}" + +###################################################################### +# +# /matrix-ma1sd +# +###################################################################### + + + +###################################################################### +# +# matrix-nginx-proxy +# +###################################################################### + +# By default, this playbook sets up a reverse-proxy nginx proxy server on TCP ports 80, 443 and 8448. +# This is fine if you're dedicating the whole server to Matrix. +# If that's not the case, you may wish to disable this and take care of proxying yourself. +matrix_nginx_proxy_enabled: true + +matrix_nginx_proxy_proxy_matrix_client_api_addr_with_container: "{{ 'matrix-corporal:41080' if matrix_corporal_enabled else 'matrix-nginx-proxy:12080' }}" +matrix_nginx_proxy_proxy_matrix_client_api_addr_sans_container: "{{ '127.0.0.1:41080' if matrix_corporal_enabled else '127.0.0.1:12080' }}" +matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size_mb: "{{ matrix_synapse_max_upload_size_mb }}" + +matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_admin_api_enabled: "{{ matrix_synapse_admin_enabled }}" + +matrix_nginx_proxy_proxy_matrix_client_redirect_root_uri_to_domain: "{{ matrix_server_fqn_element if matrix_client_element_enabled else '' }}" + +matrix_nginx_proxy_proxy_matrix_enabled: true +matrix_nginx_proxy_proxy_element_enabled: "{{ matrix_client_element_enabled }}" +matrix_nginx_proxy_proxy_hydrogen_enabled: "{{ matrix_client_hydrogen_enabled }}" +matrix_nginx_proxy_proxy_dimension_enabled: "{{ matrix_dimension_enabled }}" +matrix_nginx_proxy_proxy_bot_go_neb_enabled: "{{ matrix_bot_go_neb_enabled }}" +matrix_nginx_proxy_proxy_jitsi_enabled: "{{ matrix_jitsi_enabled }}" +matrix_nginx_proxy_proxy_grafana_enabled: "{{ matrix_grafana_enabled }}" +matrix_nginx_proxy_proxy_sygnal_enabled: "{{ matrix_sygnal_enabled }}" + +matrix_nginx_proxy_proxy_matrix_corporal_api_enabled: "{{ matrix_corporal_enabled and matrix_corporal_http_api_enabled }}" +matrix_nginx_proxy_proxy_matrix_corporal_api_addr_with_container: "matrix-corporal:41081" +matrix_nginx_proxy_proxy_matrix_corporal_api_addr_sans_container: "127.0.0.1:41081" + +matrix_nginx_proxy_proxy_matrix_identity_api_enabled: "{{ matrix_ma1sd_enabled }}" +matrix_nginx_proxy_proxy_matrix_identity_api_addr_with_container: "matrix-ma1sd:8090" +matrix_nginx_proxy_proxy_matrix_identity_api_addr_sans_container: "127.0.0.1:8090" + +# By default, we do TLS termination for the Matrix Federation API (port 8448) at matrix-nginx-proxy. +# Unless this is handled there OR Synapse's federation listener port is disabled, we'll reverse-proxy. +matrix_nginx_proxy_proxy_matrix_federation_api_enabled: "{{ matrix_synapse_federation_port_enabled and not matrix_synapse_tls_federation_listener_enabled }}" +matrix_nginx_proxy_proxy_matrix_federation_api_addr_with_container: "matrix-nginx-proxy:12088" +matrix_nginx_proxy_proxy_matrix_federation_api_addr_sans_container: "127.0.0.1:12088" + +# Settings controlling matrix-synapse-proxy.conf +matrix_nginx_proxy_proxy_synapse_enabled: "{{ matrix_synapse_enabled }}" + +# When matrix-nginx-proxy is disabled, the actual port number that the vhost uses may begin to matter. +matrix_nginx_proxy_proxy_matrix_federation_port: "{{ matrix_federation_public_port }}" + +matrix_nginx_proxy_container_federation_host_bind_port: "{{ matrix_federation_public_port }}" + +# This used to be hooked to `matrix_synapse_metrics_enabled`, but we don't do it anymore. +# The fact that someone wishes to enable Synapse metrics does not necessarily mean they want to make them public. +# A local Prometheus can consume them over the container network. +matrix_nginx_proxy_proxy_synapse_metrics: false +matrix_nginx_proxy_proxy_synapse_metrics_addr_with_container: "matrix-synapse:{{ matrix_synapse_metrics_port }}" +matrix_nginx_proxy_proxy_synapse_metrics_addr_sans_container: "127.0.0.1:{{ matrix_synapse_metrics_port }}" + +matrix_nginx_proxy_proxy_matrix_user_directory_search_enabled: "{{ matrix_ma1sd_enabled }}" +matrix_nginx_proxy_proxy_matrix_user_directory_search_addr_with_container: "{{ matrix_nginx_proxy_proxy_matrix_identity_api_addr_with_container }}" +matrix_nginx_proxy_proxy_matrix_user_directory_search_addr_sans_container: "{{ matrix_nginx_proxy_proxy_matrix_identity_api_addr_sans_container }}" + +matrix_nginx_proxy_self_check_validate_certificates: "{{ false if matrix_ssl_retrieval_method == 'self-signed' else true }}" + +# OCSP stapling does not make sense when self-signed certificates are used. +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1073 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1074 +matrix_nginx_proxy_ocsp_stapling_enabled: "{{ matrix_ssl_retrieval_method != 'self-signed' }}" + +matrix_nginx_proxy_synapse_presence_disabled: "{{ not matrix_synapse_presence_enabled }}" + +matrix_nginx_proxy_synapse_workers_enabled: "{{ matrix_synapse_workers_enabled }}" +matrix_nginx_proxy_synapse_workers_list: "{{ matrix_synapse_workers_enabled_list }}" +matrix_nginx_proxy_synapse_generic_worker_client_server_locations: "{{ matrix_synapse_workers_generic_worker_client_server_endpoints }}" +matrix_nginx_proxy_synapse_generic_worker_federation_locations: "{{ matrix_synapse_workers_generic_worker_federation_endpoints }}" +matrix_nginx_proxy_synapse_media_repository_locations: "{{matrix_synapse_workers_media_repository_endpoints|default([]) }}" +matrix_nginx_proxy_synapse_user_dir_locations: "{{ matrix_synapse_workers_user_dir_endpoints|default([]) }}" +matrix_nginx_proxy_synapse_frontend_proxy_locations: "{{ matrix_synapse_workers_frontend_proxy_endpoints|default([]) }}" + +matrix_nginx_proxy_systemd_wanted_services_list: | + {{ + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + + + (['matrix-corporal.service'] if matrix_corporal_enabled else []) + + + (['matrix-ma1sd.service'] if matrix_ma1sd_enabled else []) + + + (['matrix-client-element.service'] if matrix_client_element_enabled else []) + }} + +matrix_ssl_domains_to_obtain_certificates_for: | + {{ + ([matrix_server_fqn_matrix]) + + + ([matrix_server_fqn_element] if matrix_client_element_enabled else []) + + + ([matrix_nginx_proxy_proxy_riot_compat_redirect_hostname] if matrix_nginx_proxy_proxy_riot_compat_redirect_enabled else []) + + + ([matrix_server_fqn_hydrogen] if matrix_client_hydrogen_enabled else []) + + + ([matrix_server_fqn_dimension] if matrix_dimension_enabled else []) + + + ([matrix_server_fqn_bot_go_neb] if matrix_bot_go_neb_enabled else []) + + + ([matrix_server_fqn_jitsi] if matrix_jitsi_enabled else []) + + + ([matrix_server_fqn_grafana] if matrix_grafana_enabled else []) + + + ([matrix_server_fqn_sygnal] if matrix_sygnal_enabled else []) + + + ([matrix_domain] if matrix_nginx_proxy_base_domain_serving_enabled else []) + + + matrix_ssl_additional_domains_to_obtain_certificates_for + }} + +matrix_ssl_architecture: "{{ + { + 'amd64': 'amd64', + 'arm32': 'arm32v6', + 'arm64': 'arm64v8', + }[matrix_architecture] +}}" + +matrix_ssl_pre_obtaining_required_service_name: "{{ 'matrix-dynamic-dns' if matrix_dynamic_dns_enabled else '' }}" + +###################################################################### +# +# /matrix-nginx-proxy +# +###################################################################### + + + +###################################################################### +# +# matrix-postgres +# +###################################################################### + +matrix_postgres_enabled: true + +matrix_postgres_architecture: "{{ matrix_architecture }}" + +# We unset this if internal Postgres disabled, which will cascade to some other variables +# and tell users they need to set it (either here or in those variables). +matrix_postgres_connection_hostname: "{{ 'matrix-postgres' if matrix_postgres_enabled else '' }}" + +matrix_postgres_pgloader_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" + +matrix_postgres_additional_databases: | + {{ + ([{ + 'name': matrix_synapse_database_database, + 'username': matrix_synapse_database_user, + 'password': matrix_synapse_database_password, + }] if (matrix_synapse_enabled and matrix_synapse_database_database != matrix_postgres_db_name and matrix_synapse_database_host == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_ma1sd_database_name, + 'username': matrix_ma1sd_database_username, + 'password': matrix_ma1sd_database_password, + }] if (matrix_ma1sd_enabled and matrix_ma1sd_database_engine == 'postgres' and matrix_ma1sd_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_bot_matrix_reminder_bot_database_name, + 'username': matrix_bot_matrix_reminder_bot_database_username, + 'password': matrix_bot_matrix_reminder_bot_database_password, + }] if (matrix_bot_matrix_reminder_bot_enabled and matrix_bot_matrix_reminder_bot_database_engine == 'postgres' and matrix_bot_matrix_reminder_bot_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_registration_database_name, + 'username': matrix_registration_database_username, + 'password': matrix_registration_database_password, + }] if (matrix_registration_enabled and matrix_registration_database_engine == 'postgres' and matrix_registration_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_appservice_discord_database_name, + 'username': matrix_appservice_discord_database_username, + 'password': matrix_appservice_discord_database_password, + }] if (matrix_appservice_discord_enabled and matrix_appservice_discord_database_engine == 'postgres' and matrix_appservice_discord_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_appservice_slack_database_name, + 'username': matrix_appservice_slack_database_username, + 'password': matrix_appservice_slack_database_password, + }] if (matrix_appservice_slack_enabled and matrix_appservice_slack_database_engine == 'postgres' and matrix_appservice_slack_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_appservice_irc_database_name, + 'username': matrix_appservice_irc_database_username, + 'password': matrix_appservice_irc_database_password, + }] if (matrix_appservice_irc_enabled and matrix_appservice_irc_database_engine == 'postgres' and matrix_appservice_irc_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mautrix_facebook_database_name, + 'username': matrix_mautrix_facebook_database_username, + 'password': matrix_mautrix_facebook_database_password, + }] if (matrix_mautrix_facebook_enabled and matrix_mautrix_facebook_database_engine == 'postgres' and matrix_mautrix_facebook_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mautrix_hangouts_database_name, + 'username': matrix_mautrix_hangouts_database_username, + 'password': matrix_mautrix_hangouts_database_password, + }] if (matrix_mautrix_hangouts_enabled and matrix_mautrix_hangouts_database_engine == 'postgres' and matrix_mautrix_hangouts_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mautrix_instagram_database_name, + 'username': matrix_mautrix_instagram_database_username, + 'password': matrix_mautrix_instagram_database_password, + }] if (matrix_mautrix_instagram_enabled and matrix_mautrix_instagram_database_engine == 'postgres' and matrix_mautrix_instagram_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mautrix_signal_database_name, + 'username': matrix_mautrix_signal_database_username, + 'password': matrix_mautrix_signal_database_password, + }] if (matrix_mautrix_signal_enabled and matrix_mautrix_signal_database_engine == 'postgres' and matrix_mautrix_signal_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mautrix_telegram_database_name, + 'username': matrix_mautrix_telegram_database_username, + 'password': matrix_mautrix_telegram_database_password, + }] if (matrix_mautrix_telegram_enabled and matrix_mautrix_telegram_database_engine == 'postgres' and matrix_mautrix_telegram_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mautrix_whatsapp_database_name, + 'username': matrix_mautrix_whatsapp_database_username, + 'password': matrix_mautrix_whatsapp_database_password, + }] if (matrix_mautrix_whatsapp_enabled and matrix_mautrix_whatsapp_database_engine == 'postgres' and matrix_mautrix_whatsapp_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mx_puppet_skype_database_name, + 'username': matrix_mx_puppet_skype_database_username, + 'password': matrix_mx_puppet_skype_database_password, + }] if (matrix_mx_puppet_skype_enabled and matrix_mx_puppet_skype_database_engine == 'postgres' and matrix_mx_puppet_skype_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mx_puppet_slack_database_name, + 'username': matrix_mx_puppet_slack_database_username, + 'password': matrix_mx_puppet_slack_database_password, + }] if (matrix_mx_puppet_slack_enabled and matrix_mx_puppet_slack_database_engine == 'postgres' and matrix_mx_puppet_slack_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mx_puppet_twitter_database_name, + 'username': matrix_mx_puppet_twitter_database_username, + 'password': matrix_mx_puppet_twitter_database_password, + }] if (matrix_mx_puppet_twitter_enabled and matrix_mx_puppet_twitter_database_engine == 'postgres' and matrix_mx_puppet_twitter_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mx_puppet_instagram_database_name, + 'username': matrix_mx_puppet_instagram_database_username, + 'password': matrix_mx_puppet_instagram_database_password, + }] if (matrix_mx_puppet_instagram_enabled and matrix_mx_puppet_instagram_database_engine == 'postgres' and matrix_mx_puppet_instagram_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mx_puppet_discord_database_name, + 'username': matrix_mx_puppet_discord_database_username, + 'password': matrix_mx_puppet_discord_database_password, + }] if (matrix_mx_puppet_discord_enabled and matrix_mx_puppet_discord_database_engine == 'postgres' and matrix_mx_puppet_discord_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mx_puppet_steam_database_name, + 'username': matrix_mx_puppet_steam_database_username, + 'password': matrix_mx_puppet_steam_database_password, + }] if (matrix_mx_puppet_steam_enabled and matrix_mx_puppet_steam_database_engine == 'postgres' and matrix_mx_puppet_steam_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_mx_puppet_groupme_database_name, + 'username': matrix_mx_puppet_groupme_database_username, + 'password': matrix_mx_puppet_groupme_database_password, + }] if (matrix_mx_puppet_groupme_enabled and matrix_mx_puppet_groupme_database_engine == 'postgres' and matrix_mx_puppet_groupme_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_dimension_database_name, + 'username': matrix_dimension_database_username, + 'password': matrix_dimension_database_password, + }] if (matrix_dimension_enabled and matrix_dimension_database_engine == 'postgres' and matrix_dimension_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_etherpad_database_name, + 'username': matrix_etherpad_database_username, + 'password': matrix_etherpad_database_password, + }] if (matrix_etherpad_enabled and matrix_etherpad_database_engine == 'postgres' and matrix_etherpad_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_sygnal_database_name, + 'username': matrix_sygnal_database_username, + 'password': matrix_sygnal_database_password, + }] if (matrix_sygnal_enabled and matrix_sygnal_database_engine == 'postgres' and matrix_sygnal_database_hostname == 'matrix-postgres') else []) + + + ([{ + 'name': matrix_prometheus_postgres_exporter_database_name, + 'username': matrix_prometheus_postgres_exporter_database_username, + 'password': matrix_prometheus_postgres_exporter_database_password, + }] if (matrix_prometheus_postgres_exporter_enabled and matrix_prometheus_postgres_exporter_database_hostname == 'matrix-postgres') else []) + + }} + +matrix_postgres_import_roles_to_ignore: | + {{ + [matrix_postgres_connection_username] + + + matrix_postgres_additional_databases|map(attribute='username')|list + }} + +matrix_postgres_import_databases_to_ignore: | + {{ + [matrix_postgres_db_name] + + + matrix_postgres_additional_databases|map(attribute='name')|list + }} + +###################################################################### +# +# /matrix-postgres +# +###################################################################### + + + +###################################################################### +# +# matrix-sygnal +# +###################################################################### + +# Most people don't need their own push-server, because they also need their own app to utilize it from. +matrix_sygnal_enabled: false + +# If someone instals Prometheus via the playbook, they most likely wish to monitor Sygnal. +matrix_sygnal_metrics_prometheus_enabled: "{{ matrix_prometheus_enabled }}" + +matrix_sygnal_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:6000' }}" + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_sygnal_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_sygnal_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'sygnal') | to_uuid }}" + +###################################################################### +# +# /matrix-sygnal +# +###################################################################### + + + +###################################################################### +# +# matrix-redis +# +###################################################################### + +matrix_redis_enabled: "{{ matrix_synapse_workers_enabled }}" + +###################################################################### +# +# /matrix-redis +# +###################################################################### + + + +###################################################################### +# +# matrix-client-element +# +###################################################################### + +# By default, this playbook installs the Element web UI on the `matrix_server_fqn_element` domain. +# If you wish to connect to your Matrix server by other means, you may wish to disable this. +matrix_client_element_enabled: true + +matrix_client_element_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" + +# Normally, matrix-nginx-proxy is enabled and nginx can reach Element over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# the Element HTTP port to the local host. +matrix_client_element_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:8765' }}" + +matrix_client_element_default_hs_url: "{{ matrix_homeserver_url }}" +matrix_client_element_default_is_url: "{{ matrix_identity_server_url }}" + +# Use Dimension if enabled, otherwise fall back to Scalar +matrix_client_element_integrations_ui_url: "{{ matrix_dimension_integrations_ui_url if matrix_dimension_enabled else 'https://scalar.vector.im/' }}" +matrix_client_element_integrations_rest_url: "{{ matrix_dimension_integrations_rest_url if matrix_dimension_enabled else 'https://scalar.vector.im/api' }}" +matrix_client_element_integrations_widgets_urls: "{{ matrix_dimension_integrations_widgets_urls if matrix_dimension_enabled else ['https://scalar.vector.im/api'] }}" +matrix_client_element_integrations_jitsi_widget_url: "{{ matrix_dimension_integrations_jitsi_widget_url if matrix_dimension_enabled else 'https://scalar.vector.im/api/widgets/jitsi.html' }}" + +matrix_client_element_self_check_validate_certificates: "{{ false if matrix_ssl_retrieval_method == 'self-signed' else true }}" + +matrix_client_element_registration_enabled: "{{ matrix_synapse_enable_registration }}" + +matrix_client_element_enable_presence_by_hs_url: | + {{ + none + if matrix_synapse_presence_enabled + else {matrix_client_element_default_hs_url: false} + }} + +matrix_client_element_welcome_user_id: ~ + +matrix_client_element_jitsi_preferredDomain: "{{ matrix_server_fqn_jitsi if matrix_jitsi_enabled else '' }}" + +###################################################################### +# +# /matrix-client-element +# +###################################################################### + + + +###################################################################### +# +# matrix-client-hydrogen +# +###################################################################### + +matrix_client_hydrogen_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach Hydrogen over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# the HTTP port to the local host. +matrix_client_hydrogen_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:8768' }}" + +matrix_client_hydrogen_default_hs_url: "{{ matrix_homeserver_url }}" + +matrix_client_hydrogen_self_check_validate_certificates: "{{ false if matrix_ssl_retrieval_method == 'self-signed' else true }}" + +###################################################################### +# +# /matrix-client-hydrogen +# +###################################################################### + + + +###################################################################### +# +# matrix-synapse +# +###################################################################### + +matrix_synapse_container_image_self_build: "{{ matrix_architecture not in ['arm64', 'amd64'] }}" + +# When ma1sd is enabled, we can use it to validate email addresses and phone numbers. +# Synapse can validate email addresses by itself as well, but it's probably not what we want by default when we have an identity server. +matrix_synapse_account_threepid_delegates_email: "{{ 'http://matrix-ma1sd:8090' if matrix_ma1sd_enabled else '' }}" +matrix_synapse_account_threepid_delegates_msisdn: "{{ 'http://matrix-ma1sd:8090' if matrix_ma1sd_enabled else '' }}" + +# Normally, matrix-nginx-proxy is enabled and nginx can reach Synapse over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, +# you can expose Synapse's ports to the host. +# +# For exposing the Matrix Client API's port (plain HTTP) to the local host. +matrix_synapse_container_client_api_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:8008' }}" +# +# For exposing the Matrix Federation API's plain port (plain HTTP) to the local host. +matrix_synapse_container_federation_api_plain_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:8048' }}" +# +# For exposing the Matrix Federation API's TLS port (HTTPS) to the internet on all network interfaces. +matrix_synapse_container_federation_api_tls_host_bind_port: "{{ matrix_federation_public_port if (matrix_synapse_federation_enabled and matrix_synapse_tls_federation_listener_enabled) else '' }}" +# +# For exposing the Synapse Metrics API's port (plain HTTP) to the local host. +matrix_synapse_container_metrics_api_host_bind_port: "{{ '127.0.0.1:9100' if (matrix_synapse_metrics_enabled and not matrix_nginx_proxy_enabled) else '' }}" +# +# For exposing the Synapse Manhole port (plain HTTP) to the local host. +matrix_synapse_container_manhole_api_host_bind_port: "{{ '127.0.0.1:9000' if matrix_synapse_manhole_enabled else '' }}" +# +# For exposing the Synapse worker (and metrics) ports to the local host. +matrix_synapse_workers_container_host_bind_address: "{{ '127.0.0.1' if (matrix_synapse_workers_enabled and not matrix_nginx_proxy_enabled) else '' }}" + +matrix_synapse_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'synapse.db') | to_uuid }}" + +# We do not enable TLS in Synapse by default. +# TLS is handled by the matrix-nginx-proxy, which proxies the requests to Synapse. +matrix_synapse_tls_federation_listener_enabled: false +matrix_synapse_tls_certificate_path: ~ +matrix_synapse_tls_private_key_path: ~ + +matrix_synapse_federation_port_openid_resource_required: "{{ not matrix_synapse_federation_enabled and (matrix_dimension_enabled or matrix_ma1sd_enabled) }}" + +# If someone instals Prometheus via the playbook, they most likely wish to monitor Synapse. +matrix_synapse_metrics_enabled: "{{ matrix_prometheus_enabled }}" + +matrix_synapse_email_enabled: "{{ matrix_mailer_enabled }}" +matrix_synapse_email_smtp_host: "matrix-mailer" +matrix_synapse_email_smtp_port: 8025 +matrix_synapse_email_smtp_require_transport_security: false +matrix_synapse_email_notif_from: "Matrix <{{ matrix_mailer_sender_address }}>" + +# Even if TURN doesn't support TLS (it does by default), +# it doesn't hurt to try a secure connection anyway. +# +# When Let's Encrypt certificates are used (the default case), +# we don't enable `turns` endpoints, because WebRTC in Element can't talk to them. +# Learn more here: https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1145 +matrix_synapse_turn_uris: | + {{ + [] + + + [ + 'turns:' + matrix_server_fqn_matrix + '?transport=udp', + 'turns:' + matrix_server_fqn_matrix + '?transport=tcp', + ] if matrix_coturn_enabled and matrix_coturn_tls_enabled and matrix_ssl_retrieval_method != 'lets-encrypt' else [] + + + [ + 'turn:' + matrix_server_fqn_matrix + '?transport=udp', + 'turn:' + matrix_server_fqn_matrix + '?transport=tcp', + ] if matrix_coturn_enabled else [] + }} + +matrix_synapse_turn_shared_secret: "{{ matrix_coturn_turn_static_auth_secret if matrix_coturn_enabled else '' }}" + +matrix_synapse_self_check_validate_certificates: "{{ false if matrix_ssl_retrieval_method == 'self-signed' else true }}" + +matrix_synapse_systemd_required_services_list: | + {{ + (['docker.service']) + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + + + (['matrix-goofys'] if matrix_s3_media_store_enabled else []) + }} + +matrix_synapse_systemd_wanted_services_list: | + {{ + (['matrix-coturn.service'] if matrix_coturn_enabled else []) + + + (['matrix-mailer.service'] if matrix_mailer_enabled else []) + }} + +# Synapse workers (used for parallel load-scaling) need Redis for IPC. +matrix_synapse_redis_enabled: "{{ matrix_redis_enabled }}" +matrix_synapse_redis_host: "{{ 'matrix-redis' if matrix_redis_enabled else '' }}" +matrix_synapse_redis_password: "{{ matrix_redis_connection_password if matrix_redis_enabled else '' }}" + +###################################################################### +# +# /matrix-synapse +# +###################################################################### + + + +###################################################################### +# +# matrix-synapse-admin +# +###################################################################### + +matrix_synapse_admin_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach Synapse Admin over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# Synapse Admin's HTTP port to the local host. +matrix_synapse_admin_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:8766' }}" + +matrix_synapse_admin_container_self_build: "{{ matrix_architecture != 'amd64' }}" + +###################################################################### +# +# /matrix-synapse-admin +# +###################################################################### + + + +###################################################################### +# +# matrix-prometheus-node-exporter +# +###################################################################### + +matrix_prometheus_node_exporter_enabled: false + +###################################################################### +# +# /matrix-prometheus-node-exporter +# +###################################################################### + + + +###################################################################### +# +# matrix-prometheus +# +###################################################################### + +matrix_prometheus_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach Prometheus over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# Prometheus' HTTP port to the local host. +matrix_prometheus_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:9090' }}" + +matrix_prometheus_scraper_synapse_enabled: "{{ matrix_synapse_enabled and matrix_synapse_metrics_enabled }}" +matrix_prometheus_scraper_synapse_targets: ['matrix-synapse:{{ matrix_synapse_metrics_port }}'] +matrix_prometheus_scraper_synapse_workers_enabled_list: "{{ matrix_synapse_workers_enabled_list }}" +matrix_prometheus_scraper_synapse_rules_synapse_tag: "{{ matrix_synapse_docker_image_tag }}" + +matrix_prometheus_scraper_node_enabled: "{{ matrix_prometheus_node_exporter_enabled }}" +matrix_prometheus_scraper_node_targets: "{{ ['matrix-prometheus-node-exporter:9100'] if matrix_prometheus_node_exporter_enabled else [] }}" + +matrix_prometheus_scraper_postgres_enabled: "{{ matrix_prometheus_postgres_exporter_enabled }}" +matrix_prometheus_scraper_postgres_targets: "{{ ['matrix-prometheus-postgres-exporter:'+ matrix_prometheus_postgres_exporter_port|string] if matrix_prometheus_scraper_postgres_enabled else [] }}" + + +###################################################################### +# +# /matrix-prometheus +# +###################################################################### + + +###################################################################### +# +# matrix-prometheus-postgres-exporter +# +###################################################################### + +matrix_prometheus_postgres_exporter_enabled: false +matrix_prometheus_postgres_exporter_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'prometheus.pg.db') | to_uuid }}" + +matrix_prometheus_postgres_exporter_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + }} + +###################################################################### +# +# /matrix-prometheus-postgres-exporter +# +###################################################################### + +###################################################################### +# +# matrix-grafana +# +###################################################################### + +matrix_grafana_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach Grafana over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# Grafana's HTTP port to the local host. +matrix_grafana_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:3000' }}" + +matrix_grafana_dashboard_download_urls_all: | + {{ + matrix_grafana_dashboard_download_urls + + + (matrix_prometheus_postgres_exporter_dashboard_urls if matrix_prometheus_postgres_exporter_enabled else []) + }} + + +###################################################################### +# +# /matrix-grafana +# +###################################################################### + + + +###################################################################### +# +# matrix-registration +# +###################################################################### + +matrix_registration_enabled: false + +# Normally, matrix-nginx-proxy is enabled and nginx can reach matrix-registration over the container network. +# If matrix-nginx-proxy is not enabled, or you otherwise have a need for it, you can expose +# matrix-registration's HTTP port to the local host. +matrix_registration_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_enabled else '127.0.0.1:8767' }}" + +matrix_registration_riot_instance: "{{ ('https://' + matrix_server_fqn_element) if matrix_client_element_enabled else 'https://riot.im/app/' }}" + +matrix_registration_shared_secret: "{{ matrix_synapse_registration_shared_secret if matrix_synapse_enabled else '' }}" + +matrix_registration_server_location: "{{ matrix_homeserver_container_url }}" + +matrix_registration_api_validate_certs: "{{ false if matrix_ssl_retrieval_method == 'self-signed' else true }}" + +matrix_registration_container_image_self_build: "{{ matrix_architecture != 'amd64' }}" + +matrix_registration_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-postgres.service'] if matrix_postgres_enabled else []) + }} + +# Postgres is the default, except if not using `matrix_postgres` (internal postgres) +matrix_registration_database_engine: "{{ 'postgres' if matrix_postgres_enabled else 'sqlite' }}" +matrix_registration_database_password: "{{ matrix_synapse_macaroon_secret_key | password_hash('sha512', 'mx.registr.db') | to_uuid }}" + +###################################################################### +# +# /matrix-registration +# +###################################################################### + +###################################################################### +# +# matrix-postgres-backup +# +###################################################################### + +matrix_postgres_backup_connection_hostname: "{{ matrix_postgres_connection_hostname }}" +matrix_postgres_backup_connection_port: "{{ matrix_postgres_connection_port }}" +matrix_postgres_backup_connection_username: "{{ matrix_postgres_connection_username }}" +matrix_postgres_backup_connection_password: "{{ matrix_postgres_connection_password }}" + +matrix_postgres_backup_postgres_data_path: "{{ matrix_postgres_data_path if matrix_postgres_enabled else '' }}" + +# the default matrix synapse databse is not always part of the matrix_postgres_additional_databases variable thus we have to add it if the default database is used +matrix_postgres_backup_databases: | + {{ + (([{ + 'name': matrix_synapse_database_database + }] if (matrix_synapse_enabled and matrix_synapse_database_database == matrix_postgres_db_name and matrix_synapse_database_host == 'matrix-postgres') else []) + + + matrix_postgres_additional_databases)|map(attribute='name')|list + }} + +###################################################################### +# +# /matrix-postgres-backup +# +###################################################################### diff --git a/inventory/.gitkeep b/inventory/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/inventory/host_vars/.gitkeep b/inventory/host_vars/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/inventory/scripts/jitsi-generate-passwords.sh b/inventory/scripts/jitsi-generate-passwords.sh new file mode 100755 index 00000000..c48a0c2d --- /dev/null +++ b/inventory/scripts/jitsi-generate-passwords.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# This is a bash script for generating strong passwords for the Jitsi role in this ansible project: +# https://github.com/spantaleev/matrix-docker-ansible-deploy + +function generatePassword() { + openssl rand -hex 16 +} + +echo "# If this script fails, it's likely because you don't have the openssl tool installed." +echo "# Install it before using this script, or simply create your own passwords manually." + +echo "" + +JICOFO_AUTH_PASSWORD=$(generatePassword) +JVB_AUTH_PASSWORD=$(generatePassword) +JIBRI_RECORDER_PASSWORD=$(generatePassword) +JIBRI_XMPP_PASSWORD=$(generatePassword) + +echo "# Paste these variables into your inventory/host_vars/matrix.DOMAIN/vars.yml file:" +echo "" +echo "matrix_jitsi_jicofo_auth_password: "$JICOFO_AUTH_PASSWORD +echo "matrix_jitsi_jvb_auth_password: "$JVB_AUTH_PASSWORD +echo "matrix_jitsi_jibri_recorder_password: "$JIBRI_RECORDER_PASSWORD +echo "matrix_jitsi_jibri_xmpp_password: "$JIBRI_XMPP_PASSWORD diff --git a/roles/matrix-aux/defaults/main.yml b/roles/matrix-aux/defaults/main.yml new file mode 100644 index 00000000..e4a4e827 --- /dev/null +++ b/roles/matrix-aux/defaults/main.yml @@ -0,0 +1,72 @@ +--- + +# matrix-aux is a role that manages auxiliary files and directories on your Matrix server. +# +# Certain components (like matrix-synapse, etc.) may sometimes require additional templates (email templates, privacy policies, etc.). +# This role allows such files to be managed by the playbook. +# +# Note that files and directories created via this role are not automatically made available for containers to use. +# If you use this role to put files in a directory that's already mounted into a container, +# you can access the files without additional work. +# Otherwise, you'd need to mount the file/directory to the container that needs it. +# Roles usually provide a `matrix_*_additional_volumes` or `matrix_*_container_extra_arguments` variable +# that you can use to mount an additional volume. + +# The default permission mode when creating directories using `matrix_aux_directory_definitions` +matrix_aux_directory_default_mode: '0750' + +# Holds a list of directories to create on the server. +# +# By default, directories are: +# - created with permissions as specified in `matrix_aux_directory_default_mode` +# - owned by the `matrix_user_username` user and `matrix_user_groupname` group (usually `matrix:matrix`) +# +# Example: +# +# matrix_aux_directory_definitions: +# - dest: /matrix/aux +# +# - dest: /matrix/another +# mode: '0700' +# owner: 'some-user' +# group: 'some-group' +matrix_aux_directory_definitions: [] + +# The default permission mode when creating directories using `matrix_aux_directory_definitions` +matrix_aux_file_default_mode: '0640' + +# Holds a list of files to create on the server. +# +# By default, files are: +# - created with permissions as specified in `matrix_aux_file_default_mode` +# - owned by the `matrix_user_username` user and `matrix_user_groupname` group (usually `matrix:matrix`) +# +# You can define the file content inline (in your `vars.yml` file) or as an external file (see the example below). +# Defining the content inline in `vars.yml` has the benefit of not splitting your configuration into multiple files, +# but rather keeping everything inside `vars.yml` (which also gets backed up on the server in `/matrix/vars.yml`). +# +# Note: parent paths for files must exist. +# If you've defined a file with a destination of `/matrix/some/path/file.txt`, +# then you likely need to add `/matrix/some/path` to `matrix_aux_directory_definitions` as well. +# You don't need to do this for directories that the playbook already creates for you. +# +# Example: +# +# matrix_aux_file_definitions: +# - dest: "{{ matrix_synapse_config_dir_path }}/something.html" +# content: | +# +# Something +# +# - dest: /matrix/aux/some-other-file.txt +# content: "Something" +# mode: '0600' +# owner: 'some-user' +# group: 'some-group' +# +# - dest: /matrix/aux/yet-another-file.txt +# content: "{{ lookup('template', '/path/to/file.txt.j2') }}" +# mode: '0600' +# owner: 'some-user' +# group: 'some-group' +matrix_aux_file_definitions: [] diff --git a/roles/matrix-aux/tasks/main.yml b/roles/matrix-aux/tasks/main.yml new file mode 100644 index 00000000..ee93f63a --- /dev/null +++ b/roles/matrix-aux/tasks/main.yml @@ -0,0 +1,5 @@ +- import_tasks: "{{ role_path }}/tasks/setup.yml" + when: run_stop|bool + tags: + - setup-all + - setup-aux-files diff --git a/roles/matrix-aux/tasks/setup.yml b/roles/matrix-aux/tasks/setup.yml new file mode 100644 index 00000000..949c0b4a --- /dev/null +++ b/roles/matrix-aux/tasks/setup.yml @@ -0,0 +1,19 @@ +--- + +- name: Ensure AUX directories are created + file: + dest: "{{ item.dest }}" + state: directory + owner: "{{ item.owner|default(matrix_user_username) }}" + group: "{{ item.group|default(matrix_user_groupname) }}" + mode: "{{ item.mode|default(matrix_aux_directory_default_mode) }}" + with_items: "{{ matrix_aux_directory_definitions }}" + +- name: Ensure AUX files are created + copy: + dest: "{{ item.dest }}" + content: "{{ item.content }}" + owner: "{{ item.owner|default(matrix_user_username) }}" + group: "{{ item.group|default(matrix_user_groupname) }}" + mode: "{{ item.mode|default(matrix_aux_file_default_mode) }}" + with_items: "{{ matrix_aux_file_definitions }}" diff --git a/roles/matrix-awx/defaults/main.yml b/roles/matrix-awx/defaults/main.yml new file mode 100755 index 00000000..fb9f56ae --- /dev/null +++ b/roles/matrix-awx/defaults/main.yml @@ -0,0 +1 @@ +matrix_awx_enabled: true diff --git a/roles/matrix-awx/scripts/matrix_build_room_list.py b/roles/matrix-awx/scripts/matrix_build_room_list.py new file mode 100644 index 00000000..3abfcd8c --- /dev/null +++ b/roles/matrix-awx/scripts/matrix_build_room_list.py @@ -0,0 +1,28 @@ + +import sys +import requests +import json + +janitor_token = sys.argv[1] +synapse_container_ip = sys.argv[2] + +# collect total amount of rooms + +rooms_raw_url = 'http://' + synapse_container_ip + ':8008/_synapse/admin/v1/rooms' +rooms_raw_header = {'Authorization': 'Bearer ' + janitor_token} +rooms_raw = requests.get(rooms_raw_url, headers=rooms_raw_header) +rooms_raw_python = json.loads(rooms_raw.text) +total_rooms = rooms_raw_python["total_rooms"] + +# build complete room list file + +room_list_file = open("/tmp/room_list_complete.json", "w") + +for i in range(0, total_rooms, 100): + rooms_inc_url = 'http://' + synapse_container_ip + ':8008/_synapse/admin/v1/rooms?from=' + str(i) + rooms_inc = requests.get(rooms_inc_url, headers=rooms_raw_header) + room_list_file.write(rooms_inc.text) + +room_list_file.close() + +print(total_rooms) diff --git a/roles/matrix-awx/surveys/access_export.json.j2 b/roles/matrix-awx/surveys/access_export.json.j2 new file mode 100644 index 00000000..c20a9749 --- /dev/null +++ b/roles/matrix-awx/surveys/access_export.json.j2 @@ -0,0 +1,42 @@ +{ + "name": "Access Export", + "description": "Access the services export.", + "spec": [ + { + "question_name": "SFTP Authorisation Method", + "question_description": "Set whether you want to disable SFTP, use a password to connect to SFTP or connect with a more secure SSH key.", + "required": true, + "min": null, + "max": null, + "default": "{{ sftp_auth_method | string }}", + "choices": "Disabled\nPassword\nSSH Key", + "new_question": true, + "variable": "sftp_auth_method", + "type": "multiplechoice" + }, + { + "question_name": "SFTP Password", + "question_description": "Sets the password of the 'sftp' account, which allows you to upload a multi-file static website by SFTP, as well as export the latest copy of your Matrix service. Must be defined if 'Password' method is selected. WARNING: You must set a strong and unique password here.", + "required": false, + "min": 0, + "max": 64, + "default": "{{ sftp_password }}", + "choices": "", + "new_question": true, + "variable": "sftp_password", + "type": "password" + }, + { + "question_name": "SFTP Public SSH Key (More Secure)", + "question_description": "Sets the public SSH key used to access the 'sftp' account, which allows you to upload a multi-file static website by SFTP, as well as export the latest copy of your Matrix service. Must be defined if 'SSH Key' method is selected.", + "required": false, + "min": 0, + "max": 16384, + "default": "{{ sftp_public_key }}", + "choices": "", + "new_question": true, + "variable": "sftp_public_key", + "type": "text" + } + ] +} diff --git a/roles/matrix-awx/surveys/backup_server.json.j2 b/roles/matrix-awx/surveys/backup_server.json.j2 new file mode 100644 index 00000000..acb6e356 --- /dev/null +++ b/roles/matrix-awx/surveys/backup_server.json.j2 @@ -0,0 +1,18 @@ +{ + "name": "Backup Server", + "description": "Performs a backup of the entire service to a remote location.", + "spec": [ + { + "question_name": "Enable Backup", + "question_description": "Set if remote backup is enabled or not. If enabled a daily backup of your server will be sent to the backup server located in {{ backup_server_location }}.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_awx_backup_enabled | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_awx_backup_enabled", + "type": "multiplechoice" + } + ] +} diff --git a/roles/matrix-awx/surveys/configure_corporal.json.j2 b/roles/matrix-awx/surveys/configure_corporal.json.j2 new file mode 100755 index 00000000..14e417ce --- /dev/null +++ b/roles/matrix-awx/surveys/configure_corporal.json.j2 @@ -0,0 +1,88 @@ +{ + "name": "Configure Matrix Corporal", + "description": "Configure Matrix Corporal, a tool that manages your Matrix server according to a configuration policy.", + "spec": [ + { + "question_name": "Enable Corporal", + "question_description": "Controls if Matrix Corporal is enabled at all. If you're unsure if you need Matrix Corporal or not, you most likely don't.", + "required": true, + "min": null, + "max": null, + "default": "{{ matrix_corporal_enabled|string|lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_corporal_enabled", + "type": "multiplechoice" + }, + { + "question_name": "Corporal Policy Provider", + "question_description": "Controls what provider policy is used with Matrix Corporal.", + "required": true, + "min": null, + "max": null, + "default": "{{ matrix_corporal_policy_provider_mode }}", + "choices": "Simple Static File\nHTTP Pull Mode (API Enabled)\nHTTP Push Mode (API Enabled)", + "new_question": true, + "variable": "matrix_corporal_policy_provider_mode", + "type": "multiplechoice" + }, + { + "question_name": "Simple Static File Configuration", + "question_description": "The configuration file for Matrix Corporal, only needed if 'Simple Static File' provider is selected, any configuration entered here will be saved and applied.", + "required": false, + "min": 0, + "max": 65536, + "default": "", + "new_question": true, + "variable": "matrix_corporal_simple_static_config", + "type": "textarea" + }, + { + "question_name": "HTTP Pull Mode URI", + "question_description": "The network address to remotely fetch the configuration from. Only needed if 'HTTP Pull Mode (API Enabled)' provider is selected.", + "required": false, + "min": 0, + "max": 4096, + "default": "{{ matrix_corporal_pull_mode_uri }}", + "new_question": true, + "variable": "matrix_corporal_pull_mode_uri", + "type": "text" + }, + { + "question_name": "HTTP Pull Mode Authentication Token", + "question_description": "An authentication token for pulling the Corporal configuration from a network location. Only needed if 'HTTP Pull Mode (API Enabled)' provider is selected. WARNING: You must set a strong and unique password here.", + "required": false, + "min": 0, + "max": 256, + "default": "{{ matrix_corporal_pull_mode_token }}", + "choices": "", + "new_question": true, + "variable": "matrix_corporal_pull_mode_token", + "type": "password" + }, + { + "question_name": "Corporal API Authentication Token", + "question_description": "An authentication token for interfacing with Corporals API. Only needed to be set if 'HTTP Pull Mode (API Enabled)' or 'HTTP Push Mode (API Enabled)' provider is selected. WARNING: You must set a strong and unique password here.", + "required": false, + "min": 0, + "max": 256, + "default": "{{ matrix_corporal_http_api_auth_token }}", + "choices": "", + "new_question": true, + "variable": "matrix_corporal_http_api_auth_token", + "type": "password" + }, + { + "question_name": "Raise Synapse Ratelimits", + "question_description": "For Matrix Corporal to work you will need to temporarily raise the rate limits for logins, please return this value to 'Normal' after you're done using Corporal.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_corporal_raise_ratelimits }}", + "choices": "Normal\nRaised", + "new_question": true, + "variable": "matrix_corporal_raise_ratelimits", + "type": "multiplechoice" + } + ] +} diff --git a/roles/matrix-awx/surveys/configure_dimension.json.j2 b/roles/matrix-awx/surveys/configure_dimension.json.j2 new file mode 100644 index 00000000..2f39e80e --- /dev/null +++ b/roles/matrix-awx/surveys/configure_dimension.json.j2 @@ -0,0 +1,30 @@ +{ + "name": "Configure Dimension", + "description": "Configure Dimension, the self-hosted integrations server.", + "spec": [ + { + "question_name": "Enable Dimension", + "question_description": "Enables the Dimension integration server, before doing this you need to create a CNAME record for 'dimension.{{ matrix_domain }}' that points to 'matrix.{{ matrix_domain }}'.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_dimension_enabled | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_dimension_enabled", + "type": "multiplechoice" + }, + { + "question_name": "Dimension Users", + "question_description": "Here you can list the user accounts that will be able to configure Dimension. Entries must be seperated with newlines and must be a complete Matrix ID. For example: '@dimension:{{ matrix_domain }}'", + "required": false, + "min": 0, + "max": 65536, + "default": {{ ext_dimension_users_raw_final | to_json }}, + "choices": "", + "new_question": true, + "variable": "ext_dimension_users_raw", + "type": "textarea" + } + ] +} diff --git a/roles/matrix-awx/surveys/configure_element.json.j2 b/roles/matrix-awx/surveys/configure_element.json.j2 new file mode 100755 index 00000000..d85a0ee5 --- /dev/null +++ b/roles/matrix-awx/surveys/configure_element.json.j2 @@ -0,0 +1,66 @@ +{ + "name": "Configure Element", + "description": "Configure Element web client, Element is the most developed Matrix client software.", + "spec": [ + { + "question_name": "Enable Element-Web", + "question_description": "Set if Element web client is enabled or not.", + "required": true, + "min": null, + "max": null, + "default": "{{ matrix_client_element_enabled }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_client_element_enabled", + "type": "multiplechoice" + }, + { + "question_name": "Set Branding for Web Client", + "question_description": "Sets the 'branding' seen in the tab and on the welcome page to a custom value.", + "required": false, + "min": 0, + "max": 256, + "default": "{{ matrix_client_element_brand }}", + "choices": "", + "new_question": true, + "variable": "matrix_client_element_brand", + "type": "text" + }, + { + "question_name": "Set Theme for Web Client", + "question_description": "Sets the default theme for the web client, can be changed later by individual users.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_client_element_default_theme }}", + "choices": "light\ndark", + "new_question": true, + "variable": "matrix_client_element_default_theme", + "type": "multiplechoice" + }, + { + "question_name": "Set Welcome Page Background", + "question_description": "URL to Wallpaper, shown in background of the welcome page. Must be a 'https' link, otherwise it won't be set.", + "required": false, + "min": 0, + "max": 1024, + "default": "{{ matrix_client_element_branding_welcomeBackgroundUrl }}", + "choices": "", + "new_question": true, + "variable": "matrix_client_element_branding_welcomeBackgroundUrl", + "type": "text" + }, + { + "question_name": "Show Registration Button", + "question_description": "If you show the registration button on the welcome page.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_client_element_registration_enabled }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_client_element_registration_enabled", + "type": "multiplechoice" + } + ] +} diff --git a/roles/matrix-awx/surveys/configure_element_subdomain.json.j2 b/roles/matrix-awx/surveys/configure_element_subdomain.json.j2 new file mode 100644 index 00000000..a355af23 --- /dev/null +++ b/roles/matrix-awx/surveys/configure_element_subdomain.json.j2 @@ -0,0 +1,18 @@ +{ + "name": "Configure Element Subdomain", + "description": "Configure Element clients subdomain location. (Eg: 'element' for element.example.org)", + "spec": [ + { + "question_name": "Set Element Subdomain", + "question_description": "Sets the subdomain of the Element web-client, you should only specify the subdomain, not the base domain you've already set. (Eg: 'element' for element.example.org) Note that if you change this value you'll need to reconfigure your DNS.", + "required": false, + "min": 0, + "max": 2048, + "default": "{{ element_subdomain }}", + "choices": "", + "new_question": true, + "variable": "element_subdomain", + "type": "text" + } + ] +} diff --git a/roles/matrix-awx/surveys/configure_email_relay.json.j2 b/roles/matrix-awx/surveys/configure_email_relay.json.j2 new file mode 100644 index 00000000..65c21a94 --- /dev/null +++ b/roles/matrix-awx/surveys/configure_email_relay.json.j2 @@ -0,0 +1,19 @@ +{ + "name": "Configure Email Relay", + "description": "Enable MailGun relay to increase verification email reliability.", + "spec": [ + { + "question_name": "Enable Email Relay", + "question_description": "Enables the MailGun email relay server, enabling this will increase the reliability of your email verification.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_mailer_relay_use | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_mailer_relay_use", + "type": "multiplechoice" + } + ] +} + diff --git a/roles/matrix-awx/surveys/configure_jitsi.json.j2 b/roles/matrix-awx/surveys/configure_jitsi.json.j2 new file mode 100755 index 00000000..9cb3044d --- /dev/null +++ b/roles/matrix-awx/surveys/configure_jitsi.json.j2 @@ -0,0 +1,31 @@ +{ + "name": "Configure Jitsi", + "description": "Configure Jitsi conferencing settings.", + "spec": [ + { + "question_name": "Enable Jitsi", + "question_description": "Set if Jitsi is enabled or not. If disabled your server will use the https://jitsi.riot.im server. If you're on a smaller server disabling this might increase the performance of your Matrix service.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_jitsi_enabled }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_jitsi_enabled", + "type": "multiplechoice" + }, + { + "question_name": "Set Default Language", + "question_description": "2 digit 639-1 language code to adjust the language of the web client. For a list of possible codes see: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes", + "required": false, + "min": 0, + "max": 2, + "default": "{{ matrix_jitsi_web_config_defaultLanguage }}", + "choices": "", + "new_question": true, + "variable": "matrix_jitsi_web_config_defaultLanguage", + "type": "text" + } + ] +} + diff --git a/roles/matrix-awx/surveys/configure_ma1sd.json.j2 b/roles/matrix-awx/surveys/configure_ma1sd.json.j2 new file mode 100644 index 00000000..67c2c88d --- /dev/null +++ b/roles/matrix-awx/surveys/configure_ma1sd.json.j2 @@ -0,0 +1,41 @@ +{ + "name": "Configure ma1sd", + "description": "Configure ma1sd settings, ma1sd is a self-hosted identity server for Matrix.", + "spec": [ + { + "question_name": "Enable ma1sd", + "question_description": "Set if ma1sd is enabled or not. If disabled your server will loose identity functionality (not recommended).", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_ma1sd_enabled | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_ma1sd_enabled", + "type": "multiplechoice" + }, + { + "question_name": "ma1sd Authentication Mode", + "question_description": "Set the source of user account authentication credentials with the ma1sd.", + "required": false, + "min": null, + "max": null, + "default": "{{ ext_matrix_ma1sd_auth_store }}", + "choices": "Synapse Internal\nLDAP/AD", + "new_question": true, + "variable": "ext_matrix_ma1sd_auth_store", + "type": "multiplechoice" + }, + { + "question_name": "LDAP/AD Configuration", + "question_description": "Settings for connecting LDAP/AD to the ma1sd service. (ignored if using Synapse Internal, see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/README.md )", + "required": false, + "min": 0, + "max": 65536, + "default": {{ ext_matrix_ma1sd_configuration_extension_yaml | to_json }}, + "new_question": true, + "variable": "ext_matrix_ma1sd_configuration_extension_yaml", + "type": "textarea" + } + ] +} diff --git a/roles/matrix-awx/surveys/configure_synapse.json.j2 b/roles/matrix-awx/surveys/configure_synapse.json.j2 new file mode 100755 index 00000000..7089f7b3 --- /dev/null +++ b/roles/matrix-awx/surveys/configure_synapse.json.j2 @@ -0,0 +1,198 @@ +{ + "name": "Configure Synapse", + "description": "Configure Synapse settings. Synapse is the homeserver software that powers your Matrix instance.", + "spec": [ + { + "question_name": "Enable Public Registration", + "question_description": "Controls whether people with access to the homeserver can register by themselves.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_synapse_enable_registration | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_synapse_enable_registration", + "type": "multiplechoice" + }, + { + "question_name": "Enable Federation", + "question_description": "Controls whether Synapse will federate at all. Disable this to completely isolate your server from the rest of the Matrix network.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_synapse_federation_enabled | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_synapse_federation_enabled", + "type": "multiplechoice" + }, + { + "question_name": "Allow Public Rooms Over Federation", + "question_description": "Controls whether remote servers can fetch this server's public rooms directory via federation. For private servers, you'll most likely want to forbid this.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_synapse_allow_public_rooms_over_federation | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_synapse_allow_public_rooms_over_federation", + "type": "multiplechoice" + }, + { + "question_name": "Enable Community Creation", + "question_description": "Allows regular users (who aren't server admins) to create 'communities', which are basically groups of rooms.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_synapse_enable_group_creation | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_synapse_enable_group_creation", + "type": "multiplechoice" + }, + { + "question_name": "Enable Synapse Presence", + "question_description": "Controls whether presence is enabled. This shows who's online and reading your posts. Disabling it will increase both performance and user privacy.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_synapse_presence_enabled | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_synapse_presence_enabled", + "type": "multiplechoice" + }, + { + "question_name": "Enable URL Previews", + "question_description": "Controls whether URL previews should be generated. This will cause a request from Synapse to URLs shared by users.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_synapse_url_preview_enabled | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_synapse_url_preview_enabled", + "type": "multiplechoice" + }, + { + "question_name": "Enable Guest Access", + "question_description": "Controls whether 'guest accounts' can access rooms without registering. Guest users do not count towards your servers user limit.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_synapse_allow_guest_access | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_synapse_allow_guest_access", + "type": "multiplechoice" + }, + { + "question_name": "Registration Requires Email", + "question_description": "Controls whether an email address is required to register on the server.", + "required": false, + "min": null, + "max": null, + "default": "{{ ext_registrations_require_3pid | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "ext_registrations_require_3pid", + "type": "multiplechoice" + }, + { + "question_name": "Registration Shared Secret", + "question_description": "A secret that allows registration of standard or admin accounts by anyone who has the shared secret, even if registration is otherwise disabled. WARNING: You must set a strong and unique password here.", + "required": false, + "min": 0, + "max": 256, + "default": "", + "choices": "", + "new_question": true, + "variable": "ext_matrix_synapse_registration_shared_secret", + "type": "password" + }, + { + "question_name": "Synapse Max Upload Size", + "question_description": "Sets the maximum size for uploaded files in MB.", + "required": false, + "min": 0, + "max": 3, + "default": "{{ matrix_synapse_max_upload_size_mb }}", + "choices": "", + "new_question": true, + "variable": "matrix_synapse_max_upload_size_mb_raw", + "type": "text" + }, + { + "question_name": "URL Preview Languages", + "question_description": "Sets the languages that URL previews will be generated in. Entries are a 2-3 letter IETF language tag, they must be seperated with newlines. For example: 'fr' https://en.wikipedia.org/wiki/IETF_language_tag", + "required": false, + "min": 0, + "max": 65536, + "default": {{ ext_url_preview_accept_language_default | to_json }}, + "choices": "", + "new_question": true, + "variable": "ext_url_preview_accept_language_raw", + "type": "textarea" + }, + { + "question_name": "Federation Whitelist", + "question_description": "Here you can list the URLs of other Matrix homeservers and Synapse will only federate with those homeservers. Entries must be seperated with newlines and must not have a 'https://' prefix. For example: 'matrix.example.org'", + "required": false, + "min": 0, + "max": 65536, + "default": {{ ext_federation_whitelist_raw | to_json }}, + "choices": "", + "new_question": true, + "variable": "ext_federation_whitelist_raw", + "type": "textarea" + }, + { + "question_name": "Synapse Auto-Join Rooms", + "question_description": "Sets the 'auto-join' rooms, where new users will be automatically invited to, these rooms must already exist. Entries must be room addresses that are separated with newlines. For example: '#announcements:example.org'", + "required": false, + "min": 0, + "max": 65536, + "default": {{ matrix_synapse_auto_join_rooms_raw | to_json }}, + "choices": "", + "new_question": true, + "variable": "matrix_synapse_auto_join_rooms_raw", + "type": "textarea" + }, + { + "question_name": "Enable ReCaptcha on Registration", + "question_description": "Enables Googles ReCaptcha verification for registering an account, recommended for public servers.", + "required": false, + "min": null, + "max": null, + "default": "{{ ext_enable_registration_captcha | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "ext_enable_registration_captcha", + "type": "multiplechoice" + }, + { + "question_name": "Recaptcha Public Key", + "question_description": "Sets the Google ReCaptcha public key for this website.", + "required": false, + "min": 0, + "max": 40, + "default": "{{ ext_recaptcha_public_key }}", + "choices": "", + "new_question": true, + "variable": "ext_recaptcha_public_key", + "type": "text" + }, + { + "question_name": "Recaptcha Private Key", + "question_description": "Sets the Google ReCaptcha private key for this website.", + "required": false, + "min": 0, + "max": 40, + "default": "{{ ext_recaptcha_private_key }}", + "choices": "", + "new_question": true, + "variable": "ext_recaptcha_private_key", + "type": "text" + } + ] +} diff --git a/roles/matrix-awx/surveys/configure_synapse_admin.json.j2 b/roles/matrix-awx/surveys/configure_synapse_admin.json.j2 new file mode 100644 index 00000000..8845b83a --- /dev/null +++ b/roles/matrix-awx/surveys/configure_synapse_admin.json.j2 @@ -0,0 +1,18 @@ +{ + "name": "Configure Synapse Admin", + "description": "Configure 'Synapse Admin', a moderation tool to help you manage your server.", + "spec": [ + { + "question_name": "Enable Synapse Admin", + "question_description": "Set if Synapse Admin is enabled or not. If enabled you can access it at https://{{ matrix_server_fqn_matrix }}/synapse-admin.", + "required": false, + "min": null, + "max": null, + "default": "{{ matrix_synapse_admin_enabled | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "matrix_synapse_admin_enabled", + "type": "multiplechoice" + } + ] +} diff --git a/roles/matrix-awx/surveys/configure_website_access_export.json.j2 b/roles/matrix-awx/surveys/configure_website_access_export.json.j2 new file mode 100755 index 00000000..2b3e1637 --- /dev/null +++ b/roles/matrix-awx/surveys/configure_website_access_export.json.j2 @@ -0,0 +1,54 @@ +{ + "name": "Configure Website Access Backup", + "description": "Configure base domain website settings and access the services backup.", + "spec": [ + { + "question_name": "Customise Base Domain Website", + "question_description": "Set if you want to adjust the base domain website using SFTP.", + "required": true, + "min": null, + "max": null, + "default": "{{ customise_base_domain_website | string | lower }}", + "choices": "true\nfalse", + "new_question": true, + "variable": "customise_base_domain_website", + "type": "multiplechoice" + }, + { + "question_name": "SFTP Authorisation Method", + "question_description": "Set whether you want to disable SFTP, use a password to connect to SFTP or connect with a more secure SSH key.", + "required": true, + "min": null, + "max": null, + "default": "{{ sftp_auth_method | string }}", + "choices": "Disabled\nPassword\nSSH Key", + "new_question": true, + "variable": "sftp_auth_method", + "type": "multiplechoice" + }, + { + "question_name": "SFTP Password", + "question_description": "Sets the password of the 'sftp' account, which allows you to upload a multi-file static website by SFTP, as well as export the latest copy of your Matrix service. Must be defined if 'Password' method is selected. WARNING: You must set a strong and unique password here.", + "required": false, + "min": 0, + "max": 64, + "default": "{{ sftp_password }}", + "choices": "", + "new_question": true, + "variable": "sftp_password", + "type": "password" + }, + { + "question_name": "SFTP Public SSH Key (More Secure)", + "question_description": "Sets the public SSH key used to access the 'sftp' account, which allows you to upload a multi-file static website by SFTP, as well as export the latest copy of your Matrix service. Must be defined if 'SSH Key' method is selected.", + "required": false, + "min": 0, + "max": 16384, + "default": "{{ sftp_public_key }}", + "choices": "", + "new_question": true, + "variable": "sftp_public_key", + "type": "text" + } + ] +} diff --git a/roles/matrix-awx/tasks/backup_server.yml b/roles/matrix-awx/tasks/backup_server.yml new file mode 100644 index 00000000..b7a82b96 --- /dev/null +++ b/roles/matrix-awx/tasks/backup_server.yml @@ -0,0 +1,99 @@ + +- name: Record Backup Server variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# AWX Settings Start' + with_dict: + 'matrix_awx_backup_enabled': '{{ matrix_awx_backup_enabled }}' + tags: use-survey + +- name: Save new 'Backup Server' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/backup_server.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/backup_server.json' + tags: use-survey + +- name: Copy new 'Backup Server' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/backup_server.json' + dest: '/matrix/awx/backup_server.json' + mode: '0660' + tags: use-survey + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + tags: use-survey + +- name: Recreate 'Backup Server' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 0 - Backup Server" + description: "Performs a backup of the entire service to a remote location." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "backup-server,use-survey" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/backup_server.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + tags: use-survey + +- name: Include vars in matrix_vars.yml + include_vars: + file: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + no_log: True + +- name: Copy new 'matrix_vars.yml' to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + dest: '/matrix/awx/matrix_vars.yml' + mode: '0660' + tags: use-survey + +- name: Run initial backup of /matrix/ and snapshot the database simultaneously + command: "{{ item }}" + with_items: + - borgmatic -c /root/.config/borgmatic/config_1.yaml + - /bin/sh /usr/local/bin/awx-export-service.sh 1 0 + register: _create_instances + async: 3600 # Maximum runtime in seconds. + poll: 0 # Fire and continue (never poll) + when: matrix_awx_backup_enabled|bool + +- name: Wait for both of these jobs to finish + async_status: + jid: "{{ item.ansible_job_id }}" + register: _jobs + until: _jobs.finished + delay: 5 # Check every 5 seconds. + retries: 720 # Retry for a full hour. + with_items: "{{ _create_instances.results }}" + when: matrix_awx_backup_enabled|bool + +- name: Perform borg backup of postgres dump + command: borgmatic -c /root/.config/borgmatic/config_2.yaml + when: matrix_awx_backup_enabled|bool + +- name: Set boolean value to exit playbook + set_fact: + end_playbook: true + +- name: End playbook if this task list is called. + meta: end_play + when: end_playbook is defined and end_playbook|bool diff --git a/roles/matrix-awx/tasks/cache_matrix_variables.yml b/roles/matrix-awx/tasks/cache_matrix_variables.yml new file mode 100644 index 00000000..a34b3792 --- /dev/null +++ b/roles/matrix-awx/tasks/cache_matrix_variables.yml @@ -0,0 +1,12 @@ + +- name: Collect current datetime + set_fact: + awx_datetime: "{{ lookup('pipe', 'date +%Y-%m-%d_%H:%M') }}" + +- name: Create cached matrix_vars.yml file location + set_fact: + awx_cached_matrix_vars: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars_{{ awx_datetime }}.yml' + +- name: Create cached matrix_vars.yml + delegate_to: 127.0.0.1 + shell: "cp /var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml {{ awx_cached_matrix_vars }}" diff --git a/roles/matrix-awx/tasks/create_user.yml b/roles/matrix-awx/tasks/create_user.yml new file mode 100755 index 00000000..13a30596 --- /dev/null +++ b/roles/matrix-awx/tasks/create_user.yml @@ -0,0 +1,31 @@ +# +# Create user and define if they are admin +# +# /usr/local/bin/matrix-synapse-register-user +# + +- name: Set admin bool to zero + set_fact: + admin_bool: 0 + when: admin_access == 'false' + +- name: Examine if server admin set + set_fact: + admin_bool: 1 + when: admin_access == 'true' + +- name: Set boolean value to exit playbook + set_fact: + end_playbook: true + +- name: Create user account + command: | + /usr/local/bin/matrix-synapse-register-user {{ new_username | quote }} {{ new_password | quote }} {{ admin_bool }} + register: cmd + +- name: Result + debug: msg="{{ cmd.stdout }}" + +- name: End playbook if this task list is called. + meta: end_play + when: end_playbook is defined and end_playbook|bool diff --git a/roles/matrix-awx/tasks/customise_website_access_export.yml b/roles/matrix-awx/tasks/customise_website_access_export.yml new file mode 100755 index 00000000..d4f48f42 --- /dev/null +++ b/roles/matrix-awx/tasks/customise_website_access_export.yml @@ -0,0 +1,273 @@ + +- name: Enable index.html creation if user doesn't wish to customise base domain + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Base Domain Settings Start' + with_dict: + 'matrix_nginx_proxy_base_domain_homepage_enabled': 'true' + when: (customise_base_domain_website is defined) and not customise_base_domain_website|bool + +- name: Disable index.html creation to allow multi-file site if user does wish to customise base domain + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Base Domain Settings Start' + with_dict: + 'matrix_nginx_proxy_base_domain_homepage_enabled': 'false' + when: (customise_base_domain_website is defined) and customise_base_domain_website|bool + +- name: Record custom 'Customise Website + Access Export' variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Custom Settings Start' + with_dict: + 'sftp_auth_method': '"{{ sftp_auth_method }}"' + 'sftp_password': '"{{ sftp_password }}"' + 'sftp_public_key': '"{{ sftp_public_key }}"' + +- name: Record custom 'Customise Website + Access Export' variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Custom Settings Start' + with_dict: + 'customise_base_domain_website': '{{ customise_base_domain_website }}' + when: customise_base_domain_website is defined + +- name: Reload vars in matrix_vars.yml + include_vars: + file: '{{ awx_cached_matrix_vars }}' + no_log: True + +- name: Save new 'Customise Website + Access Export' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: './roles/matrix-awx/surveys/configure_website_access_export.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_website_access_export.json' + when: customise_base_domain_website is defined + +- name: Copy new 'Customise Website + Access Export' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_website_access_export.json' + dest: '/matrix/awx/configure_website_access_export.json' + mode: '0660' + when: customise_base_domain_website is defined + +- name: Save new 'Customise Website + Access Export' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: './roles/matrix-awx/surveys/access_export.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/access_export.json' + when: customise_base_domain_website is undefined + +- name: Copy new 'Customise Website + Access Export' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/access_export.json' + dest: '/matrix/awx/access_export.json' + mode: '0660' + when: customise_base_domain_website is undefined + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Website + Access Export' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Website + Access Export" + description: "Configure base domain website settings and access the servers export." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-nginx-proxy" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_website_access_export.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + when: customise_base_domain_website is defined + +- name: Recreate 'Access Export' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Access Export" + description: "Access the services export." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-nginx-proxy" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/access_export.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + when: customise_base_domain_website is undefined + +- name: If user doesn't define a sftp_password, create a disabled 'sftp' account + user: + name: sftp + comment: SFTP user to set custom web files and access servers export + shell: /bin/false + home: /home/sftp + group: matrix + password: '*' + update_password: always + when: sftp_password|length == 0 + +- name: If user defines sftp_password, enable account and set password on 'stfp' account + user: + name: sftp + comment: SFTP user to set custom web files and access servers export + shell: /bin/false + home: /home/sftp + group: matrix + password: "{{ sftp_password | password_hash('sha512') }}" + update_password: always + when: sftp_password|length > 0 + +- name: Ensure group "sftp" exists + group: + name: sftp + state: present + +- name: adding existing user 'sftp' to group matrix + user: + name: sftp + groups: sftp + append: yes + when: customise_base_domain_website is defined + +- name: Create the ro /chroot directory with sticky bit if it doesn't exist. (/chroot/website has matrix:matrix permissions and is mounted to nginx container) + file: + path: /chroot + state: directory + owner: root + group: root + mode: '1755' + +- name: Ensure /chroot/website location exists. + file: + path: /chroot/website + state: directory + owner: matrix + group: matrix + mode: '0770' + when: customise_base_domain_website is defined + +- name: Ensure /chroot/export location exists + file: + path: /chroot/export + state: directory + owner: sftp + group: sftp + mode: '0700' + +- name: Ensure /home/sftp/.ssh location exists + file: + path: /home/sftp/.ssh + state: directory + owner: sftp + group: sftp + mode: '0700' + +- name: Ensure /home/sftp/authorized_keys exists + file: + path: /home/sftp/.ssh/authorized_keys + state: touch + owner: sftp + group: sftp + mode: '0644' + +- name: Clear authorized_keys file + shell: echo "" > /home/sftp/.ssh/authorized_keys + +- name: Insert public SSH key into authorized_keys file + lineinfile: + path: /home/sftp/.ssh/authorized_keys + line: "{{ sftp_public_key }}" + owner: sftp + group: sftp + mode: '0644' + when: (sftp_public_key | length > 0) and (sftp_auth_method == "SSH Key") + +- name: Remove any existing Subsystem lines + lineinfile: + path: /etc/ssh/sshd_config + state: absent + regexp: '^Subsystem' + +- name: Set SSH Subsystem State + lineinfile: + path: /etc/ssh/sshd_config + insertafter: "^# override default of no subsystems" + line: "Subsystem sftp internal-sftp" + +- name: Add SSH Match User section for disabled auth + blockinfile: + path: /etc/ssh/sshd_config + state: absent + block: | + Match User sftp + ChrootDirectory /chroot + PermitTunnel no + X11Forwarding no + AllowTcpForwarding no + PasswordAuthentication yes + AuthorizedKeysFile /home/sftp/.ssh/authorized_keys + when: sftp_auth_method == "Disabled" + +- name: Add SSH Match User section for password auth + blockinfile: + path: /etc/ssh/sshd_config + state: present + block: | + Match User sftp + ChrootDirectory /chroot + PermitTunnel no + X11Forwarding no + AllowTcpForwarding no + PasswordAuthentication yes + when: sftp_auth_method == "Password" + +- name: Add SSH Match User section for publickey auth + blockinfile: + path: /etc/ssh/sshd_config + state: present + block: | + Match User sftp + ChrootDirectory /chroot + PermitTunnel no + X11Forwarding no + AllowTcpForwarding no + AuthorizedKeysFile /home/sftp/.ssh/authorized_keys + when: sftp_auth_method == "SSH Key" + +- name: Restart service ssh.service + service: + name: ssh.service + state: restarted diff --git a/roles/matrix-awx/tasks/export_server.yml b/roles/matrix-awx/tasks/export_server.yml new file mode 100644 index 00000000..c70b0beb --- /dev/null +++ b/roles/matrix-awx/tasks/export_server.yml @@ -0,0 +1,33 @@ + +- name: Run export of /matrix/ and snapshot the database simultaneously + command: "{{ item }}" + with_items: + - /bin/sh /usr/local/bin/awx-export-service.sh 1 0 + - /bin/sh /usr/local/bin/awx-export-service.sh 0 1 + register: _create_instances + async: 3600 # Maximum runtime in seconds. + poll: 0 # Fire and continue (never poll) + +- name: Wait for both of these jobs to finish + async_status: + jid: "{{ item.ansible_job_id }}" + register: _jobs + until: _jobs.finished + delay: 5 # Check every 5 seconds. + retries: 720 # Retry for a full hour. + with_items: "{{ _create_instances.results }}" + +- name: Schedule deletion of the export in 24 hours + at: + command: rm /chroot/export/matrix* + count: 1 + units: days + unique: yes + +- name: Set boolean value to exit playbook + set_fact: + end_playbook: true + +- name: End playbook if this task list is called. + meta: end_play + when: end_playbook is defined and end_playbook|bool diff --git a/roles/matrix-awx/tasks/import_awx.yml b/roles/matrix-awx/tasks/import_awx.yml new file mode 100644 index 00000000..d9c3ca6f --- /dev/null +++ b/roles/matrix-awx/tasks/import_awx.yml @@ -0,0 +1,7 @@ + +- name: Ensure correct ownership of /matrix/awx + shell: chown -R matrix:matrix /matrix/awx + +- name: Ensure correct ownership of /matrix/synapse + shell: chown -R matrix:matrix /matrix/synapse + diff --git a/roles/matrix-awx/tasks/load_hosting_and_org_variables.yml b/roles/matrix-awx/tasks/load_hosting_and_org_variables.yml new file mode 100644 index 00000000..ea866254 --- /dev/null +++ b/roles/matrix-awx/tasks/load_hosting_and_org_variables.yml @@ -0,0 +1,11 @@ + +- name: Include vars in organisation.yml + include_vars: + file: '/var/lib/awx/projects/clients/{{ member_id }}/organisation.yml' + no_log: True + +- name: Include vars in hosting_vars.yml + include_vars: + file: '/var/lib/awx/projects/hosting/hosting_vars.yml' + no_log: True + diff --git a/roles/matrix-awx/tasks/load_matrix_variables.yml b/roles/matrix-awx/tasks/load_matrix_variables.yml new file mode 100755 index 00000000..2a9f9a0d --- /dev/null +++ b/roles/matrix-awx/tasks/load_matrix_variables.yml @@ -0,0 +1,15 @@ + +- name: Include new vars in matrix_vars.yml + include_vars: + file: '{{ awx_cached_matrix_vars }}' + no_log: True + +- name: If include_vars succeeds overwrite the old matrix_vars.yml + delegate_to: 127.0.0.1 + shell: "cp {{ awx_cached_matrix_vars }} /var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml && rm {{ awx_cached_matrix_vars }}" + +- name: Copy new 'matrix_vars.yml' to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + dest: '/matrix/awx/matrix_vars.yml' + mode: '0660' diff --git a/roles/matrix-awx/tasks/main.yml b/roles/matrix-awx/tasks/main.yml new file mode 100755 index 00000000..6e192ce0 --- /dev/null +++ b/roles/matrix-awx/tasks/main.yml @@ -0,0 +1,189 @@ + +# Load initial hosting and organisation variables from AWX volume +- include_tasks: + file: "load_hosting_and_org_variables.yml" + apply: + tags: always + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - always + +# Renames the variables if needed +- include_tasks: + file: "rename_variables.yml" + apply: + tags: always + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - always + +# Perform a backup of the server +- include_tasks: + file: "backup_server.yml" + apply: + tags: backup-server + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - backup-server + +# Perform a export of the server +- include_tasks: + file: "export_server.yml" + apply: + tags: export-server + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - export-server + +# Create a user account if called +- include_tasks: + file: "create_user.yml" + apply: + tags: create-user + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - create-user + +# Purge local/remote media if called +- include_tasks: + file: "purge_media_main.yml" + apply: + tags: purge-media + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - purge-media + +# Purge Synapse database if called +- include_tasks: + file: "purge_database_main.yml" + apply: + tags: purge-database + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - purge-database + +# Import configs, media repo from /chroot/backup import +- include_tasks: + file: "import_awx.yml" + apply: + tags: import-awx + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - import-awx + +# Perform extra self-check functions +- include_tasks: + file: "self_check.yml" + apply: + tags: self-check + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - self-check + +# Create cached matrix_vars.yml file +- include_tasks: + file: "cache_matrix_variables.yml" + apply: + tags: always + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - always + +# Configure SFTP so user can upload a static website or access the servers export +- include_tasks: + file: "customise_website_access_export.yml" + apply: + tags: setup-nginx-proxy + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-nginx-proxy + +# Additional playbook to set the variable file during Element configuration +- include_tasks: + file: "set_variables_element.yml" + apply: + tags: setup-client-element + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-client-element + +# Additional playbook to set the variable file during Mailer configuration +- include_tasks: + file: "set_variables_mailer.yml" + apply: + tags: setup-mailer + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-mailer + +# Additional playbook to set the variable file during Element configuration +- include_tasks: + file: "set_variables_element_subdomain.yml" + apply: + tags: setup-client-element-subdomain + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-client-element-subdomain + +# Additional playbook to set the variable file during Synapse configuration +- include_tasks: + file: "set_variables_synapse.yml" + apply: + tags: setup-synapse + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-synapse + +# Additional playbook to set the variable file during Jitsi configuration +- include_tasks: + file: "set_variables_jitsi.yml" + apply: + tags: setup-jitsi + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-jitsi + +# Additional playbook to set the variable file during Ma1sd configuration +- include_tasks: + file: "set_variables_ma1sd.yml" + apply: + tags: setup-ma1sd + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-ma1sd + +# Additional playbook to set the variable file during Corporal configuration +- include_tasks: + file: "set_variables_corporal.yml" + apply: + tags: setup-corporal + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-corporal + +# Additional playbook to set the variable file during Dimension configuration +- include_tasks: + file: "set_variables_dimension.yml" + apply: + tags: setup-dimension + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-dimension + +# Additional playbook to set the variable file during Synapse Admin configuration +- include_tasks: + file: "set_variables_synapse_admin.yml" + apply: + tags: setup-synapse-admin + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - setup-synapse-admin + +# Load newly formed matrix variables from AWX volume +- include_tasks: + file: "load_matrix_variables.yml" + apply: + tags: always + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - always diff --git a/roles/matrix-awx/tasks/purge_database_build_list.yml b/roles/matrix-awx/tasks/purge_database_build_list.yml new file mode 100644 index 00000000..1ea05b7f --- /dev/null +++ b/roles/matrix-awx/tasks/purge_database_build_list.yml @@ -0,0 +1,10 @@ + +- name: Collect entire room list into stdout + shell: | + curl -X GET --header "Authorization: Bearer {{ janitors_token.stdout[1:-1] }}" '{{ synapse_container_ip.stdout }}:8008/_synapse/admin/v1/rooms?from={{ item }}' + register: rooms_output + +- name: Print stdout to file + delegate_to: 127.0.0.1 + shell: | + echo '{{ rooms_output.stdout }}' >> /tmp/{{ subscription_id }}_room_list_complete.json diff --git a/roles/matrix-awx/tasks/purge_database_events.yml b/roles/matrix-awx/tasks/purge_database_events.yml new file mode 100644 index 00000000..9e2ef9c2 --- /dev/null +++ b/roles/matrix-awx/tasks/purge_database_events.yml @@ -0,0 +1,13 @@ + +- name: Purge all rooms with more then N events + shell: | + curl --header "Authorization: Bearer {{ janitors_token.stdout[1:-1] }}" -X POST -H "Content-Type: application/json" -d '{ "delete_local_events": false, "purge_up_to_ts": {{ purge_epoche_time.stdout }}000 }' "{{ synapse_container_ip.stdout }}:8008/_synapse/admin/v1/purge_history/{{ item[1:-1] }}" + register: purge_command + +- name: Print output of purge command + debug: + msg: "{{ purge_command.stdout }}" + +- name: Pause for 5 seconds to let Synapse breathe + pause: + seconds: 5 diff --git a/roles/matrix-awx/tasks/purge_database_main.yml b/roles/matrix-awx/tasks/purge_database_main.yml new file mode 100644 index 00000000..76a437e1 --- /dev/null +++ b/roles/matrix-awx/tasks/purge_database_main.yml @@ -0,0 +1,317 @@ + +- name: Ensure dateutils and curl is installed in AWX + delegate_to: 127.0.0.1 + yum: + name: dateutils + state: latest + +- name: Ensure dateutils, curl and jq intalled on target machine + apt: + pkg: + - curl + - jq + state: present + +- name: Include vars in matrix_vars.yml + include_vars: + file: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + no_log: True + +- name: Collect before shrink size of Synapse database + shell: du -sh /matrix/postgres/data + register: db_size_before_stat + when: (purge_mode.find("Perform final shrink") != -1) + no_log: True + +- name: Collect the internal IP of the matrix-synapse container + shell: "/usr/bin/docker inspect --format '{''{range.NetworkSettings.Networks}''}{''{.IPAddress}''}{''{end}''}' matrix-synapse" + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + register: synapse_container_ip + +- name: Collect access token for janitor user + shell: | + curl -X POST -d '{"type":"m.login.password", "user":"janitor", "password":"{{ matrix_awx_janitor_user_password }}"}' "{{ synapse_container_ip.stdout }}:8008/_matrix/client/r0/login" | jq '.access_token' + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + register: janitors_token + no_log: True + +- name: Copy build_room_list.py script to target machine + copy: + src: ./roles/matrix-awx/scripts/matrix_build_room_list.py + dest: /usr/local/bin/matrix_build_room_list.py + owner: matrix + group: matrix + mode: '0755' + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Run build_room_list.py script + shell: | + runuser -u matrix -- python3 /usr/local/bin/matrix_build_room_list.py {{ janitors_token.stdout[1:-1] }} {{ synapse_container_ip.stdout }} + register: rooms_total + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Fetch complete room list from target machine + fetch: + src: /tmp/room_list_complete.json + dest: "/tmp/{{ subscription_id }}_room_list_complete.json" + flat: yes + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Remove complete room list from target machine + file: + path: /tmp/room_list_complete.json + state: absent + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Generate list of rooms with no local users + delegate_to: 127.0.0.1 + shell: | + jq 'try .rooms[] | select(.joined_local_members == 0) | .room_id' < /tmp/{{ subscription_id }}_room_list_complete.json > /tmp/{{ subscription_id }}_room_list_no_local_users.txt + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Count number of rooms with no local users + delegate_to: 127.0.0.1 + shell: | + wc -l /tmp/{{ subscription_id }}_room_list_no_local_users.txt | awk '{ print $1 }' + register: rooms_no_local_total + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Setting host fact room_list_no_local_users + set_fact: + room_list_no_local_users: "{{ lookup('file', '/tmp/{{ subscription_id }}_room_list_no_local_users.txt') }}" + no_log: True + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Purge all rooms with no local users + include_tasks: purge_database_no_local.yml + loop: "{{ room_list_no_local_users.splitlines() | flatten(levels=1) }}" + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Collect epoche time from date + delegate_to: 127.0.0.1 + shell: | + date -d '{{ purge_date }}' +"%s" + when: (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + register: purge_epoche_time + +- name: Generate list of rooms with more then N users + delegate_to: 127.0.0.1 + shell: | + jq 'try .rooms[] | select(.joined_members > {{ purge_metric_value }}) | .room_id' < /tmp/{{ subscription_id }}_room_list_complete.json > /tmp/{{ subscription_id }}_room_list_joined_members.txt + when: purge_mode.find("Number of users [slower]") != -1 + +- name: Count number of rooms with more then N users + delegate_to: 127.0.0.1 + shell: | + wc -l /tmp/{{ subscription_id }}_room_list_joined_members.txt | awk '{ print $1 }' + register: rooms_join_members_total + when: purge_mode.find("Number of users [slower]") != -1 + +- name: Setting host fact room_list_joined_members + delegate_to: 127.0.0.1 + set_fact: + room_list_joined_members: "{{ lookup('file', '/tmp/{{ subscription_id }}_room_list_joined_members.txt') }}" + when: purge_mode.find("Number of users [slower]") != -1 + no_log: True + +- name: Purge all rooms with more then N users + include_tasks: purge_database_users.yml + loop: "{{ room_list_joined_members.splitlines() | flatten(levels=1) }}" + when: purge_mode.find("Number of users [slower]") != -1 + +- name: Generate list of rooms with more then N events + delegate_to: 127.0.0.1 + shell: | + jq 'try .rooms[] | select(.state_events > {{ purge_metric_value }}) | .room_id' < /tmp/{{ subscription_id }}_room_list_complete.json > /tmp/{{ subscription_id }}_room_list_state_events.txt + when: purge_mode.find("Number of events [slower]") != -1 + +- name: Count number of rooms with more then N events + delegate_to: 127.0.0.1 + shell: | + wc -l /tmp/{{ subscription_id }}_room_list_state_events.txt | awk '{ print $1 }' + register: rooms_state_events_total + when: purge_mode.find("Number of events [slower]") != -1 + +- name: Setting host fact room_list_state_events + delegate_to: 127.0.0.1 + set_fact: + room_list_state_events: "{{ lookup('file', '/tmp/{{ subscription_id }}_room_list_state_events.txt') }}" + when: purge_mode.find("Number of events [slower]") != -1 + no_log: True + +- name: Purge all rooms with more then N events + include_tasks: purge_database_events.yml + loop: "{{ room_list_state_events.splitlines() | flatten(levels=1) }}" + when: purge_mode.find("Number of events [slower]") != -1 + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Adjust 'Deploy/Update a Server' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 0 - Deploy/Update a Server" + description: "Creates a new matrix service with Spantaleev's playbooks" + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "rust-synapse-compress-state" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) or (purge_mode.find("Skip purging rooms [faster]") != -1) + +- name: Execute rust-synapse-compress-state job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_launch: + job_template: "{{ matrix_domain }} - 0 - Deploy/Update a Server" + wait: yes + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) or (purge_mode.find("Skip purging rooms [faster]") != -1) + +- name: Revert 'Deploy/Update a Server' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 0 - Deploy/Update a Server" + description: "Creates a new matrix service with Spantaleev's playbooks" + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "setup-all,start" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) or (purge_mode.find("Skip purging rooms [faster]") != -1) + +- name: Ensure matrix-synapse is stopped + service: + name: matrix-synapse + state: stopped + daemon_reload: yes + when: (purge_mode.find("Perform final shrink") != -1) + +- name: Re-index Synapse database + shell: docker exec -i matrix-postgres psql "host=127.0.0.1 port=5432 dbname=synapse user=synapse password={{ matrix_synapse_connection_password }}" -c 'REINDEX (VERBOSE) DATABASE synapse' + when: (purge_mode.find("Perform final shrink") != -1) + +- name: Ensure matrix-synapse is started + service: + name: matrix-synapse + state: started + daemon_reload: yes + when: (purge_mode.find("Perform final shrink") != -1) + +- name: Adjust 'Deploy/Update a Server' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 0 - Deploy/Update a Server" + description: "Creates a new matrix service with Spantaleev's playbooks" + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "run-postgres-vacuum,start" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + when: (purge_mode.find("Perform final shrink") != -1) + +- name: Execute run-postgres-vacuum job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_launch: + job_template: "{{ matrix_domain }} - 0 - Deploy/Update a Server" + wait: yes + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + when: (purge_mode.find("Perform final shrink") != -1) + +- name: Revert 'Deploy/Update a Server' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 0 - Deploy/Update a Server" + description: "Creates a new matrix service with Spantaleev's playbooks" + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "setup-all,start" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + when: (purge_mode.find("Perform final shrink") != -1) + +- name: Cleanup room_list files + delegate_to: 127.0.0.1 + shell: | + rm /tmp/{{ subscription_id }}_room_list* + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + ignore_errors: yes + +- name: Collect after shrink size of Synapse database + shell: du -sh /matrix/postgres/data + register: db_size_after_stat + when: (purge_mode.find("Perform final shrink") != -1) + no_log: True + +- name: Print total number of rooms processed + debug: + msg: '{{ rooms_total.stdout }}' + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Print the number of rooms purged with no local users + debug: + msg: '{{ rooms_no_local_total.stdout }}' + when: (purge_mode.find("No local users [recommended]") != -1) or (purge_mode.find("Number of users [slower]") != -1) or (purge_mode.find("Number of events [slower]") != -1) + +- name: Print the number of rooms purged with more then N users + debug: + msg: '{{ rooms_join_members_total.stdout }}' + when: purge_mode.find("Number of users") != -1 + +- name: Print the number of rooms purged with more then N events + debug: + msg: '{{ rooms_state_events_total.stdout }}' + when: purge_mode.find("Number of events") != -1 + +- name: Print before purge size of Synapse database + debug: + msg: "{{ db_size_before_stat.stdout.split('\n') }}" + when: (db_size_before_stat is defined) and (purge_mode.find("Perform final shrink") != -1) + +- name: Print after purge size of Synapse database + debug: + msg: "{{ db_size_after_stat.stdout.split('\n') }}" + when: (db_size_after_stat is defined) and (purge_mode.find("Perform final shrink") != -1) + +- name: Set boolean value to exit playbook + set_fact: + end_playbook: true + +- name: End playbook early if this task is called. + meta: end_play + when: end_playbook is defined and end_playbook|bool diff --git a/roles/matrix-awx/tasks/purge_database_no_local.yml b/roles/matrix-awx/tasks/purge_database_no_local.yml new file mode 100644 index 00000000..d94fd007 --- /dev/null +++ b/roles/matrix-awx/tasks/purge_database_no_local.yml @@ -0,0 +1,13 @@ + +- name: Purge all rooms with no local users + shell: | + curl --header "Authorization: Bearer {{ janitors_token.stdout[1:-1] }}" -X POST -H "Content-Type: application/json" -d '{ "room_id": {{ item }} }' '{{ synapse_container_ip.stdout }}:8008/_synapse/admin/v1/purge_room' + register: purge_command + +- name: Print output of purge command + debug: + msg: "{{ purge_command.stdout }}" + +- name: Pause for 5 seconds to let Synapse breathe + pause: + seconds: 5 diff --git a/roles/matrix-awx/tasks/purge_database_users.yml b/roles/matrix-awx/tasks/purge_database_users.yml new file mode 100644 index 00000000..302dffd8 --- /dev/null +++ b/roles/matrix-awx/tasks/purge_database_users.yml @@ -0,0 +1,13 @@ + +- name: Purge all rooms with more then N users + shell: | + curl --header "Authorization: Bearer {{ janitors_token.stdout[1:-1] }}" -X POST -H "Content-Type: application/json" -d '{ "delete_local_events": false, "purge_up_to_ts": {{ purge_epoche_time.stdout }}000 }' "{{ synapse_container_ip.stdout }}:8008/_synapse/admin/v1/purge_history/{{ item[1:-1] }}" + register: purge_command + +- name: Print output of purge command + debug: + msg: "{{ purge_command.stdout }}" + +- name: Pause for 5 seconds to let Synapse breathe + pause: + seconds: 5 diff --git a/roles/matrix-awx/tasks/purge_media_local.yml b/roles/matrix-awx/tasks/purge_media_local.yml new file mode 100644 index 00000000..b07c32ea --- /dev/null +++ b/roles/matrix-awx/tasks/purge_media_local.yml @@ -0,0 +1,18 @@ + +- name: Collect epoche time from date + shell: | + date -d '{{ item }}' +"%s" + register: epoche_time + +- name: Purge local media to specific date + shell: | + curl -X POST --header "Authorization: Bearer {{ janitors_token.stdout[1:-1] }}" '{{ synapse_container_ip.stdout }}:8008/_synapse/admin/v1/media/matrix.{{ matrix_domain }}/delete?before_ts={{ epoche_time.stdout }}000' + register: purge_command + +- name: Print output of purge command + debug: + msg: "{{ purge_command.stdout }}" + +- name: Pause for 5 seconds to let Synapse breathe + pause: + seconds: 5 diff --git a/roles/matrix-awx/tasks/purge_media_main.yml b/roles/matrix-awx/tasks/purge_media_main.yml new file mode 100644 index 00000000..84e73a8b --- /dev/null +++ b/roles/matrix-awx/tasks/purge_media_main.yml @@ -0,0 +1,99 @@ + +- name: Ensure dateutils and curl is installed in AWX + delegate_to: 127.0.0.1 + yum: + name: dateutils + state: latest + +- name: Include vars in matrix_vars.yml + include_vars: + file: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + no_log: True + +- name: Ensure curl and jq intalled on target machine + apt: + pkg: + - curl + - jq + state: present + +- name: Collect the internal IP of the matrix-synapse container + shell: "/usr/bin/docker inspect --format '{''{range.NetworkSettings.Networks}''}{''{.IPAddress}''}{''{end}''}' matrix-synapse" + register: synapse_container_ip + +- name: Collect access token for janitor user + shell: | + curl -XPOST -d '{"type":"m.login.password", "user":"janitor", "password":"{{ matrix_awx_janitor_user_password }}"}' "{{ synapse_container_ip.stdout }}:8008/_matrix/client/r0/login" | jq '.access_token' + register: janitors_token + no_log: True + +- name: Generate list of dates to purge to + delegate_to: 127.0.0.1 + shell: "dateseq {{ matrix_purge_from_date }} {{ matrix_purge_to_date }}" + register: purge_dates + +- name: Calculate initial size of local media repository + shell: du -sh /matrix/synapse/storage/media-store/local* + register: local_media_size_before + when: matrix_purge_media_type == "Local Media" + ignore_errors: yes + no_log: True + +- name: Calculate initial size of remote media repository + shell: du -sh /matrix/synapse/storage/media-store/remote* + register: remote_media_size_before + when: matrix_purge_media_type == "Remote Media" + ignore_errors: yes + no_log: True + +- name: Purge local media with loop + include_tasks: purge_media_local.yml + loop: "{{ purge_dates.stdout_lines | flatten(levels=1) }}" + when: matrix_purge_media_type == "Local Media" + +- name: Purge remote media with loop + include_tasks: purge_media_remote.yml + loop: "{{ purge_dates.stdout_lines | flatten(levels=1) }}" + when: matrix_purge_media_type == "Remote Media" + +- name: Calculate final size of local media repository + shell: du -sh /matrix/synapse/storage/media-store/local* + register: local_media_size_after + when: matrix_purge_media_type == "Local Media" + ignore_errors: yes + no_log: True + +- name: Calculate final size of remote media repository + shell: du -sh /matrix/synapse/storage/media-store/remote* + register: remote_media_size_after + when: matrix_purge_media_type == "Remote Media" + ignore_errors: yes + no_log: True + +- name: Print size of local media repository before purge + debug: + msg: "{{ local_media_size_before.stdout.split('\n') }}" + when: matrix_purge_media_type == "Local Media" + +- name: Print size of local media repository after purge + debug: + msg: "{{ local_media_size_after.stdout.split('\n') }}" + when: matrix_purge_media_type == "Local Media" + +- name: Print size of remote media repository before purge + debug: + msg: "{{ remote_media_size_before.stdout.split('\n') }}" + when: matrix_purge_media_type == "Remote Media" + +- name: Print size of remote media repository after purge + debug: + msg: "{{ remote_media_size_after.stdout.split('\n') }}" + when: matrix_purge_media_type == "Remote Media" + +- name: Set boolean value to exit playbook + set_fact: + end_playbook: true + +- name: End playbook early if this task is called. + meta: end_play + when: end_playbook is defined and end_playbook|bool diff --git a/roles/matrix-awx/tasks/purge_media_remote.yml b/roles/matrix-awx/tasks/purge_media_remote.yml new file mode 100644 index 00000000..c2f75c81 --- /dev/null +++ b/roles/matrix-awx/tasks/purge_media_remote.yml @@ -0,0 +1,18 @@ + +- name: Collect epoche time from date + shell: | + date -d '{{ item }}' +"%s" + register: epoche_time + +- name: Purge remote media to specific date + shell: | + curl -X POST --header "Authorization: Bearer {{ janitors_token.stdout[1:-1] }}" '{{ synapse_container_ip.stdout }}:8008/_synapse/admin/v1/purge_media_cache?before_ts={{ epoche_time.stdout }}000' + register: purge_command + +- name: Print output of purge command + debug: + msg: "{{ purge_command.stdout }}" + +- name: Pause for 5 seconds to let Synapse breathe + pause: + seconds: 5 diff --git a/roles/matrix-awx/tasks/rename_variables.yml b/roles/matrix-awx/tasks/rename_variables.yml new file mode 100644 index 00000000..e8992bd8 --- /dev/null +++ b/roles/matrix-awx/tasks/rename_variables.yml @@ -0,0 +1,8 @@ + +- name: Rename synapse presence variable + delegate_to: 127.0.0.1 + replace: + path: "/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml" + regexp: 'matrix_synapse_use_presence' + replace: 'matrix_synapse_presence_enabled' + diff --git a/roles/matrix-awx/tasks/self_check.yml b/roles/matrix-awx/tasks/self_check.yml new file mode 100644 index 00000000..edf6b8b3 --- /dev/null +++ b/roles/matrix-awx/tasks/self_check.yml @@ -0,0 +1,105 @@ + +- name: Install prerequisite apt packages on target + apt: + name: + - sysstat + - curl + state: present + +- name: Install prerequisite yum packages on AWX + delegate_to: 127.0.0.1 + yum: + name: + - bind-utils + state: present + +- name: Install prerequisite pip packages on AWX + delegate_to: 127.0.0.1 + pip: + name: + - dnspython + state: present + +- name: Calculate MAU value + shell: | + curl -s localhost:9000 | grep "^synapse_admin_mau_current " + register: mau_stat + no_log: True + +- name: Print MAU value + debug: + msg: "{{ mau_stat.stdout.split('\n') }}" + when: mau_stat is defined + +- name: Calculate CPU usage statistics + shell: iostat -c + register: cpu_usage_stat + no_log: True + +- name: Print CPU usage statistics + debug: + msg: "{{ cpu_usage_stat.stdout.split('\n') }}" + when: cpu_usage_stat is defined + +- name: Calculate RAM usage statistics + shell: free -mh + register: ram_usage_stat + no_log: True + +- name: Print RAM usage statistics + debug: + msg: "{{ ram_usage_stat.stdout.split('\n') }}" + when: ram_usage_stat is defined + +- name: Calculate free disk space + shell: df -h + register: disk_space_stat + no_log: True + +- name: Print free disk space + debug: + msg: "{{ disk_space_stat.stdout.split('\n') }}" + when: disk_space_stat is defined + +- name: Calculate size of Synapse database + shell: du -sh /matrix/postgres/data + register: db_size_stat + no_log: True + +- name: Print size of Synapse database + debug: + msg: "{{ db_size_stat.stdout.split('\n') }}" + when: db_size_stat is defined + +- name: Calculate size of local media repository + shell: du -sh /matrix/synapse/storage/media-store/local* + register: local_media_size_stat + ignore_errors: yes + no_log: True + +- name: Print size of local media repository + debug: + msg: "{{ local_media_size_stat.stdout.split('\n') }}" + when: local_media_size_stat is defined + +- name: Calculate size of remote media repository + shell: du -sh /matrix/synapse/storage/media-store/remote* + register: remote_media_size_stat + ignore_errors: yes + no_log: True + +- name: Print size of remote media repository + debug: + msg: "{{ remote_media_size_stat.stdout.split('\n') }}" + when: remote_media_size_stat is defined + +- name: Calculate docker container statistics + shell: docker stats --all --no-stream + register: docker_stats + ignore_errors: yes + no_log: True + +- name: Print docker container statistics + debug: + msg: "{{ docker_stats.stdout.split('\n') }}" + when: docker_stats is defined diff --git a/roles/matrix-awx/tasks/set_variables_corporal.yml b/roles/matrix-awx/tasks/set_variables_corporal.yml new file mode 100755 index 00000000..6ae187c7 --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_corporal.yml @@ -0,0 +1,247 @@ + +- name: Record Corporal Enabled/Disabled variable + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Corporal Settings Start' + with_dict: + 'matrix_corporal_enabled': '{{ matrix_corporal_enabled }}' + +- name: Enable Shared Secret Auth if Corporal enabled + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Shared Secret Auth Settings Start' + with_dict: + 'matrix_synapse_ext_password_provider_shared_secret_auth_enabled': 'true' + when: matrix_corporal_enabled|bool + +- name: Disable Shared Secret Auth if Corporal disabled + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Shared Secret Auth Settings Start' + with_dict: + 'matrix_synapse_ext_password_provider_shared_secret_auth_enabled': 'false' + when: not matrix_corporal_enabled|bool + +- name: Enable Rest Auth Endpoint if Corporal enabled + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Synapse Extension Start' + with_dict: + 'matrix_synapse_ext_password_provider_rest_auth_enabled': 'true' + when: matrix_corporal_enabled|bool + +- name: Disable Rest Auth Endpoint if Corporal disabled + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Synapse Extension Start' + with_dict: + 'matrix_synapse_ext_password_provider_rest_auth_enabled': 'false' + when: not matrix_corporal_enabled|bool + +- name: Disable Corporal API if Simple Static File mode selected + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Corporal Settings Start' + with_dict: + 'matrix_corporal_http_api_enabled': 'false' + when: (matrix_corporal_policy_provider_mode == "Simple Static File") or (not matrix_corporal_enabled|bool) + +- name: Enable Corporal API if Push/Pull mode delected + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Corporal Settings Start' + with_dict: + 'matrix_corporal_http_api_enabled': 'true' + when: (matrix_corporal_policy_provider_mode != "Simple Static File") and (matrix_corporal_enabled|bool) + +- name: Record Corporal API Access Token if it's defined + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Corporal Settings Start' + with_dict: + 'matrix_corporal_http_api_auth_token': '{{ matrix_corporal_http_api_auth_token }}' + when: matrix_corporal_http_api_auth_token|length > 0 + +- name: Record 'Simple Static File' configuration variables in matrix_vars.yml + delegate_to: 127.0.0.1 + blockinfile: + path: '{{ awx_cached_matrix_vars }}' + insertafter: "# Corporal Policy Provider Settings Start" + block: | + matrix_corporal_policy_provider_config: | + { + "Type": "static_file", + "Path": "/etc/matrix-corporal/corporal-policy.json" + } + when: matrix_corporal_policy_provider_mode == "Simple Static File" + +- name: Touch the /matrix/corporal/ directory + file: + path: "/matrix/corporal/" + state: directory + owner: matrix + group: matrix + mode: '750' + +- name: Touch the /matrix/corporal/config/ directory + file: + path: "/matrix/corporal/config/" + state: directory + owner: matrix + group: matrix + mode: '750' + +- name: Touch the /matrix/corporal/cache/ directory + file: + path: "/matrix/corporal/cache/" + state: directory + owner: matrix + group: matrix + mode: '750' + +- name: Touch the corporal-policy.json file to ensure it exists + file: + path: "/matrix/corporal/config/corporal-policy.json" + state: touch + owner: matrix + group: matrix + mode: '660' + +- name: Touch the last-policy.json file to ensure it exists + file: + path: "/matrix/corporal/config/last-policy.json" + state: touch + owner: matrix + group: matrix + mode: '660' + +- name: Record 'Simple Static File' configuration content in corporal-policy.json + copy: + content: "{{ matrix_corporal_simple_static_config | string }}" + dest: "/matrix/corporal/config/corporal-policy.json" + owner: matrix + group: matrix + mode: '660' + when: (matrix_corporal_policy_provider_mode == "Simple Static File") and (matrix_corporal_simple_static_config|length > 0) + +- name: Record 'HTTP Pull Mode' configuration variables in matrix_vars.yml + delegate_to: 127.0.0.1 + blockinfile: + path: '{{ awx_cached_matrix_vars }}' + insertafter: "# Corporal Policy Provider Settings Start" + block: | + matrix_corporal_policy_provider_config: | + { + "Type": "http", + "Uri": "{{ matrix_corporal_pull_mode_uri }}", + "AuthorizationBearerToken": "{{ matrix_corporal_pull_mode_token }}", + "CachePath": "/var/cache/matrix-corporal/last-policy.json", + "ReloadIntervalSeconds": 1800, + "TimeoutMilliseconds": 30000 + } + when: (matrix_corporal_policy_provider_mode == "HTTP Pull Mode (API Enabled)") and (matrix_corporal_pull_mode_uri|length > 0) and (matrix_corporal_pull_mode_token|length > 0) + +- name: Record 'HTTP Push Mode' configuration variables in matrix_vars.yml + delegate_to: 127.0.0.1 + blockinfile: + path: '{{ awx_cached_matrix_vars }}' + insertafter: "# Corporal Policy Provider Settings Start" + block: | + matrix_corporal_policy_provider_config: | + { + "Type": "last_seen_store_policy", + "CachePath": "/var/cache/matrix-corporal/last-policy.json" + } + when: (matrix_corporal_policy_provider_mode == "HTTP Push Mode (API Enabled)") + +- name: Lower RateLimit if set to 'Normal' + delegate_to: 127.0.0.1 + replace: + path: '{{ awx_cached_matrix_vars }}' + regexp: ' address:\n per_second: 50\n burst_count: 300\n account:\n per_second: 0.17\n burst_count: 300' + replace: ' address:\n per_second: 0.17\n burst_count: 3\n account:\n per_second: 0.17\n burst_count: 3' + when: matrix_corporal_raise_ratelimits == "Normal" + +- name: Raise RateLimit if set to 'Raised' + delegate_to: 127.0.0.1 + replace: + path: '{{ awx_cached_matrix_vars }}' + regexp: ' address:\n per_second: 0.17\n burst_count: 3\n account:\n per_second: 0.17\n burst_count: 3' + replace: ' address:\n per_second: 50\n burst_count: 300\n account:\n per_second: 0.17\n burst_count: 300' + when: matrix_corporal_raise_ratelimits == "Raised" + +- name: Save new 'Configure Corporal' survey.json to the AWX tower + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_corporal.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_corporal.json' + +- name: Copy new 'Configure Corporal' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_corporal.json' + dest: '/matrix/awx/configure_corporal.json' + mode: '0660' + +- debug: + msg: "matrix_corporal_matrix_homeserver_api_endpoint: {{ matrix_corporal_matrix_homeserver_api_endpoint }}" + +- debug: + msg: "matrix_corporal_matrix_auth_shared_secret: {{ matrix_corporal_matrix_auth_shared_secret }}" + +- debug: + msg: "matrix_corporal_http_gateway_internal_rest_auth_enabled: {{ matrix_corporal_http_gateway_internal_rest_auth_enabled }}" + +- debug: + msg: "matrix_corporal_matrix_registration_shared_secret: {{ matrix_corporal_matrix_registration_shared_secret }}" + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Corporal (Advanced)' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Corporal (Advanced)" + description: "Configure Matrix Corporal, a tool that manages your Matrix server according to a configuration policy." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-corporal" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_corporal.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes diff --git a/roles/matrix-awx/tasks/set_variables_dimension.yml b/roles/matrix-awx/tasks/set_variables_dimension.yml new file mode 100644 index 00000000..53a4dbfc --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_dimension.yml @@ -0,0 +1,111 @@ + +- name: Include vars in matrix_vars.yml + include_vars: + file: '{{ awx_cached_matrix_vars }}' + no_log: True + +- name: Install jq and curl on remote machine + apt: + name: + - jq + - curl + state: present + +- name: Collect access token of Dimension user + shell: | + curl -X POST --header 'Content-Type: application/json' -d '{ "identifier": { "type": "m.id.user","user": "dimension" }, "password": "{{ matrix_awx_dimension_user_password }}", "type": "m.login.password"}' 'https://matrix.{{ matrix_domain }}/_matrix/client/r0/login' | jq -c '. | {access_token}' | sed 's/.*\":\"//' | sed 's/\"}//' + register: dimension_user_access_token + +- name: Record Synapse variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Dimension Settings Start' + with_dict: + 'matrix_dimension_enabled': '{{ matrix_dimension_enabled }}' + 'matrix_dimension_access_token': '"{{ dimension_user_access_token.stdout }}"' + +- name: Set final users list if users are defined + set_fact: + ext_dimension_users_raw_final: "{{ ext_dimension_users_raw }}" + when: ext_dimension_users_raw|length > 0 + +- name: Set final users list if no users are defined + set_fact: + ext_dimension_users_raw_final: '@dimension:{{ matrix_domain }}' + when: ext_dimension_users_raw|length == 0 + +- name: Remove Dimension Users + delegate_to: 127.0.0.1 + replace: + path: '{{ awx_cached_matrix_vars }}' + regexp: '^ - .*\n' + after: 'matrix_dimension_admins:' + before: '# Dimension Settings End' + +- name: Set Dimension Users Header + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + insertbefore: '# Dimension Settings End' + line: "matrix_dimension_admins:" + +- name: Set Dimension Users + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + insertafter: '^matrix_dimension_admins:' + line: ' - "{{ item }}"' + with_items: "{{ ext_dimension_users_raw_final.splitlines() }}" + +- name: Record Dimension Custom variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Custom Settings Start' + with_dict: + 'ext_dimension_users_raw': '{{ ext_dimension_users_raw.splitlines() | to_json }}' + +- name: Save new 'Configure Dimension' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_dimension.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}//configure_dimension.json' + +- name: Copy new 'Configure Dimension' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_dimension.json' + dest: '/matrix/awx/configure_dimension.json' + mode: '0660' + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Dimension' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Dimension" + description: "Configure Dimension, the self-hosted integrations server." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-all,setup-dimension" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_dimension.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes diff --git a/roles/matrix-awx/tasks/set_variables_element.yml b/roles/matrix-awx/tasks/set_variables_element.yml new file mode 100755 index 00000000..29aac37f --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_element.yml @@ -0,0 +1,69 @@ + +- name: Record Element-Web variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Element Settings Start' + with_dict: + 'matrix_client_element_enabled': '{{ matrix_client_element_enabled }}' + 'matrix_client_element_jitsi_preferredDomain': '{{ matrix_client_element_jitsi_preferredDomain }}' + 'matrix_client_element_brand': '{{ matrix_client_element_brand }}' + 'matrix_client_element_default_theme': '{{ matrix_client_element_default_theme }}' + 'matrix_client_element_registration_enabled': '{{ matrix_client_element_registration_enabled }}' + +- name: Set fact for 'https' string + set_fact: + awx_https_string: "https" + +- name: Record Element-Web Background variable locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Element Settings Start' + with_dict: + 'matrix_client_element_branding_welcomeBackgroundUrl': '{{ matrix_client_element_branding_welcomeBackgroundUrl }}' + when: (awx_https_string in matrix_client_element_branding_welcomeBackgroundUrl) and ( matrix_client_element_branding_welcomeBackgroundUrl|length > 0 ) + +- name: Save new 'Configure Element' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_element.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element.json' + +- name: Copy new 'Configure Element' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element.json' + dest: '/matrix/awx/configure_element.json' + mode: '0660' + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Element' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Element" + description: "Configure Element client via survey." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-client-element" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes diff --git a/roles/matrix-awx/tasks/set_variables_element_subdomain.yml b/roles/matrix-awx/tasks/set_variables_element_subdomain.yml new file mode 100644 index 00000000..87259d0f --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_element_subdomain.yml @@ -0,0 +1,49 @@ + +- name: Record Element-Web variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Element Settings Start' + with_dict: + 'matrix_server_fqn_element': "{{ element_subdomain }}.{{ matrix_domain }}" + +- name: Save new 'Configure Element Subdomain' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_element_subdomain.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element_subdomain.json' + +- name: Copy new 'Configure Element Subdomain' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element_subdomain.json' + dest: '/matrix/awx/configure_element_subdomain.json' + mode: '0660' + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Element Subdomain' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Element Subdomain" + description: "Configure Element clients subdomain location. (Eg: 'element' for element.example.org)" + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-all,setup-client-element-subdomain" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_element_subdomain.json') }}" + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes diff --git a/roles/matrix-awx/tasks/set_variables_jitsi.yml b/roles/matrix-awx/tasks/set_variables_jitsi.yml new file mode 100755 index 00000000..9c610685 --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_jitsi.yml @@ -0,0 +1,51 @@ + +- name: Record Jitsi variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Jitsi Settings Start' + with_dict: + 'matrix_jitsi_enabled': '{{ matrix_jitsi_enabled }}' + 'matrix_jitsi_web_config_defaultLanguage': '{{ matrix_jitsi_web_config_defaultLanguage }}' + +- name: Save new 'Configure Jitsi' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_jitsi.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_jitsi.json' + +- name: Copy new 'Configure Jitsi' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_jitsi.json' + dest: '/matrix/awx/configure_jitsi.json' + mode: '0660' + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Jitsi' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Jitsi" + description: "Configure Jitsi conferencing settings." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-jitsi" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_jitsi.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes diff --git a/roles/matrix-awx/tasks/set_variables_ma1sd.yml b/roles/matrix-awx/tasks/set_variables_ma1sd.yml new file mode 100755 index 00000000..50aea14c --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_ma1sd.yml @@ -0,0 +1,109 @@ + +- name: Record ma1sd variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# ma1sd Settings Start' + with_dict: + 'matrix_ma1sd_enabled': '{{ matrix_ma1sd_enabled }}' + +- name: Disable REST auth (matrix-corporal/ma1sd) if using internal auth + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Synapse Extension Start' + with_dict: + 'matrix_synapse_ext_password_provider_rest_auth_enabled': 'false' + when: ext_matrix_ma1sd_auth_store == 'Synapse Internal' + +- name: Enable REST auth if using external LDAP/AD with ma1sd + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Synapse Extension Start' + with_dict: + 'matrix_synapse_ext_password_provider_rest_auth_enabled': 'true' + 'matrix_synapse_ext_password_provider_rest_auth_endpoint': '"http://matrix-ma1sd:8090"' + when: ext_matrix_ma1sd_auth_store == 'LDAP/AD' + +- name: Remove entire ma1sd configuration extension + delegate_to: 127.0.0.1 + replace: + path: '{{ awx_cached_matrix_vars }}' + regexp: '^.*\n' + after: '# Start ma1sd Extension' + before: '# End ma1sd Extension' + +- name: Replace conjoined ma1sd configuration extension limiters + delegate_to: 127.0.0.1 + replace: + path: '{{ awx_cached_matrix_vars }}' + regexp: '^# Start ma1sd Extension# End ma1sd Extension' + replace: '# Start ma1sd Extension\n# End ma1sd Extension' + +- name: Insert/Update ma1sd configuration extension variables + delegate_to: 127.0.0.1 + blockinfile: + path: '{{ awx_cached_matrix_vars }}' + marker: "# {mark} ma1sd ANSIBLE MANAGED BLOCK" + insertafter: '# Start ma1sd Extension' + block: '{{ ext_matrix_ma1sd_configuration_extension_yaml }}' + +- name: Record ma1sd Custom variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertbefore: '# Custom Settings Start' + with_dict: + 'ext_matrix_ma1sd_auth_store': '{{ ext_matrix_ma1sd_auth_store }}' + 'ext_matrix_ma1sd_configuration_extension_yaml': '{{ ext_matrix_ma1sd_configuration_extension_yaml.splitlines() | to_json }}' + no_log: True + +- name: Save new 'Configure ma1sd' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_ma1sd.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_ma1sd.json' + +- name: Copy new 'Configure ma1sd' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_ma1sd.json' + dest: '/matrix/awx/configure_ma1sd.json' + mode: '0660' + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure ma1sd (Advanced)' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure ma1sd (Advanced)" + description: "Configure Jitsi conferencing settings." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-ma1sd" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_ma1sd.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes + diff --git a/roles/matrix-awx/tasks/set_variables_mailer.yml b/roles/matrix-awx/tasks/set_variables_mailer.yml new file mode 100644 index 00000000..924454d6 --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_mailer.yml @@ -0,0 +1,50 @@ + +- name: Record Mailer variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Email Settings Start' + with_dict: + 'matrix_mailer_relay_use': '{{ matrix_mailer_relay_use }}' + +- name: Save new 'Configure Email Relay' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_email_relay.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_email_relay.json' + +- name: Copy new 'Configure Email Relay' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_email_relay.json' + dest: '/matrix/awx/configure_email_relay.json' + mode: '0660' + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Email Relay' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Email Relay" + description: "Enable MailGun relay to increase verification email reliability." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-mailer" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_email_relay.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes diff --git a/roles/matrix-awx/tasks/set_variables_synapse.yml b/roles/matrix-awx/tasks/set_variables_synapse.yml new file mode 100755 index 00000000..53d78081 --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_synapse.yml @@ -0,0 +1,229 @@ + +- name: Limit max upload size to 200MB part 1 + set_fact: + matrix_synapse_max_upload_size_mb: "200" + when: matrix_synapse_max_upload_size_mb_raw|int >= 200 + +- name: Limit max upload size to 200MB part 2 + set_fact: + matrix_synapse_max_upload_size_mb: "{{ matrix_synapse_max_upload_size_mb_raw }}" + when: matrix_synapse_max_upload_size_mb_raw|int < 200 + +- name: Record Synapse variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Synapse Settings Start' + with_dict: + 'matrix_synapse_allow_public_rooms_over_federation': '{{ matrix_synapse_allow_public_rooms_over_federation }}' + 'matrix_synapse_enable_registration': '{{ matrix_synapse_enable_registration }}' + 'matrix_synapse_federation_enabled': '{{ matrix_synapse_federation_enabled }}' + 'matrix_synapse_enable_group_creation': '{{ matrix_synapse_enable_group_creation }}' + 'matrix_synapse_presence_enabled': '{{ matrix_synapse_presence_enabled }}' + 'matrix_synapse_max_upload_size_mb': '{{ matrix_synapse_max_upload_size_mb }}' + 'matrix_synapse_url_preview_enabled': '{{ matrix_synapse_url_preview_enabled }}' + 'matrix_synapse_allow_guest_access': '{{ matrix_synapse_allow_guest_access }}' + +- name: Empty Synapse variable 'matrix_synapse_auto_join_rooms' locally on AWX, if raw inputs empty + delegate_to: 127.0.0.1 + replace: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^matrix_synapse_auto_join_rooms: .*$" + replace: "matrix_synapse_auto_join_rooms: []" + when: matrix_synapse_auto_join_rooms_raw|length == 0 + +- name: If the raw inputs is not empty start constructing parsed auto_join_rooms list + set_fact: + matrix_synapse_auto_join_rooms_array: |- + {{ matrix_synapse_auto_join_rooms_raw.splitlines() | to_json }} + when: matrix_synapse_auto_join_rooms_raw|length > 0 + +- name: Record Synapse variable 'matrix_synapse_auto_join_rooms' locally on AWX, if it's not blank + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Synapse Settings Start' + with_dict: + "matrix_synapse_auto_join_rooms": "{{ matrix_synapse_auto_join_rooms_array }}" + when: matrix_synapse_auto_join_rooms_raw|length > 0 + +- name: Record Synapse Shared Secret if it's defined + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Synapse Settings Start' + with_dict: + 'matrix_synapse_registration_shared_secret': '{{ ext_matrix_synapse_registration_shared_secret }}' + when: ext_matrix_synapse_registration_shared_secret|length > 0 + +- name: Record registations_require_3pid extra variable if true + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "{{ item }}" + line: "{{ item }}" + insertbefore: '# Synapse Extension End' + with_items: + - " registrations_require_3pid:" + - " - email" + when: ext_registrations_require_3pid|bool + +- name: Remove registrations_require_3pid extra variable if false + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "{{ item }}" + line: "{{ item }}" + insertbefore: '# Synapse Extension End' + state: absent + with_items: + - " registrations_require_3pid:" + - " - email" + when: not ext_registrations_require_3pid|bool + +- name: Remove URL Languages + delegate_to: 127.0.0.1 + replace: + path: '{{ awx_cached_matrix_vars }}' + regexp: '^(?!.*\bemail\b) - [a-zA-Z\-]{2,5}\n' + after: ' url_preview_accept_language:' + before: '# Synapse Extension End' + +- name: Set URL languages default if raw inputs empty + set_fact: + ext_url_preview_accept_language_default: 'en' + when: ext_url_preview_accept_language_raw|length == 0 + +- name: Set URL languages default if raw inputs not empty + set_fact: + ext_url_preview_accept_language_default: "{{ ext_url_preview_accept_language_raw }}" + when: ext_url_preview_accept_language_raw|length > 0 + +- name: Set URL languages if raw inputs empty + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + insertafter: '^ url_preview_accept_language:' + line: " - {{ ext_url_preview_accept_language_default }}" + when: ext_url_preview_accept_language_raw|length == 0 + +- name: Set URL languages if raw inputs not empty + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + insertafter: '^ url_preview_accept_language:' + line: " - {{ item }}" + with_items: "{{ ext_url_preview_accept_language_raw.splitlines() }}" + when: ext_url_preview_accept_language_raw|length > 0 + +- name: Remove Federation Whitelisting 1 + delegate_to: 127.0.0.1 + replace: + path: '{{ awx_cached_matrix_vars }}' + regexp: '^ - [a-z0-9]+\.[a-z0-9.]+\n' + after: ' federation_domain_whitelist:' + before: '# Synapse Extension End' + +- name: Remove Federation Whitelisting 2 + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + line: " federation_domain_whitelist:" + state: absent + +- name: Set Federation Whitelisting 1 + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + insertafter: '^matrix_synapse_configuration_extension_yaml: \|' + line: " federation_domain_whitelist:" + when: ext_federation_whitelist_raw|length > 0 + +- name: Set Federation Whitelisting 2 + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + insertafter: '^ federation_domain_whitelist:' + line: " - {{ item }}" + with_items: "{{ ext_federation_whitelist_raw.splitlines() }}" + when: ext_federation_whitelist_raw|length > 0 + +- name: Set ext_recaptcha_public_key to a 'public-key' if undefined + set_fact: ext_recaptcha_public_key="public-key" + when: (ext_recaptcha_public_key is not defined) or (ext_recaptcha_public_key|length == 0) + +- name: Set ext_recaptcha_private_key to a 'private-key' if undefined + set_fact: ext_recaptcha_private_key="private-key" + when: (ext_recaptcha_private_key is not defined) or (ext_recaptcha_private_key|length == 0) + +- name: Record Synapse Extension variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertbefore: '# Synapse Extension End' + with_dict: + ' enable_registration_captcha': '{{ ext_enable_registration_captcha }}' + ' recaptcha_public_key': '{{ ext_recaptcha_public_key }}' + ' recaptcha_private_key': '{{ ext_recaptcha_private_key }}' + +- name: Record Synapse Custom variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Custom Settings Start' + with_dict: + 'ext_federation_whitelist_raw': '{{ ext_federation_whitelist_raw.splitlines() | to_json }}' + 'ext_url_preview_accept_language_default': '{{ ext_url_preview_accept_language_default.splitlines() | to_json }}' + 'ext_enable_registration_captcha': '{{ ext_enable_registration_captcha }}' + 'ext_recaptcha_public_key': '"{{ ext_recaptcha_public_key }}"' + 'ext_recaptcha_private_key': '"{{ ext_recaptcha_private_key }}"' + +- name: Save new 'Configure Synapse' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_synapse.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}//configure_synapse.json' + +- name: Copy new 'Configure Synapse' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse.json' + dest: '/matrix/awx/configure_synapse.json' + mode: '0660' + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Synapse' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Synapse" + description: "Configure Synapse (homeserver) settings." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-synapse" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes diff --git a/roles/matrix-awx/tasks/set_variables_synapse_admin.yml b/roles/matrix-awx/tasks/set_variables_synapse_admin.yml new file mode 100644 index 00000000..fa922de4 --- /dev/null +++ b/roles/matrix-awx/tasks/set_variables_synapse_admin.yml @@ -0,0 +1,50 @@ + +- name: Record Synapse Admin variables locally on AWX + delegate_to: 127.0.0.1 + lineinfile: + path: '{{ awx_cached_matrix_vars }}' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: '# Synapse Admin Settings Start' + with_dict: + 'matrix_synapse_admin_enabled': '{{ matrix_synapse_admin_enabled }}' + +- name: Save new 'Configure Synapse Admin' survey.json to the AWX tower, template + delegate_to: 127.0.0.1 + template: + src: 'roles/matrix-awx/surveys/configure_synapse_admin.json.j2' + dest: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse_admin.json' + +- name: Copy new 'Configure Synapse Admin' survey.json to target machine + copy: + src: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse_admin.json' + dest: '/matrix/awx/configure_synapse_admin.json' + mode: '0660' + +- name: Collect AWX admin token the hard way! + delegate_to: 127.0.0.1 + shell: | + curl -sku {{ tower_username }}:{{ tower_password }} -H "Content-Type: application/json" -X POST -d '{"description":"Tower CLI", "application":null, "scope":"write"}' https://{{ tower_host }}/api/v2/users/1/personal_tokens/ | jq '.token' | sed -r 's/\"//g' + register: tower_token + no_log: True + +- name: Recreate 'Configure Synapse Admin' job template + delegate_to: 127.0.0.1 + awx.awx.tower_job_template: + name: "{{ matrix_domain }} - 1 - Configure Synapse Admin" + description: "Configure 'Synapse Admin', a moderation tool to help you manage your server." + extra_vars: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/extra_vars.json') }}" + job_type: run + job_tags: "start,setup-all" + inventory: "{{ member_id }}" + project: "{{ member_id }} - Matrix Docker Ansible Deploy" + playbook: setup.yml + credential: "{{ member_id }} - AWX SSH Key" + survey_enabled: true + survey_spec: "{{ lookup('file', '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/configure_synapse_admin.json') }}" + become_enabled: yes + state: present + verbosity: 1 + tower_host: "https://{{ tower_host }}" + tower_oauthtoken: "{{ tower_token.stdout }}" + validate_certs: yes diff --git a/roles/matrix-base/defaults/main.yml b/roles/matrix-base/defaults/main.yml new file mode 100644 index 00000000..31bff105 --- /dev/null +++ b/roles/matrix-base/defaults/main.yml @@ -0,0 +1,158 @@ +# The bare domain name which represents your Matrix identity. +# Matrix user ids for your server will be of the form (`@user:`). +# +# Note: this playbook does not touch the server referenced here. +# Installation happens on another server ("matrix.", see `matrix_server_fqn_matrix`). +# +# Example value: example.com +matrix_domain: ~ + +# This is where your data lives and what we set up. +# This and the Element FQN (see below) are expected to be on the same server. +matrix_server_fqn_matrix: "matrix.{{ matrix_domain }}" + +# This is where you access federation API. +matrix_server_fqn_matrix_federation: '{{ matrix_server_fqn_matrix }}' + +# This is where you access the Element web UI from (if enabled via matrix_client_element_enabled; enabled by default). +# This and the Matrix FQN (see above) are expected to be on the same server. +matrix_server_fqn_element: "element.{{ matrix_domain }}" + +# This is where you access the Hydrogen web client from (if enabled via matrix_client_hydrogen_enabled; disabled by default). +matrix_server_fqn_hydrogen: "hydrogen.{{ matrix_domain }}" + +# This is where you access the Dimension. +matrix_server_fqn_dimension: "dimension.{{ matrix_domain }}" + +# For use with Go-NEB! (github callback url for example) +matrix_server_fqn_bot_go_neb: "goneb.{{ matrix_domain }}" + +# This is where you access Jitsi. +matrix_server_fqn_jitsi: "jitsi.{{ matrix_domain }}" + +# This is where you access Grafana. +matrix_server_fqn_grafana: "stats.{{ matrix_domain }}" + +# This is where you access the Sygnal push gateway. +matrix_server_fqn_sygnal: "sygnal.{{ matrix_domain }}" + +matrix_federation_public_port: 8448 + +# The architecture that your server runs. +# Recognized values by us are 'amd64', 'arm32' and 'arm64'. +# Not all architectures support all services, so your experience (on non-amd64) may vary. +# See docs/alternative-architectures.md +matrix_architecture: amd64 + +# The architecture for Debian packages. +# See: https://wiki.debian.org/SupportedArchitectures +# We just remap from our `matrix_architecture` values to what Debian and possibly other distros call things. +matrix_debian_arch: "{{ 'armhf' if matrix_architecture == 'arm32' else matrix_architecture }}" + +matrix_user_username: "matrix" +matrix_user_groupname: "matrix" + +# By default, the playbook creates the user (`matrix_user_username`) +# and group (`matrix_user_groupname`) with a random id. +# To use a specific user/group id, override these variables. +matrix_user_uid: ~ +matrix_user_gid: ~ + +matrix_base_data_path: "/matrix" +matrix_base_data_path_mode: "750" + +matrix_static_files_base_path: "{{ matrix_base_data_path }}/static-files" +matrix_systemd_path: "/etc/systemd/system" + +# Specifies the path to use for the `HOME` environment variable for systemd unit files. +# Docker 20.10 complains with `WARNING: Error loading config file: .dockercfg: $HOME is not defined` +# if `$HOME` is not defined, so we define something to make it happy. +matrix_systemd_unit_home_path: /root + +# This is now unused. We keep it so that cleanup tasks can use it. +# To be removed in the future. +matrix_cron_path: "/etc/cron.d" + +matrix_local_bin_path: "/usr/local/bin" + +matrix_host_command_docker: "/usr/bin/env docker" +matrix_host_command_sleep: "/usr/bin/env sleep" +matrix_host_command_chown: "/usr/bin/env chown" +matrix_host_command_fusermount: "/usr/bin/env fusermount" +matrix_host_command_openssl: "/usr/bin/env openssl" +matrix_host_command_systemctl: "/usr/bin/env systemctl" +matrix_host_command_sh: "/usr/bin/env sh" + +matrix_ntpd_package: "{{ 'systemd-timesyncd' if ansible_distribution == 'CentOS' and ansible_distribution_major_version > '7' else 'ntp' }}" +matrix_ntpd_service: "{{ 'systemd-timesyncd' if ansible_distribution == 'CentOS' and ansible_distribution_major_version > '7' else ('ntpd' if ansible_os_family == 'RedHat' or ansible_distribution == 'Archlinux' else 'ntp') }}" + +matrix_homeserver_url: "https://{{ matrix_server_fqn_matrix }}" + +# Specifies where the homeserver is on the container network. +# Where this is depends on whether there's a reverse-proxy in front of it, etc. +# This likely gets overriden elsewhere. +matrix_homeserver_container_url: "http://matrix-synapse:8008" + +matrix_identity_server_url: ~ + +matrix_integration_manager_rest_url: ~ +matrix_integration_manager_ui_url: ~ + +# The domain name where a Jitsi server is self-hosted. +# If set, `/.well-known/matrix/client` will suggest Element clients to use that Jitsi server. +# See: https://github.com/vector-im/element-web/blob/develop/docs/jitsi.md#configuring-element-to-use-your-self-hosted-jitsi-server +matrix_client_element_jitsi_preferredDomain: '' + +# Controls whether Element should use End-to-End Encryption by default. +# Setting this to false will update `/.well-known/matrix/client` and tell Element clients to avoid E2EE. +# See: https://github.com/vector-im/element-web/blob/develop/docs/e2ee.md +matrix_client_element_e2ee_default: true + +# Controls whether Element should require a secure backup set up before Element can be used. +# Setting this to true will update `/.well-known/matrix/client` and tell Element require a secure backup. +# See: https://github.com/vector-im/element-web/blob/develop/docs/e2ee.md +matrix_client_element_e2ee_secure_backup_required: false + +# Controls which backup methods from ["key", "passphrase"] should be used, both is the default. +# Setting this to other then empty will update `/.well-known/matrix/client` and tell Element which method to use +# See: https://github.com/vector-im/element-web/blob/develop/docs/e2ee.md +matrix_client_element_e2ee_secure_backup_setup_methods: [] + +# The Docker network that all services would be put into +matrix_docker_network: "matrix" + +# Controls whether we'll preserve the vars.yml file on the Matrix server. +# If you have a differently organized inventory, you may wish to disable this feature, +# or to repoint `matrix_vars_yml_snapshotting_src` to the file you'd like to preserve. +matrix_vars_yml_snapshotting_enabled: true +matrix_vars_yml_snapshotting_src: "{{ inventory_dir }}/host_vars/{{ inventory_hostname }}/vars.yml" + +# Controls whether a `/.well-known/matrix/server` file is generated and used at all. +# +# If you wish to rely on DNS SRV records only, you can disable this. +# Using DNS SRV records implies that you'll be handling Matrix Federation API traffic (tcp/8448) +# using certificates for the base domain (`matrix_domain`) and not for the +# matrix domain (`matrix_server_fqn_matrix`). +matrix_well_known_matrix_server_enabled: true + +# Controls whether Docker is automatically installed. +# If you change this to false you must install and update Docker manually. You also need to install the docker (https://pypi.org/project/docker/) Python package. +matrix_docker_installation_enabled: true + +# Controls the Docker package that is installed. +# Possible values are "docker-ce" (default) and "docker.io" (Debian). +matrix_docker_package_name: docker-ce + +# Variables to Control which parts of our roles run. +run_postgres_import: true +run_postgres_upgrade: true +run_postgres_import_sqlite_db: true +run_postgres_vacuum: true +run_synapse_register_user: true +run_synapse_update_user_password: true +run_synapse_import_media_store: true +run_synapse_rust_synapse_compress_state: true +run_setup: true +run_self_check: true +run_start: true +run_stop: true diff --git a/roles/matrix-base/files/yum.repos.d/docker-ce.repo b/roles/matrix-base/files/yum.repos.d/docker-ce.repo new file mode 100644 index 00000000..1abdbe36 --- /dev/null +++ b/roles/matrix-base/files/yum.repos.d/docker-ce.repo @@ -0,0 +1,62 @@ +[docker-ce-stable] +name=Docker CE Stable - $basearch +baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stable +enabled=1 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg + +[docker-ce-stable-debuginfo] +name=Docker CE Stable - Debuginfo $basearch +baseurl=https://download.docker.com/linux/centos/$releasever/debug-$basearch/stable +enabled=0 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg + +[docker-ce-stable-source] +name=Docker CE Stable - Sources +baseurl=https://download.docker.com/linux/centos/$releasever/source/stable +enabled=0 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg + +[docker-ce-test] +name=Docker CE Test - $basearch +baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/test +enabled=0 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg + +[docker-ce-test-debuginfo] +name=Docker CE Test - Debuginfo $basearch +baseurl=https://download.docker.com/linux/centos/$releasever/debug-$basearch/test +enabled=0 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg + +[docker-ce-test-source] +name=Docker CE Test - Sources +baseurl=https://download.docker.com/linux/centos/$releasever/source/test +enabled=0 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg + +[docker-ce-nightly] +name=Docker CE Nightly - $basearch +baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/nightly +enabled=0 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg + +[docker-ce-nightly-debuginfo] +name=Docker CE Nightly - Debuginfo $basearch +baseurl=https://download.docker.com/linux/centos/$releasever/debug-$basearch/nightly +enabled=0 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg + +[docker-ce-nightly-source] +name=Docker CE Nightly - Sources +baseurl=https://download.docker.com/linux/centos/$releasever/source/nightly +enabled=0 +gpgcheck=1 +gpgkey=https://download.docker.com/linux/centos/gpg diff --git a/roles/matrix-base/tasks/clean_up_old_files.yml b/roles/matrix-base/tasks/clean_up_old_files.yml new file mode 100644 index 00000000..01d4a83d --- /dev/null +++ b/roles/matrix-base/tasks/clean_up_old_files.yml @@ -0,0 +1,9 @@ +--- + +- name: Get rid of old files and directories + file: + path: "{{ item }}" + state: absent + with_items: + - "{{ matrix_base_data_path }}/environment-variables" + - "{{ matrix_base_data_path }}/scratchpad" \ No newline at end of file diff --git a/roles/matrix-base/tasks/main.yml b/roles/matrix-base/tasks/main.yml new file mode 100644 index 00000000..4af3564c --- /dev/null +++ b/roles/matrix-base/tasks/main.yml @@ -0,0 +1,34 @@ +- import_tasks: "{{ role_path }}/tasks/sanity_check.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/clean_up_old_files.yml" + when: run_setup|bool + tags: + - setup-all + +- import_tasks: "{{ role_path }}/tasks/server_base/setup.yml" + when: run_setup|bool + tags: + - setup-all + +# This needs to always run, because it populates `matrix_user_uid` and `matrix_user_gid`, +# which are required by many other roles. +- import_tasks: "{{ role_path }}/tasks/setup_matrix_user.yml" + when: run_setup|bool + tags: + - always + - setup-system-user + +- import_tasks: "{{ role_path }}/tasks/setup_matrix_base.yml" + when: run_setup|bool + tags: + - setup-all + +- import_tasks: "{{ role_path }}/tasks/setup_well_known.yml" + when: run_setup|bool + tags: + - setup-all + - setup-ma1sd + - setup-synapse + - setup-nginx-proxy diff --git a/roles/matrix-base/tasks/sanity_check.yml b/roles/matrix-base/tasks/sanity_check.yml new file mode 100644 index 00000000..265dc282 --- /dev/null +++ b/roles/matrix-base/tasks/sanity_check.yml @@ -0,0 +1,61 @@ +--- + +# We generally support Ansible 2.7.1 and above. +- name: Fail if running on Ansible < 2.7.1 + fail: + msg: "You are running on Ansible {{ ansible_version.string }}, which is not supported. See our guide about Ansible: https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/ansible.md" + when: + - "(ansible_version.major < 2) or (ansible_version.major == 2 and ansible_version.minor < 7) or (ansible_version.major == 2 and ansible_version.minor == 7 and ansible_version.revision < 1)" + +# Though we do not support Ansible 2.9.6 which is buggy +- name: Fail if running on Ansible 2.9.6 on Ubuntu + fail: + msg: "You are running on Ansible {{ ansible_version.string }}, which is not supported. See our guide about Ansible: https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/ansible.md" + when: + - ansible_distribution == 'Ubuntu' + - "ansible_version.major == 2 and ansible_version.minor == 9 and ansible_version.revision == 6" + +- name: (Deprecation) Catch and report renamed settings + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'host_specific_hostname_identity', 'new': 'matrix_domain'} + - {'old': 'hostname_identity', 'new': 'matrix_domain'} + - {'old': 'hostname_matrix', 'new': 'matrix_server_fqn_matrix'} + - {'old': 'hostname_riot', 'new': 'matrix_server_fqn_element'} + - {'old': 'matrix_server_fqn_riot', 'new': 'matrix_server_fqn_element'} + +- name: Fail if required variables are undefined + fail: + msg: "The `{{ item }}` variable must be defined and have a non-null value" + with_items: + - matrix_domain + - matrix_server_fqn_matrix + - matrix_server_fqn_element + when: "item not in vars or vars[item] is none" + +- name: Fail if uppercase domain used + fail: + msg: "Detected that you're using an uppercase domain name - `{{ item }}`. This will cause trouble. Please use all-lowercase!" + with_items: + - "{{ matrix_domain }}" + - "{{ matrix_server_fqn_matrix }}" + - "{{ matrix_server_fqn_element }}" + when: "item != item|lower" + +- name: Fail if using python2 on Archlinux + fail: + msg: "Detected that you're using python2 when installing onto Archlinux. Archlinux by default only supports python3." + when: + - ansible_distribution == 'Archlinux' + - ansible_python.version.major != 3 + +- name: Fail if architecture is set incorrectly + fail: + msg: "Detected that variable matrix_architecture {{ matrix_architecture }} appears to be set incorrectly. See docs/alternative-architectures.md. Server appears to be {{ ansible_architecture }}." + when: (ansible_architecture == "x86_64" and matrix_architecture != "amd64") or + (ansible_architecture == "aarch64" and matrix_architecture != "arm64") or + (ansible_architecture.startswith("armv") and matrix_architecture != "arm32") diff --git a/roles/matrix-base/tasks/server_base/setup.yml b/roles/matrix-base/tasks/server_base/setup.yml new file mode 100644 index 00000000..0869e501 --- /dev/null +++ b/roles/matrix-base/tasks/server_base/setup.yml @@ -0,0 +1,43 @@ +--- + +- include_tasks: "{{ role_path }}/tasks/server_base/setup_centos.yml" + when: ansible_distribution == 'CentOS' and ansible_distribution_major_version < '8' + +- include_tasks: "{{ role_path }}/tasks/server_base/setup_centos8.yml" + when: ansible_distribution == 'CentOS' and ansible_distribution_major_version > '7' + +- block: + # ansible_lsb is only available if lsb-release is installed. + - name: Ensure lsb-release installed + apt: + name: + - lsb-release + state: present + update_cache: yes + register: lsb_release_installation_result + + - name: Reread ansible_lsb facts if lsb-release got installed + setup: filter=ansible_lsb* + when: lsb_release_installation_result.changed + + - include_tasks: "{{ role_path }}/tasks/server_base/setup_debian.yml" + when: (ansible_os_family == 'Debian') and (ansible_lsb.id != 'Raspbian') + + - include_tasks: "{{ role_path }}/tasks/server_base/setup_raspbian.yml" + when: (ansible_os_family == 'Debian') and (ansible_lsb.id == 'Raspbian') + when: ansible_os_family == 'Debian' + +- include_tasks: "{{ role_path }}/tasks/server_base/setup_archlinux.yml" + when: ansible_distribution == 'Archlinux' + +- name: Ensure Docker is started and autoruns + service: + name: docker + state: started + enabled: yes + +- name: "Ensure {{ matrix_ntpd_service }} is started and autoruns" + service: + name: "{{ matrix_ntpd_service }}" + state: started + enabled: yes diff --git a/roles/matrix-base/tasks/server_base/setup_archlinux.yml b/roles/matrix-base/tasks/server_base/setup_archlinux.yml new file mode 100644 index 00000000..3814305d --- /dev/null +++ b/roles/matrix-base/tasks/server_base/setup_archlinux.yml @@ -0,0 +1,19 @@ +--- + +- name: Install host dependencies + pacman: + name: + - python-docker + - "{{ matrix_ntpd_package }}" + # TODO This needs to be verified. Which version do we need? + - fuse3 + - python-dnspython + state: latest + update_cache: yes + +- name: Ensure Docker is installed + pacman: + name: + - docker + state: latest + when: matrix_docker_installation_enabled|bool diff --git a/roles/matrix-base/tasks/server_base/setup_centos.yml b/roles/matrix-base/tasks/server_base/setup_centos.yml new file mode 100644 index 00000000..07776d7f --- /dev/null +++ b/roles/matrix-base/tasks/server_base/setup_centos.yml @@ -0,0 +1,34 @@ +--- + +- name: Ensure Docker repository is enabled + template: + src: "{{ role_path }}/files/yum.repos.d/{{ item }}" + dest: "/etc/yum.repos.d/{{ item }}" + owner: "root" + group: "root" + mode: 0644 + with_items: + - docker-ce.repo + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' + +- name: Ensure Docker's RPM key is trusted + rpm_key: + state: present + key: https://download.docker.com/linux/centos/gpg + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' + +- name: Ensure yum packages are installed + yum: + name: + - "{{ matrix_ntpd_package }}" + - fuse + state: latest + update_cache: yes + +- name: Ensure Docker is installed + yum: + name: + - "{{ matrix_docker_package_name }}" + - docker-python + state: latest + when: matrix_docker_installation_enabled|bool diff --git a/roles/matrix-base/tasks/server_base/setup_centos8.yml b/roles/matrix-base/tasks/server_base/setup_centos8.yml new file mode 100644 index 00000000..01666197 --- /dev/null +++ b/roles/matrix-base/tasks/server_base/setup_centos8.yml @@ -0,0 +1,47 @@ +--- + +- name: Ensure Docker repository is enabled + template: + src: "{{ role_path }}/files/yum.repos.d/{{ item }}" + dest: "/etc/yum.repos.d/{{ item }}" + owner: "root" + group: "root" + mode: 0644 + with_items: + - docker-ce.repo + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' + +- name: Ensure Docker's RPM key is trusted + rpm_key: + state: present + key: https://download.docker.com/linux/centos/gpg + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' + +- name: Ensure EPEL is installed + yum: + name: + - epel-release + state: latest + update_cache: yes + +- name: Ensure yum packages are installed + yum: + name: + - "{{ matrix_ntpd_package }}" + - fuse + state: latest + update_cache: yes + +- name: Ensure Docker is installed + yum: + name: + - "{{ matrix_docker_package_name }}" + - python3-pip + state: latest + when: matrix_docker_installation_enabled|bool + +- name: Ensure Docker-Py is installed + pip: + name: docker-py + state: latest + when: matrix_docker_installation_enabled|bool \ No newline at end of file diff --git a/roles/matrix-base/tasks/server_base/setup_debian.yml b/roles/matrix-base/tasks/server_base/setup_debian.yml new file mode 100644 index 00000000..54e52c1b --- /dev/null +++ b/roles/matrix-base/tasks/server_base/setup_debian.yml @@ -0,0 +1,49 @@ +--- + +- name: Ensure APT usage dependencies are installed + apt: + name: + - apt-transport-https + - ca-certificates + - gnupg + state: present + update_cache: yes + +- name: Ensure Docker's APT key is trusted + apt_key: + url: "https://download.docker.com/linux/{{ ansible_distribution|lower }}/gpg" + id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 + state: present + register: add_repository_key + ignore_errors: true + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' + +- name: Ensure Docker repository is enabled + apt_repository: + repo: "deb [arch={{ matrix_debian_arch }}] https://download.docker.com/linux/{{ ansible_distribution|lower }} {{ ansible_distribution_release }} stable" + state: present + update_cache: yes + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' and not ansible_distribution_release == 'bullseye' + +- name: Ensure Docker repository is enabled (using Debian Buster on Debian Bullseye, for which there is no Docker yet) + apt_repository: + repo: "deb [arch={{ matrix_debian_arch }}] https://download.docker.com/linux/{{ ansible_distribution|lower }} buster stable" + state: present + update_cache: yes + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' and ansible_distribution_release == 'bullseye' + +- name: Ensure APT packages are installed + apt: + name: + - "{{ matrix_ntpd_package }}" + - fuse + state: latest + update_cache: yes + +- name: Ensure Docker is installed + apt: + name: + - "{{ matrix_docker_package_name }}" + - "python{{'3' if ansible_python.version.major == 3 else ''}}-docker" + state: latest + when: matrix_docker_installation_enabled|bool diff --git a/roles/matrix-base/tasks/server_base/setup_raspbian.yml b/roles/matrix-base/tasks/server_base/setup_raspbian.yml new file mode 100644 index 00000000..421905a0 --- /dev/null +++ b/roles/matrix-base/tasks/server_base/setup_raspbian.yml @@ -0,0 +1,42 @@ +--- + +- name: Ensure APT usage dependencies are installed + apt: + name: + - apt-transport-https + - ca-certificates + - gnupg + state: present + update_cache: yes + +- name: Ensure Docker's APT key is trusted + apt_key: + url: https://download.docker.com/linux/raspbian/gpg + id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 + state: present + register: add_repository_key + ignore_errors: true + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' + +- name: Ensure Docker repository is enabled + apt_repository: + repo: "deb [arch={{ matrix_debian_arch }}] https://download.docker.com/linux/raspbian {{ ansible_distribution_release }} stable" + state: present + update_cache: yes + when: matrix_docker_installation_enabled|bool and matrix_docker_package_name == 'docker-ce' + +- name: Ensure APT packages are installed + apt: + name: + - "{{ matrix_ntpd_package }}" + - fuse + state: latest + update_cache: yes + +- name: Ensure Docker is installed + apt: + name: + - "{{ matrix_docker_package_name }}" + - "python{{'3' if ansible_python.version.major == 3 else ''}}-docker" + state: latest + when: matrix_docker_installation_enabled|bool diff --git a/roles/matrix-base/tasks/setup_matrix_base.yml b/roles/matrix-base/tasks/setup_matrix_base.yml new file mode 100644 index 00000000..0fad2b3d --- /dev/null +++ b/roles/matrix-base/tasks/setup_matrix_base.yml @@ -0,0 +1,31 @@ +--- + +- name: Ensure Matrix base path exists + file: + path: "{{ item }}" + state: directory + mode: "{{ matrix_base_data_path_mode }}" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_base_data_path }}" + +- name: Preserve vars.yml on the server for easily restoring if it gets lost later on + copy: + src: "{{ matrix_vars_yml_snapshotting_src }}" + dest: "{{ matrix_base_data_path }}/vars.yml" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: '0660' + when: "matrix_vars_yml_snapshotting_enabled|bool" + +- name: Ensure Matrix network is created in Docker + docker_network: + name: "{{ matrix_docker_network }}" + driver: bridge + +- name: Ensure matrix-remove-all script created + template: + src: "{{ role_path }}/templates/usr-local-bin/matrix-remove-all.j2" + dest: "{{ matrix_local_bin_path }}/matrix-remove-all" + mode: 0750 diff --git a/roles/matrix-base/tasks/setup_matrix_user.yml b/roles/matrix-base/tasks/setup_matrix_user.yml new file mode 100644 index 00000000..ab5e8111 --- /dev/null +++ b/roles/matrix-base/tasks/setup_matrix_user.yml @@ -0,0 +1,27 @@ +--- + +- name: Ensure Matrix group is created + group: + name: "{{ matrix_user_groupname }}" + gid: "{{ omit if matrix_user_gid is none else matrix_user_gid }}" + state: present + register: matrix_group + +- name: Set Matrix Group GID Variable + set_fact: + matrix_user_gid: "{{ matrix_group.gid }}" + +- name: Ensure Matrix user is created + user: + name: "{{ matrix_user_username }}" + uid: "{{ omit if matrix_user_uid is none else matrix_user_uid }}" + state: present + group: "{{ matrix_user_groupname }}" + home: "{{ matrix_base_data_path }}" + create_home: no + system: yes + register: matrix_user + +- name: Set Matrix Group UID Variable + set_fact: + matrix_user_uid: "{{ matrix_user.uid }}" diff --git a/roles/matrix-base/tasks/setup_well_known.yml b/roles/matrix-base/tasks/setup_well_known.yml new file mode 100644 index 00000000..3b81ce1e --- /dev/null +++ b/roles/matrix-base/tasks/setup_well_known.yml @@ -0,0 +1,36 @@ +# We need others to be able to read these directories too, +# so that matrix-nginx-proxy's nginx user can access the files. +# +# For running with another webserver, we recommend being part of the `matrix` group. +- name: Ensure Matrix static-files path exists + file: + path: "{{ item }}" + state: directory + mode: 0755 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_static_files_base_path }}/.well-known/matrix" + +- name: Ensure Matrix /.well-known/matrix/client file configured + template: + src: "{{ role_path }}/templates/static-files/well-known/matrix-client.j2" + dest: "{{ matrix_static_files_base_path }}/.well-known/matrix/client" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure Matrix /.well-known/matrix/server file configured + template: + src: "{{ role_path }}/templates/static-files/well-known/matrix-server.j2" + dest: "{{ matrix_static_files_base_path }}/.well-known/matrix/server" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_well_known_matrix_server_enabled|bool + +- name: Ensure Matrix /.well-known/matrix/server file deleted + file: + path: "{{ matrix_static_files_base_path }}/.well-known/matrix/server" + state: absent + when: "not matrix_well_known_matrix_server_enabled|bool" diff --git a/roles/matrix-base/templates/static-files/well-known/matrix-client.j2 b/roles/matrix-base/templates/static-files/well-known/matrix-client.j2 new file mode 100644 index 00000000..a4356d1d --- /dev/null +++ b/roles/matrix-base/templates/static-files/well-known/matrix-client.j2 @@ -0,0 +1,38 @@ +#jinja2: lstrip_blocks: "True" +{ + "m.homeserver": { + "base_url": "{{ matrix_homeserver_url }}" + } + {% if matrix_identity_server_url %}, + "m.identity_server": { + "base_url": "{{ matrix_identity_server_url }}" + } + {% endif %} + {% if matrix_integration_manager_rest_url and matrix_integration_manager_ui_url %}, + "m.integrations": { + "managers": [ + { + "api_url": "{{ matrix_integration_manager_rest_url }}", + "ui_url": "{{ matrix_integration_manager_ui_url }}" + } + ] + } + {% endif %} + {% if matrix_client_element_jitsi_preferredDomain %}, + "io.element.jitsi": { + "preferredDomain": {{ matrix_client_element_jitsi_preferredDomain|to_json }} + }, + "im.vector.riot.jitsi": { + "preferredDomain": {{ matrix_client_element_jitsi_preferredDomain|to_json }} + } + {% endif %} + , + "io.element.e2ee": { + "default": {{ matrix_client_element_e2ee_default|to_json }}, + "secure_backup_required": {{ matrix_client_element_e2ee_secure_backup_required|to_json }}, + "secure_backup_setup_methods": {{ matrix_client_element_e2ee_secure_backup_setup_methods|to_json }} + }, + "im.vector.riot.e2ee": { + "default": {{ matrix_client_element_e2ee_default|to_json }} + } +} diff --git a/roles/matrix-base/templates/static-files/well-known/matrix-server.j2 b/roles/matrix-base/templates/static-files/well-known/matrix-server.j2 new file mode 100644 index 00000000..3bc7346d --- /dev/null +++ b/roles/matrix-base/templates/static-files/well-known/matrix-server.j2 @@ -0,0 +1,4 @@ +#jinja2: lstrip_blocks: "True" +{ + "m.server": "{{ matrix_server_fqn_matrix_federation }}:{{ matrix_federation_public_port }}" +} diff --git a/roles/matrix-base/templates/usr-local-bin/matrix-remove-all.j2 b/roles/matrix-base/templates/usr-local-bin/matrix-remove-all.j2 new file mode 100644 index 00000000..f4b23b44 --- /dev/null +++ b/roles/matrix-base/templates/usr-local-bin/matrix-remove-all.j2 @@ -0,0 +1,36 @@ +#jinja2: lstrip_blocks: "True" +#!/bin/bash + +if [ "$(id -u)" != "0" ]; then + echo "This script must be executed as root! Aborting." + exit 1 +fi + +echo "WARNING! You are about to remove everything the playbook installs for {{ matrix_server_fqn_matrix }}: matrix, docker images,..." +echo -n "If you're sure you want to do this, type: 'Yes, I really want to remove everything!'" +read sure + +if [ "$sure" != "Yes, I really want to remove everything!" ]; then + echo "Good thing I asked, exiting" + exit 0 +else + echo "Stop and remove matrix services" + + for s in $(find {{ matrix_systemd_path }}/ -type f -name "matrix-*" -printf "%f\n"); do + systemctl disable --now $s + rm -f {{ matrix_systemd_path }}/$s + done + + systemctl daemon-reload + + echo "Remove matrix scripts" + find {{ matrix_local_bin_path }}/ -name "matrix-*" -delete + echo "Remove unused Docker images and resources" + docker system prune -af + echo "Remove Docker matrix network (should be gone already, but ..)" + docker network rm {{ matrix_docker_network }} + echo "Remove {{ matrix_base_data_path }} directory" + rm -fr "{{ matrix_base_data_path }}" + exit 0 +fi + diff --git a/roles/matrix-base/vars/main.yml b/roles/matrix-base/vars/main.yml new file mode 100644 index 00000000..e4e9c166 --- /dev/null +++ b/roles/matrix-base/vars/main.yml @@ -0,0 +1,3 @@ +# This will contain a list of enabled services that the playbook is managing. +# Each component is expected to append its service name to this list. +matrix_systemd_services_list: [] \ No newline at end of file diff --git a/roles/matrix-bot-go-neb/defaults/main.yml b/roles/matrix-bot-go-neb/defaults/main.yml new file mode 100644 index 00000000..4dd4f1f6 --- /dev/null +++ b/roles/matrix-bot-go-neb/defaults/main.yml @@ -0,0 +1,231 @@ +# Go-NEB is a Matrix bot written in Go. It is the successor to Matrix-NEB, the original Matrix bot written in Python. +# See: https://github.com/matrix-org/go-neb + +matrix_bot_go_neb_enabled: true +matrix_bot_go_neb_version: latest +matrix_bot_go_neb_docker_image: "matrixdotorg/go-neb:{{ matrix_bot_go_neb_version }}" +matrix_bot_go_neb_docker_image_force_pull: "{{ matrix_bot_go_neb_docker_image.endswith(':latest') }}" + +matrix_bot_go_neb_base_path: "{{ matrix_base_data_path }}/go-neb" +matrix_bot_go_neb_config_path: "{{ matrix_bot_go_neb_base_path }}/config" +matrix_bot_go_neb_config_path_in_container: "/config/config.yaml" +matrix_bot_go_neb_data_path: "{{ matrix_bot_go_neb_base_path }}/data" +matrix_bot_go_neb_data_store_path: "{{ matrix_bot_go_neb_data_path }}/store" + +# Controls whether the matrix-bot-go-neb container exposes its HTTP port (tcp/4050 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:4050"), or empty string to not expose. +matrix_bot_go_neb_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_bot_go_neb_container_extra_arguments: [] + +# List of systemd services that matrix-bot-go-neb.service depends on +matrix_bot_go_neb_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-bot-go-neb.service wants +matrix_bot_go_neb_systemd_wanted_services_list: [] + +# Database-related configuration fields. +# +# MUST be "sqlite3". No other type is supported. +matrix_bot_go_neb_database_engine: 'sqlite3' + +matrix_bot_go_neb_sqlite_database_path_local: "{{ matrix_bot_go_neb_data_path }}/bot.db" +matrix_bot_go_neb_sqlite_database_path_in_container: "/data/bot.db" + +matrix_bot_go_neb_storage_database: "{{ + { + 'sqlite3': (matrix_bot_go_neb_sqlite_database_path_in_container + '?_busy_timeout=5000'), + }[matrix_bot_go_neb_database_engine] +}}" + +# The bot's username(s). These users need to be created manually beforehand. +# The access tokens that the bot uses to authenticate. +# Generate one as described in +# https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook-dimension.md#access-token +# via curl. With the element method, you might run into decryption problems (see https://github.com/matrix-org/go-neb#quick-start) +matrix_bot_go_neb_clients: [] +# - UserID: "@goneb:{{ matrix_domain }}" +# AccessToken: "MDASDASJDIASDJASDAFGFRGER" +# DeviceID: "DEVICE1" +# HomeserverURL: "{{ matrix_homeserver_container_url }}" +# Sync: true +# AutoJoinRooms: true +# DisplayName: "Go-NEB!" +# AcceptVerificationFromUsers: [":{{ matrix_domain }}"] +# +# - UserID: "@another_goneb:{{ matrix_domain }}" +# AccessToken: "MDASDASJDIASDJASDAFGFRGER" +# DeviceID: "DEVICE2" +# HomeserverURL: "{{ matrix_homeserver_container_url }}" +# Sync: false +# AutoJoinRooms: false +# DisplayName: "Go-NEB!" +# AcceptVerificationFromUsers: ["^@admin:{{ matrix_domain }}"] + +# The list of realms which Go-NEB is aware of. +# Delete or modify this list as appropriate. +# See the docs for /configureAuthRealm for the full list of options: +# https://matrix-org.github.io/go-neb/pkg/github.com/matrix-org/go-neb/api/index.html#ConfigureAuthRealmRequest +matrix_bot_go_neb_realms: [] +# - ID: "github_realm" +# Type: "github" +# Config: {} # No need for client ID or Secret as Go-NEB isn't generating OAuth URLs + +# The list of *authenticated* sessions which Go-NEB is aware of. +# Delete or modify this list as appropriate. +# The full list of options are shown below: there is no single HTTP endpoint +# which maps to this section. +# https://matrix-org.github.io/go-neb/pkg/github.com/matrix-org/go-neb/api/index.html#Session +matrix_bot_go_neb_sessions: [] +# - SessionID: "your_github_session" +# RealmID: "github_realm" +# UserID: "@YOUR_USER_ID:{{ matrix_domain }}" # This needs to be the username of the person that's allowed to use the !github commands +# Config: +# # Populate these fields by generating a "Personal Access Token" on github.com +# AccessToken: "YOUR_GITHUB_ACCESS_TOKEN" +# Scopes: "admin:org_hook,admin:repo_hook,repo,user" + +# The list of services which Go-NEB is aware of. +# Delete or modify this list as appropriate. +# See the docs for /configureService for the full list of options: +# https://matrix-org.github.io/go-neb/pkg/github.com/matrix-org/go-neb/api/index.html#ConfigureServiceRequest +matrix_bot_go_neb_services: [] +# - ID: "echo_service" +# Type: "echo" +# UserID: "@goneb:{{ matrix_domain }}" +# Config: {} + +## Can be obtained from https://developers.giphy.com/dashboard/ +# - ID: "giphy_service" +# Type: "giphy" +# UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client +# Config: +# api_key: "qwg4672vsuyfsfe" +# use_downsized: false +# +## This service has been dead for over a year :/ +# - ID: "guggy_service" +# Type: "guggy" +# UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client +# Config: +# api_key: "2356saaqfhgfe" +# +## API Key via https://developers.google.com/custom-search/v1/introduction +## CX via http://www.google.com/cse/manage/all +## https://stackoverflow.com/questions/6562125/getting-a-cx-id-for-custom-search-google-api-python +## 'Search the entire web' and 'Image search' enabled for best results +# - ID: "google_service" +# Type: "google" +# UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client +# Config: +# api_key: "AIzaSyA4FD39m9" +# cx: "AIASDFWSRRtrtr" +# +## Get a key via https://api.imgur.com/oauth2/addclient +## Select "oauth2 without callback url" +# - ID: "imgur_service" +# Type: "imgur" +# UserID: "@imgur:{{ matrix_domain }}" # requires a Syncing client +# Config: +# client_id: "AIzaSyA4FD39m9" +# client_secret: "somesecret" +# +# - ID: "wikipedia_service" +# Type: "wikipedia" +# UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client +# Config: +# +# - ID: "rss_service" +# Type: "rssbot" +# UserID: "@another_goneb:{{ matrix_domain }}" +# Config: +# feeds: +# "http://lorem-rss.herokuapp.com/feed?unit=second&interval=60": +# rooms: ["!qmElAGdFYCHoCJuaNt:localhost"] +# must_include: +# author: +# - author1 +# description: +# - lorem +# - ipsum +# must_not_include: +# title: +# - Lorem +# - Ipsum +# +# - ID: "github_cmd_service" +# Type: "github" +# UserID: "@goneb:{{ matrix_domain }}" # requires a Syncing client +# Config: +# RealmID: "github_realm" +# +# # Make sure your BASE_URL can be accessed by Github! +# - ID: "github_webhook_service" +# Type: "github-webhook" +# UserID: "@another_goneb:{{ matrix_domain }}" +# Config: +# RealmID: "github_realm" +# ClientUserID: "@YOUR_USER_ID:{{ matrix_domain }}" # needs to be an authenticated user so Go-NEB can create webhooks. Check the UserID field in the github_realm in matrix_bot_go_neb_sessions. +# Rooms: +# "!someroom:id": +# Repos: +# "matrix-org/synapse": +# Events: ["push", "issues"] +# "matrix-org/dendron": +# Events: ["pull_request"] +# "!anotherroom:id": +# Repos: +# "matrix-org/synapse": +# Events: ["push", "issues"] +# "matrix-org/dendron": +# Events: ["pull_request"] +# +# - ID: "slackapi_service" +# Type: "slackapi" +# UserID: "@slackapi:{{ matrix_domain }}" +# Config: +# Hooks: +# "hook1": +# RoomID: "!someroom:id" +# MessageType: "m.text" # default is m.text +# +# - ID: "alertmanager_service" +# Type: "alertmanager" +# UserID: "@alertmanager:{{ matrix_domain }}" +# Config: +# # This is for information purposes only. It should point to Go-NEB path as follows: +# # `/services/hooks/` +# # Where in this case "service ID" is "alertmanager_service" +# # Make sure your BASE_URL can be accessed by the Alertmanager instance! +# webhook_url: "http://localhost/services/hooks/YWxlcnRtYW5hZ2VyX3NlcnZpY2U" +# # Each room will get the notification with the alert rendered with the given template +# rooms: +# "!someroomid:domain.tld": +# text_template: "{{range .Alerts -}} [{{ .Status }}] {{index .Labels \"alertname\" }}: {{index .Annotations \"description\"}} {{ end -}}" +# html_template: "{{range .Alerts -}} {{ $severity := index .Labels \"severity\" }} {{ if eq .Status \"firing\" }} {{ if eq $severity \"critical\"}} [FIRING - CRITICAL] {{ else if eq $severity \"warning\"}} [FIRING - WARNING] {{ else }} [FIRING - {{ $severity }}] {{ end }} {{ else }} [RESOLVED] {{ end }} {{ index .Labels \"alertname\"}} : {{ index .Annotations \"description\"}} source
{{end -}}" +# msg_type: "m.text" # Must be either `m.text` or `m.notice` + +# Default 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_bot_go_neb_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_bot_go_neb_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_bot_go_neb_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_bot_go_neb_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_bot_go_neb_configuration_yaml`. + +matrix_bot_go_neb_configuration_extension: "{{ matrix_bot_go_neb_configuration_extension_yaml|from_yaml if matrix_bot_go_neb_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_bot_go_neb_configuration_yaml`. +matrix_bot_go_neb_configuration: "{{ matrix_bot_go_neb_configuration_yaml|from_yaml|combine(matrix_bot_go_neb_configuration_extension, recursive=True) }}" + diff --git a/roles/matrix-bot-go-neb/tasks/init.yml b/roles/matrix-bot-go-neb/tasks/init.yml new file mode 100644 index 00000000..169f5978 --- /dev/null +++ b/roles/matrix-bot-go-neb/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-bot-go-neb.service'] }}" + when: matrix_bot_go_neb_enabled|bool diff --git a/roles/matrix-bot-go-neb/tasks/main.yml b/roles/matrix-bot-go-neb/tasks/main.yml new file mode 100644 index 00000000..1a4fe70a --- /dev/null +++ b/roles/matrix-bot-go-neb/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_bot_go_neb_enabled|bool" + tags: + - setup-all + - setup-bot-go-neb + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_bot_go_neb_enabled|bool" + tags: + - setup-all + - setup-bot-go-neb + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_bot_go_neb_enabled|bool" + tags: + - setup-all + - setup-bot-go-neb diff --git a/roles/matrix-bot-go-neb/tasks/setup_install.yml b/roles/matrix-bot-go-neb/tasks/setup_install.yml new file mode 100644 index 00000000..e26be080 --- /dev/null +++ b/roles/matrix-bot-go-neb/tasks/setup_install.yml @@ -0,0 +1,50 @@ +--- + +- set_fact: + matrix_bot_go_neb_requires_restart: false + +- name: Ensure go-neb paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_bot_go_neb_config_path }}", when: true } + - { path: "{{ matrix_bot_go_neb_data_path }}", when: true } + - { path: "{{ matrix_bot_go_neb_data_store_path }}", when: true } + when: "item.when|bool" + +- name: Ensure go-neb image is pulled + docker_image: + name: "{{ matrix_bot_go_neb_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_bot_go_neb_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_bot_go_neb_docker_image_force_pull }}" + +- name: Ensure go-neb config installed + copy: + content: "{{ matrix_bot_go_neb_configuration|to_nice_yaml }}" + dest: "{{ matrix_bot_go_neb_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-bot-go-neb.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-bot-go-neb.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-bot-go-neb.service" + mode: 0644 + register: matrix_bot_go_neb_systemd_service_result + +- name: Ensure systemd reloaded after matrix-bot-go-neb.service installation + service: + daemon_reload: yes + when: "matrix_bot_go_neb_systemd_service_result.changed|bool" + +- name: Ensure matrix-bot-go-neb.service restarted, if necessary + service: + name: "matrix-bot-go-neb.service" + state: restarted + when: "matrix_bot_go_neb_requires_restart|bool" diff --git a/roles/matrix-bot-go-neb/tasks/setup_uninstall.yml b/roles/matrix-bot-go-neb/tasks/setup_uninstall.yml new file mode 100644 index 00000000..49ad1fe7 --- /dev/null +++ b/roles/matrix-bot-go-neb/tasks/setup_uninstall.yml @@ -0,0 +1,35 @@ +--- + +- name: Check existence of matrix-go-neb service + stat: + path: "{{ matrix_systemd_path }}/matrix-bot-go-neb.service" + register: matrix_bot_go_neb_service_stat + +- name: Ensure matrix-go-neb is stopped + service: + name: matrix-bot-go-neb + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_bot_go_neb_service_stat.stat.exists|bool" + +- name: Ensure matrix-bot-go-neb.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-bot-go-neb.service" + state: absent + when: "matrix_bot_go_neb_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-bot-go-neb.service removal + service: + daemon_reload: yes + when: "matrix_bot_go_neb_service_stat.stat.exists|bool" + +- name: Ensure Matrix go-neb paths don't exist + file: + path: "{{ matrix_bot_go_neb_base_path }}" + state: absent + +- name: Ensure go-neb Docker image doesn't exist + docker_image: + name: "{{ matrix_bot_go_neb_docker_image }}" + state: absent diff --git a/roles/matrix-bot-go-neb/tasks/validate_config.yml b/roles/matrix-bot-go-neb/tasks/validate_config.yml new file mode 100644 index 00000000..7b292250 --- /dev/null +++ b/roles/matrix-bot-go-neb/tasks/validate_config.yml @@ -0,0 +1,13 @@ +--- + +- name: Fail if there's not at least 1 client + fail: + msg: >- + You need at least 1 client in the matrix_bot_go_neb_clients block. + when: matrix_bot_go_neb_clients is not defined or matrix_bot_go_neb_clients[0] is not defined + +- name: Fail if there's not at least 1 service + fail: + msg: >- + You need at least 1 service in the matrix_bot_go_neb_services block. + when: matrix_bot_go_neb_services is not defined or matrix_bot_go_neb_services[0] is not defined diff --git a/roles/matrix-bot-go-neb/templates/config.yaml.j2 b/roles/matrix-bot-go-neb/templates/config.yaml.j2 new file mode 100644 index 00000000..c72dbf8d --- /dev/null +++ b/roles/matrix-bot-go-neb/templates/config.yaml.j2 @@ -0,0 +1,44 @@ +# Go-NEB Configuration File +# +# This file provides an alternative way to configure Go-NEB which does not involve HTTP APIs. +# +# This file can be supplied to go-neb by the environment variable `CONFIG_FILE=config.yaml`. +# It will force Go-NEB to operate in "config" mode. This means: +# - Go-NEB will ONLY use the data contained inside this file. +# - All of Go-NEB's /admin HTTP listeners will be disabled. You will be unable to add new services at runtime. +# - The environment variable `DATABASE_URL` will be ignored and an in-memory database will be used instead. +# +# This file is broken down into 4 sections which matches the following HTTP APIs: +# - /configureClient +# - /configureAuthRealm +# - /configureService +# - /requestAuthSession (redirects not supported) + +# The list of clients which Go-NEB is aware of. +# Delete or modify this list as appropriate. +# See the docs for /configureClient for the full list of options: +# https://matrix-org.github.io/go-neb/pkg/github.com/matrix-org/go-neb/api/index.html#ClientConfig +clients: + {{ matrix_bot_go_neb_clients|to_json }} + +# The list of realms which Go-NEB is aware of. +# Delete or modify this list as appropriate. +# See the docs for /configureAuthRealm for the full list of options: +# https://matrix-org.github.io/go-neb/pkg/github.com/matrix-org/go-neb/api/index.html#ConfigureAuthRealmRequest +realms: + {{ matrix_bot_go_neb_realms|to_json }} + +# The list of *authenticated* sessions which Go-NEB is aware of. +# Delete or modify this list as appropriate. +# The full list of options are shown below: there is no single HTTP endpoint +# which maps to this section. +# https://matrix-org.github.io/go-neb/pkg/github.com/matrix-org/go-neb/api/index.html#Session +sessions: + {{ matrix_bot_go_neb_sessions|to_json }} + +# The list of services which Go-NEB is aware of. +# Delete or modify this list as appropriate. +# See the docs for /configureService for the full list of options: +# https://matrix-org.github.io/go-neb/pkg/github.com/matrix-org/go-neb/api/index.html#ConfigureServiceRequest +services: + {{ matrix_bot_go_neb_services|to_json }} diff --git a/roles/matrix-bot-go-neb/templates/systemd/matrix-bot-go-neb.service.j2 b/roles/matrix-bot-go-neb/templates/systemd/matrix-bot-go-neb.service.j2 new file mode 100644 index 00000000..eabf1137 --- /dev/null +++ b/roles/matrix-bot-go-neb/templates/systemd/matrix-bot-go-neb.service.j2 @@ -0,0 +1,49 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Go-NEB bot +{% for service in matrix_bot_go_neb_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_bot_go_neb_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-go-neb 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-go-neb 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-go-neb \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + {% if matrix_bot_go_neb_container_http_host_bind_port %} + -p {{ matrix_bot_go_neb_container_http_host_bind_port }}:4050 \ + {% endif %} + -e 'BIND_ADDRESS=:4050' \ + -e 'DATABASE_TYPE={{ matrix_bot_go_neb_database_engine }}' \ + -e 'BASE_URL=https://{{ matrix_server_fqn_bot_go_neb }}' \ + -e 'CONFIG_FILE={{ matrix_bot_go_neb_config_path_in_container }}' \ + -e 'DATABASE_URL={{ matrix_bot_go_neb_storage_database }}' \ + --mount type=bind,src={{ matrix_bot_go_neb_config_path }},dst=/config,ro \ + --mount type=bind,src={{ matrix_bot_go_neb_data_path }},dst=/data \ + --entrypoint=/bin/sh \ + {% for arg in matrix_bot_go_neb_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_bot_go_neb_docker_image }} \ + -c "go-neb /config/config.yaml" + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-go-neb 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-go-neb 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-bot-go-neb + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bot-matrix-reminder-bot/defaults/main.yml b/roles/matrix-bot-matrix-reminder-bot/defaults/main.yml new file mode 100644 index 00000000..3e955673 --- /dev/null +++ b/roles/matrix-bot-matrix-reminder-bot/defaults/main.yml @@ -0,0 +1,97 @@ +# matrix-reminder-bot is a bot for one-off and recurring reminders +# See: https://github.com/anoadragon453/matrix-reminder-bot + +matrix_bot_matrix_reminder_bot_enabled: true + +matrix_bot_matrix_reminder_bot_container_self_build: false +matrix_bot_matrix_reminder_bot_docker_repo: "https://github.com/anoadragon453/matrix-reminder-bot.git" +matrix_bot_matrix_reminder_bot_docker_src_files_path: "{{ matrix_base_data_path }}/matrix-reminder-bot/docker-src" + +matrix_bot_matrix_reminder_bot_version: release-v0.2.1 +matrix_bot_matrix_reminder_bot_docker_image: "{{ matrix_container_global_registry_prefix }}anoa/matrix-reminder-bot:{{ matrix_bot_matrix_reminder_bot_version }}" +matrix_bot_matrix_reminder_bot_docker_image_force_pull: "{{ matrix_bot_matrix_reminder_bot_docker_image.endswith(':latest') }}" + +matrix_bot_matrix_reminder_bot_base_path: "{{ matrix_base_data_path }}/matrix-reminder-bot" +matrix_bot_matrix_reminder_bot_config_path: "{{ matrix_bot_matrix_reminder_bot_base_path }}/config" +matrix_bot_matrix_reminder_bot_data_path: "{{ matrix_bot_matrix_reminder_bot_base_path }}/data" +matrix_bot_matrix_reminder_bot_data_store_path: "{{ matrix_bot_matrix_reminder_bot_data_path }}/store" + +# A list of extra arguments to pass to the container +matrix_bot_matrix_reminder_bot_container_extra_arguments: [] + +# List of systemd services that matrix-bot-matrix-reminder-bot.service depends on +matrix_bot_matrix_reminder_bot_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-bot-matrix-reminder-bot.service wants +matrix_bot_matrix_reminder_bot_systemd_wanted_services_list: [] + + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_bot_matrix_reminder_bot_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_bot_matrix_reminder_bot_database_*` variables +matrix_bot_matrix_reminder_bot_database_engine: 'sqlite' + +matrix_bot_matrix_reminder_bot_sqlite_database_path_local: "{{ matrix_bot_matrix_reminder_bot_data_path }}/bot.db" +matrix_bot_matrix_reminder_bot_sqlite_database_path_in_container: "/data/bot.db" + +matrix_bot_matrix_reminder_bot_database_username: 'matrix_reminder_bot' +matrix_bot_matrix_reminder_bot_database_password: 'some-password' +matrix_bot_matrix_reminder_bot_database_hostname: 'matrix-postgres' +matrix_bot_matrix_reminder_bot_database_port: 5432 +matrix_bot_matrix_reminder_bot_database_name: 'matrix_reminder_bot' + +matrix_bot_matrix_reminder_bot_database_connection_string: 'postgres://{{ matrix_bot_matrix_reminder_bot_database_username }}:{{ matrix_bot_matrix_reminder_bot_database_password }}@{{ matrix_bot_matrix_reminder_bot_database_hostname }}:{{ matrix_bot_matrix_reminder_bot_database_port }}/{{ matrix_bot_matrix_reminder_bot_database_name }}' + +matrix_bot_matrix_reminder_bot_storage_database: "{{ + { + 'sqlite': ('sqlite://' + matrix_bot_matrix_reminder_bot_sqlite_database_path_in_container), + 'postgres': matrix_bot_matrix_reminder_bot_database_connection_string, + }[matrix_bot_matrix_reminder_bot_database_engine] +}}" + + +# The bot's username. This user needs to be created manually beforehand. +# Also see `matrix_bot_matrix_reminder_bot_user_password`. +matrix_bot_matrix_reminder_bot_matrix_user_id_localpart: "bot.matrix-reminder-bot" + +matrix_bot_matrix_reminder_bot_matrix_user_id: '@{{ matrix_bot_matrix_reminder_bot_matrix_user_id_localpart }}:{{ matrix_domain }}' + +# The password that the bot uses to authenticate. +matrix_bot_matrix_reminder_bot_matrix_user_password: '' + +matrix_bot_matrix_reminder_bot_matrix_homeserver_url: "{{ matrix_homeserver_container_url }}" + +# The timezone to use when creating reminders. +# Examples: 'Europe/London', 'Etc/UTC' +matrix_bot_matrix_reminder_bot_reminders_timezone: '' + +# Default 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_bot_matrix_reminder_bot_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_bot_matrix_reminder_bot_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_bot_matrix_reminder_bot_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_bot_matrix_reminder_bot_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_bot_matrix_reminder_bot_configuration_yaml`. + # + # Example configuration extension follows: + # + # matrix: + # device_name: My-Reminder-Bot + +matrix_bot_matrix_reminder_bot_configuration_extension: "{{ matrix_bot_matrix_reminder_bot_configuration_extension_yaml|from_yaml if matrix_bot_matrix_reminder_bot_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_bot_matrix_reminder_bot_configuration_yaml`. +matrix_bot_matrix_reminder_bot_configuration: "{{ matrix_bot_matrix_reminder_bot_configuration_yaml|from_yaml|combine(matrix_bot_matrix_reminder_bot_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-bot-matrix-reminder-bot/tasks/init.yml b/roles/matrix-bot-matrix-reminder-bot/tasks/init.yml new file mode 100644 index 00000000..7fd12524 --- /dev/null +++ b/roles/matrix-bot-matrix-reminder-bot/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-bot-matrix-reminder-bot.service'] }}" + when: matrix_bot_matrix_reminder_bot_enabled|bool diff --git a/roles/matrix-bot-matrix-reminder-bot/tasks/main.yml b/roles/matrix-bot-matrix-reminder-bot/tasks/main.yml new file mode 100644 index 00000000..fc2afddb --- /dev/null +++ b/roles/matrix-bot-matrix-reminder-bot/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_bot_matrix_reminder_bot_enabled|bool" + tags: + - setup-all + - setup-bot-matrix-reminder-bot + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_bot_matrix_reminder_bot_enabled|bool" + tags: + - setup-all + - setup-bot-matrix-reminder-bot + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_bot_matrix_reminder_bot_enabled|bool" + tags: + - setup-all + - setup-bot-matrix-reminder-bot diff --git a/roles/matrix-bot-matrix-reminder-bot/tasks/setup_install.yml b/roles/matrix-bot-matrix-reminder-bot/tasks/setup_install.yml new file mode 100644 index 00000000..dada8167 --- /dev/null +++ b/roles/matrix-bot-matrix-reminder-bot/tasks/setup_install.yml @@ -0,0 +1,95 @@ +--- + +- set_fact: + matrix_bot_matrix_reminder_bot_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_bot_matrix_reminder_bot_sqlite_database_path_local }}" + register: matrix_bot_matrix_reminder_bot_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_bot_matrix_reminder_bot_sqlite_database_path_local }}" + dst: "{{ matrix_bot_matrix_reminder_bot_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_bot_matrix_reminder_bot_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-bot-matrix-reminder-bot.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_bot_matrix_reminder_bot_requires_restart: true + when: "matrix_bot_matrix_reminder_bot_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_bot_matrix_reminder_bot_database_engine == 'postgres'" + +- name: Ensure matrix-reminder-bot paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_bot_matrix_reminder_bot_config_path }}", when: true } + - { path: "{{ matrix_bot_matrix_reminder_bot_data_path }}", when: true } + - { path: "{{ matrix_bot_matrix_reminder_bot_data_store_path }}", when: true } + - { path: "{{ matrix_bot_matrix_reminder_bot_docker_src_files_path }}", when: true} + when: "item.when|bool" + +- name: Ensure matrix-reminder-bot image is pulled + docker_image: + name: "{{ matrix_bot_matrix_reminder_bot_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_bot_matrix_reminder_bot_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_bot_matrix_reminder_bot_docker_image_force_pull }}" + when: "not matrix_bot_matrix_reminder_bot_container_self_build|bool" + +- name: Ensure matrix-reminder-bot repository is present on self-build + git: + repo: "{{ matrix_bot_matrix_reminder_bot_docker_repo }}" + dest: "{{ matrix_bot_matrix_reminder_bot_docker_src_files_path }}" + force: "yes" + register: matrix_bot_matrix_reminder_bot_git_pull_results + when: "matrix_bot_matrix_reminder_bot_container_self_build|bool" + +- name: Ensure matrix-reminder-bot image is built + docker_image: + name: "{{ matrix_bot_matrix_reminder_bot_docker_image }}" + source: build + force_source: "{{ matrix_bot_matrix_reminder_bot_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mailer_git_pull_results.changed }}" + build: + dockerfile: docker/Dockerfile + path: "{{ matrix_bot_matrix_reminder_bot_docker_src_files_path }}" + pull: yes + when: "matrix_bot_matrix_reminder_bot_container_self_build|bool" + +- name: Ensure matrix-reminder-bot config installed + copy: + content: "{{ matrix_bot_matrix_reminder_bot_configuration|to_nice_yaml }}" + dest: "{{ matrix_bot_matrix_reminder_bot_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-bot-matrix-reminder-bot.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-bot-matrix-reminder-bot.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-bot-matrix-reminder-bot.service" + mode: 0644 + register: matrix_bot_matrix_reminder_bot_systemd_service_result + +- name: Ensure systemd reloaded after matrix-bot-matrix-reminder-bot.service installation + service: + daemon_reload: yes + when: "matrix_bot_matrix_reminder_bot_systemd_service_result.changed|bool" + +- name: Ensure matrix-bot-matrix-reminder-bot.service restarted, if necessary + service: + name: "matrix-bot-matrix-reminder-bot.service" + state: restarted + when: "matrix_bot_matrix_reminder_bot_requires_restart|bool" diff --git a/roles/matrix-bot-matrix-reminder-bot/tasks/setup_uninstall.yml b/roles/matrix-bot-matrix-reminder-bot/tasks/setup_uninstall.yml new file mode 100644 index 00000000..141e61ba --- /dev/null +++ b/roles/matrix-bot-matrix-reminder-bot/tasks/setup_uninstall.yml @@ -0,0 +1,35 @@ +--- + +- name: Check existence of matrix-matrix-reminder-bot service + stat: + path: "{{ matrix_systemd_path }}/matrix-bot-matrix-reminder-bot.service" + register: matrix_bot_matrix_reminder_bot_service_stat + +- name: Ensure matrix-matrix-reminder-bot is stopped + service: + name: matrix-bot-matrix-reminder-bot + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_bot_matrix_reminder_bot_service_stat.stat.exists|bool" + +- name: Ensure matrix-bot-matrix-reminder-bot.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-bot-matrix-reminder-bot.service" + state: absent + when: "matrix_bot_matrix_reminder_bot_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-bot-matrix-reminder-bot.service removal + service: + daemon_reload: yes + when: "matrix_bot_matrix_reminder_bot_service_stat.stat.exists|bool" + +- name: Ensure Matrix matrix-reminder-bot paths don't exist + file: + path: "{{ matrix_bot_matrix_reminder_bot_base_path }}" + state: absent + +- name: Ensure matrix-reminder-bot Docker image doesn't exist + docker_image: + name: "{{ matrix_bot_matrix_reminder_bot_docker_image }}" + state: absent diff --git a/roles/matrix-bot-matrix-reminder-bot/tasks/validate_config.yml b/roles/matrix-bot-matrix-reminder-bot/tasks/validate_config.yml new file mode 100644 index 00000000..983e7166 --- /dev/null +++ b/roles/matrix-bot-matrix-reminder-bot/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_bot_matrix_reminder_bot_matrix_user_password" + - "matrix_bot_matrix_reminder_bot_reminders_timezone" diff --git a/roles/matrix-bot-matrix-reminder-bot/templates/config.yaml.j2 b/roles/matrix-bot-matrix-reminder-bot/templates/config.yaml.j2 new file mode 100644 index 00000000..59643958 --- /dev/null +++ b/roles/matrix-bot-matrix-reminder-bot/templates/config.yaml.j2 @@ -0,0 +1,50 @@ +# The string to prefix bot commands with +command_prefix: "!" + +# Options for connecting to the bot's Matrix account +matrix: + # The Matrix User ID of the bot account + user_id: {{ matrix_bot_matrix_reminder_bot_matrix_user_id|to_json }} + # Matrix account password + user_password: {{ matrix_bot_matrix_reminder_bot_matrix_user_password|to_json }} + # The public URL at which the homeserver's Client-Server API can be accessed + homeserver_url: {{ matrix_bot_matrix_reminder_bot_matrix_homeserver_url }} + # The device ID that is a **non pre-existing** device + # If this device ID already exists, messages will be dropped silently in + # encrypted rooms + device_id: REMINDER + # What to name the logged in device + device_name: Reminder Bot + +storage: + # The database connection string + # For SQLite3, this would look like: + # database: "sqlite://bot.db" + # For Postgres, this would look like: + # database: "postgres://username:password@localhost/dbname?sslmode=disable" + #database: "postgres://matrix-reminder-bot:remindme@localhost/matrix-reminder-bot?sslmode=disable" + database: {{ matrix_bot_matrix_reminder_bot_storage_database|to_json }} + # The path to a directory for internal bot storage + # containing encryption keys, sync tokens, etc. + store_path: "/data/store" + +reminders: + # Uncomment to set a default timezone that will be used when creating reminders. + # If not set, UTC will be used + timezone: {{ matrix_bot_matrix_reminder_bot_reminders_timezone }} + +# Logging setup +logging: + # Logging level + # Allowed levels are 'INFO', 'WARNING', 'ERROR', 'DEBUG' where DEBUG is most verbose + level: INFO + # Configure logging to a file + file_logging: + # Whether logging to a file is enabled + enabled: false + # The path to the file to log to. May be relative or absolute + filepath: /data/bot.log + # Configure logging to the console (stdout/stderr) + console_logging: + # Whether console logging is enabled + enabled: true diff --git a/roles/matrix-bot-matrix-reminder-bot/templates/systemd/matrix-bot-matrix-reminder-bot.service.j2 b/roles/matrix-bot-matrix-reminder-bot/templates/systemd/matrix-bot-matrix-reminder-bot.service.j2 new file mode 100644 index 00000000..b1fe3c32 --- /dev/null +++ b/roles/matrix-bot-matrix-reminder-bot/templates/systemd/matrix-bot-matrix-reminder-bot.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix reminder bot +{% for service in matrix_bot_matrix_reminder_bot_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_bot_matrix_reminder_bot_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-reminder-bot 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-matrix-reminder-bot 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-matrix-reminder-bot \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + -e 'TZ={{ matrix_bot_matrix_reminder_bot_reminders_timezone }}' \ + --mount type=bind,src={{ matrix_bot_matrix_reminder_bot_config_path }},dst=/config,ro \ + --mount type=bind,src={{ matrix_bot_matrix_reminder_bot_data_path }},dst=/data \ + --entrypoint=/bin/sh \ + {% for arg in matrix_bot_matrix_reminder_bot_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_bot_matrix_reminder_bot_docker_image }} \ + -c "matrix-reminder-bot /config/config.yaml" + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-matrix-reminder-bot 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-matrix-reminder-bot 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-bot-matrix-reminder-bot + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bot-mjolnir/defaults/main.yml b/roles/matrix-bot-mjolnir/defaults/main.yml new file mode 100644 index 00000000..481e0d68 --- /dev/null +++ b/roles/matrix-bot-mjolnir/defaults/main.yml @@ -0,0 +1,60 @@ +# A moderation tool for Matrix +# See: https://github.com/matrix-org/mjolnir + +matrix_bot_mjolnir_enabled: true + +matrix_bot_mjolnir_version: "v0.1.18" + +matrix_bot_mjolnir_container_image_self_build: false +matrix_bot_mjolnir_container_image_self_build_repo: "https://github.com/matrix-org/mjolnir.git" + +matrix_bot_mjolnir_docker_image: "{{ matrix_bot_mjolnir_docker_image_name_prefix }}matrixdotorg/mjolnir:{{ matrix_bot_mjolnir_version }}" +matrix_bot_mjolnir_docker_image_name_prefix: "{{ 'localhost/' if matrix_bot_mjolnir_container_image_self_build else matrix_container_global_registry_prefix }}" + +matrix_bot_mjolnir_docker_image_force_pull: "{{ matrix_bot_mjolnir_docker_image.endswith(':latest') }}" + +matrix_bot_mjolnir_base_path: "{{ matrix_base_data_path }}/mjolnir" +matrix_bot_mjolnir_config_path: "{{ matrix_bot_mjolnir_base_path }}/config" +matrix_bot_mjolnir_data_path: "{{ matrix_bot_mjolnir_base_path }}/data" +matrix_bot_mjolnir_docker_src_files_path: "{{ matrix_bot_mjolnir_base_path }}/docker-src" + +# A list of extra arguments to pass to the container +matrix_bot_mjolnir_container_extra_arguments: [] + +# List of systemd services that matrix-bot-mjolnir.service depends on +matrix_bot_mjolnir_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-bot-mjolnir.service wants +matrix_bot_mjolnir_systemd_wanted_services_list: [] + +# The access token for the bot user +matrix_bot_mjolnir_access_token: "" + +# The room ID where people can use the bot. The bot has no access controls, so +# anyone in this room can use the bot - secure your room! +# This should be a room alias or room ID - not a matrix.to URL. +# Note: Mjolnir is fairly verbose - expect a lot of messages from it. +matrix_bot_mjolnir_management_room: "" + +# Default 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_bot_mjolnir_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_bot_mjolnir_configuration_yaml: "{{ lookup('template', 'templates/production.yaml.j2') }}" + +matrix_bot_mjolnir_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_bot_mjolnir_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_bot_mjolnir_configuration_yaml`. + +matrix_bot_mjolnir_configuration_extension: "{{ matrix_bot_mjolnir_configuration_extension_yaml|from_yaml if matrix_bot_mjolnir_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_bot_mjolnir_configuration_yaml`. +matrix_bot_mjolnir_configuration: "{{ matrix_bot_mjolnir_configuration_yaml|from_yaml|combine(matrix_bot_mjolnir_configuration_extension, recursive=True) }}" + diff --git a/roles/matrix-bot-mjolnir/tasks/init.yml b/roles/matrix-bot-mjolnir/tasks/init.yml new file mode 100644 index 00000000..b8ab58f1 --- /dev/null +++ b/roles/matrix-bot-mjolnir/tasks/init.yml @@ -0,0 +1,10 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Mjolnir image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_bot_mjolnir_container_image_self_build and matrix_bot_mjolnir_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-bot-mjolnir.service'] }}" + when: matrix_bot_mjolnir_enabled|bool diff --git a/roles/matrix-bot-mjolnir/tasks/main.yml b/roles/matrix-bot-mjolnir/tasks/main.yml new file mode 100644 index 00000000..eada8de5 --- /dev/null +++ b/roles/matrix-bot-mjolnir/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_bot_mjolnir_enabled|bool" + tags: + - setup-all + - setup-bot-mjolnir + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_bot_mjolnir_enabled|bool" + tags: + - setup-all + - setup-bot-mjolnir + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_bot_mjolnir_enabled|bool" + tags: + - setup-all + - setup-bot-mjolnir diff --git a/roles/matrix-bot-mjolnir/tasks/setup_install.yml b/roles/matrix-bot-mjolnir/tasks/setup_install.yml new file mode 100644 index 00000000..e770b6d5 --- /dev/null +++ b/roles/matrix-bot-mjolnir/tasks/setup_install.yml @@ -0,0 +1,72 @@ +--- + +- set_fact: + matrix_bot_mjolnir_requires_restart: false + +- name: Ensure matrix-bot-mjolnir paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_bot_mjolnir_base_path }}", when: true } + - { path: "{{ matrix_bot_mjolnir_config_path }}", when: true } + - { path: "{{ matrix_bot_mjolnir_data_path }}", when: true } + - { path: "{{ matrix_bot_mjolnir_docker_src_files_path }}", when: "{{ matrix_bot_mjolnir_container_image_self_build }}" } + when: "item.when|bool" + +- name: Ensure mjolnir Docker image is pulled + docker_image: + name: "{{ matrix_bot_mjolnir_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_bot_mjolnir_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_bot_mjolnir_docker_image_force_pull }}" + when: "not matrix_bot_mjolnir_container_image_self_build|bool" + +- name: Ensure mjolnir repository is present on self-build + git: + repo: "{{ matrix_bot_mjolnir_container_image_self_build_repo }}" + dest: "{{ matrix_bot_mjolnir_docker_src_files_path }}" + version: "{{ matrix_bot_mjolnir_docker_image.split(':')[1] }}" + force: "yes" + register: matrix_bot_mjolnir_git_pull_results + when: "matrix_bot_mjolnir_container_image_self_build|bool" + +- name: Ensure mjolnir Docker image is built + docker_image: + name: "{{ matrix_bot_mjolnir_docker_image }}" + source: build + force_source: "{{ matrix_bot_mjolnir_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_bot_mjolnir_docker_src_files_path }}" + pull: yes + when: "matrix_bot_mjolnir_container_image_self_build|bool" + +- name: Ensure matrix-bot-mjolnir config installed + copy: + content: "{{ matrix_bot_mjolnir_configuration|to_nice_yaml }}" + dest: "{{ matrix_bot_mjolnir_config_path }}/production.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-bot-mjolnir.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-bot-mjolnir.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-bot-mjolnir.service" + mode: 0644 + register: matrix_bot_mjolnir_systemd_service_result + +- name: Ensure systemd reloaded after matrix-bot-mjolnir.service installation + service: + daemon_reload: yes + when: "matrix_bot_mjolnir_systemd_service_result.changed|bool" + +- name: Ensure matrix-bot-mjolnir.service restarted, if necessary + service: + name: "matrix-bot-mjolnir.service" + state: restarted + when: "matrix_bot_mjolnir_requires_restart|bool" diff --git a/roles/matrix-bot-mjolnir/tasks/setup_uninstall.yml b/roles/matrix-bot-mjolnir/tasks/setup_uninstall.yml new file mode 100644 index 00000000..7127543e --- /dev/null +++ b/roles/matrix-bot-mjolnir/tasks/setup_uninstall.yml @@ -0,0 +1,35 @@ +--- + +- name: Check existence of matrix-bot-mjolnir service + stat: + path: "{{ matrix_systemd_path }}/matrix-bot-mjolnir.service" + register: matrix_bot_mjolnir_service_stat + +- name: Ensure matrix-bot-mjolnir is stopped + service: + name: matrix-bot-mjolnir + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_bot_mjolnir_service_stat.stat.exists|bool" + +- name: Ensure matrix-bot-mjolnir.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-bot-mjolnir.service" + state: absent + when: "matrix_bot_mjolnir_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-bot-mjolnir.service removal + service: + daemon_reload: yes + when: "matrix_bot_mjolnir_service_stat.stat.exists|bool" + +- name: Ensure matrix-bot-mjolnir paths don't exist + file: + path: "{{ matrix_bot_mjolnir_base_path }}" + state: absent + +- name: Ensure mjolnir Docker image doesn't exist + docker_image: + name: "{{ matrix_bot_mjolnir_docker_image }}" + state: absent diff --git a/roles/matrix-bot-mjolnir/tasks/validate_config.yml b/roles/matrix-bot-mjolnir/tasks/validate_config.yml new file mode 100644 index 00000000..cb961baf --- /dev/null +++ b/roles/matrix-bot-mjolnir/tasks/validate_config.yml @@ -0,0 +1,9 @@ +--- + +- name: Fail if required variables are undefined + fail: + msg: "The `{{ item }}` variable must be defined and have a non-null value." + with_items: + - "matrix_bot_mjolnir_access_token" + - "matrix_bot_mjolnir_management_room" + when: "vars[item] == '' or vars[item] is none" diff --git a/roles/matrix-bot-mjolnir/templates/production.yaml.j2 b/roles/matrix-bot-mjolnir/templates/production.yaml.j2 new file mode 100644 index 00000000..e5eb3aea --- /dev/null +++ b/roles/matrix-bot-mjolnir/templates/production.yaml.j2 @@ -0,0 +1,162 @@ +# Where the homeserver is located (client-server URL). This should point at +# pantalaimon if you're using that. +homeserverUrl: "{{ matrix_homeserver_url }}" + +# The access token for the bot to use. Do not populate if using Pantalaimon. +accessToken: "{{ matrix_bot_mjolnir_access_token }}" + +# Pantalaimon options (https://github.com/matrix-org/pantalaimon) +#pantalaimon: +# # If true, accessToken above is ignored and the username/password below will be +# # used instead. The access token of the bot will be stored in the dataPath. +# use: false +# +# # The username to login with. +# username: mjolnir +# +# # The password to login with. Can be removed after the bot has logged in once and +# # stored the access token. +# password: your_password + +# The directory the bot should store various bits of information in +dataPath: "/data" + +# If true (the default), only users in the `managementRoom` can invite the bot +# to new rooms. +autojoinOnlyIfManager: true + +# If `autojoinOnlyIfManager` is false, only the members in this group can invite +# the bot to new rooms. +#acceptInvitesFromGroup: '+example:example.org' + +# If the bot is invited to a room and it won't accept the invite (due to the +# conditions above), report it to the management room. Defaults to disabled (no +# reporting). +recordIgnoredInvites: false + +# The room ID where people can use the bot. The bot has no access controls, so +# anyone in this room can use the bot - secure your room! +# This should be a room alias or room ID - not a matrix.to URL. +# Note: Mjolnir is fairly verbose - expect a lot of messages from it. +managementRoom: "{{ matrix_bot_mjolnir_management_room }}" + +# Set to false to make the management room a bit quieter. +verboseLogging: false + +# The log level for the logs themselves. One of DEBUG, INFO, WARN, and ERROR. +# This should be at INFO or DEBUG in order to get support for Mjolnir problems. +logLevel: "INFO" + +# Set to false to disable synchronizing the ban lists on startup. If true, this +# is the same as running !mjolnir sync immediately after startup. +syncOnStartup: true + +# Set to false to prevent Mjolnir from checking its permissions on startup. This +# is recommended to be left as "true" to catch room permission problems (state +# resets, etc) before Mjolnir is needed. +verifyPermissionsOnStartup: true + +# If true, Mjolnir won't actually ban users or apply server ACLs, but will +# think it has. This is useful to see what it does in a scenario where the +# bot might not be trusted fully, yet. Default false (do bans/ACLs). +noop: false + +# Set to true to use /joined_members instead of /state to figure out who is +# in the room. Using /state is preferred because it means that users are +# banned when they are invited instead of just when they join, though if your +# server struggles with /state requests then set this to true. +fasterMembershipChecks: false + +# A case-insensitive list of ban reasons to automatically redact a user's +# messages for. Typically this is useful to avoid having to type two commands +# to the bot. Use asterisks to represent globs (ie: "spam*testing" would match +# "spam for testing" as well as "spamtesting"). +automaticallyRedactForReasons: + - "spam" + - "advertising" + +# A list of rooms to protect (matrix.to URLs) +#protectedRooms: +# - "https://matrix.to/#/#yourroom:example.org" + +# Set this option to true to protect every room the bot is joined to. Note that +# this effectively makes the protectedRooms and associated commands useless because +# the bot by nature must be joined to the room to protect it. +# +# Note: the management room is *excluded* from this condition. Add it to the +# protected rooms to protect it. +# +# Note: ban list rooms the bot is watching but didn't create will not be protected. +# Manually add these rooms to the protected rooms list if you want them protected. +protectAllJoinedRooms: false + +# Misc options for command handling and commands +commands: + # If true, Mjolnir will respond to commands like !help and !ban instead of + # requiring a prefix. This is useful if Mjolnir is the only bot running in + # your management room. + # + # Note that Mjolnir can be pinged by display name instead of having to use + # the !mjolnir prefix. For example, "my_moderator_bot: ban @spammer:example.org" + # will ban a user. + allowNoPrefix: false + + # In addition to the bot's display name, !mjolnir, and optionally no prefix + # above, the bot will respond to these names. The items here can be used either + # as display names or prefixed with exclamation points. + additionalPrefixes: + - "mjolnir_bot" + + # If true, ban commands that use wildcard characters require confirmation with + # an extra `--force` argument + confirmWildcardBan: true + +# Configuration specific to certain toggleable protections +#protections: +# # Configuration for the wordlist plugin, which can ban users based if they say certain +# # blocked words shortly after joining. +# wordlist: +# # A list of words which should be monitored by the bot. These will match if any part +# # of the word is present in the message in any case. e.g. "hello" also matches +# # "HEllO". Additionally, regular expressions can be used. +# words: +# - "CaSe" +# - "InSeNsAtIve" +# - "WoRd" +# - "LiSt" +# +# # How long after a user joins the server should the bot monitor their messages. After +# # this time, users can say words from the wordlist without being banned automatically. +# # Set to zero to disable (users will always be banned if they say a bad word) +# minutesBeforeTrusting: 20 + +# Options for monitoring the health of the bot +health: + # healthz options. These options are best for use in container environments + # like Kubernetes to detect how healthy the service is. The bot will report + # that it is unhealthy until it is able to process user requests. Typically + # this means that it'll flag itself as unhealthy for a number of minutes + # before saying "Now monitoring rooms" and flagging itself healthy. + # + # Health is flagged through HTTP status codes, defined below. + healthz: + # Whether the healthz integration should be enabled (default false) + enabled: false + + # The port to expose the webserver on. Defaults to 8080. + port: 8080 + + # The address to listen for requests on. Defaults to all addresses. + address: "0.0.0.0" + + # The path to expose the monitoring endpoint at. Defaults to `/healthz` + endpoint: "/healthz" + + # The HTTP status code which reports that the bot is healthy/ready to + # process requests. Typically this should not be changed. Defaults to + # 200. + healthyStatus: 200 + + # The HTTP status code which reports that the bot is not healthy/ready. + # Defaults to 418. + unhealthyStatus: 418 diff --git a/roles/matrix-bot-mjolnir/templates/systemd/matrix-bot-mjolnir.service.j2 b/roles/matrix-bot-mjolnir/templates/systemd/matrix-bot-mjolnir.service.j2 new file mode 100644 index 00000000..0b018f25 --- /dev/null +++ b/roles/matrix-bot-mjolnir/templates/systemd/matrix-bot-mjolnir.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mjolnir bot +{% for service in matrix_bot_mjolnir_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_bot_mjolnir_systemd_required_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-mjolnir 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-mjolnir 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-bot-mjolnir \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + --mount type=bind,src={{ matrix_bot_mjolnir_config_path }},dst=/data/config,ro \ + --mount type=bind,src={{ matrix_bot_mjolnir_data_path }},dst=/data \ + {% for arg in matrix_bot_mjolnir_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_bot_mjolnir_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-bot-mjolnir 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-bot-mjolnir 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-bot-mjolnir + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-appservice-discord/defaults/main.yml b/roles/matrix-bridge-appservice-discord/defaults/main.yml new file mode 100644 index 00000000..9b9284dc --- /dev/null +++ b/roles/matrix-bridge-appservice-discord/defaults/main.yml @@ -0,0 +1,110 @@ +# matrix-appservice-discord is a Matrix <-> Discord bridge +# See: https://github.com/Half-Shot/matrix-appservice-discord + +matrix_appservice_discord_enabled: true + +matrix_appservice_discord_version: v1.0.0 +matrix_appservice_discord_docker_image: "{{ matrix_container_global_registry_prefix }}halfshot/matrix-appservice-discord:{{ matrix_appservice_discord_version }}" +matrix_appservice_discord_docker_image_force_pull: "{{ matrix_appservice_discord_docker_image.endswith(':latest') }}" + +matrix_appservice_discord_base_path: "{{ matrix_base_data_path }}/appservice-discord" +matrix_appservice_discord_config_path: "{{ matrix_base_data_path }}/appservice-discord/config" +matrix_appservice_discord_data_path: "{{ matrix_base_data_path }}/appservice-discord/data" + +# Get your own keys at https://discordapp.com/developers/applications/me/create +matrix_appservice_discord_client_id: '' +matrix_appservice_discord_bot_token: '' + +matrix_appservice_discord_appservice_token: '' +matrix_appservice_discord_homeserver_token: '' + +matrix_appservice_discord_homeserver_domain: "{{ matrix_domain }}" + +# Controls whether the matrix-appservice-discord container exposes its HTTP port (tcp/9005 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9005"), or empty string to not expose. +matrix_appservice_discord_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_appservice_discord_container_extra_arguments: [] + +# List of systemd services that matrix-appservice-discord.service depends on. +matrix_appservice_discord_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-appservice-discord.service wants +matrix_appservice_discord_systemd_wanted_services_list: [] + +matrix_appservice_discord_appservice_url: 'http://matrix-appservice-discord:9005' + +matrix_appservice_discord_bridge_domain: "{{ matrix_domain }}" +# As of right now, the homeserver URL must be a public URL. See below. +matrix_appservice_discord_bridge_homeserverUrl: "{{ matrix_homeserver_url }}" +matrix_appservice_discord_bridge_disablePresence: false +matrix_appservice_discord_bridge_enableSelfServiceBridging: false + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_appservice_discord_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_appservice_discord_postgres_*` variables +matrix_appservice_discord_database_engine: 'sqlite' + +matrix_appservice_discord_sqlite_database_path_local: "{{ matrix_appservice_discord_data_path }}/discord.db" +matrix_appservice_discord_sqlite_database_path_in_container: "/data/discord.db" + +matrix_appservice_discord_database_username: 'matrix_appservice_discord' +matrix_appservice_discord_database_password: 'some-password' +matrix_appservice_discord_database_hostname: 'matrix-postgres' +matrix_appservice_discord_database_port: 5432 +matrix_appservice_discord_database_name: 'matrix_appservice_discord' + +# These 2 variables are what actually ends up in the bridge configuration. +# It's best if you don't change them directly, but rather redefine the sub-variables that constitute them. +matrix_appservice_discord_database_filename: "{{ matrix_appservice_discord_sqlite_database_path_in_container }}" +matrix_appservice_discord_database_connString: 'postgresql://{{ matrix_appservice_discord_database_username }}:{{ matrix_appservice_discord_database_password }}@{{ matrix_appservice_discord_database_hostname }}:{{ matrix_appservice_discord_database_port }}/{{ matrix_appservice_discord_database_name }}' + + +# Tells whether the bot should make use of "Privileged Gateway Intents". +# +# Enabling this means that you need to enable it for the bot (Discord application) as well, +# by triggering all Intent checkboxes on a page like this: `https://discord.com/developers/applications/694448564151123988/bot` +# +# Learn more: https://gist.github.com/advaith1/e69bcc1cdd6d0087322734451f15aa2f +matrix_appservice_discord_auth_usePrivilegedIntents: false + +matrix_appservice_discord_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_appservice_discord_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_appservice_discord_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_appservice_discord_configuration_yaml`. + +matrix_appservice_discord_configuration_extension: "{{ matrix_appservice_discord_configuration_extension_yaml|from_yaml if matrix_appservice_discord_configuration_extension_yaml|from_yaml is mapping else {} }}" + +matrix_appservice_discord_configuration: "{{ matrix_appservice_discord_configuration_yaml|from_yaml|combine(matrix_appservice_discord_configuration_extension, recursive=True) }}" + +matrix_appservice_discord_registration_yaml: | + #jinja2: lstrip_blocks: "True" + id: appservice-discord + as_token: "{{ matrix_appservice_discord_appservice_token }}" + hs_token: "{{ matrix_appservice_discord_homeserver_token }}" + namespaces: + users: + - exclusive: true + regex: '@_discord_.*:{{ matrix_appservice_discord_homeserver_domain|regex_escape }}' + aliases: + - exclusive: true + regex: '#_discord_.*:{{ matrix_appservice_discord_homeserver_domain|regex_escape }}' + url: {{ matrix_appservice_discord_appservice_url }} + sender_localpart: _discord_bot + rate_limited: false + protocols: + - discord + +matrix_appservice_discord_registration: "{{ matrix_appservice_discord_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-appservice-discord/tasks/init.yml b/roles/matrix-bridge-appservice-discord/tasks/init.yml new file mode 100644 index 00000000..ef64e78a --- /dev/null +++ b/roles/matrix-bridge-appservice-discord/tasks/init.yml @@ -0,0 +1,24 @@ +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-appservice-discord role needs to execute before the matrix-synapse role. + when: "matrix_appservice_discord_enabled and matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-appservice-discord.service'] }}" + when: matrix_appservice_discord_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_appservice_discord_config_path }}/registration.yaml,dst=/matrix-appservice-discord-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-appservice-discord-registration.yaml"] }} + when: matrix_appservice_discord_enabled|bool diff --git a/roles/matrix-bridge-appservice-discord/tasks/main.yml b/roles/matrix-bridge-appservice-discord/tasks/main.yml new file mode 100644 index 00000000..bad5e320 --- /dev/null +++ b/roles/matrix-bridge-appservice-discord/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_appservice_discord_enabled|bool" + tags: + - setup-all + - setup-appservice-discord + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_appservice_discord_enabled|bool" + tags: + - setup-all + - setup-appservice-discord + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_appservice_discord_enabled|bool" + tags: + - setup-all + - setup-appservice-discord diff --git a/roles/matrix-bridge-appservice-discord/tasks/setup_install.yml b/roles/matrix-bridge-appservice-discord/tasks/setup_install.yml new file mode 100644 index 00000000..6d3fdd0f --- /dev/null +++ b/roles/matrix-bridge-appservice-discord/tasks/setup_install.yml @@ -0,0 +1,114 @@ +--- + +- set_fact: + matrix_appservice_discord_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_appservice_discord_sqlite_database_path_local }}" + register: matrix_appservice_discord_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_appservice_discord_sqlite_database_path_local }}" + dst: "{{ matrix_appservice_discord_database_connString }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_appservice_discord_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-appservice-discord.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_appservice_discord_requires_restart: true + when: "matrix_appservice_discord_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_appservice_discord_database_engine == 'postgres'" + +- name: Ensure Appservice Discord image is pulled + docker_image: + name: "{{ matrix_appservice_discord_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_appservice_discord_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_appservice_discord_docker_image_force_pull }}" + +- name: Ensure AppService Discord paths exist + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_appservice_discord_base_path }}" + - "{{ matrix_appservice_discord_config_path }}" + - "{{ matrix_appservice_discord_data_path }}" + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_appservice_discord_base_path }}/discord.db" + register: matrix_appservice_discord_stat_db + +- name: (Data relocation) Ensure matrix-appservice-discord.service is stopped + service: + name: matrix-appservice-discord + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_appservice_discord_stat_db.stat.exists" + +- name: (Data relocation) Move AppService Discord discord.db file to ./data directory + command: "mv {{ matrix_appservice_discord_base_path }}/{{ item }} {{ matrix_appservice_discord_data_path }}/{{ item }}" + with_items: + - discord.db + - user-store.db + - room-store.db + when: "matrix_appservice_discord_stat_db.stat.exists" + +- name: Ensure AppService Discord config.yaml installed + copy: + content: "{{ matrix_appservice_discord_configuration|to_nice_yaml }}" + dest: "{{ matrix_appservice_discord_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure AppService Discord registration.yaml installed + copy: + content: "{{ matrix_appservice_discord_registration|to_nice_yaml }}" + dest: "{{ matrix_appservice_discord_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +# If `matrix_appservice_discord_client_id` hasn't changed, the same invite link would be generated. +# We intentionally suppress Ansible changes. +- name: Generate AppService Discord invite link + shell: >- + {{ matrix_host_command_docker }} run --rm --name matrix-appservice-discord-link-gen + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --mount type=bind,src={{ matrix_appservice_discord_config_path }},dst=/cfg + -w /cfg + {{ matrix_appservice_discord_docker_image }} + /bin/sh -c "node /build/tools/addbot.js > /cfg/invite_link" + changed_when: false + +- name: Ensure matrix-appservice-discord.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-appservice-discord.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-appservice-discord.service" + mode: 0644 + register: matrix_appservice_discord_systemd_service_result + +- name: Ensure systemd reloaded after matrix-appservice-discord.service installation + service: + daemon_reload: yes + when: "matrix_appservice_discord_systemd_service_result.changed" + +- name: Ensure matrix-appservice-discord.service restarted, if necessary + service: + name: "matrix-appservice-discord.service" + state: restarted + when: "matrix_appservice_discord_requires_restart|bool" diff --git a/roles/matrix-bridge-appservice-discord/tasks/setup_uninstall.yml b/roles/matrix-bridge-appservice-discord/tasks/setup_uninstall.yml new file mode 100644 index 00000000..4e8c1fdc --- /dev/null +++ b/roles/matrix-bridge-appservice-discord/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-appservice-discord service + stat: + path: "{{ matrix_systemd_path }}/matrix-appservice-discord.service" + register: matrix_appservice_discord_service_stat + +- name: Ensure matrix-appservice-discord is stopped + service: + name: matrix-appservice-discord + state: stopped + daemon_reload: yes + when: "matrix_appservice_discord_service_stat.stat.exists" + +- name: Ensure matrix-appservice-discord.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-appservice-discord.service" + state: absent + when: "matrix_appservice_discord_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-appservice-discord.service removal + service: + daemon_reload: yes + when: "matrix_appservice_discord_service_stat.stat.exists" diff --git a/roles/matrix-bridge-appservice-discord/tasks/validate_config.yml b/roles/matrix-bridge-appservice-discord/tasks/validate_config.yml new file mode 100644 index 00000000..73253ba0 --- /dev/null +++ b/roles/matrix-bridge-appservice-discord/tasks/validate_config.yml @@ -0,0 +1,26 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_appservice_discord_client_id" + - "matrix_appservice_discord_bot_token" + - "matrix_appservice_discord_appservice_token" + - "matrix_appservice_discord_homeserver_token" + - "matrix_appservice_discord_homeserver_domain" + +- name: (Deprecation) Catch and report renamed appservice-discord variables + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_appservice_discord_container_expose_client_server_api_port', 'new': ''} + +- name: Require a valid database engine + fail: msg="`matrix_appservice_discord_database_engine` needs to be either 'sqlite' or 'postgres'" + when: "matrix_appservice_discord_database_engine not in ['sqlite', 'postgres']" diff --git a/roles/matrix-bridge-appservice-discord/templates/config.yaml.j2 b/roles/matrix-bridge-appservice-discord/templates/config.yaml.j2 new file mode 100644 index 00000000..6286a5d4 --- /dev/null +++ b/roles/matrix-bridge-appservice-discord/templates/config.yaml.j2 @@ -0,0 +1,103 @@ +#jinja2: lstrip_blocks: "True" +bridge: + # Domain part of the bridge, e.g. matrix.org + domain: {{ matrix_appservice_discord_bridge_domain|to_json }} + # This should be your publically facing URL because Discord may use it to + # fetch media from the media store. + homeserverUrl: {{ matrix_appservice_discord_bridge_homeserverUrl|to_json }} + # Interval at which to process users in the 'presence queue'. If you have + # 5 users, one user will be processed every 500 milliseconds according to the + # value below. This has a minimum value of 250. + # WARNING: This has a high chance of spamming the homeserver with presence + # updates since it will send one each time somebody changes state or is online. + presenceInterval: 500 + # Disable setting presence for 'ghost users' which means Discord users on Matrix + # will not be shown as away or online. + disablePresence: {{ matrix_appservice_discord_bridge_disablePresence|to_json }} + # Disable sending typing notifications when somebody on Discord types. + disableTypingNotifications: false + # Disable deleting messages on Discord if a message is redacted on Matrix. + disableDeletionForwarding: false + # Enable users to bridge rooms using !discord commands. See + # https://t2bot.io/discord for instructions. + enableSelfServiceBridging: {{ matrix_appservice_discord_bridge_enableSelfServiceBridging|to_json }} + # Disable sending of read receipts for Matrix events which have been + # successfully bridged to Discord. + disableReadReceipts: false + # Disable Join Leave echos from matrix + disableJoinLeaveNotifications: false + # Disable Invite echos from matrix + disableInviteNotifications: false + # Auto-determine the language of code blocks (this can be CPU-intensive) + determineCodeLanguage: false +# Authentication configuration for the discord bot. +auth: + clientID: {{ matrix_appservice_discord_client_id|string|to_json }} + botToken: {{ matrix_appservice_discord_bot_token|to_json }} + # You must enable "Privileged Gateway Intents" in your bot settings on discord.com (e.g. https://discord.com/developers/applications/12345/bot) + # for this to work + usePrivilegedIntents: {{ matrix_appservice_discord_auth_usePrivilegedIntents|to_json }} +logging: + # What level should the logger output to the console at. + console: "warn" #silly, verbose, info, http, warn, error, silent + lineDateFormat: "MMM-D HH:mm:ss.SSS" # This is in moment.js format + # files: + # - file: "debug.log" + # disable: + # - "PresenceHandler" # Will not capture presence logging + # - file: "warn.log" # Will capture warnings + # level: "warn" + # - file: "botlogs.log" # Will capture logs from DiscordBot + # level: "info" + # enable: + # - "DiscordBot" +database: + # You may either use SQLite or Postgresql for the bridge database, which contains + # important mappings for events and user puppeting configurations. + # Use the filename option for SQLite, or connString for Postgresql. + # If you are migrating, see https://github.com/Half-Shot/matrix-appservice-discord/blob/master/docs/howto.md#migrate-to-postgres-from-sqlite + # WARNING: You will almost certainly be fine with sqlite unless your bridge + # is in heavy demand and you suffer from IO slowness. + {% if matrix_appservice_discord_database_engine == 'sqlite' %} + filename: {{ matrix_appservice_discord_database_filename|to_json }} + {% else %} + connString: {{ matrix_appservice_discord_database_connString|to_json }} + {% endif %} +room: + # Set the default visibility of alias rooms, defaults to "public". + # One of: "public", "private" + defaultVisibility: "public" +channel: + # Pattern of the name given to bridged rooms. + # Can use :guild for the guild name and :name for the channel name. + namePattern: "[Discord] :guild :name" + # Changes made to rooms when a channel is deleted. + deleteOptions: + # Prefix the room name with a string. + #namePrefix: "[Deleted]" + # Prefix the room topic with a string. + #topicPrefix: "This room has been deleted" + # Disable people from talking in the room by raising the event PL to 50 + disableMessaging: false + # Remove the discord alias from the room. + unsetRoomAlias: true + # Remove the room from the directory. + unlistFromDirectory: true + # Set the room to be unavaliable for joining without an invite. + setInviteOnly: true + # Make all the discord users leave the room. + ghostsLeave: true +limits: + # Delay in milliseconds between discord users joining a room. + roomGhostJoinDelay: 6000 + # Lock timeout in milliseconds before sending messages to discord to avoid + # echos. Default is rather high as the lock will most likely time out + # before anyways. + # echos = (Copies of a sent message may arrive from discord before we've + # fininished handling it, causing us to echo it back to the room) + discordSendDelay: 1500 +ghosts: + # Pattern for the ghosts nick, available is :nick, :username, :tag and :id + nickPattern: ":nick" + # Pattern for the ghosts username, available is :username, :tag and :id + usernamePattern: ":username#:tag" diff --git a/roles/matrix-bridge-appservice-discord/templates/systemd/matrix-appservice-discord.service.j2 b/roles/matrix-bridge-appservice-discord/templates/systemd/matrix-appservice-discord.service.j2 new file mode 100644 index 00000000..84dee801 --- /dev/null +++ b/roles/matrix-bridge-appservice-discord/templates/systemd/matrix-appservice-discord.service.j2 @@ -0,0 +1,45 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Appservice Discord bridge +{% for service in matrix_appservice_discord_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_appservice_discord_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-discord 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-discord 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-appservice-discord \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_appservice_discord_container_http_host_bind_port %} + -p {{ matrix_appservice_discord_container_http_host_bind_port }}:9005 \ + {% endif %} + --mount type=bind,src={{ matrix_appservice_discord_config_path }},dst=/cfg \ + --mount type=bind,src={{ matrix_appservice_discord_data_path }},dst=/data \ + {% for arg in matrix_appservice_discord_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_appservice_discord_docker_image }} \ + node /build/src/discordas.js -p 9005 -c /cfg/config.yaml -f /cfg/registration.yaml + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-discord 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-discord 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-appservice-discord + + [Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-appservice-irc/defaults/main.yml b/roles/matrix-bridge-appservice-irc/defaults/main.yml new file mode 100644 index 00000000..b0f27e65 --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/defaults/main.yml @@ -0,0 +1,401 @@ +# Matrix Appservice IRC is a Matrix <-> IRC bridge +# See: https://github.com/matrix-org/matrix-appservice-irc + +matrix_appservice_irc_enabled: true + +matrix_appservice_irc_container_self_build: false +matrix_appservice_irc_docker_repo: "https://github.com/matrix-org/matrix-appservice-irc.git" +matrix_appservice_irc_docker_src_files_path: "{{ matrix_base_data_path }}/appservice-irc/docker-src" + +matrix_appservice_irc_version: release-v0.29.0 +matrix_appservice_irc_docker_image: "{{ matrix_container_global_registry_prefix }}matrixdotorg/matrix-appservice-irc:{{ matrix_appservice_irc_version }}" +matrix_appservice_irc_docker_image_force_pull: "{{ matrix_appservice_irc_docker_image.endswith(':latest') }}" + +matrix_appservice_irc_base_path: "{{ matrix_base_data_path }}/appservice-irc" +matrix_appservice_irc_config_path: "{{ matrix_appservice_irc_base_path }}/config" +matrix_appservice_irc_data_path: "{{ matrix_appservice_irc_base_path }}/data" + +matrix_appservice_irc_homeserver_url: "{{ matrix_homeserver_container_url }}" +matrix_appservice_irc_homeserver_media_url: 'https://{{ matrix_server_fqn_matrix }}' +matrix_appservice_irc_homeserver_domain: '{{ matrix_domain }}' +matrix_appservice_irc_homeserver_enablePresence: true +matrix_appservice_irc_appservice_address: 'http://matrix-appservice-irc:9999' + +matrix_appservice_irc_database_engine: nedb +matrix_appservice_irc_database_username: matrix_appservice_irc +matrix_appservice_irc_database_password: ~ +matrix_appservice_irc_database_hostname: 'matrix-postgres' +matrix_appservice_irc_database_port: 5432 +matrix_appservice_irc_database_name: matrix_appservice_irc + +# This is just the Postgres connection string, if Postgres is used. +# Naming clashes with `matrix_appservice_irc_database_connectionString` somewhat. +matrix_appservice_irc_database_connection_string: 'postgresql://{{ matrix_appservice_irc_database_username }}:{{ matrix_appservice_irc_database_password }}@{{ matrix_appservice_irc_database_hostname }}:{{ matrix_appservice_irc_database_port }}/{{ matrix_appservice_irc_database_name }}?sslmode=disable' + +# This is what actually goes into `database.connectionString` for the bridge. +matrix_appservice_irc_database_connectionString: "{{ + { + 'nedb': 'nedb:///data', + 'postgres': matrix_appservice_irc_database_connection_string, + }[matrix_appservice_irc_database_engine] +}}" + +matrix_appservice_irc_ircService_servers: [] + +# Example of `matrix_appservice_irc_ircService_servers` with one server (and all its options): +# +# matrix_appservice_irc_ircService_servers: +# # The address of the server to connect to. +# irc.example.com: +# # A human-readable short name. This is used to label IRC status rooms +# # where matrix users control their connections. +# # E.g. 'ExampleNet IRC Bridge status'. +# # It is also used in the Third Party Lookup API as the instance `desc` +# # property, where each server is an instance. +# name: "ExampleNet" + +# additionalAddresses: [ "irc2.example.com" ] +# # +# # [DEPRECATED] Use `name`, above, instead. +# # A human-readable description string +# # description: "Example.com IRC network" + +# # An ID for uniquely identifying this server amongst other servers being bridged. +# # networkId: "example" + +# # URL to an icon used as the network icon whenever this network appear in +# # a network list. (Like in the riot room directory, for instance.) +# # icon: https://example.com/images/hash.png + +# # The port to connect to. Optional. +# port: 6697 +# # Whether to use SSL or not. Default: false. +# ssl: true +# # Whether or not IRC server is using a self-signed cert or not providing CA Chain +# sslselfsign: false +# # Should the connection attempt to identify via SASL (if a server or user password is given) +# # If false, this will use PASS instead. If SASL fails, we do not fallback to PASS. +# sasl: false +# # Whether to allow expired certs when connecting to the IRC server. +# # Usually this should be off. Default: false. +# allowExpiredCerts: false +# # A specific CA to trust instead of the default CAs. Optional. +# #ca: | +# # -----BEGIN CERTIFICATE----- +# # ... +# # -----END CERTIFICATE----- + +# # +# # The connection password to send for all clients as a PASS (or SASL, if enabled above) command. Optional. +# # password: 'pa$$w0rd' +# # +# # Whether or not to send connection/error notices to real Matrix users. Default: true. +# sendConnectionMessages: true + +# quitDebounce: +# # Whether parts due to net-splits are debounced for delayMs, to allow +# # time for the netsplit to resolve itself. A netsplit is detected as being +# # a QUIT rate higher than quitsPerSecond. Default: false. +# enabled: false +# # The maximum number of quits per second acceptable above which a netsplit is +# # considered ongoing. Default: 5. +# quitsPerSecond: 5 +# # The time window in which to wait before bridging a QUIT to Matrix that occurred during +# # a netsplit. Debouncing is jittered randomly between delayMinMs and delayMaxMs so that the HS +# # is not sent many requests to leave rooms all at once if a netsplit occurs and many +# # people to not rejoin. +# # If the user with the same IRC nick as the one who sent the quit rejoins a channel +# # they are considered back online and the quit is not bridged, so long as the rejoin +# # occurs before the randomly-jittered timeout is not reached. +# # Default: 3600000, = 1h +# delayMinMs: 3600000 # 1h +# # Default: 7200000, = 2h +# delayMaxMs: 7200000 # 2h + +# # A map for conversion of IRC user modes to Matrix power levels. This enables bridging +# # of IRC ops to Matrix power levels only, it does not enable the reverse. If a user has +# # been given multiple modes, the one that maps to the highest power level will be used. +# modePowerMap: +# o: 50 + +# botConfig: +# # Enable the presence of the bot in IRC channels. The bot serves as the entity +# # which maps from IRC -> Matrix. You can disable the bot entirely which +# # means IRC -> Matrix chat will be shared by active "M-Nick" connections +# # in the room. If there are no users in the room (or if there are users +# # but their connections are not on IRC) then nothing will be bridged to +# # Matrix. If you're concerned about the bot being treated as a "logger" +# # entity, then you may want to disable the bot. If you want IRC->Matrix +# # but don't want to have TCP connections to IRC unless a Matrix user speaks +# # (because your client connection limit is low), then you may want to keep +# # the bot enabled. Default: true. +# # NB: If the bot is disabled, you SHOULD have matrix-to-IRC syncing turned +# # on, else there will be no users and no bot in a channel (meaning no +# # messages to Matrix!) until a Matrix user speaks which makes a client +# # join the target IRC channel. +# # NBB: The bridge bot IRC client will still join the target IRC network so +# # it can service bridge-specific queries from the IRC-side e.g. so +# # real IRC clients have a way to change their Matrix display name. +# # See https://github.com/matrix-org/matrix-appservice-irc/issues/55 +# enabled: true +# # The nickname to give the AS bot. +# nick: "MatrixBot" +# # The password to give to NickServ or IRC Server for this nick. Optional. +# # password: "helloworld" +# # +# # Join channels even if there are no Matrix users on the other side of +# # the bridge. Set to false to prevent the bot from joining channels which have no +# # real matrix users in them, even if there is a mapping for the channel. +# # Default: true +# joinChannelsIfNoUsers: true + +# # Configuration for PMs / private 1:1 communications between users. +# privateMessages: +# # Enable the ability for PMs to be sent to/from IRC/Matrix. +# # Default: true. +# enabled: true +# # Prevent Matrix users from sending PMs to the following IRC nicks. +# # Optional. Default: []. +# # exclude: ["Alice", "Bob"] # NOT YET IMPLEMENTED + +# # Should created Matrix PM rooms be federated? If false, only users on the +# # HS attached to this AS will be able to interact with this room. +# # Optional. Default: true. +# federate: true + +# # Configuration for mappings not explicitly listed in the 'mappings' +# # section. +# dynamicChannels: +# # Enable the ability for Matrix users to join *any* channel on this IRC +# # network. +# # Default: false. +# enabled: true +# # Should the AS create a room alias for the new Matrix room? The form of +# # the alias can be modified via 'aliasTemplate'. Default: true. +# createAlias: true +# # Should the AS publish the new Matrix room to the public room list so +# # anyone can see it? Default: true. +# published: true +# # What should the join_rule be for the new Matrix room? If 'public', +# # anyone can join the room. If 'invite', only users with an invite can +# # join the room. Note that if an IRC channel has +k or +i set on it, +# # join_rules will be set to 'invite' until these modes are removed. +# # Default: "public". +# joinRule: public +# # This will set the m.room.related_groups state event in newly created rooms +# # with the given groupId. This means flares will show up on IRC users in those rooms. +# # This should be set to the same thing as namespaces.users.group_id in irc_registration. +# # This does not alter existing rooms. +# # Leaving this option empty will not set the event. +# groupId: +myircnetwork:localhost +# # Should created Matrix rooms be federated? If false, only users on the +# # HS attached to this AS will be able to interact with this room. +# # Default: true. +# federate: true +# # The room alias template to apply when creating new aliases. This only +# # applies if createAlias is 'true'. The following variables are exposed: +# # $SERVER => The IRC server address (e.g. "irc.example.com") +# # $CHANNEL => The IRC channel (e.g. "#python") +# # This MUST have $CHANNEL somewhere in it. +# # Default: '#irc_$SERVER_$CHANNEL' +# aliasTemplate: "#irc_$CHANNEL" +# # A list of user IDs which the AS bot will send invites to in response +# # to a !join. Only applies if joinRule is 'invite'. Default: [] +# # whitelist: +# # - "@foo:example.com" +# # - "@bar:example.com" +# # +# # Prevent the given list of channels from being mapped under any +# # circumstances. +# # exclude: ["#foo", "#bar"] + +# # Configuration for controlling how Matrix and IRC membership lists are +# # synced. +# membershipLists: +# # Enable the syncing of membership lists between IRC and Matrix. This +# # can have a significant effect on performance on startup as the lists are +# # synced. This must be enabled for anything else in this section to take +# # effect. Default: false. +# enabled: false + +# # Syncing membership lists at startup can result in hundreds of members to +# # process all at once. This timer drip feeds membership entries at the +# # specified rate. Default: 10000. (10s) +# floodDelayMs: 10000 + +# global: +# ircToMatrix: +# # Get a snapshot of all real IRC users on a channel (via NAMES) and +# # join their virtual matrix clients to the room. +# initial: false +# # Make virtual matrix clients join and leave rooms as their real IRC +# # counterparts join/part channels. Default: false. +# incremental: false + +# matrixToIrc: +# # Get a snapshot of all real Matrix users in the room and join all of +# # them to the mapped IRC channel on startup. Default: false. +# initial: false +# # Make virtual IRC clients join and leave channels as their real Matrix +# # counterparts join/leave rooms. Make sure your 'maxClients' value is +# # high enough! Default: false. +# incremental: false + +# # Apply specific rules to Matrix rooms. Only matrix-to-IRC takes effect. +# rooms: +# - room: "!fuasirouddJoxtwfge:localhost" +# matrixToIrc: +# initial: false +# incremental: false + +# # Apply specific rules to IRC channels. Only IRC-to-matrix takes effect. +# channels: +# - channel: "#foo" +# ircToMatrix: +# initial: false +# incremental: false + +# mappings: +# # 1:many mappings from IRC channels to room IDs on this IRC server. +# # The matrix room must already exist. Your matrix client should expose +# # the room ID in a "settings" page for the room. +# "#thepub": +# roomIds: ["!kieouiJuedJoxtVdaG:localhost"] +# # Channel key/password to use. Optional. If provided, matrix users do +# # not need to know the channel key in order to join the channel. +# # key: "secret" + +# # Configuration for virtual matrix users. The following variables are +# # exposed: +# # $NICK => The IRC nick +# # $SERVER => The IRC server address (e.g. "irc.example.com") +# matrixClients: +# # The user ID template to use when creating virtual matrix users. This +# # MUST have $NICK somewhere in it. +# # Optional. Default: "@$SERVER_$NICK". +# # Example: "@irc.example.com_Alice:example.com" +# userTemplate: "@irc_$NICK" +# # The display name to use for created matrix clients. This should have +# # $NICK somewhere in it if it is specified. Can also use $SERVER to +# # insert the IRC domain. +# # Optional. Default: "$NICK (IRC)". Example: "Alice (IRC)" +# displayName: "$NICK (IRC)" +# # Number of tries a client can attempt to join a room before the request +# # is discarded. You can also use -1 to never retry or 0 to never give up. +# # Optional. Default: -1 +# joinAttempts: -1 + +# # Configuration for virtual IRC users. The following variables are exposed: +# # $LOCALPART => The user ID localpart ("alice" in @alice:localhost) +# # $USERID => The user ID +# # $DISPLAY => The display name of this user, with excluded characters +# # (e.g. space) removed. If the user has no display name, this +# # falls back to $LOCALPART. +# ircClients: +# # The template to apply to every IRC client nick. This MUST have either +# # $DISPLAY or $USERID or $LOCALPART somewhere in it. +# # Optional. Default: "M-$DISPLAY". Example: "M-Alice". +# nickTemplate: "$DISPLAY[m]" +# # True to allow virtual IRC clients to change their nick on this server +# # by issuing !nick commands to the IRC AS bot. +# # This is completely freeform: it will NOT follow the nickTemplate. +# allowNickChanges: true +# # The max number of IRC clients that will connect. If the limit is +# # reached, the client that spoke the longest time ago will be +# # disconnected and replaced. +# # Optional. Default: 30. +# maxClients: 30 +# # IPv6 configuration. +# ipv6: +# # Optional. Set to true to force IPv6 for outgoing connections. +# only: false +# # Optional. The IPv6 prefix to use for generating unique addresses for each +# # connected user. If not specified, all users will connect from the same +# # (default) address. This may require additional OS-specific work to allow +# # for the node process to bind to multiple different source addresses +# # e.g IP_FREEBIND on Linux, which requires an LD_PRELOAD with the library +# # https://github.com/matrix-org/freebindfree as Node does not expose setsockopt. +# # prefix: "2001:0db8:85a3::" # modify appropriately +# # +# # The maximum amount of time in seconds that the client can exist +# # without sending another message before being disconnected. Use 0 to +# # not apply an idle timeout. This value is ignored if this IRC server is +# # mirroring matrix membership lists to IRC. Default: 172800 (48 hours) +# idleTimeout: 10800 +# # The number of millseconds to wait between consecutive reconnections if a +# # client gets disconnected. Setting to 0 will cause the scheduling to be +# # disabled, i.e. it will be scheduled immediately (with jitter. +# # Otherwise, the scheduling interval will be used such that one client +# # reconnect for this server will be handled every reconnectIntervalMs ms using +# # a FIFO queue. +# # Default: 5000 (5 seconds) +# reconnectIntervalMs: 5000 +# # The number of concurrent reconnects if a user has been disconnected unexpectedly +# # (e.g. a netsplit). You should set this to a reasonably high number so that +# # bridges are not waiting an eternity to reconnect all its clients if +# # we see a massive number of disconnect. This is unrelated to the reconnectIntervalMs +# # setting above which is for connecting on restart of the bridge. Set to 0 to +# # immediately try to reconnect all users. +# # Default: 50 +# concurrentReconnectLimit: 50 +# # The number of lines to allow being sent by the IRC client that has received +# # a large block of text to send from matrix. If the number of lines that would +# # be sent is > lineLimit, the text will instead be uploaded to matrix and the +# # resulting URI is treated as a file. As such, a link will be sent to the IRC +# # side instead of potentially spamming IRC and getting the IRC client kicked. +# # Default: 3. +# lineLimit: 3 +# # A list of user modes to set on every IRC client. For example, "RiG" would set +# # +R, +i and +G on every IRC connection when they have successfully connected. +# # User modes vary wildly depending on the IRC network you're connecting to, +# # so check before setting this value. Some modes may not work as intended +# # through the bridge e.g. caller ID as there is no way to /ACCEPT. +# # Default: "" (no user modes) +# # userModes: "R" + +# Controls whether the matrix-appservice-discord container exposes its HTTP port (tcp/9999 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9999"), or empty string to not expose. +matrix_appservice_irc_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_appservice_irc_container_extra_arguments: [] + +# List of systemd services that matrix-appservice-irc.service depends on. +matrix_appservice_irc_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-appservice-irc.service wants +matrix_appservice_irc_systemd_wanted_services_list: [] + +matrix_appservice_irc_appservice_token: '' +matrix_appservice_irc_homeserver_token: '' + +matrix_appservice_irc_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_appservice_irc_configuration_extension_yaml: | + # Your custom YAML configuration for Appservice IRC servers goes here. + # This configuration extends the default starting configuration (`matrix_appservice_irc_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_appservice_irc_configuration_yaml`. + +matrix_appservice_irc_configuration_extension: "{{ matrix_appservice_irc_configuration_extension_yaml|from_yaml if matrix_appservice_irc_configuration_extension_yaml|from_yaml is mapping else {} }}" + +matrix_appservice_irc_configuration: "{{ matrix_appservice_irc_configuration_yaml|from_yaml|combine(matrix_appservice_irc_configuration_extension, recursive=True) }}" + +# The original registration.yaml file generated by AppService IRC is merged with this config override, +# to produce the final registration.yaml file ultimately used by both the bridge and the homeserver. +# +# We do this to ensure consistency: +# - always having an up-to-date registration.yaml file (synced with the configuration file) +# - always having the same AS/HS token and appservice id in the registration.yaml file +# +# Learn more about this in `setup_install.yml` +matrix_appservice_irc_registration_override_yaml: | + id: appservice-irc + as_token: "{{ matrix_appservice_irc_appservice_token }}" + hs_token: "{{ matrix_appservice_irc_homeserver_token }}" + +matrix_appservice_irc_registration_override: "{{ matrix_appservice_irc_registration_override_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-appservice-irc/tasks/init.yml b/roles/matrix-bridge-appservice-irc/tasks/init.yml new file mode 100644 index 00000000..e1355796 --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/tasks/init.yml @@ -0,0 +1,31 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_appservice_irc_container_self_build and matrix_appservice_irc_enabled" + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-appservice-irc role needs to execute before the matrix-synapse role. + when: "matrix_appservice_irc_enabled|bool and matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-appservice-irc.service'] }}" + when: matrix_appservice_irc_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_appservice_irc_config_path }}/registration.yaml,dst=/matrix-appservice-irc-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-appservice-irc-registration.yaml"] }} + when: matrix_appservice_irc_enabled|bool diff --git a/roles/matrix-bridge-appservice-irc/tasks/main.yml b/roles/matrix-bridge-appservice-irc/tasks/main.yml new file mode 100644 index 00000000..da92ecf0 --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_appservice_irc_enabled|bool" + tags: + - setup-all + - setup-appservice-irc + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_appservice_irc_enabled|bool" + tags: + - setup-all + - setup-appservice-irc + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_appservice_irc_enabled|bool" + tags: + - setup-all + - setup-appservice-irc diff --git a/roles/matrix-bridge-appservice-irc/tasks/migrate_nedb_to_postgres.yml b/roles/matrix-bridge-appservice-irc/tasks/migrate_nedb_to_postgres.yml new file mode 100644 index 00000000..6b39ac62 --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/tasks/migrate_nedb_to_postgres.yml @@ -0,0 +1,70 @@ +- name: Fail if Postgres not enabled + fail: + msg: "Postgres via the matrix-postgres role is not enabled (`matrix_postgres_enabled`). Cannot migrate." + when: "not matrix_postgres_enabled|bool" + +# Defaults + +- name: Set postgres_start_wait_time, if not provided + set_fact: + postgres_start_wait_time: 15 + when: "postgres_start_wait_time|default('') == ''" + +# Actual import work + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + register: matrix_postgres_service_start_result + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + when: "matrix_postgres_service_start_result.changed|bool" + +- name: Check existence of matrix-appservice-irc service + stat: + path: "{{ matrix_systemd_path }}/matrix-appservice-irc.service" + register: matrix_appservice_irc_service_stat + +- name: Ensure matrix-appservice-irc is stopped + service: + name: matrix-appservice-irc + state: stopped + when: "matrix_appservice_irc_service_stat.stat.exists" + +- name: Import appservice-irc NeDB database into Postgres + command: + cmd: >- + {{ matrix_host_command_docker }} run + --rm + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --mount type=bind,src={{ matrix_appservice_irc_data_path }},dst=/data + --entrypoint=/bin/sh + {{ matrix_appservice_irc_docker_image }} + -c + '/usr/local/bin/node /app/lib/scripts/migrate-db-to-pgres.js --dbdir /data --privateKey /data/passkey.pem --connectionString {{ matrix_appservice_irc_database_connection_string }}' + +- name: Archive NeDB database files + command: + cmd: "mv {{ matrix_appservice_irc_data_path }}/{{ item }} {{ matrix_appservice_irc_data_path }}/{{ item }}.backup" + with_items: + - rooms.db + - users.db + +- name: Inject result + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: Your appservice-irc database files have been imported into Postgres. The original database files have been moved from `{{ matrix_appservice_irc_data_path }}/*.db` to `{{ matrix_appservice_irc_data_path }}/*.db.backup`. When you've confirmed that the import went well and everything works, you should be able to safely delete these files." + ] + }} diff --git a/roles/matrix-bridge-appservice-irc/tasks/setup_install.yml b/roles/matrix-bridge-appservice-irc/tasks/setup_install.yml new file mode 100644 index 00000000..75af882a --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/tasks/setup_install.yml @@ -0,0 +1,194 @@ +--- + +- name: Ensure Appservice IRC paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_appservice_irc_base_path }}", when: true } + - { path: "{{ matrix_appservice_irc_config_path }}", when: true } + - { path: "{{ matrix_appservice_irc_data_path }}", when: true } + - { path: "{{ matrix_appservice_irc_docker_src_files_path }}", when: "{{ matrix_appservice_irc_container_self_build }}" } + when: item.when|bool + +- name: Check if an old passkey file already exists + stat: + path: "{{ matrix_appservice_irc_base_path }}/passkey.pem" + register: matrix_appservice_irc_stat_passkey + +- block: + - name: (Data relocation) Ensure matrix-appservice-irc.service is stopped + service: + name: matrix-appservice-irc + state: stopped + daemon_reload: yes + failed_when: false + + - name: (Data relocation) Move AppService IRC passkey.pem file to ./data directory + command: "mv {{ matrix_appservice_irc_base_path }}/passkey.pem {{ matrix_appservice_irc_data_path }}/passkey.pem" + + - name: (Data relocation) Move AppService IRC database files to ./data directory + command: "mv {{ matrix_appservice_irc_base_path }}/{{ item }} {{ matrix_appservice_irc_data_path }}/{{ item }}" + with_items: + - rooms.db + - users.db + failed_when: false + when: "matrix_appservice_irc_stat_passkey.stat.exists" + +- set_fact: + matrix_appservice_irc_requires_restart: false + +- block: + - name: Check if a nedb database already exists + stat: + path: "{{ matrix_appservice_irc_data_path }}/users.db" + register: matrix_appservice_irc_nedb_database_path_local_stat_result + + - block: + - import_tasks: "{{ role_path }}/tasks/migrate_nedb_to_postgres.yml" + + - set_fact: + matrix_appservice_irc_requires_restart: true + when: "matrix_appservice_irc_nedb_database_path_local_stat_result.stat.exists|bool" + when: "matrix_appservice_irc_database_engine == 'postgres'" + +- name: Ensure Appservice IRC image is pulled + docker_image: + name: "{{ matrix_appservice_irc_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_appservice_irc_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_appservice_irc_docker_image_force_pull }}" + when: "matrix_appservice_irc_enabled|bool and not matrix_appservice_irc_container_self_build|bool" + +- name: Ensure matrix-appservice-irc repository is present when self-building + git: + repo: "{{ matrix_appservice_irc_docker_repo }}" + dest: "{{ matrix_appservice_irc_docker_src_files_path }}" + force: "yes" + register: matrix_appservice_irc_git_pull_results + when: "matrix_appservice_irc_enabled|bool and matrix_appservice_irc_container_self_build|bool" + +- name: Ensure matrix-appservice-irc Docker image is built + docker_image: + name: "{{ matrix_appservice_irc_docker_image }}" + source: build + force_source: "{{ matrix_appservice_irc_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_appservice_irc_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_appservice_irc_docker_src_files_path }}" + pull: yes + when: "matrix_appservice_irc_enabled|bool and matrix_appservice_irc_container_self_build|bool and matrix_appservice_irc_git_pull_results.changed" + +- name: Ensure Matrix Appservice IRC config installed + copy: + content: "{{ matrix_appservice_irc_configuration|to_nice_yaml }}" + dest: "{{ matrix_appservice_irc_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Check if Appservice IRC passkey exists + stat: + path: "{{ matrix_appservice_irc_data_path }}/passkey.pem" + register: irc_passkey_file + +- name: Generate Appservice IRC passkey if it doesn't exist + shell: "{{ matrix_host_command_openssl }} genpkey -out {{ matrix_appservice_irc_data_path }}/passkey.pem -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048" + become: true + become_user: "{{ matrix_user_username }}" + when: "not irc_passkey_file.stat.exists" + +# In the past, we used to generate the passkey.pem file with root, so permissions may not be okay. +# Fix it. +- name: (Migration) Ensure Appservice IRC passkey permissions are okay + file: + path: "{{ matrix_appservice_irc_data_path }}/passkey.pem" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +# Ideally, we'd like to generate the final registration.yaml file by ourselves. +# +# However, the IRC bridge supports multiple servers, which leads to multiple +# users/aliases/rooms rules in the registration file. +# +# Generating a proper file by ourselves is complicated and may lead to deviation +# from what the bridge is doing. +# +# Instead, we do another hacky thing - asking the bridge to generate a template, +# and then we parse it and fix it up with our own AS/HS token. +# We need to do this, because: +# - we'd like to have an up-to-date registration file +# - we can achieve this by asking the bridge to rebuild it each time +# - however, the bridge insists on regenerating all tokens each time +# - .. which is not friendly for integrating with the homeserver +# +# So we have a hybrid approach. We ask the bridge to always generate +# an up-to-date file, and we fix it up with some static values later on, +# to produce a final registration.yaml file, as we desire. +- name: Generate Appservice IRC registration-template.yaml + shell: >- + {{ matrix_host_command_docker }} run --rm --name matrix-appservice-irc-gen + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + -v {{ matrix_appservice_irc_config_path }}:/config:z + -v {{ matrix_appservice_irc_data_path }}:/data:z + --entrypoint=/bin/bash + {{ matrix_appservice_irc_docker_image }} + -c + 'node app.js + -r + -f /config/registration-template.yaml + -u "http://matrix-appservice-irc:9999" + -c /config/config.yaml + -l irc_bot' + changed_when: false + +- name: Read Appservice IRC registration-template.yaml + slurp: + src: "{{ matrix_appservice_irc_config_path }}/registration-template.yaml" + register: matrix_appservice_irc_registration_template_slurp + +- name: Remove unnecessary Appservice IRC registration-template.yaml + file: + path: "{{ matrix_appservice_irc_config_path }}/registration-template.yaml" + state: absent + changed_when: false + +- name: Parse registration-template.yaml + set_fact: + matrix_appservice_irc_registration_template: "{{ matrix_appservice_irc_registration_template_slurp['content'] | b64decode | from_yaml }}" + +- name: Combine registration-template.yaml and own registration override config + set_fact: + matrix_appservice_irc_registration: "{{ matrix_appservice_irc_registration_template|combine(matrix_appservice_irc_registration_override, recursive=True) }}" + +- name: Ensure Appservice IRC registration.yaml installed + copy: + content: "{{ matrix_appservice_irc_registration|to_nice_yaml }}" + dest: "{{ matrix_appservice_irc_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-appservice-irc.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-appservice-irc.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-appservice-irc.service" + mode: 0644 + register: matrix_appservice_irc_systemd_service_result + +- name: Ensure systemd reloaded after matrix-appservice-irc.service installation + service: + daemon_reload: yes + when: "matrix_appservice_irc_systemd_service_result.changed" + +- name: Ensure matrix-appservice-irc.service restarted, if necessary + service: + name: "matrix-appservice-irc.service" + state: restarted + when: "matrix_appservice_irc_requires_restart|bool" diff --git a/roles/matrix-bridge-appservice-irc/tasks/setup_uninstall.yml b/roles/matrix-bridge-appservice-irc/tasks/setup_uninstall.yml new file mode 100644 index 00000000..2b5e5dfd --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-appservice-irc service + stat: + path: "{{ matrix_systemd_path }}/matrix-appservice-irc.service" + register: matrix_appservice_irc_service_stat + +- name: Ensure matrix-appservice-irc is stopped + service: + name: matrix-appservice-irc + state: stopped + daemon_reload: yes + when: "matrix_appservice_irc_service_stat.stat.exists" + +- name: Ensure matrix-appservice-irc.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-appservice-irc.service" + state: absent + when: "matrix_appservice_irc_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-appservice-irc.service removal + service: + daemon_reload: yes + when: "matrix_appservice_irc_service_stat.stat.exists" diff --git a/roles/matrix-bridge-appservice-irc/tasks/validate_config.yml b/roles/matrix-bridge-appservice-irc/tasks/validate_config.yml new file mode 100644 index 00000000..bd08427c --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/tasks/validate_config.yml @@ -0,0 +1,35 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_appservice_irc_appservice_token" + - "matrix_appservice_irc_homeserver_token" + +# Our base configuration (`matrix_appservice_irc_configuration_yaml`) is not enough to +# let the playbook run without errors. +# +# Unless the final configuration (`matrix_appservice_irc_configuration`) contains an `ircService` definition, +# we'd fail generating the registration.yaml file with a non-helpful error. +# +# This is a safety check to ensure we fail earlier and in a nicer way. +- name: Fail if no additional configuration provided + fail: + msg: >- + Your Appservice IRC configuration is incomplete (lacking an `ircService.servers` configuration). + You need to define one or more servers by either using `matrix_appservice_irc_ircService_servers` + or by extending the base configuration with additional configuration in `matrix_appservice_irc_configuration_extension_yaml`. + Overriding the whole bridge's configuration (`matrix_appservice_irc_configuration`) is yet another possibility. + when: "matrix_appservice_irc_configuration.ircService.servers|length == 0" + +- name: (Deprecation) Catch and report renamed appservice-irc variables + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_appservice_irc_container_expose_client_server_api_port', 'new': ''} diff --git a/roles/matrix-bridge-appservice-irc/templates/config.yaml.j2 b/roles/matrix-bridge-appservice-irc/templates/config.yaml.j2 new file mode 100644 index 00000000..94bbda7b --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/templates/config.yaml.j2 @@ -0,0 +1,134 @@ +#jinja2: lstrip_blocks: True +homeserver: + # The URL to the home server for client-server API calls, also used to form the + # media URLs as displayed in bridged IRC channels: + url: {{ matrix_appservice_irc_homeserver_url }} + # + # The URL of the homeserver hosting media files. This is only used to transform + # mxc URIs to http URIs when bridging m.room.[file|image] events. Optional. By + # default, this is the homeserver URL, specified above. + # + media_url: {{ matrix_appservice_irc_homeserver_media_url }} + + # Drop Matrix messages which are older than this number of seconds, according to + # the event's origin_server_ts. + # If the bridge is down for a while, the homeserver will attempt to send all missed + # events on reconnection. These events may be hours old, which can be confusing to + # IRC users if they are then bridged. This option allows these old messages to be + # dropped. + # CAUTION: This is a very coarse heuristic. Federated homeservers may have different + # clock times and hence produce different origin_server_ts values, which may be old + # enough to cause *all* events from the homeserver to be dropped. + # Default: 0 (don't ever drop) + # dropMatrixMessagesAfterSecs: 300 # 5 minutes + + # The 'domain' part for user IDs on this home server. Usually (but not always) + # is the "domain name" part of the HS URL. + domain: {{ matrix_appservice_irc_homeserver_domain }} + + # Should presence be enabled for matrix clients on this bridge. If disabled on the + # homeserver then it should also be disabled here to avoid excess traffic. + # Default: true + enablePresence: {{ matrix_appservice_irc_homeserver_enablePresence|to_json }} + +ircService: + # WARNING: The bridge needs to send plaintext passwords to the IRC server, it cannot + # send a password hash. As a result, passwords (NOT hashes) are stored encrypted in + # the database. + # + # To generate a .pem file: + # $ openssl genpkey -out passkey.pem -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 + # + # The path to the RSA PEM-formatted private key to use when encrypting IRC passwords + # for storage in the database. Passwords are stored by using the admin room command + # `!storepass server.name passw0rd. When a connection is made to IRC on behalf of + # the Matrix user, this password will be sent as the server password (PASS command). + passwordEncryptionKeyPath: "/data/passkey.pem" # does not typically need modification + + # Config for Matrix -> IRC bridging + matrixHandler: + # Cache this many matrix events in memory to be used for m.relates_to messages (usually replies). + eventCacheSize: 4096 + + servers: {{ matrix_appservice_irc_ircService_servers|to_json }} + + # Configuration for an ident server. If you are running a public bridge it is + # advised you setup an ident server so IRC mods can ban specific matrix users + # rather than the application service itself. + ident: + # True to listen for Ident requests and respond with the + # matrix user's user_id (converted to ASCII, respecting RFC 1413). + # Default: false. + enabled: false + # The port to listen on for incoming ident requests. + # Ports below 1024 require root to listen on, and you may not want this to + # run as root. Instead, you can get something like an Apache to yank up + # incoming requests to 113 to a high numbered port. Set the port to listen + # on instead of 113 here. + # Default: 113. + port: 1113 + # The address to listen on for incoming ident requests. + # Default: 0.0.0.0 + address: "::" + + # Configuration for logging. Optional. Default: console debug level logging + # only. + logging: + # Level to log on console/logfile. One of error|warn|info|debug + level: "debug" + # The file location to log to. This is relative to the project directory. + #logfile: "debug.log" + # The file location to log errors to. This is relative to the project + # directory. + #errfile: "errors.log" + # Whether to log to the console or not. + toConsole: true + # The max number of files to keep. Files will be overwritten eventually due + # to rotations. + maxFiles: 5 + + # Optional. Enable Prometheus metrics. If this is enabled, you MUST install `prom-client`: + # $ npm install prom-client@6.3.0 + # Metrics will then be available via GET /metrics on the bridge listening port (-p). + metrics: + # Whether to actually enable the metric endpoint. Default: false + enabled: true + # When collecting remote user active times, which "buckets" should be used. Defaults are given below. + # The bucket name is formed of a duration and a period. (h=hours,d=days,w=weeks). + remoteUserAgeBuckets: + - "1h" + - "1d" + - "1w" + + # Configuration for the provisioning API. + # + # GET /_matrix/provision/link + # GET /_matrix/provision/unlink + # GET /_matrix/provision/listlinks + # + provisioning: + # True to enable the provisioning HTTP endpoint. Default: false. + enabled: false + # The number of seconds to wait before giving up on getting a response from + # an IRC channel operator. If the channel operator does not respond within the + # allotted time period, the provisioning request will fail. + # Default: 300 seconds (5 mins) + requestTimeoutSeconds: 300 + +# Options here are generally only applicable to large-scale bridges and may have +# consequences greater than other options in this configuration file. +advanced: + # The maximum number of HTTP(S) sockets to maintain. Usually this is unlimited + # however for large bridges it is important to rate limit the bridge to avoid + # accidentally overloading the homeserver. Defaults to 1000, which should be + # enough for the vast majority of use cases. + maxHttpSockets: 1000 + +# Use an external database to store bridge state. +database: + # database engine (must be 'postgres' or 'nedb'). Default: nedb + engine: {{ matrix_appservice_irc_database_engine|to_json }} + # Either a PostgreSQL connection string, or a path to the NeDB storage directory. + # For postgres, it must start with postgres:// + # For NeDB, it must start with nedb://. The path is relative to the project directory. + connectionString: {{ matrix_appservice_irc_database_connectionString|to_json }} diff --git a/roles/matrix-bridge-appservice-irc/templates/systemd/matrix-appservice-irc.service.j2 b/roles/matrix-bridge-appservice-irc/templates/systemd/matrix-appservice-irc.service.j2 new file mode 100644 index 00000000..8650bd8d --- /dev/null +++ b/roles/matrix-bridge-appservice-irc/templates/systemd/matrix-appservice-irc.service.j2 @@ -0,0 +1,46 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Appservice IRC bridge +{% for service in matrix_appservice_irc_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_appservice_irc_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-irc 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-irc 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-appservice-irc \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_appservice_irc_container_http_host_bind_port %} + -p {{ matrix_appservice_irc_container_http_host_bind_port }}:9999 \ + {% endif %} + -v {{ matrix_appservice_irc_config_path }}:/config:z \ + -v {{ matrix_appservice_irc_data_path }}:/data:z \ + {% for arg in matrix_appservice_irc_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + --entrypoint=/bin/bash \ + {{ matrix_appservice_irc_docker_image }} \ + -c 'node app.js -c /config/config.yaml -f /config/registration.yaml -p 9999' + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-irc 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-irc 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-appservice-irc + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-appservice-slack/defaults/main.yml b/roles/matrix-bridge-appservice-slack/defaults/main.yml new file mode 100644 index 00000000..bb801273 --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/defaults/main.yml @@ -0,0 +1,118 @@ +# matrix-appservice-slack is a Matrix <-> Slack bridge +# See: https://github.com/matrix-org/matrix-appservice-slack + +matrix_appservice_slack_enabled: true + +matrix_appservice_slack_container_self_build: false +matrix_appservice_slack_docker_repo: "https://github.com/matrix-org/matrix-appservice-slack.git" +matrix_appservice_slack_docker_src_files_path: "{{ matrix_base_data_path }}/appservice-slack/docker-src" + +matrix_appservice_slack_version: release-1.5.0 +matrix_appservice_slack_docker_image: "{{ matrix_container_global_registry_prefix }}matrixdotorg/matrix-appservice-slack:{{ matrix_appservice_slack_version }}" +matrix_appservice_slack_docker_image_force_pull: "{{ matrix_appservice_slack_docker_image.endswith(':latest') }}" + +matrix_appservice_slack_base_path: "{{ matrix_base_data_path }}/appservice-slack" +matrix_appservice_slack_config_path: "{{ matrix_appservice_slack_base_path }}/config" +matrix_appservice_slack_data_path: "{{ matrix_appservice_slack_base_path }}/data" + +matrix_appservice_slack_public_endpoint: /appservice-slack +matrix_appservice_slack_inbound_uri_prefix: "{{ matrix_homeserver_url }}{{ matrix_appservice_slack_public_endpoint }}" + +# Once you make a control room in Matrix, you can get its ID by typing any message and checking its source +matrix_appservice_slack_control_room_id: '' +matrix_appservice_slack_bot_name: 'slackbot' +matrix_appservice_slack_user_prefix: 'slack_' + +# Controls the SLACK_PORT and MATRIX_PORT of the installation +matrix_appservice_slack_matrix_port: 9004 +matrix_appservice_slack_slack_port: 9003 + +# Controls whether the appservice-slack container exposes its HTTP port (tcp/9003 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9999"), or empty string to not expose. +matrix_appservice_slack_container_http_host_bind_port: '' + +matrix_appservice_slack_homeserver_media_url: "{{ matrix_server_fqn_matrix }}" +matrix_appservice_slack_homeserver_url: "http://matrix-synapse:8008" +matrix_appservice_slack_homeserver_domain: "{{ matrix_domain }}" +matrix_appservice_slack_appservice_url: 'http://matrix-appservice-slack' + +# A list of extra arguments to pass to the container +matrix_appservice_slack_container_extra_arguments: [] + +# List of systemd services that matrix-appservice-slack.service depends on. +matrix_appservice_slack_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-appservice-slack.service wants +matrix_appservice_slack_systemd_wanted_services_list: [] + +matrix_appservice_slack_appservice_token: '' +matrix_appservice_slack_homeserver_token: '' +matrix_appservice_slack_id_token: '' + +matrix_appservice_slack_database_engine: nedb +matrix_appservice_slack_database_username: matrix_appservice_slack +matrix_appservice_slack_database_password: ~ +matrix_appservice_slack_database_hostname: 'matrix-postgres' +matrix_appservice_slack_database_port: 5432 +matrix_appservice_slack_database_name: matrix_appservice_slack + +# This is just the Postgres connection string, if Postgres is used. +# Naming clashes with `matrix_appservice_slack_database_connectionString` somewhat. +matrix_appservice_slack_database_connection_string: 'postgresql://{{ matrix_appservice_slack_database_username }}:{{ matrix_appservice_slack_database_password }}@{{ matrix_appservice_slack_database_hostname }}:{{ matrix_appservice_slack_database_port }}/{{ matrix_appservice_slack_database_name }}?sslmode=disable' + +# This is what actually goes into `database.connectionString` for the bridge. +matrix_appservice_slack_database_connectionString: "{{ + { + 'nedb': 'nedb:///data', + 'postgres': matrix_appservice_slack_database_connection_string, + }[matrix_appservice_slack_database_engine] +}}" + + +matrix_appservice_slack_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_appservice_slack_configuration_extension_yaml: | + #slack_hook_port: 9898 + #inbound_uri_prefix: "https://my.server.here:9898/" + #bot_username: "slackbot" + #username_prefix: "slack_" + # Optional + #slack_master_token: "abc-123-def" + # Optional + #matrix_admin_room: "!aBcDeF:matrix.org" + #homeserver: + # url: http://localhost:8008 + # server_name: my.server + # Optional + #tls: + # key_file: /path/to/tls.key + # crt_file: /path/to/tls.crt + #logging: + # console: "info" + # files: + # - "./debug.log": "info" + #- "./error.log": "error" + +matrix_appservice_slack_configuration_extension: "{{ matrix_appservice_slack_configuration_extension_yaml|from_yaml if matrix_appservice_slack_configuration_extension_yaml|from_yaml else {} }}" + +matrix_appservice_slack_configuration: "{{ matrix_appservice_slack_configuration_yaml|from_yaml|combine(matrix_appservice_slack_configuration_extension, recursive=True) }}" + +matrix_appservice_slack_registration_yaml: | + id: "{{ matrix_appservice_slack_id_token }}" + as_token: "{{ matrix_appservice_slack_appservice_token }}" + hs_token: "{{ matrix_appservice_slack_homeserver_token }}" + namespaces: + users: + - exclusive: true + regex: '@{{ matrix_appservice_slack_user_prefix }}.*' + aliases: + - exclusive: false + regex: '#{{ matrix_appservice_slack_user_prefix }}.*' + rooms: [] + url: "{{matrix_appservice_slack_appservice_url}}:{{ matrix_appservice_slack_matrix_port }}" + sender_localpart: slackbot + rate_limited: true + protocols: null + +matrix_appservice_slack_registration: "{{ matrix_appservice_slack_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-appservice-slack/tasks/init.yml b/roles/matrix-bridge-appservice-slack/tasks/init.yml new file mode 100644 index 00000000..0584e624 --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/tasks/init.yml @@ -0,0 +1,86 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_appservice_slack_container_self_build and matrix_appservice_slack_enabled" + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-appservice-slack role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-appservice-slack.service'] }}" + when: matrix_appservice_slack_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_appservice_slack_config_path }}/slack-registration.yaml,dst=/matrix-appservice-slack-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-appservice-slack-registration.yaml"] }} + when: matrix_appservice_slack_enabled|bool + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-appservice-slack role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append Slack Appservice's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-bridge-appservice-slack role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate Matrix Appservice Slack proxying configuration for matrix-nginx-proxy + set_fact: + matrix_appservice_slack_matrix_nginx_proxy_configuration: | + location {{ matrix_appservice_slack_public_endpoint }} { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_appservice_slack_appservice_url }}:{{ matrix_appservice_slack_slack_port }}"; + proxy_pass $backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_appservice_slack_slack_port }}; + {% endif %} + } + + - name: Register Slack Appservice proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks|default([]) + + + [matrix_appservice_slack_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_appservice_slack_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the Matrix Slack bridge but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ something }}` + URL endpoint to the matrix-appservice-slack container. + You can expose the container's port using the `matrix_appservice_slack_container_http_host_bind_port` variable. + when: "matrix_appservice_slack_enabled|bool and matrix_nginx_proxy_enabled is not defined" diff --git a/roles/matrix-bridge-appservice-slack/tasks/main.yml b/roles/matrix-bridge-appservice-slack/tasks/main.yml new file mode 100644 index 00000000..acd03fff --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_appservice_slack_enabled|bool" + tags: + - setup-all + - setup-appservice-slack + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_appservice_slack_enabled|bool" + tags: + - setup-all + - setup-appservice-slack + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_appservice_slack_enabled|bool" + tags: + - setup-all + - setup-appservice-slack diff --git a/roles/matrix-bridge-appservice-slack/tasks/migrate_nedb_to_postgres.yml b/roles/matrix-bridge-appservice-slack/tasks/migrate_nedb_to_postgres.yml new file mode 100644 index 00000000..fedad977 --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/tasks/migrate_nedb_to_postgres.yml @@ -0,0 +1,66 @@ +- name: Fail if Postgres not enabled + fail: + msg: "Postgres via the matrix-postgres role is not enabled (`matrix_postgres_enabled`). Cannot migrate." + when: "not matrix_postgres_enabled|bool" + +# Defaults + +- name: Set postgres_start_wait_time, if not provided + set_fact: + postgres_start_wait_time: 15 + when: "postgres_start_wait_time|default('') == ''" + +# Actual import work + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + register: matrix_postgres_service_start_result + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + when: "matrix_postgres_service_start_result.changed|bool" + +- name: Ensure matrix-appservice-slack is stopped + service: + name: matrix-appservice-slack + state: stopped + +- name: Import appservice-slack NeDB database into Postgres + command: + cmd: >- + {{ matrix_host_command_docker }} run + --rm + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --mount type=bind,src={{ matrix_appservice_slack_data_path }},dst=/data + --entrypoint=/bin/sh + {{ matrix_appservice_slack_docker_image }} + -c + '/usr/local/bin/node /usr/src/app/lib/scripts/migrateToPostgres.js --dbdir /data --connectionString {{ matrix_appservice_slack_database_connection_string }}' + +- name: Archive NeDB database files + command: + cmd: "mv {{ matrix_appservice_slack_data_path }}/{{ item }} {{ matrix_appservice_slack_data_path }}/{{ item }}.backup" + with_items: + - teams.db + - room-store.db + - user-store.db + - event-store.db + +- name: Inject result + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: Your appservice-slack database files have been imported into Postgres. The original database files have been moved from `{{ matrix_appservice_slack_data_path }}/*.db` to `{{ matrix_appservice_slack_data_path }}/*.db.backup`. When you've confirmed that the import went well and everything works, you should be able to safely delete these files." + ] + }} diff --git a/roles/matrix-bridge-appservice-slack/tasks/setup_install.yml b/roles/matrix-bridge-appservice-slack/tasks/setup_install.yml new file mode 100644 index 00000000..b170fcb8 --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/tasks/setup_install.yml @@ -0,0 +1,94 @@ +--- + +- name: Ensure AppService Slack paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_appservice_slack_base_path }}", when: true } + - { path: "{{ matrix_appservice_slack_config_path }}", when: true } + - { path: "{{ matrix_appservice_slack_data_path }}", when: true } + - { path: "{{ matrix_appservice_slack_docker_src_files_path }}", when: "{{ matrix_appservice_slack_container_self_build }}" } + when: item.when|bool + +- set_fact: + matrix_appservice_slack_requires_restart: false + +- block: + - name: Check if a nedb database already exists + stat: + path: "{{ matrix_appservice_slack_data_path }}/teams.db" + register: matrix_appservice_slack_nedb_database_path_local_stat_result + + - block: + - import_tasks: "{{ role_path }}/tasks/migrate_nedb_to_postgres.yml" + + - set_fact: + matrix_appservice_slack_requires_restart: true + when: "matrix_appservice_slack_nedb_database_path_local_stat_result.stat.exists|bool" + when: "matrix_appservice_slack_database_engine == 'postgres'" + +- name: Ensure Appservice Slack image is pulled + docker_image: + name: "{{ matrix_appservice_slack_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_appservice_slack_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_appservice_slack_docker_image_force_pull }}" + when: "not matrix_appservice_slack_container_self_build|bool" + +- name: Ensure matrix-appservice-slack repository is present when self-building + git: + repo: "{{ matrix_appservice_slack_docker_repo }}" + dest: "{{ matrix_appservice_slack_docker_src_files_path }}" + force: "yes" + register: matrix_appservice_slack_git_pull_results + when: "matrix_appservice_slack_container_self_build|bool" + +- name: Ensure matrix-appservice-slack Docker image is built + docker_image: + name: "{{ matrix_appservice_slack_docker_image }}" + source: build + force_source: "{{ matrix_appservice_slack_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_appservice_slack_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_appservice_slack_docker_src_files_path }}" + pull: yes + when: "matrix_appservice_slack_container_self_build|bool and matrix_appservice_slack_git_pull_results.changed" + +- name: Ensure Matrix Appservice Slack config installed + copy: + content: "{{ matrix_appservice_slack_configuration|to_nice_yaml }}" + dest: "{{ matrix_appservice_slack_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure appservice-slack registration.yaml installed + copy: + content: "{{ matrix_appservice_slack_registration|to_nice_yaml }}" + dest: "{{ matrix_appservice_slack_config_path }}/slack-registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-appservice-slack.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-appservice-slack.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-appservice-slack.service" + mode: 0644 + register: matrix_appservice_slack_systemd_service_result + +- name: Ensure systemd reloaded after matrix-appservice-slack.service installation + service: + daemon_reload: yes + when: "matrix_appservice_slack_systemd_service_result.changed" + +- name: Ensure matrix-appservice-slack.service restarted, if necessary + service: + name: "matrix-appservice-slack.service" + state: restarted + when: "matrix_appservice_slack_requires_restart|bool" diff --git a/roles/matrix-bridge-appservice-slack/tasks/setup_uninstall.yml b/roles/matrix-bridge-appservice-slack/tasks/setup_uninstall.yml new file mode 100644 index 00000000..0b83d02e --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-appservice-slack service + stat: + path: "{{ matrix_systemd_path }}/matrix-appservice-slack.service" + register: matrix_appservice_slack_service_stat + +- name: Ensure matrix-appservice-slack is stopped + service: + name: matrix-appservice-slack + state: stopped + daemon_reload: yes + when: "matrix_appservice_slack_service_stat.stat.exists" + +- name: Ensure matrix-appservice-slack.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-appservice-slack.service" + state: absent + when: "matrix_appservice_slack_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-appservice-slack.service removal + service: + daemon_reload: yes + when: "matrix_appservice_slack_service_stat.stat.exists" diff --git a/roles/matrix-bridge-appservice-slack/tasks/validate_config.yml b/roles/matrix-bridge-appservice-slack/tasks/validate_config.yml new file mode 100644 index 00000000..8af10f2f --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/tasks/validate_config.yml @@ -0,0 +1,12 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_appservice_slack_control_room_id" + - "matrix_appservice_slack_appservice_token" + - "matrix_appservice_slack_homeserver_token" + - "matrix_appservice_slack_id_token" diff --git a/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 b/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 new file mode 100644 index 00000000..bf8072c1 --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/templates/config.yaml.j2 @@ -0,0 +1,20 @@ +#jinja2: lstrip_blocks: True +slack_hook_port: {{ matrix_appservice_slack_slack_port }} +inbound_uri_prefix: "{{ matrix_appservice_slack_inbound_uri_prefix }}" +bot_username: "{{ matrix_appservice_slack_bot_name }}" +username_prefix: {{ matrix_appservice_slack_user_prefix }} + +homeserver: + media_url: "{{ matrix_appservice_slack_homeserver_media_url }}" + url: "{{ matrix_appservice_slack_homeserver_url }}" + server_name: "{{ matrix_domain }}" + +{% if matrix_appservice_slack_database_engine == 'nedb' %} +dbdir: "/data" +{% else %} +db: + engine: {{ matrix_appservice_slack_database_engine|to_json }} + connectionString: {{ matrix_appservice_slack_database_connectionString|to_json }} +{% endif %} + +matrix_admin_room: "{{ matrix_appservice_slack_control_room_id }}" diff --git a/roles/matrix-bridge-appservice-slack/templates/systemd/matrix-appservice-slack.service.j2 b/roles/matrix-bridge-appservice-slack/templates/systemd/matrix-appservice-slack.service.j2 new file mode 100644 index 00000000..21ba27ef --- /dev/null +++ b/roles/matrix-bridge-appservice-slack/templates/systemd/matrix-appservice-slack.service.j2 @@ -0,0 +1,45 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Appservice Slack bridge +{% for service in matrix_appservice_slack_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_appservice_slack_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-slack 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-slack 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-appservice-slack \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_appservice_slack_container_http_host_bind_port %} + -p {{ matrix_appservice_slack_container_http_host_bind_port }}:{{matrix_appservice_slack_slack_port}} \ + {% endif %} + -v {{ matrix_appservice_slack_config_path }}:/config:z \ + -v {{ matrix_appservice_slack_data_path }}:/data:z \ + {% for arg in matrix_appservice_slack_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_appservice_slack_docker_image }} \ + node app.js -p {{matrix_appservice_slack_matrix_port}} -c /config/config.yaml -f /config/slack-registration.yaml + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-slack 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-slack 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-appservice-slack + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-appservice-webhooks/defaults/main.yml b/roles/matrix-bridge-appservice-webhooks/defaults/main.yml new file mode 100644 index 00000000..7f26ea58 --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/defaults/main.yml @@ -0,0 +1,77 @@ +# matrix-appservice-webhooks is a Matrix <-> webhook bridge +# See: https://github.com/turt2live/matrix-appservice-webhooks + +matrix_appservice_webhooks_enabled: true + +matrix_appservice_webhooks_version: latest +matrix_appservice_webhooks_docker_image: "{{ matrix_container_global_registry_prefix }}turt2live/matrix-appservice-webhooks:{{ matrix_appservice_webhooks_version }}" +matrix_appservice_webhooks_docker_image_force_pull: "{{ matrix_appservice_webhooks_docker_image.endswith(':latest') }}" + +matrix_appservice_webhooks_base_path: "{{ matrix_base_data_path }}/appservice-webhooks" +matrix_appservice_webhooks_config_path: "{{ matrix_appservice_webhooks_base_path }}/config" +matrix_appservice_webhooks_data_path: "{{ matrix_appservice_webhooks_base_path }}/data" + +# If nginx-proxy is disabled, the bridge itself expects its endpoint to be on its own domain (e.g. "localhost:6789") +matrix_appservice_webhooks_public_endpoint: /appservice-webhooks +matrix_appservice_webhooks_inbound_uri_prefix: "{{ matrix_homeserver_url }}{{ matrix_appservice_webhooks_public_endpoint }}" + +# Once you make a control room in Matrix, you can get its ID by typing any message and checking its source +matrix_appservice_webhooks_control_room_id: '' +matrix_appservice_webhooks_bot_name: 'webhookbot' +matrix_appservice_webhooks_user_prefix: '_webhook' + +# Controls the webhooks_PORT and MATRIX_PORT of the installation +matrix_appservice_webhooks_matrix_port: 6789 + +# Controls whether the appservice-webhooks container exposes its HTTP port (tcp/6789 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9999"), or empty string to not expose. +matrix_appservice_webhooks_container_http_host_bind_port: '' + +matrix_appservice_webhooks_homeserver_media_url: "{{ matrix_server_fqn_matrix }}" +matrix_appservice_webhooks_homeserver_url: "http://matrix-synapse:8008" +matrix_appservice_webhooks_homeserver_domain: "{{ matrix_domain }}" +matrix_appservice_webhooks_appservice_url: 'http://matrix-appservice-webhooks' + +# A list of extra arguments to pass to the container +matrix_appservice_webhooks_container_extra_arguments: [] + +# List of systemd services that matrix-appservice-webhooks.service depends on. +matrix_appservice_webhooks_systemd_required_services_list: ['docker.service', 'matrix-synapse.service'] + +# List of systemd services that matrix-appservice-webhooks.service wants +matrix_appservice_webhooks_systemd_wanted_services_list: [] + +matrix_appservice_webhooks_appservice_token: '' +matrix_appservice_webhooks_homeserver_token: '' +matrix_appservice_webhooks_id_token: '' +matrix_appservice_webhooks_api_secret: '' + +# Logging information (info and verbose is available) default is: info +matrix_appservice_webhooks_log_level: 'info' + +matrix_appservice_webhooks_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_appservice_webhooks_configuration_extension_yaml: | + # + +matrix_appservice_webhooks_configuration_extension: "{{ matrix_appservice_webhooks_configuration_extension_yaml|from_yaml if matrix_appservice_webhooks_configuration_extension_yaml|from_yaml else {} }}" + +matrix_appservice_webhooks_configuration: "{{ matrix_appservice_webhooks_configuration_yaml|from_yaml|combine(matrix_appservice_webhooks_configuration_extension, recursive=True) }}" + +matrix_appservice_webhooks_registration_yaml: | + id: "{{ matrix_appservice_webhooks_id_token }}" + hs_token: "{{ matrix_appservice_webhooks_homeserver_token }}" + as_token: "{{ matrix_appservice_webhooks_appservice_token }}" + namespaces: + users: + - exclusive: true + regex: '^@{{ matrix_appservice_webhooks_user_prefix | regex_escape }}.*:{{ matrix_domain | regex_escape }}$' + aliases: [] + rooms: [] + url: "{{ matrix_appservice_webhooks_appservice_url }}:{{ matrix_appservice_webhooks_matrix_port }}" + sender_localpart: _webhook + rate_limited: false + protocols: null + +matrix_appservice_webhooks_registration: "{{ matrix_appservice_webhooks_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-appservice-webhooks/tasks/init.yml b/roles/matrix-bridge-appservice-webhooks/tasks/init.yml new file mode 100644 index 00000000..a42e14c9 --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/tasks/init.yml @@ -0,0 +1,81 @@ +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-appservice-webhooks role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-appservice-webhooks.service'] }}" + when: matrix_appservice_webhooks_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_appservice_webhooks_config_path }}/webhooks-registration.yaml,dst=/matrix-appservice-webhooks-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-appservice-webhooks-registration.yaml"] }} + when: matrix_appservice_webhooks_enabled|bool + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-appservice-webhooks role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append webhooks Appservice's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-bridge-appservice-webhooks role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate Matrix Appservice webhooks proxying configuration for matrix-nginx-proxy + set_fact: + matrix_appservice_webhooks_matrix_nginx_proxy_configuration: | + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + location ~ ^{{ matrix_appservice_webhooks_public_endpoint }}/(.*)$ { + resolver 127.0.0.11 valid=5s; + set $backend "matrix-appservice-webhooks:{{ matrix_appservice_webhooks_matrix_port }}"; + proxy_pass http://$backend/$1; + } + {% else %} + {# Generic configuration for use outside of our container setup #} + location {{ matrix_appservice_webhooks_public_endpoint }}/ { + proxy_pass http://127.0.0.1:{{ matrix_appservice_webhooks_matrix_port }}/; + } + {% endif %} + + - name: Register webhooks Appservice proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks|default([]) + + + [matrix_appservice_webhooks_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_appservice_webhooks_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the Matrix webhooks bridge but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ matrix_appservice_webhooks_public_endpoint }}` + URL endpoint to the matrix-appservice-webhooks container. + You can expose the container's port using the `matrix_appservice_webhooks_container_http_host_bind_port` variable. + when: "matrix_appservice_webhooks_enabled|bool and matrix_nginx_proxy_enabled is not defined" diff --git a/roles/matrix-bridge-appservice-webhooks/tasks/main.yml b/roles/matrix-bridge-appservice-webhooks/tasks/main.yml new file mode 100644 index 00000000..216905f3 --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_appservice_webhooks_enabled|bool" + tags: + - setup-all + - setup-appservice-webhooks + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_appservice_webhooks_enabled|bool" + tags: + - setup-all + - setup-appservice-webhooks + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_appservice_webhooks_enabled|bool" + tags: + - setup-all + - setup-appservice-webhooks diff --git a/roles/matrix-bridge-appservice-webhooks/tasks/setup_install.yml b/roles/matrix-bridge-appservice-webhooks/tasks/setup_install.yml new file mode 100644 index 00000000..9ddc121a --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/tasks/setup_install.yml @@ -0,0 +1,64 @@ +--- + +- name: Ensure Appservice webhooks image is pulled + docker_image: + name: "{{ matrix_appservice_webhooks_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_appservice_webhooks_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_appservice_webhooks_docker_image_force_pull }}" + +- name: Ensure AppService webhooks paths exist + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_appservice_webhooks_base_path }}" + - "{{ matrix_appservice_webhooks_config_path }}" + - "{{ matrix_appservice_webhooks_data_path }}" + +- name: Ensure Matrix Appservice webhooks config is installed + copy: + content: "{{ matrix_appservice_webhooks_configuration|to_nice_yaml }}" + dest: "{{ matrix_appservice_webhooks_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure Matrix Appservice webhooks schema.yml template exists + template: + src: "{{ role_path }}/templates/schema.yml.j2" + dest: "{{ matrix_appservice_webhooks_config_path }}/schema.yml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure Matrix Appservice webhooks database.json template exists + template: + src: "{{ role_path }}/templates/database.json.j2" + dest: "{{ matrix_appservice_webhooks_data_path }}/database.json" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure appservice-webhooks registration.yaml installed + copy: + content: "{{ matrix_appservice_webhooks_registration|to_nice_yaml }}" + dest: "{{ matrix_appservice_webhooks_config_path }}/webhooks-registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-appservice-webhooks.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-appservice-webhooks.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-appservice-webhooks.service" + mode: 0644 + register: matrix_appservice_webhooks_systemd_service_result + +- name: Ensure systemd reloaded after matrix-appservice-webhooks.service installation + service: + daemon_reload: yes + when: "matrix_appservice_webhooks_systemd_service_result.changed" diff --git a/roles/matrix-bridge-appservice-webhooks/tasks/setup_uninstall.yml b/roles/matrix-bridge-appservice-webhooks/tasks/setup_uninstall.yml new file mode 100644 index 00000000..d8e973ce --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-appservice-webhooks service + stat: + path: "{{ matrix_systemd_path }}/matrix-appservice-webhooks.service" + register: matrix_appservice_webhooks_service_stat + +- name: Ensure matrix-appservice-webhooks is stopped + service: + name: matrix-appservice-webhooks + state: stopped + daemon_reload: yes + when: "matrix_appservice_webhooks_service_stat.stat.exists" + +- name: Ensure matrix-appservice-webhooks.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-appservice-webhooks.service" + state: absent + when: "matrix_appservice_webhooks_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-appservice-webhooks.service removal + service: + daemon_reload: yes + when: "matrix_appservice_webhooks_service_stat.stat.exists" diff --git a/roles/matrix-bridge-appservice-webhooks/tasks/validate_config.yml b/roles/matrix-bridge-appservice-webhooks/tasks/validate_config.yml new file mode 100644 index 00000000..b92a0eb9 --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/tasks/validate_config.yml @@ -0,0 +1,12 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_appservice_webhooks_appservice_token" + - "matrix_appservice_webhooks_homeserver_token" + - "matrix_appservice_webhooks_id_token" + - "matrix_appservice_webhooks_api_secret" diff --git a/roles/matrix-bridge-appservice-webhooks/templates/config.yaml.j2 b/roles/matrix-bridge-appservice-webhooks/templates/config.yaml.j2 new file mode 100644 index 00000000..49751624 --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/templates/config.yaml.j2 @@ -0,0 +1,28 @@ +#jinja2: lstrip_blocks: True +# Configuration specific to the application service. All fields (unless otherwise marked) are required. +homeserver: + # The domain for the client-server API calls. + url: "{{ matrix_appservice_webhooks_homeserver_url }}" + + # The domain part for user IDs on this home server. Usually, but not always, this is the same as the + # home server's URL. + domain: "{{ matrix_domain }}" + +# Configuration specific to the bridge. All fields (unless otherwise marked) are required. +webhookBot: + # The localpart to use for the bot. May require re-registering the application service. + localpart: "_webhook" + +# Provisioning API options +provisioning: + # Your secret for the API. Required for all provisioning API requests. + secret: '{{ matrix_appservice_webhooks_api_secret }}' + +# Configuration related to the web portion of the bridge. Handles the inbound webhooks +web: + hookUrlBase: "{{ matrix_appservice_webhooks_inbound_uri_prefix }}" + +logging: + console: true + consoleLevel: {{ matrix_appservice_webhooks_log_level }} + writeFiles: false diff --git a/roles/matrix-bridge-appservice-webhooks/templates/database.json.j2 b/roles/matrix-bridge-appservice-webhooks/templates/database.json.j2 new file mode 100644 index 00000000..e70f1d83 --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/templates/database.json.j2 @@ -0,0 +1,13 @@ +{ + "defaultEnv": { + "ENV": "NODE_ENV" + }, + "development": { + "driver": "sqlite3", + "filename": "/data/development.db" + }, + "production": { + "driver": "sqlite3", + "filename": "/data/production.db" + } +} diff --git a/roles/matrix-bridge-appservice-webhooks/templates/schema.yml.j2 b/roles/matrix-bridge-appservice-webhooks/templates/schema.yml.j2 new file mode 100644 index 00000000..e999555d --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/templates/schema.yml.j2 @@ -0,0 +1,54 @@ +"$schema": "http://json-schema.org/draft-04/schema#" +type: "object" +properties: + provisioning: + type: "object" + properties: + secret: + type: "string" + homeserver: + type: "object" + properties: + domain: + type: "string" + url: + type: "string" + mediaUrl: + type: "string" + web: + type: "object" + properties: + hookUrlBase: + type: "string" + webhookBot: + type: "object" + properties: + localpart: + type: "string" + appearance: + type: "object" + properties: + displayName: + type: "string" + avatarUrl: + type: "string" + logging: + type: "object" + properties: + file: + type: "string" + console: + type: "boolean" + consoleLevel: + type: "string" + fileLevel: + type: "string" + writeFiles: + type: "boolean" + rotate: + type: "object" + properties: + size: + type: "number" + count: + type: "number" diff --git a/roles/matrix-bridge-appservice-webhooks/templates/systemd/matrix-appservice-webhooks.service.j2 b/roles/matrix-bridge-appservice-webhooks/templates/systemd/matrix-appservice-webhooks.service.j2 new file mode 100644 index 00000000..f27111b3 --- /dev/null +++ b/roles/matrix-bridge-appservice-webhooks/templates/systemd/matrix-appservice-webhooks.service.j2 @@ -0,0 +1,45 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Appservice webhooks bridge +{% for service in matrix_appservice_webhooks_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_appservice_webhooks_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-webhooks 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-webhooks 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-appservice-webhooks \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_appservice_webhooks_container_http_host_bind_port %} + -p {{ matrix_appservice_webhooks_container_http_host_bind_port }}:{{matrix_appservice_webhooks_matrix_port}} \ + {% endif %} + -v {{ matrix_appservice_webhooks_config_path }}:/config:z \ + -v {{ matrix_appservice_webhooks_data_path }}:/data:z \ + {% for arg in matrix_appservice_webhooks_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_appservice_webhooks_docker_image }} \ + node index.js -p {{ matrix_appservice_webhooks_matrix_port }} -c /config/config.yaml -f /config/webhooks-registration.yaml + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-appservice-webhooks 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-appservice-webhooks 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-appservice-webhooks + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-heisenbridge/defaults/main.yml b/roles/matrix-bridge-heisenbridge/defaults/main.yml new file mode 100644 index 00000000..be95af8d --- /dev/null +++ b/roles/matrix-bridge-heisenbridge/defaults/main.yml @@ -0,0 +1,47 @@ +# heisenbridge is a bouncer-style Matrix IRC bridge +# See: https://github.com/hifi/heisenbridge + +matrix_heisenbridge_enabled: true + +matrix_heisenbridge_version: latest +matrix_heisenbridge_docker_image: "{{ matrix_container_global_registry_prefix }}hif1/heisenbridge:{{ matrix_heisenbridge_version }}" +matrix_heisenbridge_docker_image_force_pull: "{{ matrix_heisenbridge_docker_image.endswith(':latest') }}" + +# Set this to your Matrix ID if you want to enforce the owner, otherwise first _local_ user becomes one +matrix_heisenbridge_owner: "" + +# Enabling identd will bind to host port 113/TCP +matrix_heisenbridge_identd_enabled: false + +matrix_heisenbridge_base_path: "{{ matrix_base_data_path }}/heisenbridge" + +# A list of extra arguments to pass to the container +matrix_heisenbridge_container_extra_arguments: [] + +# List of systemd services that service depends on. +matrix_heisenbridge_systemd_required_services_list: ['docker.service'] + +# List of systemd services that service wants +matrix_heisenbridge_systemd_wanted_services_list: [] + +matrix_heisenbridge_homeserver_url: "{{ matrix_homeserver_container_url }}" + +matrix_heisenbridge_appservice_token: '' +matrix_heisenbridge_homeserver_token: '' + +# Default registration file +matrix_heisenbridge_registration_yaml: + id: heisenbridge + url: http://matrix-heisenbridge:9898 + as_token: "{{ matrix_heisenbridge_appservice_token }}" + hs_token: "{{ matrix_heisenbridge_homeserver_token }}" + rate_limited: false + sender_localpart: heisenbridge + namespaces: + users: + - regex: '@hbirc_.*' + exclusive: true + aliases: [] + rooms: [] + +matrix_heisenbridge_registration: "{{ matrix_heisenbridge_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-heisenbridge/tasks/init.yml b/roles/matrix-bridge-heisenbridge/tasks/init.yml new file mode 100644 index 00000000..18e89b68 --- /dev/null +++ b/roles/matrix-bridge-heisenbridge/tasks/init.yml @@ -0,0 +1,24 @@ +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-heisenbridge role needs to execute before the matrix-synapse role. + when: "matrix_heisenbridge_enabled and matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-heisenbridge.service'] }}" + when: matrix_heisenbridge_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_heisenbridge_base_path }}/registration.yaml,dst=/heisenbridge-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/heisenbridge-registration.yaml"] }} + when: matrix_heisenbridge_enabled|bool diff --git a/roles/matrix-bridge-heisenbridge/tasks/main.yml b/roles/matrix-bridge-heisenbridge/tasks/main.yml new file mode 100644 index 00000000..1358709d --- /dev/null +++ b/roles/matrix-bridge-heisenbridge/tasks/main.yml @@ -0,0 +1,15 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_heisenbridge_enabled|bool" + tags: + - setup-all + - setup-heisenbridge + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_heisenbridge_enabled|bool" + tags: + - setup-all + - setup-heisenbridge diff --git a/roles/matrix-bridge-heisenbridge/tasks/setup_install.yml b/roles/matrix-bridge-heisenbridge/tasks/setup_install.yml new file mode 100644 index 00000000..03cf9ec3 --- /dev/null +++ b/roles/matrix-bridge-heisenbridge/tasks/setup_install.yml @@ -0,0 +1,38 @@ +--- + +- name: Ensure heisenbridge image is pulled + docker_image: + name: "{{ matrix_heisenbridge_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_heisenbridge_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_heisenbridge_docker_image_force_pull }}" + +- name: Ensure heisenbridge paths exist + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_heisenbridge_base_path }}" + +- name: Ensure heisenbridge registration.yaml installed if provided + copy: + content: "{{ matrix_heisenbridge_registration|to_nice_yaml }}" + dest: "{{ matrix_heisenbridge_base_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-heisenbridge.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-heisenbridge.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-heisenbridge.service" + mode: 0644 + register: matrix_heisenbridge_systemd_service_result + +- name: Ensure systemd reloaded after matrix-heisenbridge.service installation + service: + daemon_reload: yes + when: matrix_heisenbridge_systemd_service_result.changed diff --git a/roles/matrix-bridge-heisenbridge/tasks/setup_uninstall.yml b/roles/matrix-bridge-heisenbridge/tasks/setup_uninstall.yml new file mode 100644 index 00000000..853faf7a --- /dev/null +++ b/roles/matrix-bridge-heisenbridge/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-heisenbridge service + stat: + path: "{{ matrix_systemd_path }}/matrix-heisenbridge.service" + register: matrix_heisenbridge_service_stat + +- name: Ensure matrix-heisenbridge is stopped + service: + name: matrix-heisenbridge + state: stopped + daemon_reload: yes + when: "matrix_heisenbridge_service_stat.stat.exists" + +- name: Ensure matrix-heisenbridge.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-heisenbridge.service" + state: absent + when: "matrix_heisenbridge_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-heisenbridge.service removal + service: + daemon_reload: yes + when: "matrix_heisenbridge_service_stat.stat.exists" diff --git a/roles/matrix-bridge-heisenbridge/templates/systemd/matrix-heisenbridge.service.j2 b/roles/matrix-bridge-heisenbridge/templates/systemd/matrix-heisenbridge.service.j2 new file mode 100644 index 00000000..e27b88f1 --- /dev/null +++ b/roles/matrix-bridge-heisenbridge/templates/systemd/matrix-heisenbridge.service.j2 @@ -0,0 +1,51 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=a bouncer-style Matrix IRC bridge +{% for service in matrix_heisenbridge_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_heisenbridge_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_docker }} kill matrix-heisenbridge +ExecStartPre=-{{ matrix_host_command_docker }} rm matrix-heisenbridge + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-heisenbridge \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_heisenbridge_identd_enabled %} + -p 113:13113 \ + {% endif %} + -v {{ matrix_heisenbridge_base_path }}:/config:z \ + {% for arg in matrix_heisenbridge_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_heisenbridge_docker_image }} \ + {% if matrix_heisenbridge_identd_enabled %} + --identd \ + --identd-port 13113 \ + {% endif %} + {% if matrix_heisenbridge_owner %} + -o {{ matrix_heisenbridge_owner }} \ + {% endif %} + --config /config/registration.yaml \ + --listen-address 0.0.0.0 \ + --listen-port 9898 \ + {{ matrix_heisenbridge_homeserver_url }} + +ExecStop=-{{ matrix_host_command_docker }} kill matrix-heisenbridge +ExecStop=-{{ matrix_host_command_docker }} rm matrix-heisenbridge +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-heisenbridge + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mautrix-facebook/defaults/main.yml b/roles/matrix-bridge-mautrix-facebook/defaults/main.yml new file mode 100644 index 00000000..71a225f7 --- /dev/null +++ b/roles/matrix-bridge-mautrix-facebook/defaults/main.yml @@ -0,0 +1,115 @@ +# mautrix-facebook is a Matrix <-> Facebook bridge +# See: https://github.com/tulir/mautrix-facebook + +matrix_mautrix_facebook_enabled: true + +matrix_mautrix_facebook_container_image_self_build: false +matrix_mautrix_facebook_container_image_self_build_repo: "https://github.com/tulir/mautrix-facebook.git" + +matrix_mautrix_facebook_version: latest +# See: https://mau.dev/tulir/mautrix-facebook/container_registry +matrix_mautrix_facebook_docker_image: "{{ matrix_mautrix_facebook_docker_image_name_prefix }}tulir/mautrix-facebook:{{ matrix_mautrix_facebook_version }}" +matrix_mautrix_facebook_docker_image_name_prefix: "{{ 'localhost/' if matrix_mautrix_facebook_container_image_self_build else 'dock.mau.dev/' }}" +matrix_mautrix_facebook_docker_image_force_pull: "{{ matrix_mautrix_facebook_docker_image.endswith(':latest') }}" + +matrix_mautrix_facebook_base_path: "{{ matrix_base_data_path }}/mautrix-facebook" +matrix_mautrix_facebook_config_path: "{{ matrix_mautrix_facebook_base_path }}/config" +matrix_mautrix_facebook_data_path: "{{ matrix_mautrix_facebook_base_path }}/data" +matrix_mautrix_facebook_docker_src_files_path: "{{ matrix_mautrix_facebook_base_path }}/docker-src" + +matrix_mautrix_facebook_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mautrix_facebook_homeserver_domain: '{{ matrix_domain }}' +matrix_mautrix_facebook_appservice_address: 'http://matrix-mautrix-facebook:29319' + +# A list of extra arguments to pass to the container +matrix_mautrix_facebook_container_extra_arguments: [] + +# List of systemd services that matrix-mautrix-facebook.service depends on. +matrix_mautrix_facebook_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-mautrix-facebook.service wants +matrix_mautrix_facebook_systemd_wanted_services_list: [] + +matrix_mautrix_facebook_appservice_token: '' +matrix_mautrix_facebook_homeserver_token: '' + + +# Database-related configuration fields. +# +# To use SQLite: +# - change the engine (`matrix_mautrix_facebook_database_engine: 'sqlite'`) +# - change to the last bridge version that supported SQLite: +# `matrix_mautrix_facebook_docker_image: "{{ matrix_mautrix_facebook_docker_image_name_prefix }}tulir/mautrix-facebook:da1b4ec596e334325a1589e70829dea46e73064b"` +# - plan your migration to Postgres, as this bridge does not support SQLite anymore (and neither will the playbook in the future). +# +# To use Postgres: +# - adjust your database credentials via the `matrix_mautrix_facebook_postgres_*` variables +matrix_mautrix_facebook_database_engine: 'postgres' + +matrix_mautrix_facebook_sqlite_database_path_local: "{{ matrix_mautrix_facebook_data_path }}/mautrix-facebook.db" +matrix_mautrix_facebook_sqlite_database_path_in_container: "/data/mautrix-facebook.db" + +matrix_mautrix_facebook_database_username: 'matrix_mautrix_facebook' +matrix_mautrix_facebook_database_password: 'some-password' +matrix_mautrix_facebook_database_hostname: 'matrix-postgres' +matrix_mautrix_facebook_database_port: 5432 +matrix_mautrix_facebook_database_name: 'matrix_mautrix_facebook' + +matrix_mautrix_facebook_database_connection_string: 'postgres://{{ matrix_mautrix_facebook_database_username }}:{{ matrix_mautrix_facebook_database_password }}@{{ matrix_mautrix_facebook_database_hostname }}:{{ matrix_mautrix_facebook_database_port }}/{{ matrix_mautrix_facebook_database_name }}' + +matrix_mautrix_facebook_appservice_database: "{{ + { + 'sqlite': ('sqlite:///' + matrix_mautrix_facebook_sqlite_database_path_in_container), + 'postgres': matrix_mautrix_facebook_database_connection_string, + }[matrix_mautrix_facebook_database_engine] +}}" + + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mautrix_facebook_login_shared_secret: '' + +matrix_mautrix_facebook_bridge_login_shared_secret_map: "{{ {matrix_mautrix_facebook_homeserver_domain: matrix_mautrix_facebook_login_shared_secret} if matrix_mautrix_facebook_login_shared_secret else {} }}" + +matrix_mautrix_facebook_appservice_bot_username: facebookbot + +matrix_mautrix_facebook_bridge_presence: true + +# Default 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_mautrix_facebook_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mautrix_facebook_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mautrix_facebook_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mautrix_facebook_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mautrix_facebook_configuration_yaml`. + +matrix_mautrix_facebook_configuration_extension: "{{ matrix_mautrix_facebook_configuration_extension_yaml|from_yaml if matrix_mautrix_facebook_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mautrix_facebook_configuration_yaml`. +matrix_mautrix_facebook_configuration: "{{ matrix_mautrix_facebook_configuration_yaml|from_yaml|combine(matrix_mautrix_facebook_configuration_extension, recursive=True) }}" + +matrix_mautrix_facebook_registration_yaml: | + id: facebook + as_token: "{{ matrix_mautrix_facebook_appservice_token }}" + hs_token: "{{ matrix_mautrix_facebook_homeserver_token }}" + namespaces: + users: + - exclusive: true + regex: '^@facebook_.+:{{ matrix_mautrix_facebook_homeserver_domain|regex_escape }}$' + - exclusive: true + regex: '^@{{ matrix_mautrix_facebook_appservice_bot_username|regex_escape }}:{{ matrix_mautrix_facebook_homeserver_domain|regex_escape }}$' + url: {{ matrix_mautrix_facebook_appservice_address }} + # See https://github.com/tulir/mautrix-signal/issues/43 + sender_localpart: _bot_{{ matrix_mautrix_facebook_appservice_bot_username }} + rate_limited: false + de.sorunome.msc2409.push_ephemeral: true + +matrix_mautrix_facebook_registration: "{{ matrix_mautrix_facebook_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mautrix-facebook/tasks/init.yml b/roles/matrix-bridge-mautrix-facebook/tasks/init.yml new file mode 100644 index 00000000..cf67f227 --- /dev/null +++ b/roles/matrix-bridge-mautrix-facebook/tasks/init.yml @@ -0,0 +1,23 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mautrix_facebook_container_image_self_build and matrix_mautrix_facebook_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mautrix-facebook.service'] }}" + when: matrix_mautrix_facebook_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mautrix_facebook_config_path }}/registration.yaml,dst=/matrix-mautrix-facebook-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mautrix-facebook-registration.yaml"] }} + when: matrix_mautrix_facebook_enabled|bool diff --git a/roles/matrix-bridge-mautrix-facebook/tasks/main.yml b/roles/matrix-bridge-mautrix-facebook/tasks/main.yml new file mode 100644 index 00000000..54fb6f9d --- /dev/null +++ b/roles/matrix-bridge-mautrix-facebook/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mautrix_facebook_enabled|bool" + tags: + - setup-all + - setup-mautrix-facebook + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mautrix_facebook_enabled|bool" + tags: + - setup-all + - setup-mautrix-facebook + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mautrix_facebook_enabled|bool" + tags: + - setup-all + - setup-mautrix-facebook diff --git a/roles/matrix-bridge-mautrix-facebook/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-facebook/tasks/setup_install.yml new file mode 100644 index 00000000..4f98635d --- /dev/null +++ b/roles/matrix-bridge-mautrix-facebook/tasks/setup_install.yml @@ -0,0 +1,129 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mautrix-facebook role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_mautrix_facebook_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mautrix_facebook_sqlite_database_path_local }}" + register: matrix_mautrix_facebook_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mautrix_facebook_sqlite_database_path_local }}" + dst: "{{ matrix_mautrix_facebook_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mautrix_facebook_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mautrix-facebook.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mautrix_facebook_requires_restart: true + when: "matrix_mautrix_facebook_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mautrix_facebook_database_engine == 'postgres'" + +- name: Ensure Mautrix Facebook image is pulled + docker_image: + name: "{{ matrix_mautrix_facebook_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mautrix_facebook_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_facebook_docker_image_force_pull }}" + when: not matrix_mautrix_facebook_container_image_self_build + +- name: Ensure Mautrix Facebook paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mautrix_facebook_base_path }}", when: true } + - { path: "{{ matrix_mautrix_facebook_config_path }}", when: true } + - { path: "{{ matrix_mautrix_facebook_data_path }}", when: true } + - { path: "{{ matrix_mautrix_facebook_docker_src_files_path }}", when: "{{ matrix_mautrix_facebook_container_image_self_build }}" } + when: item.when|bool + +- name: Ensure Mautrix Facebook repository is present on self-build + git: + repo: "{{ matrix_mautrix_facebook_container_image_self_build_repo }}" + dest: "{{ matrix_mautrix_facebook_docker_src_files_path }}" +# version: "{{ matrix_coturn_docker_image.split(':')[1] }}" + force: "yes" + register: matrix_mautrix_facebook_git_pull_results + when: "matrix_mautrix_facebook_container_image_self_build|bool" + +- name: Ensure Mautrix Facebook Docker image is built + docker_image: + name: "{{ matrix_mautrix_facebook_docker_image }}" + source: build + force_source: "{{ matrix_mautrix_facebook_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_facebook_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mautrix_facebook_docker_src_files_path }}" + pull: yes + when: "matrix_mautrix_facebook_container_image_self_build|bool" + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mautrix_facebook_base_path }}/mautrix-facebook.db" + register: matrix_mautrix_facebook_stat_database + +- name: (Data relocation) Ensure matrix-mautrix-facebook.service is stopped + service: + name: matrix-mautrix-facebook + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mautrix_facebook_stat_database.stat.exists" + +- name: (Data relocation) Move mautrix-facebook database file to ./data directory + command: "mv {{ matrix_mautrix_facebook_base_path }}/mautrix-facebook.db {{ matrix_mautrix_facebook_data_path }}/mautrix-facebook.db" + when: "matrix_mautrix_facebook_stat_database.stat.exists" + +- name: Ensure mautrix-facebook config.yaml installed + copy: + content: "{{ matrix_mautrix_facebook_configuration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_facebook_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mautrix-facebook registration.yaml installed + copy: + content: "{{ matrix_mautrix_facebook_registration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_facebook_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mautrix-facebook.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mautrix-facebook.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-mautrix-facebook.service" + mode: 0644 + register: matrix_mautrix_facebook_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mautrix-facebook.service installation + service: + daemon_reload: yes + when: "matrix_mautrix_facebook_systemd_service_result.changed" + +- name: Ensure matrix-mautrix-facebook.service restarted, if necessary + service: + name: "matrix-mautrix-facebook.service" + state: restarted + when: "matrix_mautrix_facebook_requires_restart|bool" diff --git a/roles/matrix-bridge-mautrix-facebook/tasks/setup_uninstall.yml b/roles/matrix-bridge-mautrix-facebook/tasks/setup_uninstall.yml new file mode 100644 index 00000000..efc8aa74 --- /dev/null +++ b/roles/matrix-bridge-mautrix-facebook/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mautrix-facebook service + stat: + path: "{{ matrix_systemd_path }}/matrix-mautrix-facebook.service" + register: matrix_mautrix_facebook_service_stat + +- name: Ensure matrix-mautrix-facebook is stopped + service: + name: matrix-mautrix-facebook + state: stopped + daemon_reload: yes + when: "matrix_mautrix_facebook_service_stat.stat.exists" + +- name: Ensure matrix-mautrix-facebook.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mautrix-facebook.service" + state: absent + when: "matrix_mautrix_facebook_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mautrix-facebook.service removal + service: + daemon_reload: yes + when: "matrix_mautrix_facebook_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mautrix-facebook/tasks/validate_config.yml b/roles/matrix-bridge-mautrix-facebook/tasks/validate_config.yml new file mode 100644 index 00000000..0879bad9 --- /dev/null +++ b/roles/matrix-bridge-mautrix-facebook/tasks/validate_config.yml @@ -0,0 +1,31 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mautrix_facebook_appservice_token" + - "matrix_mautrix_facebook_homeserver_token" + +- block: + - name: Fail if on SQLite, unless on the last version supporting SQLite + fail: + msg: >- + You're trying to use the mautrix-facebook bridge with an SQLite database. + Going forward, this bridge only supports Postgres. + To learn more about this, see our changelog: https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/CHANGELOG.md#breaking-change-the-mautrix-facebook-bridge-now-requires-a-postgres-database + when: "not matrix_mautrix_facebook_docker_image.endswith(':da1b4ec596e334325a1589e70829dea46e73064b')" + + - name: Inject warning if still on SQLite + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: Your mautrix-facebook bridge setup is still on SQLite. Your bridge is not getting any updates and will likely stop working at some point. To learn more about this, see our changelog: https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/CHANGELOG.md#breaking-change-the-mautrix-facebook-bridge-now-requires-a-postgres-database" + ] + }} + when: "matrix_mautrix_facebook_database_engine == 'sqlite'" diff --git a/roles/matrix-bridge-mautrix-facebook/templates/config.yaml.j2 b/roles/matrix-bridge-mautrix-facebook/templates/config.yaml.j2 new file mode 100644 index 00000000..628db713 --- /dev/null +++ b/roles/matrix-bridge-mautrix-facebook/templates/config.yaml.j2 @@ -0,0 +1,227 @@ +#jinja2: lstrip_blocks: "True" +# Homeserver details +homeserver: + # The address that this appservice can use to connect to the homeserver. + address: {{ matrix_mautrix_facebook_homeserver_address }} + # The domain of the homeserver (for MXIDs, etc). + domain: {{ matrix_mautrix_facebook_homeserver_domain }} + # Whether or not to verify the SSL certificate of the homeserver. + # Only applies if address starts with https:// + verify_ssl: true + # Whether or not the homeserver supports asmux-specific endpoints, + # such as /_matrix/client/unstable/net.maunium.asmux/dms for atomically + # updating m.direct. + asmux: false + +# Application service host/registration related details +# Changing these values requires regeneration of the registration. +appservice: + # The address that the homeserver can use to connect to this appservice. + address: {{ matrix_mautrix_facebook_appservice_address }} + + # The hostname and port where this appservice should listen. + hostname: 0.0.0.0 + port: 29319 + # The maximum body size of appservice API requests (from the homeserver) in mebibytes + # Usually 1 is enough, but on high-traffic bridges you might need to increase this to avoid 413s + max_body_size: 1 + + # The full URI to the database. Only Postgres is currently supported. + database: {{ matrix_mautrix_facebook_appservice_database|to_json }} + + # Public part of web server for out-of-Matrix interaction with the bridge. + public: + # Whether or not the public-facing endpoints should be enabled. + enabled: false + # The prefix to use in the public-facing endpoints. + prefix: /public + # The base URL where the public-facing endpoints are available. The prefix is not added + # implicitly. + external: https://example.com/public + # Shared secret for integration managers such as mautrix-manager. + # If set to "generate", a random string will be generated on the next startup. + # If null, integration manager access to the API will not be possible. + shared_secret: generate + + # The unique ID of this appservice. + id: facebook + # Username of the appservice bot. + bot_username: {{ matrix_mautrix_facebook_appservice_bot_username|to_json }} + # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty + # to leave display name/avatar as-is. + bot_displayname: Facebook bridge bot + bot_avatar: mxc://maunium.net/ygtkteZsXnGJLJHRchUwYWak + + # Authentication tokens for AS <-> HS communication. + as_token: "{{ matrix_mautrix_facebook_appservice_token }}" + hs_token: "{{ matrix_mautrix_facebook_homeserver_token }}" + +# Prometheus telemetry config. Requires prometheus-client to be installed. +metrics: + enabled: false + listen_port: 8000 + +# Bridge config +bridge: + # Localpart template of MXIDs for Facebook users. + # {userid} is replaced with the user ID of the Facebook user. + username_template: "facebook_{userid}" + # Localpart template for per-user room grouping community IDs. + # The bridge will create these communities and add all of the specific user's portals to the community. + # {localpart} is the MXID localpart and {server} is the MXID server part of the user. + # + # `facebook_{localpart}={server}` is a good value. + community_template: null + # Displayname template for Facebook users. + # {displayname} is replaced with the display name of the Facebook user + # as defined below in displayname_preference. + # Keys available for displayname_preference are also available here. + displayname_template: '{displayname} (FB)' + # Available keys: + # "name" (full name) + # "first_name" + # "last_name" + # "nickname" + # "own_nickname" (user-specific!) + displayname_preference: + - name + - first_name + + # The prefix for commands. Only required in non-management rooms. + command_prefix: "!fb" + + # Number of chats to sync (and create portals for) on startup/login. + # Set 0 to disable automatic syncing. + initial_chat_sync: 10 + # Whether or not the Facebook users of logged in Matrix users should be + # invited to private chats when the user sends a message from another client. + invite_own_puppet_to_pm: false + # Whether or not to use /sync to get presence, read receipts and typing notifications + # when double puppeting is enabled + sync_with_custom_puppets: true + # Whether or not to update the m.direct account data event when double puppeting is enabled. + # Note that updating the m.direct event is not atomic (except with mautrix-asmux) + # and is therefore prone to race conditions. + sync_direct_chat_list: false + # Servers to always allow double puppeting from + double_puppet_server_map: {} + # example.com: https://example.com + # Allow using double puppeting from any server with a valid client .well-known file. + double_puppet_allow_discovery: false + # Shared secrets for https://github.com/devture/matrix-synapse-shared-secret-auth + # + # If set, custom puppets will be enabled automatically for local users + # instead of users having to find an access token and run `login-matrix` + # manually. + # If using this for other servers than the bridge's server, + # you must also set the URL in the double_puppet_server_map. + login_shared_secret_map: {{ matrix_mautrix_facebook_bridge_login_shared_secret_map|to_json }} + presence: {{ matrix_mautrix_facebook_bridge_presence|to_json }} + # Whether or not to update avatars when syncing all contacts at startup. + update_avatar_initial_sync: true + # End-to-bridge encryption support options. These require matrix-nio to be installed with pip + # and login_shared_secret to be configured in order to get a device for the bridge bot. + # + # Additionally, https://github.com/matrix-org/synapse/pull/5758 is required if using a normal + # application service. + encryption: + # Allow encryption, work in group chat rooms with e2ee enabled + allow: false + # Default to encryption, force-enable encryption in all portals the bridge creates + # This will cause the bridge bot to be in private chats for the encryption to work properly. + default: false + # Options for automatic key sharing. + key_sharing: + # Enable key sharing? If enabled, key requests for rooms where users are in will be fulfilled. + # You must use a client that supports requesting keys from other users to use this feature. + allow: false + # Require the requesting device to have a valid cross-signing signature? + # This doesn't require that the bridge has verified the device, only that the user has verified it. + # Not yet implemented. + require_cross_signing: false + # Require devices to be verified by the bridge? + # Verification by the bridge is not yet implemented. + require_verification: true + # Whether or not the bridge should send a read receipt from the bridge bot when a message has + # been sent to Facebook. + delivery_receipts: false + # Whether to allow inviting arbitrary mxids to portal rooms + allow_invites: false + # Settings for backfilling messages from Facebook. + backfill: + # Whether or not the Facebook users of logged in Matrix users should be + # invited to private chats when backfilling history from Facebook. This is + # usually needed to prevent rate limits and to allow timestamp massaging. + invite_own_puppet: true + # Maximum number of messages to backfill initially. + # Set to 0 to disable backfilling when creating portal. + initial_limit: 0 + # Maximum number of messages to backfill if messages were missed while + # the bridge was disconnected. + # Set to 0 to disable backfilling missed messages. + missed_limit: 1000 + # If using double puppeting, should notifications be disabled + # while the initial backfill is in progress? + disable_notifications: false + periodic_reconnect: + # Interval in seconds in which to automatically reconnect all users. + # This can be used to automatically mitigate the bug where Facebook stops sending messages. + # Set to -1 to disable periodic reconnections entirely. + interval: -1 + # What to do in periodic reconnects. Either "refresh" or "reconnect" + mode: refresh + # Should even disconnected users be reconnected? + always: false + # The number of seconds that a disconnection can last without triggering an automatic re-sync + # and missed message backfilling when reconnecting. + # Set to 0 to always re-sync, or -1 to never re-sync automatically. + resync_max_disconnected_time: 5 + # Whether or not temporary disconnections should send notices to the notice room. + # If this is false, disconnections will never send messages and connections will only send + # messages if it was disconnected for more than resync_max_disconnected_time seconds. + temporary_disconnect_notices: true + # Whether or not the bridge should try to "refresh" the connection if a normal reconnection + # attempt fails. + refresh_on_reconnection_fail: false + # Set this to true to tell the bridge to re-send m.bridge events to all rooms on the next run. + # This field will automatically be changed back to false after it, + # except if the config file is not writable. + resend_bridge_info: false + + # Permissions for using the bridge. + # Permitted values: + # user - Use the bridge with puppeting. + # admin - Use and administrate the bridge. + # Permitted keys: + # * - All Matrix users + # domain - All users on that homeserver + # mxid - Specific user + permissions: + '{{ matrix_mautrix_facebook_homeserver_domain }}': user + +# Python logging configuration. +# +# See section 16.7.2 of the Python documentation for more info: +# https://docs.python.org/3.6/library/logging.config.html#configuration-dictionary-schema +logging: + version: 1 + formatters: + colored: + (): mautrix_facebook.util.ColorFormatter + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + normal: + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + handlers: + console: + class: logging.StreamHandler + formatter: colored + loggers: + mau: + level: DEBUG + paho: + level: INFO + aiohttp: + level: INFO + root: + level: DEBUG + handlers: [console] diff --git a/roles/matrix-bridge-mautrix-facebook/templates/systemd/matrix-mautrix-facebook.service.j2 b/roles/matrix-bridge-mautrix-facebook/templates/systemd/matrix-mautrix-facebook.service.j2 new file mode 100644 index 00000000..f3af4b9f --- /dev/null +++ b/roles/matrix-bridge-mautrix-facebook/templates/systemd/matrix-mautrix-facebook.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mautrix Facebook bridge +{% for service in matrix_mautrix_facebook_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mautrix_facebook_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-facebook 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-facebook 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-facebook \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -v {{ matrix_mautrix_facebook_config_path }}:/config:z \ + -v {{ matrix_mautrix_facebook_data_path }}:/data:z \ + {% for arg in matrix_mautrix_facebook_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mautrix_facebook_docker_image }} \ + python3 -m mautrix_facebook -c /config/config.yaml --no-update + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-facebook 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-facebook 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mautrix-facebook + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mautrix-hangouts/defaults/main.yml b/roles/matrix-bridge-mautrix-hangouts/defaults/main.yml new file mode 100644 index 00000000..48b66b8d --- /dev/null +++ b/roles/matrix-bridge-mautrix-hangouts/defaults/main.yml @@ -0,0 +1,115 @@ +# mautrix-hangouts is a Matrix <-> Hangouts bridge +# See: https://github.com/tulir/mautrix-hangouts + +matrix_mautrix_hangouts_enabled: true + +matrix_mautrix_hangouts_container_image_self_build: false +matrix_mautrix_hangouts_container_image_self_build_repo: "https://github.com/tulir/mautrix-hangouts.git" + +matrix_mautrix_hangouts_version: latest +# See: https://mau.dev/tulir/mautrix-hangouts/container_registry +matrix_mautrix_hangouts_docker_image: "{{ matrix_mautrix_hangouts_docker_image_name_prefix }}tulir/mautrix-hangouts:{{ matrix_mautrix_hangouts_version }}" +matrix_mautrix_hangouts_docker_image_name_prefix: "{{ 'localhost/' if matrix_mautrix_hangouts_container_image_self_build else 'dock.mau.dev/' }}" +matrix_mautrix_hangouts_docker_image_force_pull: "{{ matrix_mautrix_hangouts_docker_image.endswith(':latest') }}" + +matrix_mautrix_hangouts_base_path: "{{ matrix_base_data_path }}/mautrix-hangouts" +matrix_mautrix_hangouts_config_path: "{{ matrix_mautrix_hangouts_base_path }}/config" +matrix_mautrix_hangouts_data_path: "{{ matrix_mautrix_hangouts_base_path }}/data" +matrix_mautrix_hangouts_docker_src_files_path: "{{ matrix_mautrix_hangouts_base_path }}/docker-src" + +matrix_mautrix_hangouts_public_endpoint: '/mautrix-hangouts' + +matrix_mautrix_hangouts_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mautrix_hangouts_homeserver_domain: '{{ matrix_domain }}' +matrix_mautrix_hangouts_appservice_address: 'http://matrix-mautrix-hangouts:8080' + +# Controls whether the matrix-mautrix-hangouts container exposes its HTTP port (tcp/8080 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9007"), or empty string to not expose. +matrix_mautrix_hangouts_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_mautrix_hangouts_container_extra_arguments: [] + +# List of systemd services that matrix-mautrix-hangouts.service depends on. +matrix_mautrix_hangouts_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-mautrix-hangouts.service wants +matrix_mautrix_hangouts_systemd_wanted_services_list: [] + +matrix_mautrix_hangouts_appservice_token: '' +matrix_mautrix_hangouts_homeserver_token: '' + + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_mautrix_hangouts_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_mautrix_hangouts_postgres_*` variables +matrix_mautrix_hangouts_database_engine: 'sqlite' + +matrix_mautrix_hangouts_sqlite_database_path_local: "{{ matrix_mautrix_hangouts_data_path }}/mautrix-hangouts.db" +matrix_mautrix_hangouts_sqlite_database_path_in_container: "/data/mautrix-hangouts.db" + +matrix_mautrix_hangouts_database_username: 'matrix_mautrix_hangouts' +matrix_mautrix_hangouts_database_password: 'some-password' +matrix_mautrix_hangouts_database_hostname: 'matrix-postgres' +matrix_mautrix_hangouts_database_port: 5432 +matrix_mautrix_hangouts_database_name: 'matrix_mautrix_hangouts' + +matrix_mautrix_hangouts_database_connection_string: 'postgres://{{ matrix_mautrix_hangouts_database_username }}:{{ matrix_mautrix_hangouts_database_password }}@{{ matrix_mautrix_hangouts_database_hostname }}:{{ matrix_mautrix_hangouts_database_port }}/{{ matrix_mautrix_hangouts_database_name }}' + +matrix_mautrix_hangouts_appservice_database: "{{ + { + 'sqlite': ('sqlite:///' + matrix_mautrix_hangouts_sqlite_database_path_in_container), + 'postgres': matrix_mautrix_hangouts_database_connection_string, + }[matrix_mautrix_hangouts_database_engine] +}}" + + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mautrix_hangouts_login_shared_secret: '' + +matrix_mautrix_hangouts_appservice_bot_username: hangoutsbot + +# Default 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_mautrix_hangouts_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mautrix_hangouts_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mautrix_hangouts_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mautrix_hangouts_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mautrix_hangouts_configuration_yaml`. + +matrix_mautrix_hangouts_configuration_extension: "{{ matrix_mautrix_hangouts_configuration_extension_yaml|from_yaml if matrix_mautrix_hangouts_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mautrix_hangouts_configuration_yaml`. +matrix_mautrix_hangouts_configuration: "{{ matrix_mautrix_hangouts_configuration_yaml|from_yaml|combine(matrix_mautrix_hangouts_configuration_extension, recursive=True) }}" + +matrix_mautrix_hangouts_registration_yaml: | + id: hangouts + as_token: "{{ matrix_mautrix_hangouts_appservice_token }}" + hs_token: "{{ matrix_mautrix_hangouts_homeserver_token }}" + namespaces: + users: + - exclusive: true + regex: '^@hangouts_.+:{{ matrix_mautrix_hangouts_homeserver_domain|regex_escape }}$' + - exclusive: true + regex: '^@{{ matrix_mautrix_hangouts_appservice_bot_username|regex_escape }}:{{ matrix_mautrix_hangouts_homeserver_domain|regex_escape }}$' + url: {{ matrix_mautrix_hangouts_appservice_address }} + # See https://github.com/tulir/mautrix-signal/issues/43 + sender_localpart: _bot_{{ matrix_mautrix_hangouts_appservice_bot_username }} + rate_limited: false + de.sorunome.msc2409.push_ephemeral: true + +matrix_mautrix_hangouts_registration: "{{ matrix_mautrix_hangouts_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mautrix-hangouts/tasks/init.yml b/roles/matrix-bridge-mautrix-hangouts/tasks/init.yml new file mode 100644 index 00000000..6cc194fe --- /dev/null +++ b/roles/matrix-bridge-mautrix-hangouts/tasks/init.yml @@ -0,0 +1,69 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mautrix_hangouts_container_image_self_build and matrix_mautrix_hangouts_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mautrix-hangouts.service'] }}" + when: matrix_mautrix_hangouts_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mautrix_hangouts_config_path }}/registration.yaml,dst=/matrix-mautrix-hangouts-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mautrix-hangouts-registration.yaml"] }} + when: matrix_mautrix_hangouts_enabled|bool + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append Mautrix Hangouts's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-bridge-mautrix-hangouts role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate Mautrix Hangouts proxying configuration for matrix-nginx-proxy + set_fact: + matrix_mautrix_hangouts_matrix_nginx_proxy_configuration: | + location {{ matrix_mautrix_hangouts_public_endpoint }} { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-mautrix-hangouts:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:9007; + {% endif %} + } + - name: Register Mautrix Hangouts proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks|default([]) + + + [matrix_mautrix_hangouts_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_mautrix_hangouts_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the Mautrix Hangouts bridge but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ matrix_mautrix_hangouts_public_endpoint }}` + URL endpoint to the matrix-mautrix-hangouts container. + You can expose the container's port using the `matrix_mautrix_hangouts_container_http_host_bind_port` variable. + when: "matrix_mautrix_hangouts_enabled|bool and (matrix_nginx_proxy_enabled is not defined or matrix_nginx_proxy_enabled|bool == false)" diff --git a/roles/matrix-bridge-mautrix-hangouts/tasks/main.yml b/roles/matrix-bridge-mautrix-hangouts/tasks/main.yml new file mode 100644 index 00000000..0df0d0e3 --- /dev/null +++ b/roles/matrix-bridge-mautrix-hangouts/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mautrix_hangouts_enabled|bool" + tags: + - setup-all + - setup-mautrix-hangouts + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mautrix_hangouts_enabled|bool" + tags: + - setup-all + - setup-mautrix-hangouts + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mautrix_hangouts_enabled|bool" + tags: + - setup-all + - setup-mautrix-hangouts diff --git a/roles/matrix-bridge-mautrix-hangouts/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-hangouts/tasks/setup_install.yml new file mode 100644 index 00000000..d5373134 --- /dev/null +++ b/roles/matrix-bridge-mautrix-hangouts/tasks/setup_install.yml @@ -0,0 +1,128 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mautrix-hangouts role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_mautrix_hangouts_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mautrix_hangouts_sqlite_database_path_local }}" + register: matrix_mautrix_hangouts_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mautrix_hangouts_sqlite_database_path_local }}" + dst: "{{ matrix_mautrix_hangouts_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mautrix_hangouts_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mautrix-hangouts.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mautrix_hangouts_requires_restart: true + when: "matrix_mautrix_hangouts_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mautrix_hangouts_database_engine == 'postgres'" + +- name: Ensure Mautrix Hangouts image is pulled + docker_image: + name: "{{ matrix_mautrix_hangouts_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mautrix_hangouts_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_hangouts_docker_image_force_pull }}" + when: not matrix_mautrix_hangouts_container_image_self_build + +- name: Ensure Mautrix Hangouts paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mautrix_hangouts_base_path }}", when: true } + - { path: "{{ matrix_mautrix_hangouts_config_path }}", when: true } + - { path: "{{ matrix_mautrix_hangouts_data_path }}", when: true } + - { path: "{{ matrix_mautrix_hangouts_docker_src_files_path }}", when: "{{ matrix_mautrix_hangouts_container_image_self_build }}" } + when: "item.when|bool" + +- name: Ensure Mautrix Hangots repository is present on self build + git: + repo: "{{ matrix_mautrix_hangouts_container_image_self_build_repo }}" + dest: "{{ matrix_mautrix_hangouts_docker_src_files_path }}" + force: "yes" + register: matrix_mautrix_hangouts_git_pull_results + when: "matrix_mautrix_hangouts_container_image_self_build|bool" + +- name: Ensure Mautrix Hangouts Docker image is built + docker_image: + name: "{{ matrix_mautrix_hangouts_docker_image }}" + source: build + force_source: "{{ matrix_mautrix_hangouts_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_hangouts_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mautrix_hangouts_docker_src_files_path }}" + pull: yes + when: "matrix_mautrix_hangouts_container_image_self_build|bool" + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mautrix_hangouts_base_path }}/mautrix-hangouts.db" + register: matrix_mautrix_hangouts_stat_database + +- name: (Data relocation) Ensure matrix-mautrix-hangouts.service is stopped + service: + name: matrix-mautrix-hangouts + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mautrix_hangouts_stat_database.stat.exists" + +- name: (Data relocation) Move mautrix-hangouts database file to ./data directory + command: "mv {{ matrix_mautrix_hangouts_base_path }}/mautrix-hangouts.db {{ matrix_mautrix_hangouts_data_path }}/mautrix-hangouts.db" + when: "matrix_mautrix_hangouts_stat_database.stat.exists" + +- name: Ensure mautrix-hangouts config.yaml installed + copy: + content: "{{ matrix_mautrix_hangouts_configuration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_hangouts_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mautrix-hangouts registration.yaml installed + copy: + content: "{{ matrix_mautrix_hangouts_registration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_hangouts_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mautrix-hangouts.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mautrix-hangouts.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-mautrix-hangouts.service" + mode: 0644 + register: matrix_mautrix_hangouts_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mautrix-hangouts.service installation + service: + daemon_reload: yes + when: "matrix_mautrix_hangouts_systemd_service_result.changed" + +- name: Ensure matrix-mautrix-hangouts.service restarted, if necessary + service: + name: "matrix-mautrix-hangouts.service" + state: restarted + when: "matrix_mautrix_hangouts_requires_restart|bool" diff --git a/roles/matrix-bridge-mautrix-hangouts/tasks/setup_uninstall.yml b/roles/matrix-bridge-mautrix-hangouts/tasks/setup_uninstall.yml new file mode 100644 index 00000000..14413e94 --- /dev/null +++ b/roles/matrix-bridge-mautrix-hangouts/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mautrix-hangouts service + stat: + path: "{{ matrix_systemd_path }}/matrix-mautrix-hangouts.service" + register: matrix_mautrix_hangouts_service_stat + +- name: Ensure matrix-mautrix-hangouts is stopped + service: + name: matrix-mautrix-hangouts + state: stopped + daemon_reload: yes + when: "matrix_mautrix_hangouts_service_stat.stat.exists" + +- name: Ensure matrix-mautrix-hangouts.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mautrix-hangouts.service" + state: absent + when: "matrix_mautrix_hangouts_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mautrix-hangouts.service removal + service: + daemon_reload: yes + when: "matrix_mautrix_hangouts_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mautrix-hangouts/tasks/validate_config.yml b/roles/matrix-bridge-mautrix-hangouts/tasks/validate_config.yml new file mode 100644 index 00000000..8922bef4 --- /dev/null +++ b/roles/matrix-bridge-mautrix-hangouts/tasks/validate_config.yml @@ -0,0 +1,14 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mautrix_hangouts_public_endpoint" + - "matrix_mautrix_hangouts_appservice_token" + - "matrix_mautrix_hangouts_homeserver_token" +- debug: + msg: + - '`matrix_mautrix_hangouts_homeserver_domain` == {{ matrix_mautrix_hangouts_homeserver_domain }}' diff --git a/roles/matrix-bridge-mautrix-hangouts/templates/config.yaml.j2 b/roles/matrix-bridge-mautrix-hangouts/templates/config.yaml.j2 new file mode 100644 index 00000000..7ff7d539 --- /dev/null +++ b/roles/matrix-bridge-mautrix-hangouts/templates/config.yaml.j2 @@ -0,0 +1,145 @@ +#jinja2: lstrip_blocks: "True" +# Homeserver details +homeserver: + # The address that this appservice can use to connect to the homeserver. + address: {{ matrix_mautrix_hangouts_homeserver_address }} + # The domain of the homeserver (for MXIDs, etc). + domain: {{ matrix_mautrix_hangouts_homeserver_domain }} + # Whether or not to verify the SSL certificate of the homeserver. + # Only applies if address starts with https:// + verify_ssl: true + +# Application service host/registration related details +# Changing these values requires regeneration of the registration. +appservice: + # The address that the homeserver can use to connect to this appservice. + address: {{ matrix_mautrix_hangouts_appservice_address }} + + # The hostname and port where this appservice should listen. + hostname: 0.0.0.0 + port: 8080 + # The maximum body size of appservice API requests (from the homeserver) in mebibytes + # Usually 1 is enough, but on high-traffic bridges you might need to increase this to avoid 413s + max_body_size: 1 + + # The full URI to the database. SQLite and Postgres are fully supported. + # Other DBMSes supported by SQLAlchemy may or may not work. + # Format examples: + # SQLite: sqlite:///filename.db + # Postgres: postgres://username:password@hostname/dbname + database: {{ matrix_mautrix_hangouts_appservice_database|to_json }} + + # The unique ID of this appservice. + id: hangouts + # Username of the appservice bot. + bot_username: {{ matrix_mautrix_hangouts_appservice_bot_username|to_json }} + # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty + # to leave display name/avatar as-is. + bot_displayname: Hangouts bridge bot + bot_avatar: mxc://maunium.net/FBXZnpfORkBEruORbikmleAy + + # Authentication tokens for AS <-> HS communication. + as_token: "{{ matrix_mautrix_hangouts_appservice_token }}" + hs_token: "{{ matrix_mautrix_hangouts_homeserver_token }}" + +# Bridge config +bridge: + # Localpart template of MXIDs for Hangouts users. + # {userid} is replaced with the user ID of the Hangouts user. + username_template: "hangouts_{userid}" + # Displayname template for Hangouts users. + # {displayname} is replaced with the display name of the Hangouts user + # as defined below in displayname_preference. + # Keys available for displayname_preference are also available here. + displayname_template: '{full_name} (Hangouts)' + # Available keys: + # "name" (full name) + # "first_name" + # "last_name" + # "nickname" + # "own_nickname" (user-specific!) + displayname_preference: + - name + + # The prefix for commands. Only required in non-management rooms. + command_prefix: "!HO" + + # Number of chats to sync (and create portals for) on startup/login. + # Maximum 20, set 0 to disable automatic syncing. + initial_chat_sync: 20 + # Whether or not the Hangouts users of logged in Matrix users should be + # invited to private chats when the user sends a message from another client. + invite_own_puppet_to_pm: false + # Whether or not to use /sync to get presence, read receipts and typing notifications when using + # your own Matrix account as the Matrix puppet for your Hangouts account. + sync_with_custom_puppets: true + # Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth + # + # If set, custom puppets will be enabled automatically for local users + # instead of users having to find an access token and run `login-matrix` + # manually. + login_shared_secret: {{ matrix_mautrix_hangouts_login_shared_secret|to_json }} + # Whether or not to update avatars when syncing all contacts at startup. + update_avatar_initial_sync: true + # End-to-bridge encryption support options. These require matrix-nio to be installed with pip + # and login_shared_secret to be configured in order to get a device for the bridge bot. + # + # Additionally, https://github.com/matrix-org/synapse/pull/5758 is required if using a normal + # application service. + encryption: + # Allow encryption, work in group chat rooms with e2ee enabled + allow: false + # Default to encryption, force-enable encryption in all portals the bridge creates + # This will cause the bridge bot to be in private chats for the encryption to work properly. + default: false + + # Public website and API configs + web: + # Auth server config + auth: + # Publicly accessible base URL for the login endpoints. + # The prefix below is not implicitly added. This URL and all subpaths should be proxied + # or otherwise pointed to the appservice's webserver to the path specified below (prefix). + # This path should usually include a trailing slash. + # Internal prefix in the appservice web server for the login endpoints. + public: "{{ matrix_homeserver_url }}{{ matrix_mautrix_hangouts_public_endpoint }}/login" + prefix: "{{ matrix_mautrix_hangouts_public_endpoint }}/login" + + + # Permissions for using the bridge. + # Permitted values: + # user - Use the bridge with puppeting. + # admin - Use and administrate the bridge. + # Permitted keys: + # * - All Matrix users + # domain - All users on that homeserver + # mxid - Specific user + permissions: + '{{ matrix_mautrix_hangouts_homeserver_domain }}': user + +# Python logging configuration. +# +# See section 16.7.2 of the Python documentation for more info: +# https://docs.python.org/3.6/library/logging.config.html#configuration-dictionary-schema +logging: + version: 1 + formatters: + colored: + (): mautrix_hangouts.util.ColorFormatter + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + normal: + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + handlers: + console: + class: logging.StreamHandler + formatter: colored + loggers: + mau: + level: DEBUG + hangups: + level: DEBUG + aiohttp: + level: INFO + root: + level: DEBUG + handlers: [console] diff --git a/roles/matrix-bridge-mautrix-hangouts/templates/systemd/matrix-mautrix-hangouts.service.j2 b/roles/matrix-bridge-mautrix-hangouts/templates/systemd/matrix-mautrix-hangouts.service.j2 new file mode 100644 index 00000000..60f0e055 --- /dev/null +++ b/roles/matrix-bridge-mautrix-hangouts/templates/systemd/matrix-mautrix-hangouts.service.j2 @@ -0,0 +1,54 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mautrix Hangouts bridge +{% for service in matrix_mautrix_hangouts_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mautrix_hangouts_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-hangouts matrix-mautrix-hangouts-db 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-hangouts matrix-mautrix-hangouts-db 2>/dev/null' +ExecStartPre={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-hangouts-db \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -v {{ matrix_mautrix_hangouts_config_path }}:/config:z \ + -v {{ matrix_mautrix_hangouts_data_path }}:/data:z \ + {{ matrix_mautrix_hangouts_docker_image }} \ + alembic -x config=/config/config.yaml upgrade head + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-hangouts \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_mautrix_hangouts_container_http_host_bind_port %} + -p {{ matrix_mautrix_hangouts_container_http_host_bind_port }}:8080 \ + {% endif %} + -v {{ matrix_mautrix_hangouts_config_path }}:/config:z \ + -v {{ matrix_mautrix_hangouts_data_path }}:/data:z \ + {% for arg in matrix_mautrix_hangouts_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mautrix_hangouts_docker_image }} \ + python3 -m mautrix_hangouts -c /config/config.yaml --no-update + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-hangouts 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-hangouts 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mautrix-hangouts + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mautrix-instagram/defaults/main.yml b/roles/matrix-bridge-mautrix-instagram/defaults/main.yml new file mode 100644 index 00000000..5204386d --- /dev/null +++ b/roles/matrix-bridge-mautrix-instagram/defaults/main.yml @@ -0,0 +1,105 @@ +# mautrix-instagram is a Matrix <-> Instagram bridge +# See: https://github.com/tulir/mautrix-instagram + +matrix_mautrix_instagram_enabled: true + +matrix_mautrix_instagram_container_image_self_build: false +matrix_mautrix_instagram_container_image_self_build_repo: "https://github.com/tulir/mautrix-instagram.git" + +matrix_mautrix_instagram_version: latest +# See: https://mau.dev/tulir/mautrix-instagram/container_registry +matrix_mautrix_instagram_docker_image: "{{ matrix_mautrix_instagram_docker_image_name_prefix }}tulir/mautrix-instagram:{{ matrix_mautrix_instagram_version }}" +matrix_mautrix_instagram_docker_image_name_prefix: "{{ 'localhost/' if matrix_mautrix_instagram_container_image_self_build else 'dock.mau.dev/' }}" +matrix_mautrix_instagram_docker_image_force_pull: "{{ matrix_mautrix_instagram_docker_image.endswith(':latest') }}" + +matrix_mautrix_instagram_base_path: "{{ matrix_base_data_path }}/mautrix-instagram" +matrix_mautrix_instagram_config_path: "{{ matrix_mautrix_instagram_base_path }}/config" +matrix_mautrix_instagram_data_path: "{{ matrix_mautrix_instagram_base_path }}/data" +matrix_mautrix_instagram_docker_src_files_path: "{{ matrix_mautrix_instagram_base_path }}/docker-src" + +matrix_mautrix_instagram_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mautrix_instagram_homeserver_domain: '{{ matrix_domain }}' +matrix_mautrix_instagram_appservice_address: 'http://matrix-mautrix-instagram:29330' + +# A list of extra arguments to pass to the container +matrix_mautrix_instagram_container_extra_arguments: [] + +# List of systemd services that matrix-mautrix-instagram.service depends on. +matrix_mautrix_instagram_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-mautrix-instagram.service wants +matrix_mautrix_instagram_systemd_wanted_services_list: [] + +matrix_mautrix_instagram_appservice_token: '' +matrix_mautrix_instagram_homeserver_token: '' + + +# Database-related configuration fields. +# +# To use Postgres: +# - adjust your database credentials via the `matrix_mautrix_instagram_postgres_*` variables +matrix_mautrix_instagram_database_engine: 'postgres' + +matrix_mautrix_instagram_database_username: 'matrix_mautrix_instagram' +matrix_mautrix_instagram_database_password: 'some-password' +matrix_mautrix_instagram_database_hostname: 'matrix-postgres' +matrix_mautrix_instagram_database_port: 5432 +matrix_mautrix_instagram_database_name: 'matrix_mautrix_instagram' + +matrix_mautrix_instagram_database_connection_string: 'postgres://{{ matrix_mautrix_instagram_database_username }}:{{ matrix_mautrix_instagram_database_password }}@{{ matrix_mautrix_instagram_database_hostname }}:{{ matrix_mautrix_instagram_database_port }}/{{ matrix_mautrix_instagram_database_name }}' + +matrix_mautrix_instagram_appservice_database: "{{ + { + 'postgres': matrix_mautrix_instagram_database_connection_string, + }[matrix_mautrix_instagram_database_engine] +}}" + + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mautrix_instagram_login_shared_secret: '' + +matrix_mautrix_instagram_bridge_login_shared_secret_map: "{{ {matrix_mautrix_instagram_homeserver_domain: matrix_mautrix_instagram_login_shared_secret} if matrix_mautrix_instagram_login_shared_secret else {} }}" + +matrix_mautrix_instagram_appservice_bot_username: instagrambot + +matrix_mautrix_instagram_bridge_presence: true + +# Default 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_mautrix_instagram_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mautrix_instagram_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mautrix_instagram_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mautrix_instagram_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mautrix_instagram_configuration_yaml`. + +matrix_mautrix_instagram_configuration_extension: "{{ matrix_mautrix_instagram_configuration_extension_yaml|from_yaml if matrix_mautrix_instagram_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mautrix_instagram_configuration_yaml`. +matrix_mautrix_instagram_configuration: "{{ matrix_mautrix_instagram_configuration_yaml|from_yaml|combine(matrix_mautrix_instagram_configuration_extension, recursive=True) }}" + +matrix_mautrix_instagram_registration_yaml: | + id: instagram + as_token: "{{ matrix_mautrix_instagram_appservice_token }}" + hs_token: "{{ matrix_mautrix_instagram_homeserver_token }}" + namespaces: + users: + - exclusive: true + regex: '^@instagram_.+:{{ matrix_mautrix_instagram_homeserver_domain|regex_escape }}$' + - exclusive: true + regex: '^@{{ matrix_mautrix_instagram_appservice_bot_username|regex_escape }}:{{ matrix_mautrix_instagram_homeserver_domain|regex_escape }}$' + url: {{ matrix_mautrix_instagram_appservice_address }} + # See https://github.com/tulir/mautrix-signal/issues/43 + sender_localpart: _bot_{{ matrix_mautrix_instagram_appservice_bot_username }} + rate_limited: false + de.sorunome.msc2409.push_ephemeral: true + +matrix_mautrix_instagram_registration: "{{ matrix_mautrix_instagram_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mautrix-instagram/tasks/init.yml b/roles/matrix-bridge-mautrix-instagram/tasks/init.yml new file mode 100644 index 00000000..c44855d8 --- /dev/null +++ b/roles/matrix-bridge-mautrix-instagram/tasks/init.yml @@ -0,0 +1,23 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mautrix_instagram_container_image_self_build and matrix_mautrix_instagram_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mautrix-instagram.service'] }}" + when: matrix_mautrix_instagram_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mautrix_instagram_config_path }}/registration.yaml,dst=/matrix-mautrix-instagram-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mautrix-instagram-registration.yaml"] }} + when: matrix_mautrix_instagram_enabled|bool diff --git a/roles/matrix-bridge-mautrix-instagram/tasks/main.yml b/roles/matrix-bridge-mautrix-instagram/tasks/main.yml new file mode 100644 index 00000000..7326e22d --- /dev/null +++ b/roles/matrix-bridge-mautrix-instagram/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mautrix_instagram_enabled|bool" + tags: + - setup-all + - setup-mautrix-instagram + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mautrix_instagram_enabled|bool" + tags: + - setup-all + - setup-mautrix-instagram + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mautrix_instagram_enabled|bool" + tags: + - setup-all + - setup-mautrix-instagram diff --git a/roles/matrix-bridge-mautrix-instagram/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-instagram/tasks/setup_install.yml new file mode 100644 index 00000000..38a7f62e --- /dev/null +++ b/roles/matrix-bridge-mautrix-instagram/tasks/setup_install.yml @@ -0,0 +1,81 @@ +--- +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mautrix-instagram role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- name: Ensure Mautrix instagram image is pulled + docker_image: + name: "{{ matrix_mautrix_instagram_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mautrix_instagram_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_instagram_docker_image_force_pull }}" + when: not matrix_mautrix_instagram_container_image_self_build + +- name: Ensure Mautrix instagram paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mautrix_instagram_base_path }}", when: true } + - { path: "{{ matrix_mautrix_instagram_config_path }}", when: true } + - { path: "{{ matrix_mautrix_instagram_data_path }}", when: true } + - { + path: "{{ matrix_mautrix_instagram_docker_src_files_path }}", + when: "{{ matrix_mautrix_instagram_container_image_self_build }}", + } + when: item.when|bool + +- name: Ensure Mautrix instagram repository is present on self-build + git: + repo: "{{ matrix_mautrix_instagram_container_image_self_build_repo }}" + dest: "{{ matrix_mautrix_instagram_docker_src_files_path }}" + force: "yes" + register: matrix_mautrix_instagram_git_pull_results + when: "matrix_mautrix_instagram_container_image_self_build|bool" + +- name: Ensure Mautrix instagram Docker image is built + docker_image: + name: "{{ matrix_mautrix_instagram_docker_image }}" + source: build + force_source: "{{ matrix_mautrix_instagram_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_instagram_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mautrix_instagram_docker_src_files_path }}" + pull: yes + when: "matrix_mautrix_instagram_container_image_self_build|bool" + +- name: Ensure mautrix-instagram config.yaml installed + copy: + content: "{{ matrix_mautrix_instagram_configuration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_instagram_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mautrix-instagram registration.yaml installed + copy: + content: "{{ matrix_mautrix_instagram_registration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_instagram_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mautrix-instagram.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mautrix-instagram.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-mautrix-instagram.service" + mode: 0644 + register: matrix_mautrix_instagram_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mautrix-instagram.service installation + service: + daemon_reload: yes + when: "matrix_mautrix_instagram_systemd_service_result.changed" diff --git a/roles/matrix-bridge-mautrix-instagram/tasks/setup_uninstall.yml b/roles/matrix-bridge-mautrix-instagram/tasks/setup_uninstall.yml new file mode 100644 index 00000000..c5c8a3e6 --- /dev/null +++ b/roles/matrix-bridge-mautrix-instagram/tasks/setup_uninstall.yml @@ -0,0 +1,23 @@ +--- +- name: Check existence of matrix-mautrix-instagram service + stat: + path: "{{ matrix_systemd_path }}/matrix-mautrix-instagram.service" + register: matrix_mautrix_instagram_service_stat + +- name: Ensure matrix-mautrix-instagram is stopped + service: + name: matrix-mautrix-instagram + state: stopped + daemon_reload: yes + when: "matrix_mautrix_instagram_service_stat.stat.exists" + +- name: Ensure matrix-mautrix-instagram.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mautrix-instagram.service" + state: absent + when: "matrix_mautrix_instagram_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mautrix-instagram.service removal + service: + daemon_reload: yes + when: "matrix_mautrix_instagram_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mautrix-instagram/tasks/validate_config.yml b/roles/matrix-bridge-mautrix-instagram/tasks/validate_config.yml new file mode 100644 index 00000000..24992ff5 --- /dev/null +++ b/roles/matrix-bridge-mautrix-instagram/tasks/validate_config.yml @@ -0,0 +1,9 @@ +--- +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mautrix_instagram_appservice_token" + - "matrix_mautrix_instagram_homeserver_token" diff --git a/roles/matrix-bridge-mautrix-instagram/templates/config.yaml.j2 b/roles/matrix-bridge-mautrix-instagram/templates/config.yaml.j2 new file mode 100644 index 00000000..db57bd0d --- /dev/null +++ b/roles/matrix-bridge-mautrix-instagram/templates/config.yaml.j2 @@ -0,0 +1,234 @@ +#jinja2: lstrip_blocks: "True" +# Homeserver details +homeserver: + # The address that this appservice can use to connect to the homeserver. + address: {{ matrix_mautrix_instagram_homeserver_address }} + # The domain of the homeserver (for MXIDs, etc). + domain: {{ matrix_mautrix_instagram_homeserver_domain }} + # Whether or not to verify the SSL certificate of the homeserver. + # Only applies if address starts with https:// + verify_ssl: true + # Whether or not the homeserver supports asmux-specific endpoints, + # such as /_matrix/client/unstable/net.maunium.asmux/dms for atomically + # updating m.direct. + asmux: false + +# Application service host/registration related details +# Changing these values requires regeneration of the registration. +appservice: + # The address that the homeserver can use to connect to this appservice. + address: {{ matrix_mautrix_instagram_appservice_address }} + # When using https:// the TLS certificate and key files for the address. + tls_cert: false + tls_key: false + + # The hostname and port where this appservice should listen. + hostname: 0.0.0.0 + port: 29330 + # The maximum body size of appservice API requests (from the homeserver) in mebibytes + # Usually 1 is enough, but on high-traffic bridges you might need to increase this to avoid 413s + max_body_size: 1 + + # The full URI to the database. Only Postgres is currently supported. + database: {{ matrix_mautrix_instagram_appservice_database|to_json }} + # Additional arguments for asyncpg.create_pool() + # https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.pool.create_pool + database_opts: + min_size: 5 + max_size: 10 + + # The unique ID of this appservice. + id: instagram + # Username of the appservice bot. + bot_username: {{ matrix_mautrix_instagram_appservice_bot_username|to_json }} + # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty + # to leave display name/avatar as-is. + bot_displayname: instagram bridge bot + bot_avatar: mxc://maunium.net/JxjlbZUlCPULEeHZSwleUXQv + + # Community ID for bridged users (changes registration file) and rooms. + # Must be created manually. + # + # Example: "+instagram:example.com". Set to false to disable. + community_id: false + + # Whether or not to receive ephemeral events via appservice transactions. + # Requires MSC2409 support (i.e. Synapse 1.22+). + # You should disable bridge -> sync_with_custom_puppets when this is enabled. + ephemeral_events: false + + # Authentication tokens for AS <-> HS communication. + as_token: "{{ matrix_mautrix_instagram_appservice_token }}" + hs_token: "{{ matrix_mautrix_instagram_homeserver_token }}" + +# Prometheus telemetry config. Requires prometheus-client to be installed. +metrics: + enabled: false + listen_port: 8000 + +instagram: + # Seed for generating devices. This is secret because the seed is used to generate + # device IDs, which can apparently be used to bypass two-factor authentication after + # logging out, because Instagram is insecure. + device_seed: generate + +# Bridge config +bridge: + # Localpart template of MXIDs for Instagram users. + # {userid} is replaced with the user ID of the Instagram user. + username_template: "instagram_{userid}" + # Displayname template for Instagram users. + # {displayname} is replaced with the display name of the Instagram user. + # {username} is replaced with the username of the Instagram user. + displayname_template: "{username} (Instagram)" + + # Maximum length of displayname + displayname_max_length: 100 + + # Maximum number of seconds since the last activity in a chat to automatically create portals. + portal_create_max_age: 86400 + # Maximum number of chats to fetch for startup sync + chat_sync_limit: 100 + # Whether or not to use /sync to get read receipts and typing notifications + # when double puppeting is enabled + sync_with_custom_puppets: true + # Whether or not to update the m.direct account data event when double puppeting is enabled. + # Note that updating the m.direct event is not atomic (except with mautrix-asmux) + # and is therefore prone to race conditions. + sync_direct_chat_list: false + # Allow using double puppeting from any server with a valid client .well-known file. + double_puppet_allow_discovery: false + # Servers to allow double puppeting from, even if double_puppet_allow_discovery is false. + double_puppet_server_map: {} + # example.com: https://example.com + # Allow using double puppeting from any server with a valid client .well-known file. + double_puppet_allow_discovery: false + # Shared secrets for https://github.com/devture/matrix-synapse-shared-secret-auth + # + # If set, custom puppets will be enabled automatically for local users + # instead of users having to find an access token and run `login-matrix` + # manually. + # If using this for other servers than the bridge's server, + # you must also set the URL in the double_puppet_server_map. + login_shared_secret_map: + {{ matrix_mautrix_instagram_bridge_login_shared_secret_map|to_json }} + # Whether or not to update avatars when syncing all contacts at startup. + update_avatar_initial_sync: true + # Whether or not created rooms should have federation enabled. + # If false, created portal rooms will never be federated. + federate_rooms: true + # Settings for backfilling messages from Instagram. + backfill: + # Whether or not the Instagram users of logged in Matrix users should be + # invited to private chats when backfilling history from Instagram. This is + # usually needed to prevent rate limits and to allow timestamp massaging. + invite_own_puppet: true + # Maximum number of messages to backfill initially. + # Set to 0 to disable backfilling when creating portal. + initial_limit: 0 + # Maximum number of messages to backfill if messages were missed while + # the bridge was disconnected. + # Set to 0 to disable backfilling missed messages. + missed_limit: 1000 + # If using double puppeting, should notifications be disabled + # while the initial backfill is in progress? + disable_notifications: false + periodic_reconnect: + # Interval in seconds in which to automatically reconnect all users. + # This can be used to automatically mitigate the bug where Instagram stops sending messages. + # Set to -1 to disable periodic reconnections entirely. + interval: -1 + # Whether or not the bridge should backfill chats when reconnecting. + resync: true + # Should even disconnected users be reconnected? + always: false + # End-to-bridge encryption support options. These require matrix-nio to be installed with pip + # and login_shared_secret to be configured in order to get a device for the bridge bot. + # + # Additionally, https://github.com/matrix-org/synapse/pull/5758 is required if using a normal + # application service. + encryption: + # Allow encryption, work in group chat rooms with e2ee enabled + allow: false + # Default to encryption, force-enable encryption in all portals the bridge creates + # This will cause the bridge bot to be in private chats for the encryption to work properly. + default: false + # Options for automatic key sharing. + key_sharing: + # Enable key sharing? If enabled, key requests for rooms where users are in will be fulfilled. + # You must use a client that supports requesting keys from other users to use this feature. + allow: false + # Require the requesting device to have a valid cross-signing signature? + # This doesn't require that the bridge has verified the device, only that the user has verified it. + # Not yet implemented. + require_cross_signing: false + # Require devices to be verified by the bridge? + # Verification by the bridge is not yet implemented. + require_verification: true + # Whether or not to explicitly set the avatar and room name for private + # chat portal rooms. This will be implicitly enabled if encryption.default is true. + private_chat_portal_meta: false + # Whether or not the bridge should send a read receipt from the bridge bot when a message has + # been sent to Instagram. + delivery_receipts: false + # Whether or not delivery errors should be reported as messages in the Matrix room. + delivery_error_reports: false + # Set this to true to tell the bridge to re-send m.bridge events to all rooms on the next run. + # This field will automatically be changed back to false after it, + # except if the config file is not writable. + resend_bridge_info: false + # Whether or not unimportant bridge notices should be sent to the user. + # (e.g. connected, disconnected but will retry) + unimportant_bridge_notices: true + + # The prefix for commands. Only required in non-management rooms. + command_prefix: "!ig" + # Permissions for using the bridge. + # Permitted values: + # user - Use the bridge with puppeting. + # admin - Use and administrate the bridge. + # Permitted keys: + # * - All Matrix users + # domain - All users on that homeserver + # mxid - Specific user + permissions: + "{{ matrix_mautrix_instagram_homeserver_domain }}": user + # Provisioning API part of the web server for automated portal creation and fetching information. + # Used by things like mautrix-manager (https://github.com/tulir/mautrix-manager). + provisioning: + # Whether or not the provisioning API should be enabled. + enabled: true + # The prefix to use in the provisioning API endpoints. + prefix: /_matrix/provision/v1 + # The shared secret to authorize users of the API. + # Set to "generate" to generate and save a new token. + shared_secret: generate + +# Python logging configuration. +# +# See section 16.7.2 of the Python documentation for more info: +# https://docs.python.org/3.6/library/logging.config.html#configuration-dictionary-schema +logging: + version: 1 + formatters: + colored: + (): mautrix_instagram.util.ColorFormatter + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + normal: + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + handlers: + console: + class: logging.StreamHandler + formatter: colored + loggers: + mau: + level: DEBUG + mauigpapi: + level: DEBUG + paho: + level: INFO + aiohttp: + level: INFO + root: + level: DEBUG + handlers: [console] diff --git a/roles/matrix-bridge-mautrix-instagram/templates/systemd/matrix-mautrix-instagram.service.j2 b/roles/matrix-bridge-mautrix-instagram/templates/systemd/matrix-mautrix-instagram.service.j2 new file mode 100644 index 00000000..33a5bab3 --- /dev/null +++ b/roles/matrix-bridge-mautrix-instagram/templates/systemd/matrix-mautrix-instagram.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mautrix Instagram bridge +{% for service in matrix_mautrix_instagram_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mautrix_instagram_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-instagram 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-instagram 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-instagram \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -v {{ matrix_mautrix_instagram_config_path }}:/config:z \ + -v {{ matrix_mautrix_instagram_data_path }}:/data:z \ + {% for arg in matrix_mautrix_instagram_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mautrix_instagram_docker_image }} \ + python3 -m mautrix_instagram -c /config/config.yaml --no-update + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-instagram 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-instagram 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mautrix-instagram + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mautrix-signal/defaults/main.yml b/roles/matrix-bridge-mautrix-signal/defaults/main.yml new file mode 100644 index 00000000..8ff2fbb6 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/defaults/main.yml @@ -0,0 +1,107 @@ +# mautrix-signal is a Matrix <-> Signal bridge +# See: https://github.com/mautrix/signal + +matrix_mautrix_signal_enabled: true + +matrix_mautrix_signal_container_self_build: false +matrix_mautrix_signal_docker_repo: "https://mau.dev/mautrix/signal.git" +matrix_mautrix_signal_docker_src_files_path: "{{ matrix_base_data_path }}/mautrix-signal/docker-src" + +matrix_mautrix_signal_version: latest +matrix_mautrix_signal_daemon_version: latest +# See: https://mau.dev/mautrix/signal/container_registry +matrix_mautrix_signal_docker_image: "dock.mau.dev/mautrix/signal:{{ matrix_mautrix_signal_version }}" +matrix_mautrix_signal_docker_image_force_pull: "{{ matrix_mautrix_signal_docker_image.endswith(':latest') }}" + +matrix_mautrix_signal_daemon_container_self_build: false +matrix_mautrix_signal_daemon_docker_repo: "https://mau.dev/maunium/signald.git" +matrix_mautrix_signal_daemon_docker_src_files_path: "{{ matrix_base_data_path }}/mautrix-signald/docker-src" + +matrix_mautrix_signal_daemon_docker_image: "dock.mau.dev/maunium/signald:{{ matrix_mautrix_signal_daemon_version }}" +matrix_mautrix_signal_daemon_docker_image_force_pull: "{{ matrix_mautrix_signal_daemon_docker_image.endswith(':latest') }}" + +matrix_mautrix_signal_base_path: "{{ matrix_base_data_path }}/mautrix-signal" +matrix_mautrix_signal_config_path: "{{ matrix_mautrix_signal_base_path }}/bridge" +matrix_mautrix_signal_daemon_path: "{{ matrix_mautrix_signal_base_path }}/signald" + +matrix_mautrix_signal_homeserver_address: '' +matrix_mautrix_signal_homeserver_domain: '' +matrix_mautrix_signal_appservice_address: 'http://matrix-mautrix-signal:29328' + +# Controls whether the matrix-mautrix-signal container exposes its port (tcp/29328 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9006"), or empty string to not expose. +matrix_mautrix_signal_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_mautrix_signal_container_extra_arguments: [] + +# List of systemd services that matrix-mautrix-signal.service depends on. +matrix_mautrix_signal_systemd_required_services_list: + - 'docker.service' + - 'matrix-mautrix-signal-daemon.service' + +# List of systemd services that matrix-mautrix-signal.service wants +matrix_mautrix_signal_systemd_wanted_services_list: [] + +# List of systemd services that matrix-mautrix-signal-daemon.service depends on. +matrix_mautrix_signal_daemon_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-mautrix-signal-daemon.service wants +matrix_mautrix_signal_daemon_systemd_wanted_services_list: [] + +matrix_mautrix_signal_appservice_token: '' +matrix_mautrix_signal_homeserver_token: '' + +matrix_mautrix_signal_appservice_bot_username: signalbot + +# Database-related configuration fields +# +# This bridge only supports postgres. +# +matrix_mautrix_signal_database_engine: 'postgres' + +matrix_mautrix_signal_database_username: 'matrix_mautrix_signal' +matrix_mautrix_signal_database_password: 'some-password' +matrix_mautrix_signal_database_hostname: 'matrix-postgres' +matrix_mautrix_signal_database_port: 5432 +matrix_mautrix_signal_database_name: 'matrix_mautrix_signal' + +matrix_mautrix_signal_database_connection_string: 'postgres://{{ matrix_mautrix_signal_database_username }}:{{ matrix_mautrix_signal_database_password }}@{{ matrix_mautrix_signal_database_hostname }}:{{ matrix_mautrix_signal_database_port }}/{{ matrix_mautrix_signal_database_name }}' + +matrix_mautrix_signal_appservice_database: "{{ + { + 'postgres': matrix_mautrix_signal_database_connection_string, + }[matrix_mautrix_signal_database_engine] + }}" + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mautrix_signal_login_shared_secret: '' + +# Default 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_mautrix_signal_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mautrix_signal_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mautrix_signal_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mautrix_signal_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mautrix_signal_configuration_yaml`. + +matrix_mautrix_signal_configuration_extension: "{{ matrix_mautrix_signal_configuration_extension_yaml|from_yaml if matrix_mautrix_signal_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mautrix_signal_configuration_yaml`. +matrix_mautrix_signal_configuration: "{{ matrix_mautrix_signal_configuration_yaml|from_yaml|combine(matrix_mautrix_signal_configuration_extension, recursive=True) }}" + +matrix_mautrix_signal_registration_yaml: "{{ lookup('template', 'templates/registration.yaml.j2') }}" + +matrix_mautrix_signal_registration: "{{ matrix_mautrix_signal_registration_yaml|from_yaml }}" + +matrix_mautrix_signal_log_level: 'DEBUG' diff --git a/roles/matrix-bridge-mautrix-signal/tasks/init.yml b/roles/matrix-bridge-mautrix-signal/tasks/init.yml new file mode 100644 index 00000000..6133e865 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/tasks/init.yml @@ -0,0 +1,16 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mautrix-signal.service', 'matrix-mautrix-signal-daemon.service'] }}" + when: matrix_mautrix_signal_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mautrix_signal_config_path }}/registration.yaml,dst=/matrix-mautrix-signal-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mautrix-signal-registration.yaml"] }} + when: matrix_mautrix_signal_enabled|bool diff --git a/roles/matrix-bridge-mautrix-signal/tasks/main.yml b/roles/matrix-bridge-mautrix-signal/tasks/main.yml new file mode 100644 index 00000000..edca20e6 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mautrix_signal_enabled|bool" + tags: + - setup-all + - setup-mautrix-signal + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mautrix_signal_enabled|bool" + tags: + - setup-all + - setup-mautrix-signal + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mautrix_signal_enabled|bool" + tags: + - setup-all + - setup-mautrix-signal diff --git a/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml new file mode 100644 index 00000000..88710868 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/tasks/setup_install.yml @@ -0,0 +1,118 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mautrix-signal role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- name: Ensure Mautrix Signal image is pulled + docker_image: + name: "{{ matrix_mautrix_signal_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mautrix_signal_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_signal_docker_image_force_pull }}" + when: "not matrix_mautrix_signal_container_self_build|bool" + + +- name: Ensure Mautrix Signal repository is present on self-build + git: + repo: "{{ matrix_mautrix_signal_docker_repo }}" + dest: "{{ matrix_mautrix_signal_docker_src_files_path }}" + force: "yes" + register: matrix_mautrix_signal_git_pull_results + when: "matrix_mautrix_signal_container_self_build|bool" + +- name: Ensure Mautrix Signal image is built + docker_image: + name: "{{ matrix_mautrix_signal_docker_image }}" + source: build + force_source: "{{ matrix_mautrix_signal_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mailer_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mautrix_signal_docker_src_files_path }}" + pull: yes + when: "matrix_mautrix_signal_container_self_build|bool" + + +- name: Ensure Mautrix Signal Daemon image is pulled + docker_image: + name: "{{ matrix_mautrix_signal_daemon_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mautrix_signal_daemon_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_signal_daemon_docker_image_force_pull }}" + when: matrix_mautrix_signal_enabled and not matrix_mautrix_signal_daemon_container_self_build|bool + register: matrix_mautrix_signal_daemon_pull_results + +- name: Ensure Mautrix Signal Daemon repository is present on self-build + git: + repo: "{{ matrix_mautrix_signal_daemon_docker_repo }}" + dest: "{{ matrix_mautrix_signal_daemon_docker_src_files_path }}" + force: "yes" + register: matrix_mautrix_signal_daemon_git_pull_results + when: "matrix_mautrix_signal_daemon_container_self_build|bool" + +- name: Ensure Mautrix Signal Daemon image is built + docker_image: + name: "{{ matrix_mautrix_signal_daemon_docker_image }}" + source: build + force_source: "{{ matrix_mautrix_signal_daemon_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mailer_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mautrix_signal_daemon_docker_src_files_path }}" + pull: yes + when: "matrix_mautrix_signal_daemon_container_self_build|bool" + +- name: Ensure Mautrix Signal paths exist + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_mautrix_signal_base_path }}" + - "{{ matrix_mautrix_signal_config_path }}" + - "{{ matrix_mautrix_signal_daemon_path }}" + - "{{ matrix_mautrix_signal_daemon_path }}/avatars" + - "{{ matrix_mautrix_signal_daemon_path }}/attachments" + - "{{ matrix_mautrix_signal_daemon_path }}/data" + +- name: Ensure mautrix-signal config.yaml installed + copy: + content: "{{ matrix_mautrix_signal_configuration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_signal_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mautrix-signal registration.yaml installed + copy: + content: "{{ matrix_mautrix_signal_registration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_signal_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mautrix-signal-daemon.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mautrix-signal-daemon.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-mautrix-signal-daemon.service" + mode: 0644 + register: matrix_mautrix_signal_daemon_systemd_service_result + +- name: Ensure matrix-mautrix-signal.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mautrix-signal.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-mautrix-signal.service" + mode: 0644 + register: matrix_mautrix_signal_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mautrix-signal.service installation + service: + daemon_reload: yes + when: "matrix_mautrix_signal_systemd_service_result.changed or matrix_mautrix_signal_daemon_systemd_service_result.changed" diff --git a/roles/matrix-bridge-mautrix-signal/tasks/setup_uninstall.yml b/roles/matrix-bridge-mautrix-signal/tasks/setup_uninstall.yml new file mode 100644 index 00000000..2ca6a9a9 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/tasks/setup_uninstall.yml @@ -0,0 +1,45 @@ +--- + +# Signal daemon service +- name: Check existence of matrix-mautrix-signal-daemon service + stat: + path: "{{ matrix_systemd_path }}/matrix-mautrix-signal-daemon.service" + register: matrix_mautrix_signal_daemon_service_stat + +- name: Ensure matrix-mautrix-signal-daemon is stopped + service: + name: matrix-mautrix-signal-daemon + state: stopped + daemon_reload: yes + when: "matrix_mautrix_signal_daemon_service_stat.stat.exists" + +- name: Ensure matrix-mautrix-signal-daemon.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mautrix-signal-daemon.service" + state: absent + when: "matrix_mautrix_signal_daemon_service_stat.stat.exists" + +# Bridge service +- name: Check existence of matrix-mautrix-signal service + stat: + path: "{{ matrix_systemd_path }}/matrix-mautrix-signal.service" + register: matrix_mautrix_signal_service_stat + +- name: Ensure matrix-mautrix-signal is stopped + service: + name: matrix-mautrix-signal + state: stopped + daemon_reload: yes + when: "matrix_mautrix_signal_service_stat.stat.exists" + +- name: Ensure matrix-mautrix-signal.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mautrix-signal.service" + state: absent + when: "matrix_mautrix_signal_service_stat.stat.exists" + +# All services +- name: Ensure systemd reloaded after matrix-mautrix-signal_X.service removal + service: + daemon_reload: yes + when: "matrix_mautrix_signal_service_stat.stat.exists or matrix_mautrix_signal_daemon_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mautrix-signal/tasks/validate_config.yml b/roles/matrix-bridge-mautrix-signal/tasks/validate_config.yml new file mode 100644 index 00000000..100af3f8 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/tasks/validate_config.yml @@ -0,0 +1,28 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mautrix_signal_homeserver_domain" + - "matrix_mautrix_signal_homeserver_address" + - "matrix_mautrix_signal_homeserver_token" + - "matrix_mautrix_signal_appservice_token" + +- name: (Deprecation) Catch and report renamed Signal variables + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_mautrix_signal_container_exposed_port_number', 'new': ''} + - {'old': 'matrix_mautrix_signal_db_user', 'new': 'matrix_mautrix_signal_database_username'} + - {'old': 'matrix_mautrix_signal_db_password', 'new': 'matrix_mautrix_signal_database_password'} + - {'old': 'matrix_mautrix_signal_db_database', 'new': 'matrix_mautrix_signal_database_name'} + - {'old': 'matrix_mautrix_signal_db_host', 'new': 'matrix_mautrix_signal_database_hostname'} + - {'old': 'matrix_mautrix_signal_db_port', 'new': 'matrix_mautrix_signal_database_port'} + - {'old': 'matrix_mautrix_signal_db_url', 'new': 'matrix_mautrix_signal_database_connection_string'} + - {'old': 'matrix_mautrix_signal_configuration_permissions', 'new': ''} diff --git a/roles/matrix-bridge-mautrix-signal/templates/config.yaml.j2 b/roles/matrix-bridge-mautrix-signal/templates/config.yaml.j2 new file mode 100644 index 00000000..ca5060a7 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/templates/config.yaml.j2 @@ -0,0 +1,206 @@ +#jinja2: lstrip_blocks: "True" +# Homeserver details +homeserver: + # The address that this appservice can use to connect to the homeserver. + address: {{ matrix_mautrix_signal_homeserver_address }} + # The domain of the homeserver (for MXIDs, etc). + domain: {{ matrix_mautrix_signal_homeserver_domain }} + # Whether or not to verify the SSL certificate of the homeserver. + # Only applies if address starts with https:// + verify_ssl: true + asmux: false + +# Application service host/registration related details +# Changing these values requires regeneration of the registration. +appservice: + # The address that the homeserver can use to connect to this appservice. + address: {{ matrix_mautrix_signal_appservice_address }} + # When using https:// the TLS certificate and key files for the address. + tls_cert: false + tls_key: false + + # The hostname and port where this appservice should listen. + hostname: 0.0.0.0 + port: 29328 + # The maximum body size of appservice API requests (from the homeserver) in mebibytes + # Usually 1 is enough, but on high-traffic bridges you might need to increase this to avoid 413s + max_body_size: 1 + + # The full URI to the database. Only Postgres is currently supported. + database: {{ matrix_mautrix_signal_database_connection_string }} + # Additional arguments for asyncpg.create_pool() + # https://magicstack.github.io/asyncpg/current/api/index.html#asyncpg.pool.create_pool + database_opts: + min_size: 5 + max_size: 10 + + # Provisioning API part of the web server for automated portal creation and fetching information. + # Used by things like mautrix-manager (https://github.com/tulir/mautrix-manager). + provisioning: + # Whether or not the provisioning API should be enabled. + enabled: true + # The prefix to use in the provisioning API endpoints. + prefix: /_matrix/provision/v1 + # The shared secret to authorize users of the API. + # Set to "generate" to generate and save a new token. + shared_secret: generate + + # The unique ID of this appservice. + id: signal + # Username of the appservice bot. + bot_username: {{ matrix_mautrix_signal_appservice_bot_username|to_json }} + # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty + # to leave display name/avatar as-is. + bot_displayname: Signal bridge bot + bot_avatar: mxc://maunium.net/wPJgTQbZOtpBFmDNkiNEMDUp + + # Community ID for bridged users (changes registration file) and rooms. + # Must be created manually. + # + # Example: "+signal:example.com". Set to false to disable. + community_id: false + + # Authentication tokens for AS <-> HS communication. + as_token: "{{ matrix_mautrix_signal_appservice_token }}" + hs_token: "{{ matrix_mautrix_signal_homeserver_token }}" + +# Prometheus telemetry config. Requires prometheus-client to be installed. +metrics: + enabled: false + listen_port: 8000 + +signal: + # Path to signald unix socket + socket_path: /signald/signald.sock + # Directory for temp files when sending files to Signal. This should be an + # absolute path that signald can read. For attachments in the other direction, + # make sure signald is configured to use an absolute path as the data directory. + outgoing_attachment_dir: /signald/attachments + # Directory where signald stores avatars for groups. + avatar_dir: /signald/avatars + # Directory where signald stores auth data. Used to delete data when logging out. + data_dir: /signald/data + # Whether or not message attachments should be removed from disk after they're bridged. + remove_file_after_handling: true + +# Bridge config +bridge: + # Localpart template of MXIDs for Signal users. + # {userid} is replaced with an identifier for the Signal user. + username_template: "signal_{userid}" + # Displayname template for Signal users. + # {displayname} is replaced with the displayname of the Signal user, which is the first + # available variable in displayname_preference. The variables in displayname_preference + # can also be used here directly. + displayname_template: "{displayname} (Signal)" + # Possible values: disallow, allow, prefer + # + # Multi-user instances are recommended to disallow contact list names, as otherwise there can + # be conflicts between names from different users' contact lists. + contact_list_names: disallow + # Available variables: full_name, first_name, last_name, phone, uuid + displayname_preference: + - full_name + - phone + + # Whether or not to create portals for all groups on login/connect. + autocreate_group_portal: true + # Whether or not to create portals for all contacts on login/connect. + autocreate_contact_portal: false + # Whether or not to use /sync to get read receipts and typing notifications + # when double puppeting is enabled + sync_with_custom_puppets: true + # Whether or not to update the m.direct account data event when double puppeting is enabled. + # Note that updating the m.direct event is not atomic (except with mautrix-asmux) + # and is therefore prone to race conditions. + sync_direct_chat_list: false + # Allow using double puppeting from any server with a valid client .well-known file. + double_puppet_allow_discovery: false + # Servers to allow double puppeting from, even if double_puppet_allow_discovery is false. + double_puppet_server_map: {} + # Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth + # + # If set, custom puppets will be enabled automatically for local users + # instead of users having to find an access token and run `login-matrix` + # manually. + # If using this for other servers than the bridge's server, + # you must also set the URL in the double_puppet_server_map. + login_shared_secret_map: + {{ matrix_mautrix_signal_homeserver_domain }}: {{ matrix_mautrix_signal_login_shared_secret|to_json }} + # Whether or not created rooms should have federation enabled. + # If false, created portal rooms will never be federated. + federate_rooms: true + # End-to-bridge encryption support options. You must install the e2be optional dependency for + # this to work. See https://github.com/tulir/mautrix-telegram/wiki/End‐to‐bridge-encryption + encryption: + # Allow encryption, work in group chat rooms with e2ee enabled + allow: false + # Default to encryption, force-enable encryption in all portals the bridge creates + # This will cause the bridge bot to be in private chats for the encryption to work properly. + default: false + # Options for automatic key sharing. + key_sharing: + # Enable key sharing? If enabled, key requests for rooms where users are in will be fulfilled. + # You must use a client that supports requesting keys from other users to use this feature. + allow: false + # Require the requesting device to have a valid cross-signing signature? + # This doesn't require that the bridge has verified the device, only that the user has verified it. + # Not yet implemented. + require_cross_signing: false + # Require devices to be verified by the bridge? + # Verification by the bridge is not yet implemented. + require_verification: true + # Whether or not to explicitly set the avatar and room name for private + # chat portal rooms. This will be implicitly enabled if encryption.default is true. + private_chat_portal_meta: false + # Whether or not the bridge should send a read receipt from the bridge bot when a message has + # been sent to Signal. This let's you check manually whether the bridge is receiving your + # messages. + # Note that this is not related to Signal delivery receipts. + delivery_receipts: false + # Whether or not delivery errors should be reported as messages in the Matrix room. (not yet implemented) + delivery_error_reports: false + # Set this to true to tell the bridge to re-send m.bridge events to all rooms on the next run. + # This field will automatically be changed back to false after it, + # except if the config file is not writable. + resend_bridge_info: false + + # The prefix for commands. Only required in non-management rooms. + command_prefix: "!signal" + + # Permissions for using the bridge. + # Permitted values: + # user - Use the bridge with puppeting. + # admin - Use and administrate the bridge. + # Permitted keys: + # * - All Matrix users + # domain - All users on that homeserver + # mxid - Specific user + permissions: + '{{ matrix_mautrix_signal_homeserver_domain }}': user + + +# Python logging configuration. +# +# See section 16.7.2 of the Python documentation for more info: +# https://docs.python.org/3.6/library/logging.config.html#configuration-dictionary-schema +logging: + version: 1 + formatters: + colored: + (): mautrix_signal.util.ColorFormatter + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + normal: + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + handlers: + console: + class: logging.StreamHandler + formatter: colored + loggers: + mau: + level: {{ matrix_mautrix_signal_log_level }} + aiohttp: + level: INFO + root: + level: {{ matrix_mautrix_signal_log_level }} + handlers: [console] diff --git a/roles/matrix-bridge-mautrix-signal/templates/registration.yaml.j2 b/roles/matrix-bridge-mautrix-signal/templates/registration.yaml.j2 new file mode 100644 index 00000000..54df82da --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/templates/registration.yaml.j2 @@ -0,0 +1,18 @@ +#jinja2: lstrip_blocks: "True" +id: signal +as_token: "{{ matrix_mautrix_signal_appservice_token }}" +hs_token: "{{ matrix_mautrix_signal_homeserver_token }}" +namespaces: + users: + - exclusive: true + regex: '^@signal_.+:{{ matrix_mautrix_signal_homeserver_domain|regex_escape }}$' + - exclusive: true + regex: '^@{{ matrix_mautrix_signal_appservice_bot_username|regex_escape }}:{{ matrix_mautrix_signal_homeserver_domain|regex_escape }}$' + aliases: + - exclusive: true + regex: '^#signal_.+:{{ matrix_mautrix_signal_homeserver_domain|regex_escape }}$' +url: {{ matrix_mautrix_signal_appservice_address }} +# See https://github.com/tulir/mautrix-signal/issues/43 +sender_localpart: _bot_{{ matrix_mautrix_signal_appservice_bot_username }} +rate_limited: false +de.sorunome.msc2409.push_ephemeral: true diff --git a/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal-daemon.service.j2 b/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal-daemon.service.j2 new file mode 100644 index 00000000..6f128da3 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal-daemon.service.j2 @@ -0,0 +1,41 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mautrix Signal daemon + +{% for service in matrix_mautrix_signal_daemon_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} + +{% for service in matrix_mautrix_signal_daemon_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" + +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal-daemon 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal-daemon 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +# We can't use `--read-only` for this bridge. +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-signal-daemon \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -v {{ matrix_mautrix_signal_daemon_path }}:/signald:z \ + {{ matrix_mautrix_signal_daemon_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal-daemon 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal-daemon 2>/dev/null' + +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mautrix-signal-daemon + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal.service.j2 b/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal.service.j2 new file mode 100644 index 00000000..e3e02424 --- /dev/null +++ b/roles/matrix-bridge-mautrix-signal/templates/systemd/matrix-mautrix-signal.service.j2 @@ -0,0 +1,48 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mautrix Signal server + +{% for service in matrix_mautrix_signal_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} + +{% for service in matrix_mautrix_signal_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-signal \ + --log-driver=none \ + --network={{ matrix_docker_network }} \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + {% if matrix_mautrix_signal_container_http_host_bind_port %} + -p {{ matrix_mautrix_signal_container_http_host_bind_port }}:29328 \ + {% endif %} + -v {{ matrix_mautrix_signal_daemon_path }}:/signald:z \ + -v {{ matrix_mautrix_signal_config_path }}:/config:z \ + {% for arg in matrix_mautrix_signal_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mautrix_signal_docker_image }} \ + python3 -m mautrix_signal -c /config/config.yaml --no-update + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-signal 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-signal 2>/dev/null' + +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mautrix-signal + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mautrix-telegram/defaults/main.yml b/roles/matrix-bridge-mautrix-telegram/defaults/main.yml new file mode 100644 index 00000000..e49de8e3 --- /dev/null +++ b/roles/matrix-bridge-mautrix-telegram/defaults/main.yml @@ -0,0 +1,132 @@ +# mautrix-telegram is a Matrix <-> Telegram bridge +# See: https://github.com/tulir/mautrix-telegram + +matrix_mautrix_telegram_enabled: true + +matrix_telegram_lottieconverter_container_self_build: false +matrix_telegram_lottieconverter_container_self_build_mask_arch: false +matrix_telegram_lottieconverter_docker_repo: "https://mau.dev/tulir/lottieconverter.git" +matrix_telegram_lottieconverter_docker_src_files_path: "{{ matrix_base_data_path }}/lotticonverter/docker-src" +matrix_telegram_lottieconverter_docker_image: "dock.mau.dev/tulir/lottieconverter:alpine-3.14" # needs to be ajusted according to FROM clause of Dockerfile of mautrix-telegram + +matrix_mautrix_telegram_container_self_build: false +matrix_mautrix_telegram_docker_repo: "https://mau.dev/tulir/mautrix-telegram.git" +matrix_mautrix_telegram_docker_src_files_path: "{{ matrix_base_data_path }}/mautrix-telegram/docker-src" + +matrix_mautrix_telegram_version: v0.9.0 +# See: https://mau.dev/tulir/mautrix-telegram/container_registry +matrix_mautrix_telegram_docker_image: "dock.mau.dev/tulir/mautrix-telegram:{{ matrix_mautrix_telegram_version }}" +matrix_mautrix_telegram_docker_image_force_pull: "{{ matrix_mautrix_telegram_docker_image.endswith(':latest') }}" + +matrix_mautrix_telegram_base_path: "{{ matrix_base_data_path }}/mautrix-telegram" +matrix_mautrix_telegram_config_path: "{{ matrix_mautrix_telegram_base_path }}/config" +matrix_mautrix_telegram_data_path: "{{ matrix_mautrix_telegram_base_path }}/data" + +# Get your own API keys at https://my.telegram.org/apps +matrix_mautrix_telegram_api_id: '' +matrix_mautrix_telegram_api_hash: '' +matrix_mautrix_telegram_bot_token: disabled + +# Mautrix telegram public endpoint to log in to telegram +# Use an uuid so it's not easily discoverable. +# Example: /741a0483-ba17-4682-9900-30bd7269f1cc +matrix_mautrix_telegram_public_endpoint: '' + +matrix_mautrix_telegram_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mautrix_telegram_homeserver_domain: '{{ matrix_domain }}' +matrix_mautrix_telegram_appservice_address: 'http://matrix-mautrix-telegram:8080' +matrix_mautrix_telegram_appservice_public_external: 'https://{{ matrix_server_fqn_matrix }}{{ matrix_mautrix_telegram_public_endpoint }}' + +matrix_mautrix_telegram_appservice_bot_username: telegrambot + +# Controls whether the matrix-mautrix-telegram container exposes its HTTP port (tcp/8080 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9006"), or empty string to not expose. +matrix_mautrix_telegram_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_mautrix_telegram_container_extra_arguments: [] + +# List of systemd services that matrix-mautrix-telegram.service depends on. +matrix_mautrix_telegram_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-mautrix-telegram.service wants +matrix_mautrix_telegram_systemd_wanted_services_list: [] + +matrix_mautrix_telegram_appservice_token: '' +matrix_mautrix_telegram_homeserver_token: '' + + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_mautrix_telegram_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_mautrix_telegram_postgres_*` variables +matrix_mautrix_telegram_database_engine: 'sqlite' + +matrix_mautrix_telegram_sqlite_database_path_local: "{{ matrix_mautrix_telegram_data_path }}/mautrix-telegram.db" +matrix_mautrix_telegram_sqlite_database_path_in_container: "/data/mautrix-telegram.db" + +matrix_mautrix_telegram_database_username: 'matrix_mautrix_telegram' +matrix_mautrix_telegram_database_password: 'some-password' +matrix_mautrix_telegram_database_hostname: 'matrix-postgres' +matrix_mautrix_telegram_database_port: 5432 +matrix_mautrix_telegram_database_name: 'matrix_mautrix_telegram' + +matrix_mautrix_telegram_database_connection_string: 'postgres://{{ matrix_mautrix_telegram_database_username }}:{{ matrix_mautrix_telegram_database_password }}@{{ matrix_mautrix_telegram_database_hostname }}:{{ matrix_mautrix_telegram_database_port }}/{{ matrix_mautrix_telegram_database_name }}' + +matrix_mautrix_telegram_appservice_database: "{{ + { + 'sqlite': ('sqlite:///' + matrix_mautrix_telegram_sqlite_database_path_in_container), + 'postgres': matrix_mautrix_telegram_database_connection_string, + }[matrix_mautrix_telegram_database_engine] +}}" + + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mautrix_telegram_login_shared_secret: '' + +# Default 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_mautrix_telegram_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mautrix_telegram_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mautrix_telegram_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mautrix_telegram_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mautrix_telegram_configuration_yaml`. + +matrix_mautrix_telegram_configuration_extension: "{{ matrix_mautrix_telegram_configuration_extension_yaml|from_yaml if matrix_mautrix_telegram_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mautrix_telegram_configuration_yaml`. +matrix_mautrix_telegram_configuration: "{{ matrix_mautrix_telegram_configuration_yaml|from_yaml|combine(matrix_mautrix_telegram_configuration_extension, recursive=True) }}" + +matrix_mautrix_telegram_registration_yaml: | + id: telegram + as_token: "{{ matrix_mautrix_telegram_appservice_token }}" + hs_token: "{{ matrix_mautrix_telegram_homeserver_token }}" + namespaces: + users: + - exclusive: true + regex: '^@telegram_.+:{{ matrix_mautrix_telegram_homeserver_domain|regex_escape }}$' + - exclusive: true + regex: '^@{{ matrix_mautrix_telegram_appservice_bot_username|regex_escape }}:{{ matrix_mautrix_telegram_homeserver_domain|regex_escape }}$' + aliases: + - exclusive: true + regex: '^#telegram_.+:{{ matrix_mautrix_telegram_homeserver_domain|regex_escape }}$' + # See https://github.com/tulir/mautrix-signal/issues/43 + sender_localpart: _bot_{{ matrix_mautrix_telegram_appservice_bot_username }} + url: {{ matrix_mautrix_telegram_appservice_address }} + rate_limited: false + de.sorunome.msc2409.push_ephemeral: true + +matrix_mautrix_telegram_registration: "{{ matrix_mautrix_telegram_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mautrix-telegram/tasks/init.yml b/roles/matrix-bridge-mautrix-telegram/tasks/init.yml new file mode 100644 index 00000000..84ac86d0 --- /dev/null +++ b/roles/matrix-bridge-mautrix-telegram/tasks/init.yml @@ -0,0 +1,70 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mautrix_telegram_container_self_build and matrix_mautrix_telegram_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mautrix-telegram.service'] }}" + when: matrix_mautrix_telegram_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mautrix_telegram_config_path }}/registration.yaml,dst=/matrix-mautrix-telegram-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mautrix-telegram-registration.yaml"] }} + when: matrix_mautrix_telegram_enabled|bool + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append Mautrix Telegram's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-bridge-mautrix-telegram role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate Mautrix Telegram proxying configuration for matrix-nginx-proxy + set_fact: + matrix_mautrix_telegram_matrix_nginx_proxy_configuration: | + location {{ matrix_mautrix_telegram_public_endpoint }} { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-mautrix-telegram:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:9006; + {% endif %} + } + + - name: Register Mautrix Telegram proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks|default([]) + + + [matrix_mautrix_telegram_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_mautrix_telegram_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the Mautrix Telegram bridge but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ matrix_mautrix_telegram_public_endpoint }}` + URL endpoint to the matrix-mautrix-telegram container. + You can expose the container's port using the `matrix_mautrix_telegram_container_http_host_bind_port` variable. + when: "matrix_mautrix_telegram_enabled|bool and matrix_nginx_proxy_enabled is not defined" diff --git a/roles/matrix-bridge-mautrix-telegram/tasks/main.yml b/roles/matrix-bridge-mautrix-telegram/tasks/main.yml new file mode 100644 index 00000000..8a218ed8 --- /dev/null +++ b/roles/matrix-bridge-mautrix-telegram/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mautrix_telegram_enabled|bool" + tags: + - setup-all + - setup-mautrix-telegram + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mautrix_telegram_enabled|bool" + tags: + - setup-all + - setup-mautrix-telegram + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mautrix_telegram_enabled|bool" + tags: + - setup-all + - setup-mautrix-telegram diff --git a/roles/matrix-bridge-mautrix-telegram/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-telegram/tasks/setup_install.yml new file mode 100644 index 00000000..e2e583f2 --- /dev/null +++ b/roles/matrix-bridge-mautrix-telegram/tasks/setup_install.yml @@ -0,0 +1,150 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mautrix-telegram role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_mautrix_telegram_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mautrix_telegram_sqlite_database_path_local }}" + register: matrix_mautrix_telegram_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mautrix_telegram_sqlite_database_path_local }}" + dst: "{{ matrix_mautrix_telegram_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mautrix_telegram_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mautrix-telegram.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mautrix_telegram_requires_restart: true + when: "matrix_mautrix_telegram_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mautrix_telegram_database_engine == 'postgres'" + +- name: Ensure Mautrix Telegram paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mautrix_telegram_base_path }}", when: true } + - { path: "{{ matrix_mautrix_telegram_config_path }}", when: true } + - { path: "{{ matrix_mautrix_telegram_data_path }}", when: true } + - { path: "{{ matrix_mautrix_telegram_docker_src_files_path }}", when: "{{ matrix_mautrix_telegram_container_self_build }}" } + when: item.when|bool + +- name: Ensure Mautrix Telegram image is pulled + docker_image: + name: "{{ matrix_mautrix_telegram_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mautrix_telegram_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_telegram_docker_image_force_pull }}" + when: "not matrix_mautrix_telegram_container_self_build|bool" + +- name: Ensure lottieconverter is present when self-building + git: + repo: "{{ matrix_telegram_lottieconverter_docker_repo }}" + dest: "{{ matrix_telegram_lottieconverter_docker_src_files_path }}" + force: "yes" + register: matrix_telegram_lottieconverter_git_pull_results + when: "matrix_telegram_lottieconverter_container_self_build|bool and matrix_mautrix_telegram_container_self_build|bool" + +- name: Ensure lottieconverter Docker image is built + docker_image: + name: "{{ matrix_telegram_lottieconverter_docker_image }}" + source: build + force_source: "{{ matrix_telegram_lottieconverter_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_telegram_lottieconverter_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_telegram_lottieconverter_docker_src_files_path }}" + pull: yes + when: "matrix_telegram_lottieconverter_container_self_build|bool and matrix_telegram_lottieconverter_git_pull_results.changed and matrix_mautrix_telegram_container_self_build|bool" + +- name: Ensure matrix-mautrix-telegram repository is present when self-building + git: + repo: "{{ matrix_mautrix_telegram_docker_repo }}" + dest: "{{ matrix_mautrix_telegram_docker_src_files_path }}" + force: "yes" + register: matrix_mautrix_telegram_git_pull_results + when: "matrix_mautrix_telegram_container_self_build|bool" + +- name: Ensure matrix-mautrix-telegram Docker image is built + docker_image: + name: "{{ matrix_mautrix_telegram_docker_image }}" + source: build + force_source: "{{ matrix_mautrix_telegram_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_telegram_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mautrix_telegram_docker_src_files_path }}" + pull: "{{ not matrix_telegram_lottieconverter_container_self_build_mask_arch|bool }}" + args: + TARGETARCH: "" + when: "matrix_mautrix_telegram_container_self_build|bool and matrix_mautrix_telegram_git_pull_results.changed" + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mautrix_telegram_base_path }}/mautrix-telegram.db" + register: matrix_mautrix_telegram_stat_database + +- name: (Data relocation) Ensure matrix-mautrix-telegram.service is stopped + service: + name: matrix-mautrix-telegram + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mautrix_telegram_stat_database.stat.exists" + +- name: (Data relocation) Move mautrix-telegram database file to ./data directory + command: "mv {{ matrix_mautrix_telegram_base_path }}/mautrix-telegram.db {{ matrix_mautrix_telegram_data_path }}/mautrix-telegram.db" + when: "matrix_mautrix_telegram_stat_database.stat.exists" + +- name: Ensure mautrix-telegram config.yaml installed + copy: + content: "{{ matrix_mautrix_telegram_configuration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_telegram_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mautrix-telegram registration.yaml installed + copy: + content: "{{ matrix_mautrix_telegram_registration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_telegram_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mautrix-telegram.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mautrix-telegram.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-mautrix-telegram.service" + mode: 0644 + register: matrix_mautrix_telegram_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mautrix-telegram.service installation + service: + daemon_reload: yes + when: "matrix_mautrix_telegram_systemd_service_result.changed" + +- name: Ensure matrix-mautrix-telegram.service restarted, if necessary + service: + name: "matrix-mautrix-telegram.service" + state: restarted + when: "matrix_mautrix_telegram_requires_restart|bool" diff --git a/roles/matrix-bridge-mautrix-telegram/tasks/setup_uninstall.yml b/roles/matrix-bridge-mautrix-telegram/tasks/setup_uninstall.yml new file mode 100644 index 00000000..b14bd737 --- /dev/null +++ b/roles/matrix-bridge-mautrix-telegram/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mautrix-telegram service + stat: + path: "{{ matrix_systemd_path }}/matrix-mautrix-telegram.service" + register: matrix_mautrix_telegram_service_stat + +- name: Ensure matrix-mautrix-telegram is stopped + service: + name: matrix-mautrix-telegram + state: stopped + daemon_reload: yes + when: "matrix_mautrix_telegram_service_stat.stat.exists" + +- name: Ensure matrix-mautrix-telegram.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mautrix-telegram.service" + state: absent + when: "matrix_mautrix_telegram_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mautrix-telegram.service removal + service: + daemon_reload: yes + when: "matrix_mautrix_telegram_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mautrix-telegram/tasks/validate_config.yml b/roles/matrix-bridge-mautrix-telegram/tasks/validate_config.yml new file mode 100644 index 00000000..5b1f3b00 --- /dev/null +++ b/roles/matrix-bridge-mautrix-telegram/tasks/validate_config.yml @@ -0,0 +1,22 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mautrix_telegram_api_id" + - "matrix_mautrix_telegram_api_hash" + - "matrix_mautrix_telegram_public_endpoint" + - "matrix_mautrix_telegram_appservice_token" + - "matrix_mautrix_telegram_homeserver_token" + +- name: (Deprecation) Catch and report renamed Telegram variables + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_mautrix_telegram_container_exposed_port_number', 'new': ''} diff --git a/roles/matrix-bridge-mautrix-telegram/templates/config.yaml.j2 b/roles/matrix-bridge-mautrix-telegram/templates/config.yaml.j2 new file mode 100644 index 00000000..39a18462 --- /dev/null +++ b/roles/matrix-bridge-mautrix-telegram/templates/config.yaml.j2 @@ -0,0 +1,419 @@ +#jinja2: lstrip_blocks: "True" +# Homeserver details +homeserver: + # The address that this appservice can use to connect to the homeserver. + address: {{ matrix_mautrix_telegram_homeserver_address }} + # The domain of the homeserver (for MXIDs, etc). + domain: {{ matrix_mautrix_telegram_homeserver_domain }} + # Whether or not to verify the SSL certificate of the homeserver. + # Only applies if address starts with https:// + verify_ssl: true + +# Application service host/registration related details +# Changing these values requires regeneration of the registration. +appservice: + # The address that the homeserver can use to connect to this appservice. + address: {{ matrix_mautrix_telegram_appservice_address|to_json }} + + # The hostname and port where this appservice should listen. + hostname: 0.0.0.0 + port: 8080 + # The maximum body size of appservice API requests (from the homeserver) in mebibytes + # Usually 1 is enough, but on high-traffic bridges you might need to increase this to avoid 413s + max_body_size: 1 + + # The full URI to the database. SQLite and Postgres are fully supported. + # Other DBMSes supported by SQLAlchemy may or may not work. + # Format examples: + # SQLite: sqlite:///filename.db + # Postgres: postgres://username:password@hostname/dbname + database: {{ matrix_mautrix_telegram_appservice_database|to_json }} + + # Public part of web server for out-of-Matrix interaction with the bridge. + # Used for things like login if the user wants to make sure the 2FA password isn't stored in + # the HS database. + public: + # Whether or not the public-facing endpoints should be enabled. + enabled: true + # The prefix to use in the public-facing endpoints. + prefix: {{ matrix_mautrix_telegram_public_endpoint|to_json }} + # The base URL where the public-facing endpoints are available. The prefix is not added + # implicitly. + external: {{ matrix_mautrix_telegram_appservice_public_external|to_json }} + + # Provisioning API part of the web server for automated portal creation and fetching information. + # Used by things like Dimension (https://dimension.t2bot.io/). + provisioning: + # Whether or not the provisioning API should be enabled. + enabled: false + # The prefix to use in the provisioning API endpoints. + prefix: /_matrix/provision/v1 + # The shared secret to authorize users of the API. + # Set to "generate" to generate and save a new token. + shared_secret: generate + + # The unique ID of this appservice. + id: telegram + # Username of the appservice bot. + bot_username: {{ matrix_mautrix_telegram_appservice_bot_username|to_json }} + # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty + # to leave display name/avatar as-is. + bot_displayname: Telegram bridge bot + bot_avatar: mxc://maunium.net/tJCRmUyJDsgRNgqhOgoiHWbX + + # Authentication tokens for AS <-> HS communication. + as_token: {{ matrix_mautrix_telegram_appservice_token|to_json }} + hs_token: {{ matrix_mautrix_telegram_homeserver_token|to_json }} + +# Bridge config +bridge: + # Localpart template of MXIDs for Telegram users. + # {userid} is replaced with the user ID of the Telegram user. + username_template: "telegram_{userid}" + # Localpart template of room aliases for Telegram portal rooms. + # {groupname} is replaced with the name part of the public channel/group invite link ( https://t.me/{} ) + alias_template: "telegram_{groupname}" + # Displayname template for Telegram users. + # {displayname} is replaced with the display name of the Telegram user. + displayname_template: "{displayname} (Telegram)" + + # Set the preferred order of user identifiers which to use in the Matrix puppet display name. + # In the (hopefully unlikely) scenario that none of the given keys are found, the numeric user + # ID is used. + # + # If the bridge is working properly, a phone number or an username should always be known, but + # the other one can very well be empty. + # + # Valid keys: + # "full name" (First and/or last name) + # "full name reversed" (Last and/or first name) + # "first name" + # "last name" + # "username" + # "phone number" + displayname_preference: + - full name + - username + - phone number + # Maximum length of displayname + displayname_max_length: 100 + + # Maximum number of members to sync per portal when starting up. Other members will be + # synced when they send messages. The maximum is 10000, after which the Telegram server + # will not send any more members. + # Defaults to no local limit (-> limited to 10000 by server) + max_initial_member_sync: -1 + # Whether or not to sync the member list in channels. + # If no channel admins have logged into the bridge, the bridge won't be able to sync the member + # list regardless of this setting. + sync_channel_members: true + # Whether or not to skip deleted members when syncing members. + skip_deleted_members: true + # Whether or not to automatically synchronize contacts and chats of Matrix users logged into + # their Telegram account at startup. + startup_sync: true + # Number of most recently active dialogs to check when syncing chats. + # Dialogs include groups and private chats, but only groups are synced. + # Set to 0 to remove limit. + sync_dialog_limit: 30 + # Whether or not to sync and create portals for direct chats at startup. + sync_direct_chats: false + # The maximum number of simultaneous Telegram deletions to handle. + # A large number of simultaneous redactions could put strain on your homeserver. + max_telegram_delete: 10 + # Whether or not to automatically sync the Matrix room state (mostly unpuppeted displaynames) + # at startup and when creating a bridge. + sync_matrix_state: true + # Allow logging in within Matrix. If false, users can only log in using login-qr or the + # out-of-Matrix login website (see appservice.public config section) + allow_matrix_login: true + # Whether or not to bridge plaintext highlights. + # Only enable this if your displayname_template has some static part that the bridge can use to + # reliably identify what is a plaintext highlight. + plaintext_highlights: false + # Whether or not to make portals of publicly joinable channels/supergroups publicly joinable on Matrix. + public_portals: true + # Whether or not to use /sync to get presence, read receipts and typing notifications when using + # your own Matrix account as the Matrix puppet for your Telegram account. + sync_with_custom_puppets: true + # Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth + # + # If set, custom puppets will be enabled automatically for local users + # instead of users having to find an access token and run `login-matrix` + # manually. + login_shared_secret: {{ matrix_mautrix_telegram_login_shared_secret|to_json }} + # Set to false to disable link previews in messages sent to Telegram. + telegram_link_preview: true + # Use inline images instead of a separate message for the caption. + # N.B. Inline images are not supported on all clients (e.g. Element iOS). + inline_images: false + # Maximum size of image in megabytes before sending to Telegram as a document. + image_as_file_size: 10 + # Maximum size of Telegram documents in megabytes to bridge. + max_document_size: 100 + # Enable experimental parallel file transfer, which makes uploads/downloads much faster by + # streaming from/to Matrix and using many connections for Telegram. + # Note that generating HQ thumbnails for videos is not possible with streamed transfers. + parallel_file_transfer: false + # Whether or not created rooms should have federation enabled. + # If false, created portal rooms will never be federated. + federate_rooms: true + # Settings for converting animated stickers. + animated_sticker: + # Format to which animated stickers should be converted. + # disable - No conversion, send as-is (gzipped lottie) + # png - converts to non-animated png (fastest), + # gif - converts to animated gif, but loses transparency + # webm - converts to webm video, requires ffmpeg executable with vp9 codec and webm container support + target: gif + # Arguments for converter. All converters take width and height. + # GIF converter takes background as a hex color. + args: + width: 256 + height: 256 + background: "020202" # only for gif + fps: 30 # only for webm + # End-to-bridge encryption support options. These require matrix-nio to be installed with pip + # and login_shared_secret to be configured in order to get a device for the bridge bot. + # + # Additionally, https://github.com/matrix-org/synapse/pull/5758 is required if using a normal + # application service. + encryption: + # Allow encryption, work in group chat rooms with e2ee enabled + allow: false + # Default to encryption, force-enable encryption in all portals the bridge creates + # This will cause the bridge bot to be in private chats for the encryption to work properly. + default: false + # Database for the encryption data. Currently only supports Postgres and an in-memory + # store that's persisted as a pickle. + # If set to `default`, will use the appservice postgres database + # or a pickle file if the appservice database is sqlite. + # + # Format examples: + # Pickle: pickle:///filename.pickle + # Postgres: postgres://username:password@hostname/dbname + database: default + + # Whether or not to explicitly set the avatar and room name for private + # chat portal rooms. This will be implicitly enabled if encryption.default is true. + private_chat_portal_meta: false + # Whether or not the bridge should send a read receipt from the bridge bot when a message has + # been sent to Telegram. + delivery_receipts: false + # Whether or not delivery errors should be reported as messages in the Matrix room. + delivery_error_reports: false + # Set this to true to tell the bridge to re-send m.bridge events to all rooms on the next run. + # This field will automatically be changed back to false after it, + # except if the config file is not writable. + resend_bridge_info: false + + # Overrides for base power levels. + initial_power_level_overrides: + user: {} + group: {} + + # Whether to bridge Telegram bot messages as m.notices or m.texts. + bot_messages_as_notices: true + bridge_notices: + # Whether or not Matrix bot messages (type m.notice) should be bridged. + default: false + # List of user IDs for whom the previous flag is flipped. + # e.g. if bridge_notices.default is false, notices from other users will not be bridged, but + # notices from users listed here will be bridged. + exceptions: [] + + # Some config options related to Telegram message deduplication. + # The default values are usually fine, but some debug messages/warnings might recommend you + # change these. + deduplication: + # Whether or not to check the database if the message about to be sent is a duplicate. + pre_db_check: false + # The number of latest events to keep when checking for duplicates. + # You might need to increase this on high-traffic bridge instances. + cache_queue_length: 20 + + + # The formats to use when sending messages to Telegram via the relay bot. + # + # Telegram doesn't have built-in emotes, so the m.emote format is also used for non-relaybot users. + # + # Available variables: + # $sender_displayname - The display name of the sender (e.g. Example User) + # $sender_username - The username (Matrix ID localpart) of the sender (e.g. exampleuser) + # $sender_mxid - The Matrix ID of the sender (e.g. @exampleuser:example.com) + # $message - The message content as HTML + message_formats: + m.text: "$sender_displayname: $message" + m.notice: "$sender_displayname: $message" + m.emote: "* $sender_displayname $message" + m.file: "$sender_displayname sent a file: $message" + m.image: "$sender_displayname sent an image: $message" + m.audio: "$sender_displayname sent an audio file: $message" + m.video: "$sender_displayname sent a video: $message" + m.location: "$sender_displayname sent a location: $message" + # Telegram doesn't have built-in emotes, this field specifies how m.emote's from authenticated + # users are sent to telegram. All fields in message_formats are supported. Additionally, the + # Telegram user info is available in the following variables: + # $displayname - Telegram displayname + # $username - Telegram username (may not exist) + # $mention - Telegram @username or displayname mention (depending on which exists) + emote_format: "* $mention $formatted_body" + + # The formats to use when sending state events to Telegram via the relay bot. + # + # Variables from `message_formats` that have the `sender_` prefix are available without the prefix. + # In name_change events, `$prev_displayname` is the previous displayname. + # + # Set format to an empty string to disable the messages for that event. + state_event_formats: + join: "$displayname joined the room." + leave: "$displayname left the room." + name_change: "$prev_displayname changed their name to $displayname" + + # Filter rooms that can/can't be bridged. Can also be managed using the `filter` and + # `filter-mode` management commands. + # + # Filters do not affect direct chats. + # An empty blacklist will essentially disable the filter. + filter: + # Filter mode to use. Either "blacklist" or "whitelist". + # If the mode is "blacklist", the listed chats will never be bridged. + # If the mode is "whitelist", only the listed chats can be bridged. + mode: blacklist + # The list of group/channel IDs to filter. + list: [] + + # The prefix for commands. Only required in non-management rooms. + command_prefix: "!tg" + + # Permissions for using the bridge. + # Permitted values: + # relaybot - Only use the bridge via the relaybot, no access to commands. + # user - Relaybot level + access to commands to create bridges. + # puppeting - User level + logging in with a Telegram account. + # full - Full access to use the bridge, i.e. previous levels + Matrix login. + # admin - Full access to use the bridge and some extra administration commands. + # Permitted keys: + # * - All Matrix users + # domain - All users on that homeserver + # mxid - Specific user + permissions: + '{{ matrix_mautrix_telegram_homeserver_domain }}': full + + # Options related to the message relay Telegram bot. + relaybot: + private_chat: + # List of users to invite to the portal when someone starts a private chat with the bot. + # If empty, private chats with the bot won't create a portal. + invite: [] + # Whether or not to bridge state change messages in relaybot private chats. + state_changes: true + # When private_chat_invite is empty, this message is sent to users /starting the + # relaybot. Telegram's "markdown" is supported. + message: This is a Matrix bridge relaybot and does not support direct chats + # List of users to invite to all group chat portals created by the bridge. + group_chat_invite: [] + # Whether or not the relaybot should not bridge events in unbridged group chats. + # If false, portals will be created when the relaybot receives messages, just like normal + # users. This behavior is usually not desirable, as it interferes with manually bridging + # the chat to another room. + ignore_unbridged_group_chat: true + # Whether or not to allow creating portals from Telegram. + authless_portals: true + # Whether or not to allow Telegram group admins to use the bot commands. + whitelist_group_admins: true + # Whether or not to ignore incoming events sent by the relay bot. + ignore_own_incoming_events: true + # List of usernames/user IDs who are also allowed to use the bot commands. + whitelist: [] + +# Telegram config +telegram: + # Get your own API keys at https://my.telegram.org/apps + api_id: {{ matrix_mautrix_telegram_api_id|to_json }} + api_hash: {{ matrix_mautrix_telegram_api_hash|to_json }} + # (Optional) Create your own bot at https://t.me/BotFather + bot_token: {{ matrix_mautrix_telegram_bot_token|to_json }} + + # Telethon connection options. + connection: + # The timeout in seconds to be used when connecting. + timeout: 120 + # How many times the reconnection should retry, either on the initial connection or when + # Telegram disconnects us. May be set to a negative or null value for infinite retries, but + # this is not recommended, since the program can get stuck in an infinite loop. + retries: 5 + # The delay in seconds to sleep between automatic reconnections. + retry_delay: 1 + # The threshold below which the library should automatically sleep on flood wait errors + # (inclusive). For instance, if a FloodWaitError for 17s occurs and flood_sleep_threshold + # is 20s, the library will sleep automatically. If the error was for 21s, it would raise + # the error instead. Values larger than a day (86400) will be changed to a day. + flood_sleep_threshold: 60 + # How many times a request should be retried. Request are retried when Telegram is having + # internal issues, when there is a FloodWaitError less than flood_sleep_threshold, or when + # there's a migrate error. May take a negative or null value for infinite retries, but this + # is not recommended, since some requests can always trigger a call fail (such as searching + # for messages). + request_retries: 5 + + # Device info sent to Telegram. + device_info: + # "auto" = OS name+version. + device_model: auto + # "auto" = Telethon version. + system_version: auto + # "auto" = mautrix-telegram version. + app_version: auto + lang_code: en + system_lang_code: en + + # Custom server to connect to. + server: + # Set to true to use these server settings. If false, will automatically + # use production server assigned by Telegram. Set to false in production. + enabled: false + # The DC ID to connect to. + dc: 2 + # The IP to connect to. + ip: 149.154.167.40 + # The port to connect to. 443 may not work, 80 is better and both are equally secure. + port: 80 + + # Telethon proxy configuration. + # You must install PySocks from pip for proxies to work. + proxy: + # Allowed types: disabled, socks4, socks5, http + type: disabled + # Proxy IP address and port. + address: 127.0.0.1 + port: 1080 + # Whether or not to perform DNS resolving remotely. + rdns: true + # Proxy authentication (optional). + username: "" + password: "" + +# Python logging configuration. +# +# See section 16.7.2 of the Python documentation for more info: +# https://docs.python.org/3.6/library/logging.config.html#configuration-dictionary-schema +logging: + version: 1 + formatters: + precise: + format: "[%(asctime)s] [%(levelname)s@%(name)s] %(message)s" + handlers: + console: + class: logging.StreamHandler + formatter: precise + loggers: + mau: + level: DEBUG + telethon: + level: DEBUG + aiohttp: + level: INFO + root: + level: DEBUG + handlers: [console] diff --git a/roles/matrix-bridge-mautrix-telegram/templates/systemd/matrix-mautrix-telegram.service.j2 b/roles/matrix-bridge-mautrix-telegram/templates/systemd/matrix-mautrix-telegram.service.j2 new file mode 100644 index 00000000..ae1ac675 --- /dev/null +++ b/roles/matrix-bridge-mautrix-telegram/templates/systemd/matrix-mautrix-telegram.service.j2 @@ -0,0 +1,54 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mautrix Telegram bridge +{% for service in matrix_mautrix_telegram_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mautrix_telegram_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-telegram 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-telegram 2>/dev/null' +ExecStartPre={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-telegram-db \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -v {{ matrix_mautrix_telegram_config_path }}:/config:z \ + -v {{ matrix_mautrix_telegram_data_path }}:/data:z \ + {{ matrix_mautrix_telegram_docker_image }} \ + alembic -x config=/config/config.yaml upgrade head + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-telegram \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_mautrix_telegram_container_http_host_bind_port %} + -p {{ matrix_mautrix_telegram_container_http_host_bind_port }}:8080 \ + {% endif %} + -v {{ matrix_mautrix_telegram_config_path }}:/config:z \ + -v {{ matrix_mautrix_telegram_data_path }}:/data:z \ + {% for arg in matrix_mautrix_telegram_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mautrix_telegram_docker_image }} \ + python3 -m mautrix_telegram -c /config/config.yaml --no-update + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-telegram 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-telegram 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mautrix-telegram + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mautrix-whatsapp/defaults/main.yml b/roles/matrix-bridge-mautrix-whatsapp/defaults/main.yml new file mode 100644 index 00000000..41bfb8be --- /dev/null +++ b/roles/matrix-bridge-mautrix-whatsapp/defaults/main.yml @@ -0,0 +1,110 @@ +# mautrix-whatsapp is a Matrix <-> Whatsapp bridge +# See: https://github.com/tulir/mautrix-whatsapp + +matrix_mautrix_whatsapp_enabled: true + +matrix_mautrix_whatsapp_version: latest +# See: https://mau.dev/tulir/mautrix-whatsapp/container_registry +matrix_mautrix_whatsapp_docker_image: "dock.mau.dev/mautrix/whatsapp:{{ matrix_mautrix_whatsapp_version }}" +matrix_mautrix_whatsapp_docker_image_force_pull: "{{ matrix_mautrix_whatsapp_docker_image.endswith(':latest') }}" + +matrix_mautrix_whatsapp_base_path: "{{ matrix_base_data_path }}/mautrix-whatsapp" +matrix_mautrix_whatsapp_config_path: "{{ matrix_mautrix_whatsapp_base_path }}/config" +matrix_mautrix_whatsapp_data_path: "{{ matrix_mautrix_whatsapp_base_path }}/data" + +matrix_mautrix_whatsapp_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mautrix_whatsapp_homeserver_domain: "{{ matrix_domain }}" +matrix_mautrix_whatsapp_appservice_address: "http://matrix-mautrix-whatsapp:8080" + +# A list of extra arguments to pass to the container +matrix_mautrix_whatsapp_container_extra_arguments: [] + +# List of systemd services that matrix-mautrix-whatsapp.service depends on. +matrix_mautrix_whatsapp_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-mautrix-whatsapp.service wants +matrix_mautrix_whatsapp_systemd_wanted_services_list: [] + +matrix_mautrix_whatsapp_appservice_token: '' +matrix_mautrix_whatsapp_homeserver_token: '' + +matrix_mautrix_whatsapp_appservice_bot_username: whatsappbot + + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_mautrix_whatsapp_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_mautrix_whatsapp_postgres_*` variables +matrix_mautrix_whatsapp_database_engine: 'sqlite' + +matrix_mautrix_whatsapp_sqlite_database_path_local: "{{ matrix_mautrix_whatsapp_data_path }}/mautrix-whatsapp.db" +matrix_mautrix_whatsapp_sqlite_database_path_in_container: "/data/mautrix-whatsapp.db" + +matrix_mautrix_whatsapp_database_username: 'matrix_mautrix_whatsapp' +matrix_mautrix_whatsapp_database_password: 'some-password' +matrix_mautrix_whatsapp_database_hostname: 'matrix-postgres' +matrix_mautrix_whatsapp_database_port: 5432 +matrix_mautrix_whatsapp_database_name: 'matrix_mautrix_whatsapp' + +matrix_mautrix_whatsapp_database_connection_string: 'postgresql://{{ matrix_mautrix_whatsapp_database_username }}:{{ matrix_mautrix_whatsapp_database_password }}@{{ matrix_mautrix_whatsapp_database_hostname }}:{{ matrix_mautrix_whatsapp_database_port }}/{{ matrix_mautrix_whatsapp_database_name }}?sslmode=disable' + +matrix_mautrix_whatsapp_appservice_database_type: "{{ + { + 'sqlite': 'sqlite3', + 'postgres':'postgres', + }[matrix_mautrix_whatsapp_database_engine] +}}" + +matrix_mautrix_whatsapp_appservice_database_uri: "{{ + { + 'sqlite': matrix_mautrix_whatsapp_sqlite_database_path_in_container, + 'postgres': matrix_mautrix_whatsapp_database_connection_string, + }[matrix_mautrix_whatsapp_database_engine] +}}" + + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mautrix_whatsapp_login_shared_secret: '' + +# Default mautrix-whatsapp 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_mautrix_whatsapp_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mautrix_whatsapp_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mautrix_whatsapp_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mautrix_whatsapp_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mautrix_whatsapp_configuration_yaml`. + +matrix_mautrix_whatsapp_configuration_extension: "{{ matrix_mautrix_whatsapp_configuration_extension_yaml|from_yaml if matrix_mautrix_whatsapp_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mautrix_whatsapp_configuration_yaml`. +matrix_mautrix_whatsapp_configuration: "{{ matrix_mautrix_whatsapp_configuration_yaml|from_yaml|combine(matrix_mautrix_whatsapp_configuration_extension, recursive=True) }}" + +matrix_mautrix_whatsapp_registration_yaml: | + id: whatsapp + url: {{ matrix_mautrix_whatsapp_appservice_address }} + as_token: "{{ matrix_mautrix_whatsapp_appservice_token }}" + hs_token: "{{ matrix_mautrix_whatsapp_homeserver_token }}" + # See https://github.com/tulir/mautrix-signal/issues/43 + sender_localpart: _bot_{{ matrix_mautrix_whatsapp_appservice_bot_username }} + rate_limited: false + namespaces: + users: + - regex: '^@whatsapp_[0-9]+:{{ matrix_mautrix_whatsapp_homeserver_domain|regex_escape }}$' + exclusive: true + - exclusive: true + regex: '^@{{ matrix_mautrix_whatsapp_appservice_bot_username|regex_escape }}:{{ matrix_mautrix_whatsapp_homeserver_domain|regex_escape }}$' + de.sorunome.msc2409.push_ephemeral: true + +matrix_mautrix_whatsapp_registration: "{{ matrix_mautrix_whatsapp_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mautrix-whatsapp/tasks/init.yml b/roles/matrix-bridge-mautrix-whatsapp/tasks/init.yml new file mode 100644 index 00000000..f320bc74 --- /dev/null +++ b/roles/matrix-bridge-mautrix-whatsapp/tasks/init.yml @@ -0,0 +1,16 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mautrix-whatsapp.service'] }}" + when: matrix_mautrix_whatsapp_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mautrix_whatsapp_config_path }}/registration.yaml,dst=/matrix-mautrix-whatsapp-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mautrix-whatsapp-registration.yaml"] }} + when: matrix_mautrix_whatsapp_enabled|bool diff --git a/roles/matrix-bridge-mautrix-whatsapp/tasks/main.yml b/roles/matrix-bridge-mautrix-whatsapp/tasks/main.yml new file mode 100644 index 00000000..188eae4a --- /dev/null +++ b/roles/matrix-bridge-mautrix-whatsapp/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mautrix_whatsapp_enabled|bool" + tags: + - setup-all + - setup-mautrix-whatsapp + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup and matrix_mautrix_whatsapp_enabled" + tags: + - setup-all + - setup-mautrix-whatsapp + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup and not matrix_mautrix_whatsapp_enabled" + tags: + - setup-all + - setup-mautrix-whatsapp diff --git a/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_install.yml b/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_install.yml new file mode 100644 index 00000000..9691a58f --- /dev/null +++ b/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_install.yml @@ -0,0 +1,115 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mautrix-whatsapp role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_mautrix_whatsapp_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mautrix_whatsapp_sqlite_database_path_local }}" + register: matrix_mautrix_whatsapp_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mautrix_whatsapp_sqlite_database_path_local }}" + dst: "{{ matrix_mautrix_whatsapp_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mautrix_whatsapp_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mautrix-whatsapp.service'] + pgloader_options: ['--with "quote identifiers"'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mautrix_whatsapp_requires_restart: true + when: "matrix_mautrix_whatsapp_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mautrix_whatsapp_database_engine == 'postgres'" + +- name: Ensure Mautrix Whatsapp image is pulled + docker_image: + name: "{{ matrix_mautrix_whatsapp_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mautrix_whatsapp_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mautrix_whatsapp_docker_image_force_pull }}" + +- name: Ensure Mautrix Whatsapp paths exists + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_mautrix_whatsapp_base_path }}" + - "{{ matrix_mautrix_whatsapp_config_path }}" + - "{{ matrix_mautrix_whatsapp_data_path }}" + +- name: Check if an old database file exists + stat: + path: "{{ matrix_mautrix_whatsapp_base_path }}/mautrix-whatsapp.db" + register: matrix_mautrix_whatsapp_stat_database + +- name: Check if an old matrix state file exists + stat: + path: "{{ matrix_mautrix_whatsapp_base_path }}/mx-state.json" + register: matrix_mautrix_whatsapp_stat_mx_state + +- name: (Data relocation) Ensure matrix-mautrix-whatsapp.service is stopped + service: + name: matrix-mautrix-whatsapp + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mautrix_whatsapp_stat_database.stat.exists" + +- name: (Data relocation) Move mautrix-whatsapp database file to ./data directory + command: "mv {{ matrix_mautrix_whatsapp_base_path }}/mautrix-whatsapp.db {{ matrix_mautrix_whatsapp_data_path }}/mautrix-whatsapp.db" + when: "matrix_mautrix_whatsapp_stat_database.stat.exists" + +- name: (Data relocation) Move mautrix-whatsapp mx-state file to ./data directory + command: "mv {{ matrix_mautrix_whatsapp_base_path }}/mx-state.json {{ matrix_mautrix_whatsapp_data_path }}/mx-state.json" + when: "matrix_mautrix_whatsapp_stat_mx_state.stat.exists" + +- name: Ensure mautrix-whatsapp config.yaml installed + copy: + content: "{{ matrix_mautrix_whatsapp_configuration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_whatsapp_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mautrix-whatsapp registration.yaml installed + copy: + content: "{{ matrix_mautrix_whatsapp_registration|to_nice_yaml }}" + dest: "{{ matrix_mautrix_whatsapp_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mautrix-whatsapp.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mautrix-whatsapp.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-mautrix-whatsapp.service" + mode: 0644 + register: matrix_mautrix_whatsapp_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mautrix-whatsapp.service installation + service: + daemon_reload: yes + when: "matrix_mautrix_whatsapp_systemd_service_result.changed" + +- name: Ensure matrix-mautrix-whatsapp.service restarted, if necessary + service: + name: "matrix-mautrix-whatsapp.service" + state: restarted + when: "matrix_mautrix_whatsapp_requires_restart|bool" diff --git a/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_uninstall.yml b/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_uninstall.yml new file mode 100644 index 00000000..93f5c4c8 --- /dev/null +++ b/roles/matrix-bridge-mautrix-whatsapp/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mautrix-whatsapp service + stat: + path: "{{ matrix_systemd_path }}/matrix-mautrix-whatsapp.service" + register: matrix_mautrix_whatsapp_service_stat + +- name: Ensure matrix-mautrix-whatsapp is stopped + service: + name: matrix-mautrix-whatsapp + state: stopped + daemon_reload: yes + when: "matrix_mautrix_whatsapp_service_stat.stat.exists" + +- name: Ensure matrix-mautrix-whatsapp.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mautrix-whatsapp.service" + state: absent + when: "matrix_mautrix_whatsapp_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mautrix-whatsapp.service removal + service: + daemon_reload: yes + when: "matrix_mautrix_whatsapp_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mautrix-whatsapp/tasks/validate_config.yml b/roles/matrix-bridge-mautrix-whatsapp/tasks/validate_config.yml new file mode 100644 index 00000000..48314190 --- /dev/null +++ b/roles/matrix-bridge-mautrix-whatsapp/tasks/validate_config.yml @@ -0,0 +1,11 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mautrix_whatsapp_appservice_token" + - "matrix_mautrix_whatsapp_homeserver_token" + diff --git a/roles/matrix-bridge-mautrix-whatsapp/templates/config.yaml.j2 b/roles/matrix-bridge-mautrix-whatsapp/templates/config.yaml.j2 new file mode 100644 index 00000000..b3b1caf1 --- /dev/null +++ b/roles/matrix-bridge-mautrix-whatsapp/templates/config.yaml.j2 @@ -0,0 +1,169 @@ +#jinja2: lstrip_blocks: "True" +# Homeserver details. +homeserver: + # The address that this appservice can use to connect to the homeserver. + address: {{ matrix_mautrix_whatsapp_homeserver_address }} + # The domain of the homeserver (for MXIDs, etc). + domain: {{ matrix_mautrix_whatsapp_homeserver_domain }} +# Application service host/registration related details. +# Changing these values requires regeneration of the registration. + +appservice: + # The address that the homeserver can use to connect to this appservice. + address: {{ matrix_mautrix_whatsapp_appservice_address }} + + # The hostname and port where this appservice should listen. + hostname: 0.0.0.0 + port: 8080 + + # Database config. + database: + # The database type. "sqlite3" and "postgres" are supported. + type: {{ matrix_mautrix_whatsapp_appservice_database_type|to_json }} + # The database URI. + # SQLite: File name is enough. https://github.com/mattn/go-sqlite3#connection-string + # Postgres: Connection string. For example, postgres://user:password@host/database + uri: {{ matrix_mautrix_whatsapp_appservice_database_uri|to_json }} + # Maximum number of connections. Mostly relevant for Postgres. + max_open_conns: 20 + max_idle_conns: 2 + + # Path to the Matrix room state store. + state_store_path: ./mx-state.json + + # The unique ID of this appservice. + id: whatsapp + # Appservice bot details. + bot: + # Username of the appservice bot. + username: {{ matrix_mautrix_whatsapp_appservice_bot_username|to_json }} + # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty + # to leave display name/avatar as-is. + displayname: WhatsApp bridge bot + avatar: mxc://maunium.net/NeXNQarUbrlYBiPCpprYsRqr + + # Authentication tokens for AS <-> HS communication. Autogenerated; do not modify. + as_token: "{{ matrix_mautrix_whatsapp_appservice_token }}" + hs_token: "{{ matrix_mautrix_whatsapp_homeserver_token }}" + +# Bridge config +bridge: + # Localpart template of MXIDs for WhatsApp users. + # {{ '{{.}}' }} is replaced with the phone number of the WhatsApp user. + username_template: "{{ 'whatsapp_{{.}}' }}" + # Displayname template for WhatsApp users. + # {{ '{{.Notify'}}' }} - nickname set by the WhatsApp user + # {{ '{{.Jid}}' }} - phone number (international format) + # The following variables are also available, but will cause problems on multi-user instances: + # {{ '{{.Name}}' }} - display name from contact list + # {{ '{{.Short}}' }} - short display name from contact list + displayname_template: "{{ '{{if .Notify}}{{.Notify}}{{else}}{{.Jid}}{{end}} (WA)' }}" + # WhatsApp connection timeout in seconds. + connection_timeout: 20 + # Maximum number of times to retry connecting on connection error. + max_connection_attempts: 3 + # Number of seconds to wait between connection attempts. + # Negative numbers are exponential backoff: -connection_retry_delay + 1 + 2^attempts + connection_retry_delay: -1 + # Whether or not the bridge should send a notice to the user's management room when it retries connecting. + # If false, it will only report when it stops retrying. + report_connection_retry: true + # Maximum number of seconds to wait for chats to be sent at startup. + # If this is too low and you have lots of chats, it could cause backfilling to fail. + chat_list_wait: 30 + # Maximum number of seconds to wait to sync portals before force unlocking message processing. + # If this is too low and you have lots of chats, it could cause backfilling to fail. + portal_sync_wait: 600 + + # Whether or not to send call start/end notices to Matrix. + call_notices: + start: true + end: true + + # Number of chats to sync for new users. + initial_chat_sync_count: 10 + # Number of old messages to fill when creating new portal rooms. + initial_history_fill_count: 20 + # Maximum number of chats to sync when recovering from downtime. + # Set to -1 to sync all new chats during downtime. + recovery_chat_sync_limit: -1 + # Whether or not to sync history when recovering from downtime. + recovery_history_backfill: true + # Maximum number of seconds since last message in chat to skip + # syncing the chat in any case. This setting will take priority + # over both recovery_chat_sync_limit and initial_chat_sync_count. + # Default is 3 days = 259200 seconds + sync_max_chat_age: 259200 + + # Whether or not to sync with custom puppets to receive EDUs that + # are not normally sent to appservices. + sync_with_custom_puppets: true + # Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth + # + # If set, custom puppets will be enabled automatically for local users + # instead of users having to find an access token and run `login-matrix` + # manually. + login_shared_secret: {{ matrix_mautrix_whatsapp_login_shared_secret|to_json }} + + # Whether or not to invite own WhatsApp user's Matrix puppet into private + # chat portals when backfilling if needed. + # This always uses the default puppet instead of custom puppets due to + # rate limits and timestamp massaging. + invite_own_puppet_for_backfilling: true + # Whether or not to explicitly set the avatar and room name for private + # chat portal rooms. This can be useful if the previous field works fine, + # but causes room avatar/name bugs. + private_chat_portal_meta: false + + # Allow invite permission for user. User can invite any bots to room with whatsapp + # users (private chat and groups) + allow_user_invite: false + + # The prefix for commands. Only required in non-management rooms. + command_prefix: "!wa" + + # Permissions for using the bridge. + # Permitted values: + # user - Access to use the bridge to chat with a WhatsApp account. + # admin - User level and some additional administration tools + # Permitted keys: + # * - All Matrix users + # domain - All users on that homeserver + # mxid - Specific user + permissions: + "{{ matrix_mautrix_whatsapp_homeserver_domain }}": user + + relaybot: + # Whether or not relaybot support is enabled. + enabled: false + # The management room for the bot. This is where all status notifications are posted and + # in this room, you can use `!wa ` instead of `!wa relaybot `. Omitting + # the command prefix completely like in user management rooms is not possible. + management: '!foo:example.com' + # List of users to invite to all created rooms that include the relaybot. + invites: [] + # The formats to use when sending messages to WhatsApp via the relaybot. + message_formats: + m.text: "{{ '{{ .Sender.Displayname }}' }}: {{ '{{ .Message }}' }}" + m.notice: "{{ '{{ .Sender.Displayname }}' }}:: {{ '{{ .Message }}' }}" + m.emote: "* {{ '{{ .Sender.Displayname }}' }}: {{ '{{ .Message }}' }}" + m.file: "{{ '{{ .Sender.Displayname }}' }}: sent a file" + m.image: "{{ '{{ .Sender.Displayname }}' }}: sent an image" + m.audio: "{{ '{{ .Sender.Displayname }}' }}: sent an audio file" + m.video: "{{ '{{ .Sender.Displayname }}' }}: sent a video" + m.location: "{{ '{{ .Sender.Displayname }}' }}: sent a location" +# Logging config. +logging: + # The directory for log files. Will be created if not found. + directory: ./logs + # Available variables: .Date for the file date and .Index for different log files on the same day. + file_name_format: "{{ '{{.Date}}-{{.Index}}.log' }}" + # Date format for file names in the Go time format: https://golang.org/pkg/time/#pkg-constants + file_date_format: "2006-01-02" + # Log file permissions. + file_mode: 0600 + # Timestamp format for log entries in the Go time format. + timestamp_format: "Jan _2, 2006 15:04:05" + # Minimum severity for log messages. + # Options: debug, info, warn, error, fatal + print_level: debug diff --git a/roles/matrix-bridge-mautrix-whatsapp/templates/systemd/matrix-mautrix-whatsapp.service.j2 b/roles/matrix-bridge-mautrix-whatsapp/templates/systemd/matrix-mautrix-whatsapp.service.j2 new file mode 100644 index 00000000..4a492492 --- /dev/null +++ b/roles/matrix-bridge-mautrix-whatsapp/templates/systemd/matrix-mautrix-whatsapp.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mautrix Whatsapp bridge +{% for service in matrix_mautrix_whatsapp_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mautrix_whatsapp_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-whatsapp 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-whatsapp 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mautrix-whatsapp \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -v {{ matrix_mautrix_whatsapp_config_path }}:/config:z \ + -v {{ matrix_mautrix_whatsapp_data_path }}:/data:z \ + --workdir=/data \ + {% for arg in matrix_mautrix_whatsapp_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mautrix_whatsapp_docker_image }} \ + /usr/bin/mautrix-whatsapp -c /config/config.yaml -r /config/registration.yaml + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mautrix-whatsapp 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mautrix-whatsapp 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mautrix-whatsapp + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mx-puppet-discord/defaults/main.yml b/roles/matrix-bridge-mx-puppet-discord/defaults/main.yml new file mode 100644 index 00000000..1113bb1c --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-discord/defaults/main.yml @@ -0,0 +1,110 @@ +# Mx Puppet Discord is a Matrix <-> Discord bridge +# See: https://github.com/matrix-discord/mx-puppet-discord + +matrix_mx_puppet_discord_enabled: true + +matrix_mx_puppet_discord_container_image_self_build: false +matrix_mx_puppet_discord_container_image_self_build_repo: "https://github.com/matrix-discord/mx-puppet-discord.git" + +# Controls whether the mx-puppet-discord container exposes its HTTP port (tcp/8432 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8432"), or empty string to not expose. +matrix_mx_puppet_discord_container_http_host_bind_port: '' + +matrix_mx_puppet_discord_version: latest +matrix_mx_puppet_discord_docker_image: "{{ matrix_mx_puppet_discord_docker_image_name_prefix }}sorunome/mx-puppet-discord:{{ matrix_mx_puppet_discord_version }}" +matrix_mx_puppet_discord_docker_image_name_prefix: "{{ 'localhost/' if matrix_mx_puppet_discord_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_mx_puppet_discord_docker_image_force_pull: "{{ matrix_mx_puppet_discord_docker_image.endswith(':latest') }}" + +matrix_mx_puppet_discord_base_path: "{{ matrix_base_data_path }}/mx-puppet-discord" +matrix_mx_puppet_discord_config_path: "{{ matrix_mx_puppet_discord_base_path }}/config" +matrix_mx_puppet_discord_data_path: "{{ matrix_mx_puppet_discord_base_path }}/data" +matrix_mx_puppet_discord_docker_src_files_path: "{{ matrix_mx_puppet_discord_base_path }}/docker-src" + +matrix_mx_puppet_discord_appservice_port: "8432" + +matrix_mx_puppet_discord_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mx_puppet_discord_homeserver_domain: '{{ matrix_domain }}' +matrix_mx_puppet_discord_appservice_address: 'http://matrix-mx-puppet-discord:{{ matrix_mx_puppet_discord_appservice_port }}' + +# "@user:server.com" to allow specific user +# "@.*:yourserver.com" to allow users on a specific homeserver +# "@.*" to allow anyone +matrix_mx_puppet_discord_provisioning_whitelist: + - "@.*:{{ matrix_domain|regex_escape }}" + +# Leave empty to disable blacklist +# "@user:server.com" disallow a specific user +# "@.*:yourserver.com" disallow users on a specific homeserver +matrix_mx_puppet_discord_provisioning_blacklist: [] + +# A list of extra arguments to pass to the container +matrix_mx_puppet_discord_container_extra_arguments: [] + +# List of systemd services that matrix-puppet-discord.service depends on. +matrix_mx_puppet_discord_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-puppet-discord.service wants +matrix_mx_puppet_discord_systemd_wanted_services_list: [] + +matrix_mx_puppet_discord_appservice_token: '' +matrix_mx_puppet_discord_homeserver_token: '' + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mx_puppet_discord_login_shared_secret: '' + +# Database configuration +matrix_mx_puppet_discord_database_engine: 'sqlite' + +matrix_mx_puppet_discord_sqlite_database_path_local: "{{ matrix_mx_puppet_discord_data_path }}/database.db" +matrix_mx_puppet_discord_sqlite_database_path_in_container: "/data/database.db" + +matrix_mx_puppet_discord_database_username: matrix_mx_puppet_discord +matrix_mx_puppet_discord_database_password: ~ +matrix_mx_puppet_discord_database_hostname: 'matrix-postgres' +matrix_mx_puppet_discord_database_port: 5432 +matrix_mx_puppet_discord_database_name: matrix_mx_puppet_discord + +matrix_mx_puppet_discord_database_connection_string: 'postgresql://{{ matrix_mx_puppet_discord_database_username }}:{{ matrix_mx_puppet_discord_database_password }}@{{ matrix_mx_puppet_discord_database_hostname }}:{{ matrix_mx_puppet_discord_database_port }}/{{ matrix_mx_puppet_discord_database_name }}?sslmode=disable' + +# Default 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_mx_puppet_discord_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mx_puppet_discord_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mx_puppet_discord_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mx_puppet_discord_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mx_puppet_discord_configuration_yaml`. + +matrix_mx_puppet_discord_configuration_extension: "{{ matrix_mx_puppet_discord_configuration_extension_yaml|from_yaml if matrix_mx_puppet_discord_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mx_puppet_discord_configuration_yaml`. +matrix_mx_puppet_discord_configuration: "{{ matrix_mx_puppet_discord_configuration_yaml|from_yaml|combine(matrix_mx_puppet_discord_configuration_extension, recursive=True) }}" + +matrix_mx_puppet_discord_registration_yaml: | + as_token: "{{ matrix_mx_puppet_discord_appservice_token }}" + hs_token: "{{ matrix_mx_puppet_discord_homeserver_token }}" + id: discord-puppet + namespaces: + users: + - exclusive: true + regex: '@_discordpuppet_.*:{{ matrix_mx_puppet_discord_homeserver_domain|regex_escape }}' + rooms: [] + aliases: + - exclusive: true + regex: '#_discordpuppet_.*:{{ matrix_mx_puppet_discord_homeserver_domain|regex_escape }}' + protocols: [] + rate_limited: false + sender_localpart: _discordpuppet_bot + url: {{ matrix_mx_puppet_discord_appservice_address }} + de.sorunome.msc2409.push_ephemeral: true + +matrix_mx_puppet_discord_registration: "{{ matrix_mx_puppet_discord_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mx-puppet-discord/tasks/init.yml b/roles/matrix-bridge-mx-puppet-discord/tasks/init.yml new file mode 100644 index 00000000..6fa43037 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-discord/tasks/init.yml @@ -0,0 +1,23 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mx_puppet_discord_container_image_self_build and matrix_mx_puppet_discord_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mx-puppet-discord.service'] }}" + when: matrix_mx_puppet_discord_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mx_puppet_discord_config_path }}/registration.yaml,dst=/matrix-mx-puppet-discord-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mx-puppet-discord-registration.yaml"] }} + when: matrix_mx_puppet_discord_enabled|bool diff --git a/roles/matrix-bridge-mx-puppet-discord/tasks/main.yml b/roles/matrix-bridge-mx-puppet-discord/tasks/main.yml new file mode 100644 index 00000000..3ca32335 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-discord/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mx_puppet_discord_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-discord + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mx_puppet_discord_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-discord + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mx_puppet_discord_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-discord diff --git a/roles/matrix-bridge-mx-puppet-discord/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-discord/tasks/setup_install.yml new file mode 100644 index 00000000..f9985ed8 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-discord/tasks/setup_install.yml @@ -0,0 +1,128 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mx-puppet-discord role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- name: Ensure MX Puppet Discord paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mx_puppet_discord_base_path }}", when: true } + - { path: "{{ matrix_mx_puppet_discord_config_path }}", when: true } + - { path: "{{ matrix_mx_puppet_discord_data_path }}", when: true } + - { path: "{{ matrix_mx_puppet_discord_docker_src_files_path }}", when: "{{ matrix_mx_puppet_discord_container_image_self_build }}" } + when: matrix_mx_puppet_discord_enabled|bool and item.when|bool + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mx_puppet_discord_base_path }}/database.db" + register: matrix_mx_puppet_discord_stat_database + +- block: + - name: (Data relocation) Ensure matrix-mx-puppet-discord.service is stopped + service: + name: matrix-mx-puppet-discord + state: stopped + daemon_reload: yes + failed_when: False + + - name: (Data relocation) Move mx-puppet-discord database file to ./data directory + command: "mv {{ matrix_mx_puppet_discord_base_path }}/database.db {{ matrix_mx_puppet_discord_data_path }}/database.db" + when: "matrix_mx_puppet_discord_stat_database.stat.exists" + +- set_fact: + matrix_mx_puppet_discord_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mx_puppet_discord_sqlite_database_path_local }}" + register: matrix_mx_puppet_discord_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mx_puppet_discord_sqlite_database_path_local }}" + dst: "{{ matrix_mx_puppet_discord_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mx_puppet_discord_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mx-puppet-discord.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mx_puppet_discord_requires_restart: true + when: "matrix_mx_puppet_discord_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mx_puppet_discord_database_engine == 'postgres'" + +- name: Ensure MX Puppet Discord image is pulled + docker_image: + name: "{{ matrix_mx_puppet_discord_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mx_puppet_discord_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mx_puppet_discord_docker_image_force_pull }}" + when: matrix_mx_puppet_discord_enabled|bool and not matrix_mx_puppet_discord_container_image_self_build + +- name: Ensure MX Puppet Discord repository is present on self build + git: + repo: "{{ matrix_mx_puppet_discord_container_image_self_build_repo }}" + dest: "{{ matrix_mx_puppet_discord_docker_src_files_path }}" + force: "yes" + register: matrix_mx_puppet_discord_git_pull_results + when: "matrix_mx_puppet_discord_enabled|bool and matrix_mx_puppet_discord_container_image_self_build" + +- name: Ensure MX Puppet Discord Docker image is built + docker_image: + name: "{{ matrix_mx_puppet_discord_docker_image }}" + source: build + force_source: "{{ matrix_mx_puppet_discord_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_mx_puppet_discord_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mx_puppet_discord_docker_src_files_path }}" + pull: yes + when: "matrix_mx_puppet_discord_enabled|bool and matrix_mx_puppet_discord_container_image_self_build|bool" + +- name: Ensure mx-puppet-discord config.yaml installed + copy: + content: "{{ matrix_mx_puppet_discord_configuration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_discord_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mx-puppet-discord discord-registration.yaml installed + copy: + content: "{{ matrix_mx_puppet_discord_registration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_discord_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mx-puppet-discord.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mx-puppet-discord.service.j2" + dest: "/etc/systemd/system/matrix-mx-puppet-discord.service" + mode: 0644 + register: matrix_mx_puppet_discord_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mx-puppet-discord.service installation + service: + daemon_reload: yes + when: "matrix_mx_puppet_discord_systemd_service_result.changed" + +- name: Ensure matrix-mx-puppet-discord.service restarted, if necessary + service: + name: "matrix-mx-puppet-discord.service" + state: restarted + when: "matrix_mx_puppet_discord_requires_restart|bool" diff --git a/roles/matrix-bridge-mx-puppet-discord/tasks/setup_uninstall.yml b/roles/matrix-bridge-mx-puppet-discord/tasks/setup_uninstall.yml new file mode 100644 index 00000000..b5b83c98 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-discord/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mx-puppet-discord service + stat: + path: "/etc/systemd/system/matrix-mx-puppet-discord.service" + register: matrix_mx_puppet_discord_service_stat + +- name: Ensure matrix-mx-puppet-discord is stopped + service: + name: matrix-mx-puppet-discord + state: stopped + daemon_reload: yes + when: "matrix_mx_puppet_discord_service_stat.stat.exists" + +- name: Ensure matrix-mx-puppet-discord.service doesn't exist + file: + path: "/etc/systemd/system/matrix-mx-puppet-discord.service" + state: absent + when: "matrix_mx_puppet_discord_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mx-puppet-discord.service removal + service: + daemon_reload: yes + when: "matrix_mx_puppet_discord_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mx-puppet-discord/tasks/validate_config.yml b/roles/matrix-bridge-mx-puppet-discord/tasks/validate_config.yml new file mode 100644 index 00000000..c253eda2 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-discord/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mx_puppet_discord_appservice_token" + - "matrix_mx_puppet_discord_homeserver_token" diff --git a/roles/matrix-bridge-mx-puppet-discord/templates/config.yaml.j2 b/roles/matrix-bridge-mx-puppet-discord/templates/config.yaml.j2 new file mode 100644 index 00000000..93c0a491 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-discord/templates/config.yaml.j2 @@ -0,0 +1,125 @@ +#jinja2: lstrip_blocks: "True" +bridge: + # Port to host the bridge on + # Used for communication between the homeserver and the bridge + port: {{ matrix_mx_puppet_discord_appservice_port }} + # The host connections to the bridge's webserver are allowed from + bindAddress: 0.0.0.0 + # Public domain of the homeserver + domain: {{ matrix_mx_puppet_discord_homeserver_domain }} + # Reachable URL of the Matrix homeserver + homeserverUrl: {{ matrix_mx_puppet_discord_homeserver_address }} + {% if matrix_mx_puppet_discord_login_shared_secret != '' %} + loginSharedSecretMap: + {{ matrix_domain }}: {{ matrix_mx_puppet_discord_login_shared_secret }} + {% endif %} + # Display name of the bridge bot + displayname: Discord Puppet Bridge + # Optionally specify a different media URL used for the media store + # + # This is where Discord will download user profile pictures and media + # from + #mediaUrl: https://external-url.org + +presence: + # Bridge Discord online/offline status + enabled: true + # How often to send status to the homeserver in milliseconds + interval: 500 + +provisioning: + # Regex of Matrix IDs allowed to use the puppet bridge + whitelist: {{ matrix_mx_puppet_discord_provisioning_whitelist|to_json }} + # Allow a specific user + #- "@user:server\\.com" + # Allow users on a specific homeserver + #- "@.*:yourserver\\.com" + # Allow anyone + #- ".*" + # Regex of Matrix IDs forbidden from using the puppet bridge + #blacklist: + # Disallow a specific user + #- "@user:server\\.com" + # Disallow users on a specific homeserver + #- "@.*:yourserver\\.com" + blacklist: {{ matrix_mx_puppet_discord_provisioning_blacklist|to_json }} + +relay: + # Regex of Matrix IDs who are allowed to use the bridge in relay mode. + # Relay mode is when a single Discord bot account relays messages of + # multiple Matrix users + # + # Same format as in provisioning + whitelist: {{ matrix_mx_puppet_discord_provisioning_whitelist|to_json }} + blacklist: {{ matrix_mx_puppet_discord_provisioning_blacklist|to_json }} + +selfService: + # Regex of Matrix IDs who are allowed to use bridge self-servicing (plumbed rooms) + # + # Same format as in provisioning + whitelist: {{ matrix_mx_puppet_discord_provisioning_whitelist|to_json }} + blacklist: {{ matrix_mx_puppet_discord_provisioning_blacklist|to_json }} + +# Override the default name patterns for users, rooms and groups +# +# Variable names must be prefixed with a ':' +namePatterns: + # The default displayname for a bridged user + # + # Available variables: + # + # name: username of the user + # discriminator: hashtag of the user (ex. #1234) + user: :name + + # A user's guild-specific displayname - if they've set a custom nick in + # a guild + # + # Available variables: + # + # name: username of the user + # discriminator: hashtag of the user (ex. #1234) + # displayname: the user's custom group-specific nick + # channel: the name of the channel + # guild: the name of the guild + userOverride: :name + + # Room names for bridged Discord channels + # + # Available variables: + # + # name: name of the channel + # guild: name of the guild + room: :name + + # Group names for bridged Discord servers + # + # Available variables: + # + # name: name of the guide + group: :name + +database: +{% if matrix_mx_puppet_discord_database_engine == 'sqlite' %} + # Use SQLite3 as a database backend + # The name of the database file + filename: {{ matrix_mx_puppet_discord_sqlite_database_path_in_container|to_json }} +{% else %} + # Use Postgres as a database backend + # If set, will be used instead of SQLite3 + # Connection string to connect to the Postgres instance + # with username "user", password "pass", host "localhost" and database name "dbname". + # Modify each value as necessary + connString: {{ matrix_mx_puppet_discord_database_connection_string|to_json }} +{% endif %} + +logging: + # Log level of console output + # Allowed values starting with most verbose: + # silly, debug, verbose, info, warn, error + console: info + # Date and time formatting + lineDateFormat: MMM-D HH:mm:ss.SSS + # Logging files + # Log files are rotated daily by default + files: [] diff --git a/roles/matrix-bridge-mx-puppet-discord/templates/systemd/matrix-mx-puppet-discord.service.j2 b/roles/matrix-bridge-mx-puppet-discord/templates/systemd/matrix-mx-puppet-discord.service.j2 new file mode 100644 index 00000000..6ffb87cd --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-discord/templates/systemd/matrix-mx-puppet-discord.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mx Puppet Discord bridge +{% for service in matrix_mx_puppet_discord_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mx_puppet_discord_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-discord 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-discord 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-discord \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -e CONFIG_PATH=/config/config.yaml \ + -e REGISTRATION_PATH=/config/registration.yaml \ + -v {{ matrix_mx_puppet_discord_config_path }}:/config:z \ + -v {{ matrix_mx_puppet_discord_data_path }}:/data:z \ + {% for arg in matrix_mx_puppet_discord_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mx_puppet_discord_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-discord 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-discord 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mx-puppet-discord + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mx-puppet-groupme/defaults/main.yml b/roles/matrix-bridge-mx-puppet-groupme/defaults/main.yml new file mode 100644 index 00000000..8b382605 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-groupme/defaults/main.yml @@ -0,0 +1,109 @@ +# Mx Puppet GroupMe is a Matrix <-> GroupMe bridge +# See: https://gitlab.com/robintown/mx-puppet-groupme + +matrix_mx_puppet_groupme_enabled: true + +matrix_mx_puppet_groupme_container_image_self_build: false +matrix_mx_puppet_groupme_container_image_self_build_repo: "https://gitlab.com/robintown/mx-puppet-groupme" + +# Controls whether the mx-puppet-groupme container exposes its HTTP port (tcp/8437 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8437"), or empty string to not expose. +matrix_mx_puppet_groupme_container_http_host_bind_port: '' + +matrix_mx_puppet_groupme_version: latest +matrix_mx_puppet_groupme_docker_image: "{{ matrix_mx_puppet_groupme_docker_image_name_prefix }}xangelix/mx-puppet-groupme:{{ matrix_mx_puppet_groupme_version }}" +matrix_mx_puppet_groupme_docker_image_name_prefix: "{{ 'localhost/' if matrix_mx_puppet_groupme_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_mx_puppet_groupme_docker_image_force_pull: "{{ matrix_mx_puppet_groupme_docker_image.endswith(':latest') }}" + +matrix_mx_puppet_groupme_base_path: "{{ matrix_base_data_path }}/mx-puppet-groupme" +matrix_mx_puppet_groupme_config_path: "{{ matrix_mx_puppet_groupme_base_path }}/config" +matrix_mx_puppet_groupme_data_path: "{{ matrix_mx_puppet_groupme_base_path }}/data" +matrix_mx_puppet_groupme_docker_src_files_path: "{{ matrix_mx_puppet_groupme_base_path }}/docker-src" + +matrix_mx_puppet_groupme_appservice_port: "8437" + +matrix_mx_puppet_groupme_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mx_puppet_groupme_homeserver_domain: '{{ matrix_domain }}' +matrix_mx_puppet_groupme_appservice_address: 'http://matrix-mx-puppet-groupme:{{ matrix_mx_puppet_groupme_appservice_port }}' + +# "@user:server.com" to allow specific user +# "@.*:yourserver.com" to allow users on a specific homeserver +# "@.*" to allow anyone +matrix_mx_puppet_groupme_provisioning_whitelist: + - "@.*:{{ matrix_domain|regex_escape }}" + +# Leave empty to disable blacklist +# "@user:server.com" disallow a specific user +# "@.*:yourserver.com" disallow users on a specific homeserver +matrix_mx_puppet_groupme_provisioning_blacklist: [] + +# A list of extra arguments to pass to the container +matrix_mx_puppet_groupme_container_extra_arguments: [] + +# List of systemd services that matrix-puppet-groupme.service depends on. +matrix_mx_puppet_groupme_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-puppet-groupme.service wants +matrix_mx_puppet_groupme_systemd_wanted_services_list: [] + +matrix_mx_puppet_groupme_appservice_token: '' +matrix_mx_puppet_groupme_homeserver_token: '' + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mx_puppet_groupme_login_shared_secret: '' + +matrix_mx_puppet_groupme_database_engine: sqlite + +matrix_mx_puppet_groupme_sqlite_database_path_local: "{{ matrix_mx_puppet_groupme_data_path }}/database.db" +matrix_mx_puppet_groupme_sqlite_database_path_in_container: "/data/database.db" + +matrix_mx_puppet_groupme_database_username: matrix_mx_puppet_groupme +matrix_mx_puppet_groupme_database_password: ~ +matrix_mx_puppet_groupme_database_hostname: 'matrix-postgres' +matrix_mx_puppet_groupme_database_port: 5432 +matrix_mx_puppet_groupme_database_name: matrix_mx_puppet_groupme + +matrix_mx_puppet_groupme_database_connection_string: 'postgresql://{{ matrix_mx_puppet_groupme_database_username }}:{{ matrix_mx_puppet_groupme_database_password }}@{{ matrix_mx_puppet_groupme_database_hostname }}:{{ matrix_mx_puppet_groupme_database_port }}/{{ matrix_mx_puppet_groupme_database_name }}?sslmode=disable' + +# Default 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_mx_puppet_groupme_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mx_puppet_groupme_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mx_puppet_groupme_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mx_puppet_groupme_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mx_puppet_groupme_configuration_yaml`. + +matrix_mx_puppet_groupme_configuration_extension: "{{ matrix_mx_puppet_groupme_configuration_extension_yaml|from_yaml if matrix_mx_puppet_groupme_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mx_puppet_groupme_configuration_yaml`. +matrix_mx_puppet_groupme_configuration: "{{ matrix_mx_puppet_groupme_configuration_yaml|from_yaml|combine(matrix_mx_puppet_groupme_configuration_extension, recursive=True) }}" + +matrix_mx_puppet_groupme_registration_yaml: | + as_token: "{{ matrix_mx_puppet_groupme_appservice_token }}" + hs_token: "{{ matrix_mx_puppet_groupme_homeserver_token }}" + id: groupme-puppet + namespaces: + users: + - exclusive: true + regex: '@_groupmepuppet_.*:{{ matrix_mx_puppet_groupme_homeserver_domain|regex_escape }}' + rooms: [] + aliases: + - exclusive: true + regex: '#_groupmepuppet_.*:{{ matrix_mx_puppet_groupme_homeserver_domain|regex_escape }}' + protocols: [] + rate_limited: false + sender_localpart: _groupmepuppet_bot + url: {{ matrix_mx_puppet_groupme_appservice_address }} + de.sorunome.msc2409.push_ephemeral: true + +matrix_mx_puppet_groupme_registration: "{{ matrix_mx_puppet_groupme_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mx-puppet-groupme/tasks/init.yml b/roles/matrix-bridge-mx-puppet-groupme/tasks/init.yml new file mode 100644 index 00000000..b4469ea1 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-groupme/tasks/init.yml @@ -0,0 +1,23 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mx_puppet_groupme_container_image_self_build and matrix_mx_puppet_groupme_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mx-puppet-groupme.service'] }}" + when: matrix_mx_puppet_groupme_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mx_puppet_groupme_config_path }}/registration.yaml,dst=/matrix-mx-puppet-groupme-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mx-puppet-groupme-registration.yaml"] }} + when: matrix_mx_puppet_groupme_enabled|bool diff --git a/roles/matrix-bridge-mx-puppet-groupme/tasks/main.yml b/roles/matrix-bridge-mx-puppet-groupme/tasks/main.yml new file mode 100644 index 00000000..994e7e45 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-groupme/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mx_puppet_groupme_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-groupme + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mx_puppet_groupme_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-groupme + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mx_puppet_groupme_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-groupme diff --git a/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_install.yml new file mode 100644 index 00000000..b1d5f0b5 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_install.yml @@ -0,0 +1,128 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mx-puppet-groupme role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- name: Ensure MX Puppet Groupme paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mx_puppet_groupme_base_path }}", when: true } + - { path: "{{ matrix_mx_puppet_groupme_config_path }}", when: true } + - { path: "{{ matrix_mx_puppet_groupme_data_path }}", when: true } + - { path: "{{ matrix_mx_puppet_groupme_docker_src_files_path }}", when: "{{ matrix_mx_puppet_groupme_container_image_self_build }}" } + when: matrix_mx_puppet_groupme_enabled|bool and item.when|bool + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mx_puppet_groupme_base_path }}/database.db" + register: matrix_mx_puppet_groupme_stat_database + +- name: (Data relocation) Ensure matrix-mx-puppet-groupme.service is stopped + service: + name: matrix-mx-puppet-groupme + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mx_puppet_groupme_stat_database.stat.exists" + +- name: (Data relocation) Move mx-puppet-groupme database file to ./data directory + command: "mv {{ matrix_mx_puppet_groupme_base_path }}/database.db {{ matrix_mx_puppet_groupme_data_path }}/database.db" + when: "matrix_mx_puppet_groupme_stat_database.stat.exists" + +- set_fact: + matrix_mx_puppet_groupme_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mx_puppet_groupme_sqlite_database_path_local }}" + register: matrix_mx_puppet_groupme_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mx_puppet_groupme_sqlite_database_path_local }}" + dst: "{{ matrix_mx_puppet_groupme_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mx_puppet_groupme_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mx-puppet-groupme.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mx_puppet_groupme_requires_restart: true + when: "matrix_mx_puppet_groupme_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mx_puppet_groupme_database_engine == 'postgres'" + +- name: Ensure MX Puppet Groupme image is pulled + docker_image: + name: "{{ matrix_mx_puppet_groupme_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mx_puppet_groupme_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mx_puppet_groupme_docker_image_force_pull }}" + when: matrix_mx_puppet_groupme_enabled|bool and not matrix_mx_puppet_groupme_container_image_self_build + +- name: Ensure MX Puppet Groupme repository is present on self build + git: + repo: "{{ matrix_mx_puppet_groupme_container_image_self_build_repo }}" + dest: "{{ matrix_mx_puppet_groupme_docker_src_files_path }}" + force: "yes" + register: matrix_mx_puppet_groupme_git_pull_results + when: "matrix_mx_puppet_groupme_enabled|bool and matrix_mx_puppet_groupme_container_image_self_build" + +- name: Ensure MX Puppet Groupme Docker image is built + docker_image: + name: "{{ matrix_mx_puppet_groupme_docker_image }}" + source: build + force_source: "{{ matrix_mx_puppet_groupme_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_mx_puppet_groupme_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mx_puppet_groupme_docker_src_files_path }}" + pull: yes + when: "matrix_mx_puppet_groupme_enabled|bool and matrix_mx_puppet_groupme_container_image_self_build" + +- name: Ensure mx-puppet-groupme config.yaml installed + copy: + content: "{{ matrix_mx_puppet_groupme_configuration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_groupme_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mx-puppet-groupme groupme-registration.yaml installed + copy: + content: "{{ matrix_mx_puppet_groupme_registration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_groupme_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mx-puppet-groupme.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mx-puppet-groupme.service.j2" + dest: "/etc/systemd/system/matrix-mx-puppet-groupme.service" + mode: 0644 + register: matrix_mx_puppet_groupme_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mx-puppet-groupme.service installation + service: + daemon_reload: yes + when: "matrix_mx_puppet_groupme_systemd_service_result.changed" + +- name: Ensure matrix-mx-puppet-groupme.service restarted, if necessary + service: + name: "matrix-mx-puppet-groupme.service" + state: restarted + when: "matrix_mx_puppet_groupme_requires_restart|bool" diff --git a/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_uninstall.yml b/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_uninstall.yml new file mode 100644 index 00000000..cc4fdfa5 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-groupme/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mx-puppet-groupme service + stat: + path: "/etc/systemd/system/matrix-mx-puppet-groupme.service" + register: matrix_mx_puppet_groupme_service_stat + +- name: Ensure matrix-mx-puppet-groupme is stopped + service: + name: matrix-mx-puppet-groupme + state: stopped + daemon_reload: yes + when: "matrix_mx_puppet_groupme_service_stat.stat.exists" + +- name: Ensure matrix-mx-puppet-groupme.service doesn't exist + file: + path: "/etc/systemd/system/matrix-mx-puppet-groupme.service" + state: absent + when: "matrix_mx_puppet_groupme_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mx-puppet-groupme.service removal + service: + daemon_reload: yes + when: "matrix_mx_puppet_groupme_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mx-puppet-groupme/tasks/validate_config.yml b/roles/matrix-bridge-mx-puppet-groupme/tasks/validate_config.yml new file mode 100644 index 00000000..5c5463ce --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-groupme/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mx_puppet_groupme_appservice_token" + - "matrix_mx_puppet_groupme_homeserver_token" diff --git a/roles/matrix-bridge-mx-puppet-groupme/templates/config.yaml.j2 b/roles/matrix-bridge-mx-puppet-groupme/templates/config.yaml.j2 new file mode 100644 index 00000000..a9ab7701 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-groupme/templates/config.yaml.j2 @@ -0,0 +1,86 @@ +#jinja2: lstrip_blocks: "True" +bridge: + # Port to host the bridge on + # Used for communication between the homeserver and the bridge + port: {{ matrix_mx_puppet_groupme_appservice_port }} + # The host connections to the bridge's webserver are allowed from + bindAddress: 0.0.0.0 + # Public domain of the homeserver + domain: {{ matrix_mx_puppet_groupme_homeserver_domain }} + # Reachable URL of the Matrix homeserver + homeserverUrl: {{ matrix_mx_puppet_groupme_homeserver_address }} + {% if matrix_mx_puppet_groupme_login_shared_secret != '' %} + loginSharedSecretMap: + {{ matrix_domain }}: {{ matrix_mx_puppet_groupme_login_shared_secret }} + {% endif %} + # Display name of the bridge bot + displayname: GroupMe Puppet Bridge + # Optionally specify a different media URL used for the media store + # + # This is where GroupMe will download user profile pictures and media + # from + #mediaUrl: https://external-url.org + +presence: + # Bridge GroupMe online/offline status + enabled: true + # How often to send status to the homeserver in milliseconds + interval: 5000 + +provisioning: + # Regex of Matrix IDs allowed to use the puppet bridge + whitelist: {{ matrix_mx_puppet_groupme_provisioning_whitelist|to_json }} + # Allow a specific user + #- "@user:server\\.com" + # Allow users on a specific homeserver + #- "@.*:yourserver\\.com" + # Allow anyone + #- ".*" + # Regex of Matrix IDs forbidden from using the puppet bridge + #blacklist: + # Disallow a specific user + #- "@user:server\\.com" + # Disallow users on a specific homeserver + #- "@.*:yourserver\\.com" + blacklist: {{ matrix_mx_puppet_groupme_provisioning_blacklist|to_json }} + +relay: + # Regex of Matrix IDs who are allowed to use the bridge in relay mode. + # Relay mode is when a single GroupMe bot account relays messages of + # multiple Matrix users + # + # Same format as in provisioning + whitelist: {{ matrix_mx_puppet_groupme_provisioning_whitelist|to_json }} + blacklist: {{ matrix_mx_puppet_groupme_provisioning_blacklist|to_json }} + +selfService: + # Regex of Matrix IDs who are allowed to use bridge self-servicing (plumbed rooms) + # + # Same format as in provisioning + whitelist: {{ matrix_mx_puppet_groupme_provisioning_whitelist|to_json }} + blacklist: {{ matrix_mx_puppet_groupme_provisioning_blacklist|to_json }} + +database: +{% if matrix_mx_puppet_groupme_database_engine == 'postgres' %} + # Use Postgres as a database backend + # If set, will be used instead of SQLite3 + # Connection string to connect to the Postgres instance + # with username "user", password "pass", host "localhost" and database name "dbname". + # Modify each value as necessary + connString: {{ matrix_mx_puppet_groupme_database_connection_string|to_json }} +{% else %} + # Use SQLite3 as a database backend + # The name of the database file + filename: {{ matrix_mx_puppet_groupme_sqlite_database_path_in_container|to_json }} +{% endif %} + +logging: + # Log level of console output + # Allowed values starting with most verbose: + # silly, debug, verbose, info, warn, error + console: info + # Date and time formatting + lineDateFormat: MMM-D HH:mm:ss.SSS + # Logging files + # Log files are rotated daily by default + files: [] diff --git a/roles/matrix-bridge-mx-puppet-groupme/templates/systemd/matrix-mx-puppet-groupme.service.j2 b/roles/matrix-bridge-mx-puppet-groupme/templates/systemd/matrix-mx-puppet-groupme.service.j2 new file mode 100644 index 00000000..dabafd18 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-groupme/templates/systemd/matrix-mx-puppet-groupme.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mx Puppet Groupme bridge +{% for service in matrix_mx_puppet_groupme_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mx_puppet_groupme_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-groupme 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-groupme 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-groupme \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -e CONFIG_PATH=/config/config.yaml \ + -e REGISTRATION_PATH=/config/registration.yaml \ + -v {{ matrix_mx_puppet_groupme_config_path }}:/config:z \ + -v {{ matrix_mx_puppet_groupme_data_path }}:/data:z \ + {% for arg in matrix_mx_puppet_groupme_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mx_puppet_groupme_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-groupme 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-groupme 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mx-puppet-groupme + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mx-puppet-instagram/defaults/main.yml b/roles/matrix-bridge-mx-puppet-instagram/defaults/main.yml new file mode 100644 index 00000000..27210360 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-instagram/defaults/main.yml @@ -0,0 +1,103 @@ +# mx-puppet-instagram bridges instagram DMs +# See: https://github.com/Sorunome/mx-puppet-instagram + +matrix_mx_puppet_instagram_enabled: true + +matrix_mx_puppet_instagram_container_image_self_build: false +matrix_mx_puppet_instagram_container_image_self_build_repo: "https://github.com/Sorunome/mx-puppet-instagram.git" + +matrix_mx_puppet_instagram_version: latest +matrix_mx_puppet_instagram_docker_image: "{{ matrix_mx_puppet_instagram_docker_image_name_prefix }}sorunome/mx-puppet-instagram:{{ matrix_mx_puppet_instagram_version }}" +matrix_mx_puppet_instagram_docker_image_name_prefix: "{{ 'localhost/' if matrix_mx_puppet_instagram_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_mx_puppet_instagram_docker_image_force_pull: "{{ matrix_mx_puppet_instagram_docker_image.endswith(':latest') }}" + +matrix_mx_puppet_instagram_base_path: "{{ matrix_base_data_path }}/mx-puppet-instagram" +matrix_mx_puppet_instagram_config_path: "{{ matrix_mx_puppet_instagram_base_path }}/config" +matrix_mx_puppet_instagram_data_path: "{{ matrix_mx_puppet_instagram_base_path }}/data" +matrix_mx_puppet_instagram_docker_src_files_path: "{{ matrix_mx_puppet_instagram_base_path }}/docker-src" + +matrix_mx_puppet_instagram_appservice_port: "8440" +matrix_mx_puppet_instagram_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mx_puppet_instagram_homeserver_domain: '{{ matrix_domain }}' +matrix_mx_puppet_instagram_appservice_address: 'http://matrix-mx-puppet-instagram:{{ matrix_mx_puppet_instagram_appservice_port }}' + +# "@user:server.com" to allow specific user +# "@.*:yourserver.com" to allow users on a specific homeserver +# "@.*" to allow anyone +matrix_mx_puppet_instagram_provisioning_whitelist: + - "@.*:{{ matrix_domain|regex_escape }}" + +# Leave empty to disable blacklist +# "@user:server.com" disallow a specific user +# "@.*:yourserver.com" disallow users on a specific homeserver +matrix_mx_puppet_instagram_provisioning_blacklist: [] + +# A list of extra arguments to pass to the container +matrix_mx_puppet_instagram_container_extra_arguments: [] + +# List of systemd services that matrix-puppet-instagram.service depends on. +matrix_mx_puppet_instagram_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-puppet-instagram.service wants +matrix_mx_puppet_instagram_systemd_wanted_services_list: [] + +matrix_mx_puppet_instagram_appservice_token: '' +matrix_mx_puppet_instagram_homeserver_token: '' + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mx_puppet_instagram_login_shared_secret: '' + +matrix_mx_puppet_instagram_database_engine: sqlite + +matrix_mx_puppet_instagram_sqlite_database_path_local: "{{ matrix_mx_puppet_instagram_data_path }}/database.db" +matrix_mx_puppet_instagram_sqlite_database_path_in_container: "/data/database.db" + +matrix_mx_puppet_instagram_database_username: matrix_mx_puppet_instagram +matrix_mx_puppet_instagram_database_password: ~ +matrix_mx_puppet_instagram_database_hostname: 'matrix-postgres' +matrix_mx_puppet_instagram_database_port: 5432 +matrix_mx_puppet_instagram_database_name: matrix_mx_puppet_instagram + +matrix_mx_puppet_instagram_database_connection_string: 'postgresql://{{ matrix_mx_puppet_instagram_database_username }}:{{ matrix_mx_puppet_instagram_database_password }}@{{ matrix_mx_puppet_instagram_database_hostname }}:{{ matrix_mx_puppet_instagram_database_port }}/{{ matrix_mx_puppet_instagram_database_name }}?sslmode=disable' + +# Default 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_mx_puppet_instagram_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mx_puppet_instagram_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mx_puppet_instagram_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mx_puppet_instagram_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mx_puppet_instagram_configuration_yaml`. + +matrix_mx_puppet_instagram_configuration_extension: "{{ matrix_mx_puppet_instagram_configuration_extension_yaml|from_yaml if matrix_mx_puppet_instagram_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mx_puppet_instagram_configuration_yaml`. +matrix_mx_puppet_instagram_configuration: "{{ matrix_mx_puppet_instagram_configuration_yaml|from_yaml|combine(matrix_mx_puppet_instagram_configuration_extension, recursive=True) }}" + +matrix_mx_puppet_instagram_registration_yaml: | + as_token: "{{ matrix_mx_puppet_instagram_appservice_token }}" + hs_token: "{{ matrix_mx_puppet_instagram_homeserver_token }}" + id: instagram-puppet + namespaces: + users: + - exclusive: true + regex: '@_instagrampuppet_.*:{{ matrix_mx_puppet_instagram_homeserver_domain|regex_escape }}' + rooms: [] + aliases: + - exclusive: true + regex: '#_instagrampuppet_.*:{{ matrix_mx_puppet_instagram_homeserver_domain|regex_escape }}' + protocols: [] + rate_limited: false + sender_localpart: _instagrampuppet_bot + url: {{ matrix_mx_puppet_instagram_appservice_address }} + de.sorunome.msc2409.push_ephemeral: true + +matrix_mx_puppet_instagram_registration: "{{ matrix_mx_puppet_instagram_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mx-puppet-instagram/tasks/init.yml b/roles/matrix-bridge-mx-puppet-instagram/tasks/init.yml new file mode 100644 index 00000000..a12885e7 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-instagram/tasks/init.yml @@ -0,0 +1,24 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mx_puppet_instagram_container_image_self_build and matrix_mx_puppet_instagram_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mx-puppet-instagram.service'] }}" + when: matrix_mx_puppet_instagram_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mx_puppet_instagram_config_path }}/registration.yaml,dst=/matrix-mx-puppet-instagram-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mx-puppet-instagram-registration.yaml"] }} + when: matrix_mx_puppet_instagram_enabled|bool + diff --git a/roles/matrix-bridge-mx-puppet-instagram/tasks/main.yml b/roles/matrix-bridge-mx-puppet-instagram/tasks/main.yml new file mode 100644 index 00000000..d0fe90e4 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-instagram/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mx_puppet_instagram_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-instagram + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mx_puppet_instagram_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-instagram + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mx_puppet_instagram_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-instagram diff --git a/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_install.yml new file mode 100644 index 00000000..cdbaa18e --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_install.yml @@ -0,0 +1,112 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mx-puppet-instagram role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + + +- set_fact: + matrix_mx_puppet_instagram_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mx_puppet_instagram_sqlite_database_path_local }}" + register: matrix_mx_puppet_instagram_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mx_puppet_instagram_sqlite_database_path_local }}" + dst: "{{ matrix_mx_puppet_instagram_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mx_puppet_instagram_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mx-puppet-instagram.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mx_puppet_instagram_requires_restart: true + when: "matrix_mx_puppet_instagram_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mx_puppet_instagram_database_engine == 'postgres'" + +- name: Ensure mx-puppet-instagram image is pulled + docker_image: + name: "{{ matrix_mx_puppet_instagram_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mx_puppet_instagram_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mx_puppet_instagram_docker_image_force_pull }}" + when: matrix_mx_puppet_instagram_enabled|bool and not matrix_mx_puppet_instagram_container_image_self_build + +- name: Ensure mx-puppet-instagram paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mx_puppet_instagram_base_path }}", when: true } + - { path: "{{ matrix_mx_puppet_instagram_config_path }}", when: true } + - { path: "{{ matrix_mx_puppet_instagram_data_path }}", when: true } + - { path: "{{ matrix_mx_puppet_instagram_docker_src_files_path }}", when: "{{ matrix_mx_puppet_instagram_container_image_self_build }}" } + when: matrix_mx_puppet_instagram_enabled|bool and item.when|bool + +- name: Ensure mx-puppet-instagram repository is present on self build + git: + repo: "{{ matrix_mx_puppet_instagram_container_image_self_build_repo }}" + dest: "{{ matrix_mx_puppet_instagram_docker_src_files_path }}" + force: "yes" + register: matrix_mx_puppet_instagram_git_pull_results + when: "matrix_mx_puppet_instagram_enabled|bool and matrix_mx_puppet_instagram_container_image_self_build|bool" + +- name: Ensure mx-puppet-instagram Docker image is built + docker_image: + name: "{{ matrix_mx_puppet_instagram_docker_image }}" + source: build + force_source: "{{ matrix_mx_puppet_instagram_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_mx_puppet_instagram_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mx_puppet_instagram_docker_src_files_path }}" + pull: yes + when: "matrix_mx_puppet_instagram_enabled|bool and matrix_mx_puppet_instagram_container_image_self_build|bool" + +- name: Ensure mx-puppet-instagram config.yaml installed + copy: + content: "{{ matrix_mx_puppet_instagram_configuration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_instagram_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mx-puppet-instagram-registration.yaml installed + copy: + content: "{{ matrix_mx_puppet_instagram_registration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_instagram_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mx-puppet-instagram.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mx-puppet-instagram.service.j2" + dest: "/etc/systemd/system/matrix-mx-puppet-instagram.service" + mode: 0644 + register: matrix_mx_puppet_instagram_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mx-puppet-instagram.service installation + service: + daemon_reload: yes + when: "matrix_mx_puppet_instagram_systemd_service_result.changed" + +- name: Ensure matrix-mx-puppet-instagram.service restarted, if necessary + service: + name: "matrix-mx-puppet-instagram.service" + state: restarted + when: "matrix_mx_puppet_instagram_requires_restart|bool" diff --git a/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_uninstall.yml b/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_uninstall.yml new file mode 100644 index 00000000..4b5e67ac --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-instagram/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mx-puppet-instagram service + stat: + path: "/etc/systemd/system/matrix-mx-puppet-instagram.service" + register: matrix_mx_puppet_instagram_service_stat + +- name: Ensure matrix-mx-puppet-instagram is stopped + service: + name: matrix-mx-puppet-instagram + state: stopped + daemon_reload: yes + when: "matrix_mx_puppet_instagram_service_stat.stat.exists" + +- name: Ensure matrix-mx-puppet-instagram.service doesn't exist + file: + path: "/etc/systemd/system/matrix-mx-puppet-instagram.service" + state: absent + when: "matrix_mx_puppet_instagram_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mx-puppet-instagram.service removal + service: + daemon_reload: yes + when: "matrix_mx_puppet_instagram_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mx-puppet-instagram/tasks/validate_config.yml b/roles/matrix-bridge-mx-puppet-instagram/tasks/validate_config.yml new file mode 100644 index 00000000..b6d9d994 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-instagram/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mx_puppet_instagram_appservice_token" + - "matrix_mx_puppet_instagram_homeserver_token" diff --git a/roles/matrix-bridge-mx-puppet-instagram/templates/config.yaml.j2 b/roles/matrix-bridge-mx-puppet-instagram/templates/config.yaml.j2 new file mode 100644 index 00000000..1c4bb1bd --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-instagram/templates/config.yaml.j2 @@ -0,0 +1,69 @@ +#jinja2: lstrip_blocks: "True" +bridge: + # Port to host the bridge on + # Used for communication between the homeserver and the bridge + port: {{ matrix_mx_puppet_instagram_appservice_port }} + # The host connections to the bridge's webserver are allowed from + bindAddress: 0.0.0.0 + # Public domain of the homeserver + domain: {{ matrix_mx_puppet_instagram_homeserver_domain }} + # Reachable URL of the Matrix homeserver + homeserverUrl: {{ matrix_mx_puppet_instagram_homeserver_address }} + {% if matrix_mx_puppet_instagram_login_shared_secret != '' %} + loginSharedSecretMap: + {{ matrix_domain }}: {{ matrix_mx_puppet_instagram_login_shared_secret }} + {% endif %} + +presence: + # Bridge Instagram online/offline status + enabled: true + # How often to send status to the homeserver in milliseconds + interval: 500 + +provisioning: + # Regex of Matrix IDs allowed to use the puppet bridge + whitelist: {{ matrix_mx_puppet_instagram_provisioning_whitelist|to_json }} + # Allow a specific user + #- "@user:server\\.com" + # Allow users on a specific homeserver + #- "@.*:yourserver\\.com" + # Allow anyone + #- ".*" + # Regex of Matrix IDs forbidden from using the puppet bridge + #blacklist: + # Disallow a specific user + #- "@user:server\\.com" + # Disallow users on a specific homeserver + #- "@.*:yourserver\\.com" + blacklist: {{ matrix_mx_puppet_instagram_provisioning_blacklist|to_json }} + + # Shared secret for the provisioning API for use by integration managers. + # If this is not set, the provisioning API will not be enabled. + #sharedSecret: random string + # Path prefix for the provisioning API. /v1 will be appended to the prefix automatically. + apiPrefix: /_matrix/provision + +database: +{% if matrix_mx_puppet_instagram_database_engine == 'postgres' %} + # Use Postgres as a database backend + # If set, will be used instead of SQLite3 + # Connection string to connect to the Postgres instance + # with username "user", password "pass", host "localhost" and database name "dbname". + # Modify each value as necessary + connString: {{ matrix_mx_puppet_instagram_database_connection_string|to_json }} +{% else %} + # Use SQLite3 as a database backend + # The name of the database file + filename: {{ matrix_mx_puppet_instagram_sqlite_database_path_in_container|to_json }} +{% endif %} + +logging: + # Log level of console output + # Allowed values starting with most verbose: + # silly, debug, verbose, info, warn, error + console: info + # Date and time formatting + lineDateFormat: MMM-D HH:mm:ss.SSS + # Logging files + # Log files are rotated daily by default + files: [] diff --git a/roles/matrix-bridge-mx-puppet-instagram/templates/systemd/matrix-mx-puppet-instagram.service.j2 b/roles/matrix-bridge-mx-puppet-instagram/templates/systemd/matrix-mx-puppet-instagram.service.j2 new file mode 100644 index 00000000..965bb41c --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-instagram/templates/systemd/matrix-mx-puppet-instagram.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mx Puppet Instagram bridge +{% for service in matrix_mx_puppet_instagram_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mx_puppet_instagram_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-instagram 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-instagram 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-instagram \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -e CONFIG_PATH=/config/config.yaml \ + -e REGISTRATION_PATH=/config/registration.yaml \ + -v {{ matrix_mx_puppet_instagram_config_path }}:/config:z \ + -v {{ matrix_mx_puppet_instagram_data_path }}:/data:z \ + {% for arg in matrix_mx_puppet_instagram_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mx_puppet_instagram_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-instagram 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-instagram 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mx-puppet-instagram + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mx-puppet-skype/defaults/main.yml b/roles/matrix-bridge-mx-puppet-skype/defaults/main.yml new file mode 100644 index 00000000..8dcb2faf --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-skype/defaults/main.yml @@ -0,0 +1,111 @@ +# Mx Puppet Skype is a Matrix <-> Skype bridge +# See: https://github.com/Sorunome/mx-puppet-skype + +matrix_mx_puppet_skype_enabled: true + +matrix_mx_puppet_skype_container_image_self_build: false +matrix_mx_puppet_skype_container_image_self_build_repo: "https://github.com/Sorunome/mx-puppet-skype.git" + +matrix_mx_puppet_skype_version: latest +matrix_mx_puppet_skype_docker_image: "{{ matrix_mx_puppet_skype_docker_image_name_prefix }}sorunome/mx-puppet-skype:{{ matrix_mx_puppet_skype_version }}" +matrix_mx_puppet_skype_docker_image_name_prefix: "{{ 'localhost/' if matrix_mx_puppet_skype_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_mx_puppet_skype_docker_image_force_pull: "{{ matrix_mx_puppet_skype_docker_image.endswith(':latest') }}" + +matrix_mx_puppet_skype_base_path: "{{ matrix_base_data_path }}/mx-puppet-skype" +matrix_mx_puppet_skype_config_path: "{{ matrix_mx_puppet_skype_base_path }}/config" +matrix_mx_puppet_skype_data_path: "{{ matrix_mx_puppet_skype_base_path }}/data" +matrix_mx_puppet_skype_docker_src_files_path: "{{ matrix_mx_puppet_skype_base_path }}/docker-src" + +matrix_mx_puppet_skype_appservice_port: "8438" + +matrix_mx_puppet_skype_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mx_puppet_skype_appservice_address: 'http://matrix-mx-puppet-skype:{{ matrix_mx_puppet_skype_appservice_port }}' + +# "@user:server.com" to allow specific user +# "@.*:yourserver.com" to allow users on a specific homeserver +# "@.*" to allow anyone +matrix_mx_puppet_skype_provisioning_whitelist: + - "@.*:{{ matrix_domain|regex_escape }}" + +# Leave empty to disable blacklist +# "@user:server.com" disallow a specific user +# "@.*:yourserver.com" disallow users on a specific homeserver +matrix_mx_puppet_skype_provisioning_blacklist: [] + +# Same as provisioning +matrix_mx_puppet_skype_relay_whitelist: + - "@.*:{{ matrix_domain|regex_escape }}" + +# Same as provisioning +matrix_mx_puppet_skype_relay_blacklist: [] + +# A list of extra arguments to pass to the container +matrix_mx_puppet_skype_container_extra_arguments: [] + +# List of systemd services that matrix-puppet-skype.service depends on. +matrix_mx_puppet_skype_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-puppet-skype.service wants +matrix_mx_puppet_skype_systemd_wanted_services_list: [] + +matrix_mx_puppet_skype_appservice_token: '' +matrix_mx_puppet_skype_homeserver_token: '' + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mx_puppet_skype_login_shared_secret: '' + +# Database configuration, role default is `sqlite` but playbook default is `postgres` +matrix_mx_puppet_skype_database_engine: sqlite + +matrix_mx_puppet_skype_sqlite_database_path_local: "{{ matrix_mx_puppet_skype_data_path }}/database.db" +matrix_mx_puppet_skype_sqlite_database_path_in_container: "/data/database.db" + +matrix_mx_puppet_skype_database_username: matrix_mx_puppet_skype +matrix_mx_puppet_skype_database_password: ~ +matrix_mx_puppet_skype_database_hostname: 'matrix-postgres' +matrix_mx_puppet_skype_database_port: 5432 +matrix_mx_puppet_skype_database_name: matrix_mx_puppet_skype + +matrix_mx_puppet_skype_database_connection_string: 'postgresql://{{ matrix_mx_puppet_skype_database_username }}:{{ matrix_mx_puppet_skype_database_password }}@{{ matrix_mx_puppet_skype_database_hostname }}:{{ matrix_mx_puppet_skype_database_port }}/{{ matrix_mx_puppet_skype_database_name }}?sslmode=disable' + +# Default 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_mx_puppet_skype_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mx_puppet_skype_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mx_puppet_skype_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mx_puppet_skype_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mx_puppet_skype_configuration_yaml`. + +matrix_mx_puppet_skype_configuration_extension: "{{ matrix_mx_puppet_skype_configuration_extension_yaml|from_yaml if matrix_mx_puppet_skype_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mx_puppet_skype_configuration_yaml`. +matrix_mx_puppet_skype_configuration: "{{ matrix_mx_puppet_skype_configuration_yaml|from_yaml|combine(matrix_mx_puppet_skype_configuration_extension, recursive=True) }}" + +matrix_mx_puppet_skype_registration_yaml: | + as_token: "{{ matrix_mx_puppet_skype_appservice_token }}" + hs_token: "{{ matrix_mx_puppet_skype_homeserver_token }}" + id: skype-puppet + namespaces: + users: + - exclusive: true + regex: '@_skypepuppet_.*:{{ matrix_domain|regex_escape }}' + rooms: [] + aliases: + - exclusive: true + regex: '#_skypepuppet_.*:{{ matrix_domain|regex_escape }}' + protocols: [] + rate_limited: false + sender_localpart: _skypepuppet_bot + url: {{ matrix_mx_puppet_skype_appservice_address }} + de.sorunome.msc2409.push_ephemeral: true + +matrix_mx_puppet_skype_registration: "{{ matrix_mx_puppet_skype_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mx-puppet-skype/tasks/init.yml b/roles/matrix-bridge-mx-puppet-skype/tasks/init.yml new file mode 100644 index 00000000..5618821b --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-skype/tasks/init.yml @@ -0,0 +1,23 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mx_puppet_skype_container_image_self_build and matrix_mx_puppet_skype_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mx-puppet-skype.service'] }}" + when: matrix_mx_puppet_skype_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mx_puppet_skype_config_path }}/registration.yaml,dst=/matrix-mx-puppet-skype-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mx-puppet-skype-registration.yaml"] }} + when: matrix_mx_puppet_skype_enabled|bool diff --git a/roles/matrix-bridge-mx-puppet-skype/tasks/main.yml b/roles/matrix-bridge-mx-puppet-skype/tasks/main.yml new file mode 100644 index 00000000..01ddd7d8 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-skype/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mx_puppet_skype_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-skype + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mx_puppet_skype_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-skype + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mx_puppet_skype_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-skype diff --git a/roles/matrix-bridge-mx-puppet-skype/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-skype/tasks/setup_install.yml new file mode 100644 index 00000000..997a6317 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-skype/tasks/setup_install.yml @@ -0,0 +1,128 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mx-puppet-skype role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- name: Ensure MX Puppet Skype paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mx_puppet_skype_base_path }}", when: true } + - { path: "{{ matrix_mx_puppet_skype_config_path }}", when: true } + - { path: "{{ matrix_mx_puppet_skype_data_path }}", when: true } + - { path: "{{ matrix_mx_puppet_skype_docker_src_files_path }}", when: "{{ matrix_mx_puppet_skype_container_image_self_build }}" } + when: matrix_mx_puppet_skype_enabled|bool and item.when|bool + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mx_puppet_skype_base_path }}/database.db" + register: matrix_mx_puppet_skype_stat_database + +- name: (Data relocation) Ensure matrix-mx-puppet-skype.service is stopped + service: + name: matrix-mx-puppet-skype + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mx_puppet_skype_stat_database.stat.exists" + +- name: (Data relocation) Move mx-puppet-skype database file to ./data directory + command: "mv {{ matrix_mx_puppet_skype_base_path }}/database.db {{ matrix_mx_puppet_skype_data_path }}/database.db" + when: "matrix_mx_puppet_skype_stat_database.stat.exists" + +- set_fact: + matrix_mx_puppet_skype_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mx_puppet_skype_sqlite_database_path_local }}" + register: matrix_mx_puppet_skype_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mx_puppet_skype_sqlite_database_path_local }}" + dst: "{{ matrix_mx_puppet_skype_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mx_puppet_skype_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mx-puppet-skype.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mx_puppet_skype_requires_restart: true + when: "matrix_mx_puppet_skype_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mx_puppet_skype_database_engine == 'postgres'" + +- name: Ensure MX Puppet Skype image is pulled + docker_image: + name: "{{ matrix_mx_puppet_skype_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mx_puppet_skype_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mx_puppet_skype_docker_image_force_pull }}" + when: matrix_mx_puppet_skype_enabled|bool and not matrix_mx_puppet_skype_container_image_self_build + +- name: Ensure MX Puppet Skype repository is present on self build + git: + repo: "{{ matrix_mx_puppet_skype_container_image_self_build_repo }}" + dest: "{{ matrix_mx_puppet_skype_docker_src_files_path }}" + force: "yes" + register: matrix_mx_puppet_skype_git_pull_results + when: "matrix_mx_puppet_skype_enabled|bool and matrix_mx_puppet_skype_container_image_self_build|bool" + +- name: Ensure MX Puppet Skype Docker image is built + docker_image: + name: "{{ matrix_mx_puppet_skype_docker_image }}" + source: build + force_source: "{{ matrix_mx_puppet_skype_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_mx_puppet_skype_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mx_puppet_skype_docker_src_files_path }}" + pull: yes + when: "matrix_mx_puppet_skype_enabled|bool and matrix_mx_puppet_skype_container_image_self_build|bool" + +- name: Ensure mx-puppet-skype config.yaml installed + copy: + content: "{{ matrix_mx_puppet_skype_configuration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_skype_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mx-puppet-skype skype-registration.yaml installed + copy: + content: "{{ matrix_mx_puppet_skype_registration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_skype_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mx-puppet-skype.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mx-puppet-skype.service.j2" + dest: "/etc/systemd/system/matrix-mx-puppet-skype.service" + mode: 0644 + register: matrix_mx_puppet_skype_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mx-puppet-skype.service installation + service: + daemon_reload: yes + when: "matrix_mx_puppet_skype_systemd_service_result.changed" + +- name: Ensure matrix-mx-puppet-skype.service restarted, if necessary + service: + name: "matrix-mx-puppet-skype.service" + state: restarted + when: "matrix_mx_puppet_skype_requires_restart|bool" diff --git a/roles/matrix-bridge-mx-puppet-skype/tasks/setup_uninstall.yml b/roles/matrix-bridge-mx-puppet-skype/tasks/setup_uninstall.yml new file mode 100644 index 00000000..72b3a945 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-skype/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mx-puppet-skype service + stat: + path: "/etc/systemd/system/matrix-mx-puppet-skype.service" + register: matrix_mx_puppet_skype_service_stat + +- name: Ensure matrix-mx-puppet-skype is stopped + service: + name: matrix-mx-puppet-skype + state: stopped + daemon_reload: yes + when: "matrix_mx_puppet_skype_service_stat.stat.exists" + +- name: Ensure matrix-mx-puppet-skype.service doesn't exist + file: + path: "/etc/systemd/system/matrix-mx-puppet-skype.service" + state: absent + when: "matrix_mx_puppet_skype_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mx-puppet-skype.service removal + service: + daemon_reload: yes + when: "matrix_mx_puppet_skype_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mx-puppet-skype/tasks/validate_config.yml b/roles/matrix-bridge-mx-puppet-skype/tasks/validate_config.yml new file mode 100644 index 00000000..7ed433b1 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-skype/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mx_puppet_skype_appservice_token" + - "matrix_mx_puppet_skype_homeserver_token" diff --git a/roles/matrix-bridge-mx-puppet-skype/templates/config.yaml.j2 b/roles/matrix-bridge-mx-puppet-skype/templates/config.yaml.j2 new file mode 100644 index 00000000..1d6d4828 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-skype/templates/config.yaml.j2 @@ -0,0 +1,118 @@ +#jinja2: lstrip_blocks: "True" +bridge: + # Address for the bridge to bind to; if running as a Docker container, you + # probably want 0.0.0.0 here + bindAddress: 0.0.0.0 + # Port to host the bridge on which your homeserver will connect to + port: {{ matrix_mx_puppet_skype_appservice_port }} + # Name of your homeserver + domain: {{ matrix_domain }} + # URL where the bridge can connect to your homeserver + homeserverUrl: {{ matrix_mx_puppet_skype_homeserver_address }} + # Optionally specify a different media URL used for the media store + mediaURL: https://{{ matrix_server_fqn_matrix }} + # This enabled automatic double-puppeting: + # A map for shared secrets of the homeserver URL to the shared secret + # See https://github.com/devture/matrix-synapse-shared-secret-auth + #loginSharedSecretMap: + # yourserver.com: supersecretsharedsecret + {% if matrix_mx_puppet_skype_login_shared_secret != '' %} + loginSharedSecretMap: + {{ matrix_domain }}: {{ matrix_mx_puppet_skype_login_shared_secret }} + {% endif %} + # optionally override the display name of the bridge bot + #displayname: Protocol Bot + # optionally set the avatar of the bridge bot + #avatarUrl: mxc://yourserver.com/somefile + +logging: + # Log level of console output + # Allowed values starting with most verbose: + # silly, debug, verbose, info, warn, error + console: info + # Optionally, you can apply filters to the console logging + #console: + # level: info + # enabled: + # - Store + # disabled: + # - PresenceHandler + + # Date and time formatting + lineDateFormat: MMM-D HH:mm:ss.SSS + # Logging files + # Log files are rotated daily by default + files: [] + +database: +{% if matrix_mx_puppet_skype_database_engine == 'postgres' %} + # Use Postgres as a database backend + # If set, will be used instead of SQLite3 + # Connection string to connect to the Postgres instance + # with username "user", password "pass", host "localhost" and database name "dbname". + # Modify each value as necessary + connString: {{ matrix_mx_puppet_skype_database_connection_string|to_json }} +{% else %} + # Use SQLite3 as a database backend + # The name of the database file + filename: {{ matrix_mx_puppet_skype_sqlite_database_path_in_container|to_json }} +{% endif %} + +provisioning: + # Regex of Matrix IDs allowed to use the puppet bridge + whitelist: {{ matrix_mx_puppet_skype_provisioning_whitelist|to_json }} + # Allow a specific user + #- "@user:server\\.com" + # Allow users on a specific homeserver + #- "@.*:yourserver\\.com" + # Allow anyone + #- ".*" + + # Regex of Matrix IDs forbidden from using the puppet bridge + #blacklist: + # Disallow a specific user + #- "@user:server\\.com" + # Disallow users on a specific homeserver + #- "@.*:yourserver\\.com" + blacklist: {{ matrix_mx_puppet_skype_provisioning_blacklist|to_json }} + +presence: + # Bridge online/offline status + enabled: true + # How often to send status to the homeserver in milliseconds + interval: 500 + # if the im.vector.user_status state setting should be diabled + #disableStatusState: false + # A blacklist of remote user IDs for the im.vector.user_status state setting + #statusStateBlacklist: + # - baduser + +relay: + # Regex of Matrix IDs to allow to use the relay mode + # Same format as in provisioning + #whitelist: + #- "@.*:yourserver\\.com" + whitelist: {{ matrix_mx_puppet_skype_relay_whitelist|to_json }} + + #blacklist: + #- "@user:yourserver\\.com" + blacklist: {{ matrix_mx_puppet_skype_relay_blacklist|to_json }} + +# Map certain homeserver URLs to the C-S API endpoint +# Useful for double-puppeting if .well-known is unavailable for some reason +#homeserverUrlMap: +# yourserver.com: http://localhost:1234 + +namePatterns: + # Override the protocols set default name patterns + # Which variables are available depends on protocol implementation + user: :name + room: :name + +limits: + # Up to how many users should be auto-joined on room creation? -1 to disable + # Defaults to 200 + maxAutojoinUsers: 200 + # How long the delay between two autojoin users should be, in millisectonds. + # Defaults to 5000 + roomUserAutojoinDelay: 5000 diff --git a/roles/matrix-bridge-mx-puppet-skype/templates/systemd/matrix-mx-puppet-skype.service.j2 b/roles/matrix-bridge-mx-puppet-skype/templates/systemd/matrix-mx-puppet-skype.service.j2 new file mode 100644 index 00000000..9a7986e4 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-skype/templates/systemd/matrix-mx-puppet-skype.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mx Puppet Skype bridge +{% for service in matrix_mx_puppet_skype_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mx_puppet_skype_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-skype 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-skype 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-skype \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -e CONFIG_PATH=/config/config.yaml \ + -e REGISTRATION_PATH=/config/registration.yaml \ + -v {{ matrix_mx_puppet_skype_config_path }}:/config:z \ + -v {{ matrix_mx_puppet_skype_data_path }}:/data:z \ + {% for arg in matrix_mx_puppet_skype_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mx_puppet_skype_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-skype 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-skype 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mx-puppet-skype + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mx-puppet-slack/defaults/main.yml b/roles/matrix-bridge-mx-puppet-slack/defaults/main.yml new file mode 100644 index 00000000..30d42475 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-slack/defaults/main.yml @@ -0,0 +1,113 @@ +# Mx Puppet Slack is a Matrix <-> Slack bridge +# See: https://github.com/Sorunome/mx-puppet-slack + +matrix_mx_puppet_slack_enabled: true + +matrix_mx_puppet_slack_container_image_self_build: false +matrix_mx_puppet_slack_container_image_self_build_repo: "https://github.com/Sorunome/mx-puppet-slack.git" + +# Controls whether the mx-puppet-slack container exposes its HTTP port (tcp/8432 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8432"), or empty string to not expose. +matrix_mx_puppet_slack_container_http_host_bind_port: '' + +matrix_mx_puppet_slack_version: latest +matrix_mx_puppet_slack_docker_image: "{{ matrix_mx_puppet_slack_docker_image_name_prefix }}sorunome/mx-puppet-slack:{{ matrix_mx_puppet_slack_version }}" +matrix_mx_puppet_slack_docker_image_name_prefix: "{{ 'localhost/' if matrix_mx_puppet_slack_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_mx_puppet_slack_docker_image_force_pull: "{{ matrix_mx_puppet_slack_docker_image.endswith(':latest') }}" + +matrix_mx_puppet_slack_base_path: "{{ matrix_base_data_path }}/mx-puppet-slack" +matrix_mx_puppet_slack_config_path: "{{ matrix_mx_puppet_slack_base_path }}/config" +matrix_mx_puppet_slack_data_path: "{{ matrix_mx_puppet_slack_base_path }}/data" +matrix_mx_puppet_slack_docker_src_files_path: "{{ matrix_mx_puppet_slack_base_path }}/docker-src" + +matrix_mx_puppet_slack_appservice_port: "8432" + +matrix_mx_puppet_slack_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mx_puppet_slack_homeserver_domain: '{{ matrix_domain }}' +matrix_mx_puppet_slack_appservice_address: 'http://matrix-mx-puppet-slack:{{ matrix_mx_puppet_slack_appservice_port }}' + +matrix_mx_puppet_slack_redirect_path: '/slack/oauth' +matrix_mx_puppet_slack_redirect_uri: 'https://{{ matrix_server_fqn_matrix }}{{ matrix_mx_puppet_slack_redirect_path }}' + +# "@user:server.com" to allow specific user +# "@.*:yourserver.com" to allow users on a specific homeserver +# "@.*" to allow anyone +matrix_mx_puppet_slack_provisioning_whitelist: + - "@.*:{{ matrix_domain|regex_escape }}" + +# Leave empty to disable blacklist +# "@user:server.com" disallow a specific user +# "@.*:yourserver.com" disallow users on a specific homeserver +matrix_mx_puppet_slack_provisioning_blacklist: [] + +# A list of extra arguments to pass to the container +matrix_mx_puppet_slack_container_extra_arguments: [] + +# List of systemd services that matrix-puppet-slack.service depends on. +matrix_mx_puppet_slack_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-puppet-slack.service wants +matrix_mx_puppet_slack_systemd_wanted_services_list: [] + +matrix_mx_puppet_slack_appservice_token: '' +matrix_mx_puppet_slack_homeserver_token: '' + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mx_puppet_slack_login_shared_secret: '' + +# Database configuration, role uses 'sqlite' per default but playbook sets up postgres by default +matrix_mx_puppet_slack_database_engine: sqlite + +matrix_mx_puppet_slack_sqlite_database_path_local: "{{ matrix_mx_puppet_slack_data_path }}/database.db" +matrix_mx_puppet_slack_sqlite_database_path_in_container: "/data/database.db" + +matrix_mx_puppet_slack_database_username: matrix_mx_puppet_slack +matrix_mx_puppet_slack_database_password: ~ +matrix_mx_puppet_slack_database_hostname: 'matrix-postgres' +matrix_mx_puppet_slack_database_port: 5432 +matrix_mx_puppet_slack_database_name: matrix_mx_puppet_slack + +matrix_mx_puppet_slack_database_connection_string: 'postgresql://{{ matrix_mx_puppet_slack_database_username }}:{{ matrix_mx_puppet_slack_database_password }}@{{ matrix_mx_puppet_slack_database_hostname }}:{{ matrix_mx_puppet_slack_database_port }}/{{ matrix_mx_puppet_slack_database_name }}?sslmode=disable' + +# Default 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_mx_puppet_slack_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mx_puppet_slack_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mx_puppet_slack_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mx_puppet_slack_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mx_puppet_slack_configuration_yaml`. + +matrix_mx_puppet_slack_configuration_extension: "{{ matrix_mx_puppet_slack_configuration_extension_yaml|from_yaml if matrix_mx_puppet_slack_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mx_puppet_slack_configuration_yaml`. +matrix_mx_puppet_slack_configuration: "{{ matrix_mx_puppet_slack_configuration_yaml|from_yaml|combine(matrix_mx_puppet_slack_configuration_extension, recursive=True) }}" + +matrix_mx_puppet_slack_registration_yaml: | + as_token: "{{ matrix_mx_puppet_slack_appservice_token }}" + hs_token: "{{ matrix_mx_puppet_slack_homeserver_token }}" + id: slack-puppet + namespaces: + users: + - exclusive: true + regex: '@_slackpuppet_.*:{{ matrix_mx_puppet_slack_homeserver_domain|regex_escape }}' + rooms: [] + aliases: + - exclusive: true + regex: '#_slackpuppet_.*:{{ matrix_mx_puppet_slack_homeserver_domain|regex_escape }}' + protocols: [] + rate_limited: false + sender_localpart: _slackpuppet_bot + url: {{ matrix_mx_puppet_slack_appservice_address }} + de.sorunome.msc2409.push_ephemeral: true + +matrix_mx_puppet_slack_registration: "{{ matrix_mx_puppet_slack_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mx-puppet-slack/tasks/init.yml b/roles/matrix-bridge-mx-puppet-slack/tasks/init.yml new file mode 100644 index 00000000..f484c687 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-slack/tasks/init.yml @@ -0,0 +1,70 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mx_puppet_slack_container_image_self_build and matrix_mx_puppet_slack_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mx-puppet-slack.service'] }}" + when: matrix_mx_puppet_slack_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mx_puppet_slack_config_path }}/registration.yaml,dst=/matrix-mx-puppet-slack-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mx-puppet-slack-registration.yaml"] }} + when: matrix_mx_puppet_slack_enabled|bool + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append Slack Appservice's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-mx-puppet-slack role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate Matrix MX Puppet Slack proxying configuration for matrix-nginx-proxy + set_fact: + matrix_mx_puppet_slack_matrix_nginx_proxy_configuration: | + location {{ matrix_mx_puppet_slack_redirect_path }} { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_mx_puppet_slack_appservice_address }}"; + proxy_pass $backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_mx_puppet_slack_appservice_port }}; + {% endif %} + } + + - name: Register Slack Appservice proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks|default([]) + + + [matrix_mx_puppet_slack_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_mx_puppet_slack_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the Matrix Slack bridge but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ matrix_mx_puppet_slack_redirect_path }}` + URL endpoint to the matrix-mx-puppet-slack container. + You can expose the container's port using the `matrix_appservice_slack_container_http_host_bind_port` variable. + when: "matrix_mx_puppet_slack_enabled|bool and matrix_nginx_proxy_enabled is not defined" diff --git a/roles/matrix-bridge-mx-puppet-slack/tasks/main.yml b/roles/matrix-bridge-mx-puppet-slack/tasks/main.yml new file mode 100644 index 00000000..6aa0fd0f --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-slack/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mx_puppet_slack_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-slack + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mx_puppet_slack_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-slack + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mx_puppet_slack_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-slack diff --git a/roles/matrix-bridge-mx-puppet-slack/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-slack/tasks/setup_install.yml new file mode 100644 index 00000000..d816ceeb --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-slack/tasks/setup_install.yml @@ -0,0 +1,128 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mx-puppet-slack role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- name: Ensure MX Puppet Slack paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mx_puppet_slack_base_path }}", when: true } + - { path: "{{ matrix_mx_puppet_slack_config_path }}", when: true } + - { path: "{{ matrix_mx_puppet_slack_data_path }}", when: true } + - { path: "{{ matrix_mx_puppet_slack_docker_src_files_path }}", when: "{{ matrix_mx_puppet_slack_container_image_self_build }}" } + when: matrix_mx_puppet_slack_enabled|bool and item.when|bool + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mx_puppet_slack_base_path }}/database.db" + register: matrix_mx_puppet_slack_stat_database + +- name: (Data relocation) Ensure matrix-mx-puppet-slack.service is stopped + service: + name: matrix-mx-puppet-slack + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mx_puppet_slack_stat_database.stat.exists" + +- set_fact: + matrix_mx_puppet_slack_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mx_puppet_slack_sqlite_database_path_local }}" + register: matrix_mx_puppet_slack_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mx_puppet_slack_sqlite_database_path_local }}" + dst: "{{ matrix_mx_puppet_slack_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mx_puppet_slack_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mx-puppet-slack.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mx_puppet_slack_requires_restart: true + when: "matrix_mx_puppet_slack_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mx_puppet_slack_database_engine == 'postgres'" + +- name: Ensure MX Puppet Slack image is pulled + docker_image: + name: "{{ matrix_mx_puppet_slack_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mx_puppet_slack_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mx_puppet_slack_docker_image_force_pull }}" + when: matrix_mx_puppet_slack_enabled|bool and not matrix_mx_puppet_slack_container_image_self_build + +- name: Ensure MX Puppet Slack repository is present on self build + git: + repo: "{{ matrix_mx_puppet_slack_container_image_self_build_repo }}" + dest: "{{ matrix_mx_puppet_slack_docker_src_files_path }}" + force: "yes" + register: matrix_mx_puppet_slack_git_pull_results + when: "matrix_mx_puppet_slack_enabled|bool and matrix_mx_puppet_slack_container_image_self_build" + +- name: Ensure MX Puppet Slack Docker image is built + docker_image: + name: "{{ matrix_mx_puppet_slack_docker_image }}" + source: build + force_source: "{{ matrix_mx_puppet_slack_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_mx_puppet_slack_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mx_puppet_slack_docker_src_files_path }}" + pull: yes + when: "matrix_mx_puppet_slack_enabled|bool and matrix_mx_puppet_slack_container_image_self_build" + +- name: (Data relocation) Move mx-puppet-slack database file to ./data directory + command: "mv {{ matrix_mx_puppet_slack_base_path }}/database.db {{ matrix_mx_puppet_slack_data_path }}/database.db" + when: "matrix_mx_puppet_slack_stat_database.stat.exists" + +- name: Ensure mx-puppet-slack config.yaml installed + copy: + content: "{{ matrix_mx_puppet_slack_configuration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_slack_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mx-puppet-slack slack-registration.yaml installed + copy: + content: "{{ matrix_mx_puppet_slack_registration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_slack_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mx-puppet-slack.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mx-puppet-slack.service.j2" + dest: "/etc/systemd/system/matrix-mx-puppet-slack.service" + mode: 0644 + register: matrix_mx_puppet_slack_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mx-puppet-slack.service installation + service: + daemon_reload: yes + when: "matrix_mx_puppet_slack_systemd_service_result.changed" + +- name: Ensure matrix-mx-puppet-slack.service restarted, if necessary + service: + name: "matrix-mx-puppet-slack.service" + state: restarted + when: "matrix_mx_puppet_slack_requires_restart|bool" diff --git a/roles/matrix-bridge-mx-puppet-slack/tasks/setup_uninstall.yml b/roles/matrix-bridge-mx-puppet-slack/tasks/setup_uninstall.yml new file mode 100644 index 00000000..73314a66 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-slack/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mx-puppet-slack service + stat: + path: "/etc/systemd/system/matrix-mx-puppet-slack.service" + register: matrix_mx_puppet_slack_service_stat + +- name: Ensure matrix-mx-puppet-slack is stopped + service: + name: matrix-mx-puppet-slack + state: stopped + daemon_reload: yes + when: "matrix_mx_puppet_slack_service_stat.stat.exists" + +- name: Ensure matrix-mx-puppet-slack.service doesn't exist + file: + path: "/etc/systemd/system/matrix-mx-puppet-slack.service" + state: absent + when: "matrix_mx_puppet_slack_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mx-puppet-slack.service removal + service: + daemon_reload: yes + when: "matrix_mx_puppet_slack_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mx-puppet-slack/tasks/validate_config.yml b/roles/matrix-bridge-mx-puppet-slack/tasks/validate_config.yml new file mode 100644 index 00000000..3a0bca11 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-slack/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mx_puppet_slack_appservice_token" + - "matrix_mx_puppet_slack_homeserver_token" diff --git a/roles/matrix-bridge-mx-puppet-slack/templates/config.yaml.j2 b/roles/matrix-bridge-mx-puppet-slack/templates/config.yaml.j2 new file mode 100644 index 00000000..b1917b86 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-slack/templates/config.yaml.j2 @@ -0,0 +1,79 @@ +#jinja2: lstrip_blocks: "True" +bridge: + # Port to host the bridge on + # Used for communication between the homeserver and the bridge + port: {{ matrix_mx_puppet_slack_appservice_port }} + # The host connections to the bridge's webserver are allowed from + bindAddress: 0.0.0.0 + # Public domain of the homeserver + domain: {{ matrix_mx_puppet_slack_homeserver_domain }} + # Reachable URL of the Matrix homeserver + homeserverUrl: {{ matrix_mx_puppet_slack_homeserver_address }} + {% if matrix_mx_puppet_slack_login_shared_secret != '' %} + loginSharedSecretMap: + {{ matrix_domain }}: {{ matrix_mx_puppet_slack_login_shared_secret }} + {% endif %} + + +# Slack OAuth settings. Create a slack app at https://api.slack.com/apps +oauth: + enabled: true + # Path where to listen for OAuth redirect callbacks. + redirectPath: {{ matrix_mx_puppet_slack_redirect_path }} + # Set up proxying from https://your.domain/redirect_path to http://bindAddress:port/redirect_path, + # then set this field and the Slack app redirect URI field to the former. + redirectUri: {{ matrix_mx_puppet_slack_redirect_uri }} + +presence: + # Bridge Discord online/offline status + enabled: true + # How often to send status to the homeserver in milliseconds + interval: 500 + +provisioning: + # Regex of Matrix IDs allowed to use the puppet bridge + whitelist: {{ matrix_mx_puppet_slack_provisioning_whitelist|to_json }} + # Allow a specific user + #- "@user:server\\.com" + # Allow users on a specific homeserver + #- "@.*:yourserver\\.com" + # Allow anyone + #- ".*" + # Regex of Matrix IDs forbidden from using the puppet bridge + #blacklist: + # Disallow a specific user + #- "@user:server\\.com" + # Disallow users on a specific homeserver + #- "@.*:yourserver\\.com" + blacklist: {{ matrix_mx_puppet_slack_provisioning_blacklist|to_json }} + + # Shared secret for the provisioning API for use by integration managers. + # If this is not set, the provisioning API will not be enabled. + #sharedSecret: random string + # Path prefix for the provisioning API. /v1 will be appended to the prefix automatically. + apiPrefix: /_matrix/provision + +database: +{% if matrix_mx_puppet_slack_database_engine == 'postgres' %} + # Use Postgres as a database backend + # If set, will be used instead of SQLite3 + # Connection string to connect to the Postgres instance + # with username "user", password "pass", host "localhost" and database name "dbname". + # Modify each value as necessary + connString: {{ matrix_mx_puppet_slack_database_connection_string|to_json }} +{% else %} + # Use SQLite3 as a database backend + # The name of the database file + filename: {{ matrix_mx_puppet_slack_sqlite_database_path_in_container|to_json }} +{% endif %} + +logging: + # Log level of console output + # Allowed values starting with most verbose: + # silly, debug, verbose, info, warn, error + console: info + # Date and time formatting + lineDateFormat: MMM-D HH:mm:ss.SSS + # Logging files + # Log files are rotated daily by default + files: [] diff --git a/roles/matrix-bridge-mx-puppet-slack/templates/systemd/matrix-mx-puppet-slack.service.j2 b/roles/matrix-bridge-mx-puppet-slack/templates/systemd/matrix-mx-puppet-slack.service.j2 new file mode 100644 index 00000000..973771b3 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-slack/templates/systemd/matrix-mx-puppet-slack.service.j2 @@ -0,0 +1,46 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mx Puppet Slack bridge +{% for service in matrix_mx_puppet_slack_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mx_puppet_slack_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-slack 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-slack 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-slack \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_mx_puppet_slack_container_http_host_bind_port %} + -p {{ matrix_mx_puppet_slack_container_http_host_bind_port }}:{{ matrix_mx_puppet_slack_appservice_port }} \ + {% endif %} + -e CONFIG_PATH=/config/config.yaml \ + -e REGISTRATION_PATH=/config/registration.yaml \ + -v {{ matrix_mx_puppet_slack_config_path }}:/config:z \ + -v {{ matrix_mx_puppet_slack_data_path }}:/data:z \ + {% for arg in matrix_mx_puppet_slack_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mx_puppet_slack_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-slack 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-slack 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mx-puppet-slack + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mx-puppet-steam/defaults/main.yml b/roles/matrix-bridge-mx-puppet-steam/defaults/main.yml new file mode 100644 index 00000000..2af4a32a --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-steam/defaults/main.yml @@ -0,0 +1,109 @@ +# Mx Puppet Steam is a Matrix <-> Steam bridge +# See: https://github.com/matrix-steam/mx-puppet-steam + +matrix_mx_puppet_steam_enabled: true + +matrix_mx_puppet_steam_container_image_self_build: false +matrix_mx_puppet_steam_container_image_self_build_repo: "https://github.com/icewind1991/mx-puppet-steam.git" + +# Controls whether the mx-puppet-steam container exposes its HTTP port (tcp/8432 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8432"), or empty string to not expose. +matrix_mx_puppet_steam_container_http_host_bind_port: '' + +matrix_mx_puppet_steam_version: latest +matrix_mx_puppet_steam_docker_image: "{{ matrix_mx_puppet_steam_docker_image_name_prefix }}icewind1991/mx-puppet-steam:{{ matrix_mx_puppet_steam_version }}" +matrix_mx_puppet_steam_docker_image_name_prefix: "{{ 'localhost/' if matrix_mx_puppet_steam_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_mx_puppet_steam_docker_image_force_pull: "{{ matrix_mx_puppet_steam_docker_image.endswith(':latest') }}" + +matrix_mx_puppet_steam_base_path: "{{ matrix_base_data_path }}/mx-puppet-steam" +matrix_mx_puppet_steam_config_path: "{{ matrix_mx_puppet_steam_base_path }}/config" +matrix_mx_puppet_steam_data_path: "{{ matrix_mx_puppet_steam_base_path }}/data" +matrix_mx_puppet_steam_docker_src_files_path: "{{ matrix_mx_puppet_steam_base_path }}/docker-src" + +matrix_mx_puppet_steam_appservice_port: "8432" + +matrix_mx_puppet_steam_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mx_puppet_steam_homeserver_domain: '{{ matrix_domain }}' +matrix_mx_puppet_steam_appservice_address: 'http://matrix-mx-puppet-steam:{{ matrix_mx_puppet_steam_appservice_port }}' + +# "@user:server.com" to allow specific user +# "@.*:yourserver.com" to allow users on a specific homeserver +# "@.*" to allow anyone +matrix_mx_puppet_steam_provisioning_whitelist: + - "@.*:{{ matrix_domain|regex_escape }}" + +# Leave empty to disable blacklist +# "@user:server.com" disallow a specific user +# "@.*:yourserver.com" disallow users on a specific homeserver +matrix_mx_puppet_steam_provisioning_blacklist: [] + +# A list of extra arguments to pass to the container +matrix_mx_puppet_steam_container_extra_arguments: [] + +# List of systemd services that matrix-puppet-steam.service depends on. +matrix_mx_puppet_steam_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-puppet-steam.service wants +matrix_mx_puppet_steam_systemd_wanted_services_list: [] + +matrix_mx_puppet_steam_appservice_token: '' +matrix_mx_puppet_steam_homeserver_token: '' + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mx_puppet_steam_login_shared_secret: '' + +matrix_mx_puppet_steam_database_engine: sqlite + +matrix_mx_puppet_steam_sqlite_database_path_local: "{{ matrix_mx_puppet_steam_data_path }}/database.db" +matrix_mx_puppet_steam_sqlite_database_path_in_container: "/data/database.db" + +matrix_mx_puppet_steam_database_username: matrix_mx_puppet_steam +matrix_mx_puppet_steam_database_password: ~ +matrix_mx_puppet_steam_database_hostname: 'matrix-postgres' +matrix_mx_puppet_steam_database_port: 5432 +matrix_mx_puppet_steam_database_name: matrix_mx_puppet_steam + +matrix_mx_puppet_steam_database_connection_string: 'postgresql://{{ matrix_mx_puppet_steam_database_username }}:{{ matrix_mx_puppet_steam_database_password }}@{{ matrix_mx_puppet_steam_database_hostname }}:{{ matrix_mx_puppet_steam_database_port }}/{{ matrix_mx_puppet_steam_database_name }}?sslmode=disable' + +# Default 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_mx_puppet_steam_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mx_puppet_steam_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mx_puppet_steam_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mx_puppet_steam_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mx_puppet_steam_configuration_yaml`. + +matrix_mx_puppet_steam_configuration_extension: "{{ matrix_mx_puppet_steam_configuration_extension_yaml|from_yaml if matrix_mx_puppet_steam_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mx_puppet_steam_configuration_yaml`. +matrix_mx_puppet_steam_configuration: "{{ matrix_mx_puppet_steam_configuration_yaml|from_yaml|combine(matrix_mx_puppet_steam_configuration_extension, recursive=True) }}" + +matrix_mx_puppet_steam_registration_yaml: | + as_token: "{{ matrix_mx_puppet_steam_appservice_token }}" + hs_token: "{{ matrix_mx_puppet_steam_homeserver_token }}" + id: steam-puppet + namespaces: + users: + - exclusive: true + regex: '@_steampuppet_.*:{{ matrix_mx_puppet_steam_homeserver_domain|regex_escape }}' + rooms: [] + aliases: + - exclusive: true + regex: '#_steampuppet_.*:{{ matrix_mx_puppet_steam_homeserver_domain|regex_escape }}' + protocols: [] + rate_limited: false + sender_localpart: _steampuppet_bot + url: {{ matrix_mx_puppet_steam_appservice_address }} + de.sorunome.msc2409.push_ephemeral: true + +matrix_mx_puppet_steam_registration: "{{ matrix_mx_puppet_steam_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mx-puppet-steam/tasks/init.yml b/roles/matrix-bridge-mx-puppet-steam/tasks/init.yml new file mode 100644 index 00000000..c3218e89 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-steam/tasks/init.yml @@ -0,0 +1,23 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mx_puppet_steam_container_image_self_build and matrix_mx_puppet_steam_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mx-puppet-steam.service'] }}" + when: matrix_mx_puppet_steam_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mx_puppet_steam_config_path }}/registration.yaml,dst=/matrix-mx-puppet-steam-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mx-puppet-steam-registration.yaml"] }} + when: matrix_mx_puppet_steam_enabled|bool diff --git a/roles/matrix-bridge-mx-puppet-steam/tasks/main.yml b/roles/matrix-bridge-mx-puppet-steam/tasks/main.yml new file mode 100644 index 00000000..cd6bb147 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-steam/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mx_puppet_steam_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-steam + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mx_puppet_steam_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-steam + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mx_puppet_steam_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-steam diff --git a/roles/matrix-bridge-mx-puppet-steam/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-steam/tasks/setup_install.yml new file mode 100644 index 00000000..3bcef36e --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-steam/tasks/setup_install.yml @@ -0,0 +1,128 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mx-puppet-steam role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- name: Ensure MX Puppet Steam paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mx_puppet_steam_base_path }}", when: true } + - { path: "{{ matrix_mx_puppet_steam_config_path }}", when: true } + - { path: "{{ matrix_mx_puppet_steam_data_path }}", when: true } + - { path: "{{ matrix_mx_puppet_steam_docker_src_files_path }}", when: "{{ matrix_mx_puppet_steam_container_image_self_build }}" } + when: matrix_mx_puppet_steam_enabled|bool and item.when|bool + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mx_puppet_steam_base_path }}/database.db" + register: matrix_mx_puppet_steam_stat_database + +- name: (Data relocation) Ensure matrix-mx-puppet-steam.service is stopped + service: + name: matrix-mx-puppet-steam + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mx_puppet_steam_stat_database.stat.exists" + +- name: (Data relocation) Move mx-puppet-steam database file to ./data directory + command: "mv {{ matrix_mx_puppet_steam_base_path }}/database.db {{ matrix_mx_puppet_steam_data_path }}/database.db" + when: "matrix_mx_puppet_steam_stat_database.stat.exists" + +- set_fact: + matrix_mx_puppet_steam_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mx_puppet_steam_sqlite_database_path_local }}" + register: matrix_mx_puppet_steam_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mx_puppet_steam_sqlite_database_path_local }}" + dst: "{{ matrix_mx_puppet_steam_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mx_puppet_steam_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mx-puppet-steam.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mx_puppet_steam_requires_restart: true + when: "matrix_mx_puppet_steam_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mx_puppet_steam_database_engine == 'postgres'" + +- name: Ensure MX Puppet Steam image is pulled + docker_image: + name: "{{ matrix_mx_puppet_steam_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mx_puppet_steam_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mx_puppet_steam_docker_image_force_pull }}" + when: matrix_mx_puppet_steam_enabled|bool and not matrix_mx_puppet_steam_container_image_self_build + +- name: Ensure MX Puppet Steam repository is present on self build + git: + repo: "{{ matrix_mx_puppet_steam_container_image_self_build_repo }}" + dest: "{{ matrix_mx_puppet_steam_docker_src_files_path }}" + force: "yes" + register: matrix_mx_puppet_steam_git_pull_results + when: "matrix_mx_puppet_steam_enabled|bool and matrix_mx_puppet_steam_container_image_self_build" + +- name: Ensure MX Puppet Steam Docker image is built + docker_image: + name: "{{ matrix_mx_puppet_steam_docker_image }}" + source: build + force_source: "{{ matrix_mx_puppet_steam_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_mx_puppet_steam_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mx_puppet_steam_docker_src_files_path }}" + pull: yes + when: "matrix_mx_puppet_steam_enabled|bool and matrix_mx_puppet_steam_container_image_self_build" + +- name: Ensure mx-puppet-steam config.yaml installed + copy: + content: "{{ matrix_mx_puppet_steam_configuration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_steam_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mx-puppet-steam steam-registration.yaml installed + copy: + content: "{{ matrix_mx_puppet_steam_registration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_steam_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mx-puppet-steam.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mx-puppet-steam.service.j2" + dest: "/etc/systemd/system/matrix-mx-puppet-steam.service" + mode: 0644 + register: matrix_mx_puppet_steam_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mx-puppet-steam.service installation + service: + daemon_reload: yes + when: "matrix_mx_puppet_steam_systemd_service_result.changed" + +- name: Ensure matrix-mx-puppet-steam.service restarted, if necessary + service: + name: "matrix-mx-puppet-steam.service" + state: restarted + when: "matrix_mx_puppet_steam_requires_restart|bool" diff --git a/roles/matrix-bridge-mx-puppet-steam/tasks/setup_uninstall.yml b/roles/matrix-bridge-mx-puppet-steam/tasks/setup_uninstall.yml new file mode 100644 index 00000000..1ee95eb3 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-steam/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mx-puppet-steam service + stat: + path: "/etc/systemd/system/matrix-mx-puppet-steam.service" + register: matrix_mx_puppet_steam_service_stat + +- name: Ensure matrix-mx-puppet-steam is stopped + service: + name: matrix-mx-puppet-steam + state: stopped + daemon_reload: yes + when: "matrix_mx_puppet_steam_service_stat.stat.exists" + +- name: Ensure matrix-mx-puppet-steam.service doesn't exist + file: + path: "/etc/systemd/system/matrix-mx-puppet-steam.service" + state: absent + when: "matrix_mx_puppet_steam_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mx-puppet-steam.service removal + service: + daemon_reload: yes + when: "matrix_mx_puppet_steam_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mx-puppet-steam/tasks/validate_config.yml b/roles/matrix-bridge-mx-puppet-steam/tasks/validate_config.yml new file mode 100644 index 00000000..a8bc6a42 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-steam/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mx_puppet_steam_appservice_token" + - "matrix_mx_puppet_steam_homeserver_token" diff --git a/roles/matrix-bridge-mx-puppet-steam/templates/config.yaml.j2 b/roles/matrix-bridge-mx-puppet-steam/templates/config.yaml.j2 new file mode 100644 index 00000000..fd59471d --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-steam/templates/config.yaml.j2 @@ -0,0 +1,86 @@ +#jinja2: lstrip_blocks: "True" +bridge: + # Port to host the bridge on + # Used for communication between the homeserver and the bridge + port: {{ matrix_mx_puppet_steam_appservice_port }} + # The host connections to the bridge's webserver are allowed from + bindAddress: 0.0.0.0 + # Public domain of the homeserver + domain: {{ matrix_mx_puppet_steam_homeserver_domain }} + # Reachable URL of the Matrix homeserver + homeserverUrl: {{ matrix_mx_puppet_steam_homeserver_address }} + {% if matrix_mx_puppet_steam_login_shared_secret != '' %} + loginSharedSecretMap: + {{ matrix_domain }}: {{ matrix_mx_puppet_steam_login_shared_secret }} + {% endif %} + # Display name of the bridge bot + displayname: Steam Puppet Bridge + # Optionally specify a different media URL used for the media store + # + # This is where Steam will download user profile pictures and media + # from + #mediaUrl: https://external-url.org + +presence: + # Bridge Steam online/offline status + enabled: true + # How often to send status to the homeserver in milliseconds + interval: 5000 + +provisioning: + # Regex of Matrix IDs allowed to use the puppet bridge + whitelist: {{ matrix_mx_puppet_steam_provisioning_whitelist|to_json }} + # Allow a specific user + #- "@user:server\\.com" + # Allow users on a specific homeserver + #- "@.*:yourserver\\.com" + # Allow anyone + #- ".*" + # Regex of Matrix IDs forbidden from using the puppet bridge + #blacklist: + # Disallow a specific user + #- "@user:server\\.com" + # Disallow users on a specific homeserver + #- "@.*:yourserver\\.com" + blacklist: {{ matrix_mx_puppet_steam_provisioning_blacklist|to_json }} + +relay: + # Regex of Matrix IDs who are allowed to use the bridge in relay mode. + # Relay mode is when a single Steam bot account relays messages of + # multiple Matrix users + # + # Same format as in provisioning + whitelist: {{ matrix_mx_puppet_steam_provisioning_whitelist|to_json }} + blacklist: {{ matrix_mx_puppet_steam_provisioning_blacklist|to_json }} + +selfService: + # Regex of Matrix IDs who are allowed to use bridge self-servicing (plumbed rooms) + # + # Same format as in provisioning + whitelist: {{ matrix_mx_puppet_steam_provisioning_whitelist|to_json }} + blacklist: {{ matrix_mx_puppet_steam_provisioning_blacklist|to_json }} + +database: +{% if matrix_mx_puppet_steam_database_engine == 'postgres' %} + # Use Postgres as a database backend + # If set, will be used instead of SQLite3 + # Connection string to connect to the Postgres instance + # with username "user", password "pass", host "localhost" and database name "dbname". + # Modify each value as necessary + connString: {{ matrix_mx_puppet_steam_database_connection_string|to_json }} +{% else %} + # Use SQLite3 as a database backend + # The name of the database file + filename: {{ matrix_mx_puppet_steam_sqlite_database_path_in_container|to_json }} +{% endif %} + +logging: + # Log level of console output + # Allowed values starting with most verbose: + # silly, debug, verbose, info, warn, error + console: info + # Date and time formatting + lineDateFormat: MMM-D HH:mm:ss.SSS + # Logging files + # Log files are rotated daily by default + files: [] diff --git a/roles/matrix-bridge-mx-puppet-steam/templates/systemd/matrix-mx-puppet-steam.service.j2 b/roles/matrix-bridge-mx-puppet-steam/templates/systemd/matrix-mx-puppet-steam.service.j2 new file mode 100644 index 00000000..0772872b --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-steam/templates/systemd/matrix-mx-puppet-steam.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mx Puppet Steam bridge +{% for service in matrix_mx_puppet_steam_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mx_puppet_steam_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-steam 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-steam 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-steam \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + -e CONFIG_PATH=/config/config.yaml \ + -e REGISTRATION_PATH=/config/registration.yaml \ + -v {{ matrix_mx_puppet_steam_config_path }}:/config:z \ + -v {{ matrix_mx_puppet_steam_data_path }}:/data:z \ + {% for arg in matrix_mx_puppet_steam_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mx_puppet_steam_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-steam 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-steam 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mx-puppet-steam + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-mx-puppet-twitter/defaults/main.yml b/roles/matrix-bridge-mx-puppet-twitter/defaults/main.yml new file mode 100644 index 00000000..0e37d51f --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-twitter/defaults/main.yml @@ -0,0 +1,122 @@ +# Mx Puppet Twitter is a Matrix <-> Twitter bridge +# See: https://github.com/Sorunome/mx-puppet-twitter + +matrix_mx_puppet_twitter_enabled: true + +matrix_mx_puppet_twitter_container_image_self_build: false +matrix_mx_puppet_twitter_container_image_self_build_repo: "https://github.com/Sorunome/mx-puppet-twitter.git" + +# Controls whether the mx-puppet-twitter container exposes its HTTP port (tcp/8432 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8432"), or empty string to not expose. +matrix_mx_puppet_twitter_container_http_host_bind_port: '' + +matrix_mx_puppet_twitter_version: latest +matrix_mx_puppet_twitter_docker_image: "{{ matrix_mx_puppet_twitter_docker_image_name_prefix }}sorunome/mx-puppet-twitter:{{ matrix_mx_puppet_twitter_version }}" +matrix_mx_puppet_twitter_docker_image_name_prefix: "{{ 'localhost/' if matrix_mx_puppet_twitter_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_mx_puppet_twitter_docker_image_force_pull: "{{ matrix_mx_puppet_twitter_docker_image.endswith(':latest') }}" + +matrix_mx_puppet_twitter_base_path: "{{ matrix_base_data_path }}/mx-puppet-twitter" +matrix_mx_puppet_twitter_config_path: "{{ matrix_mx_puppet_twitter_base_path }}/config" +matrix_mx_puppet_twitter_data_path: "{{ matrix_mx_puppet_twitter_base_path }}/data" +matrix_mx_puppet_twitter_docker_src_files_path: "{{ matrix_mx_puppet_twitter_base_path }}/docker-src" + +matrix_mx_puppet_twitter_appservice_port: "8432" + +matrix_mx_puppet_twitter_homeserver_address: "{{ matrix_homeserver_container_url }}" +matrix_mx_puppet_twitter_homeserver_domain: '{{ matrix_domain }}' +matrix_mx_puppet_twitter_appservice_address: 'http://matrix-mx-puppet-twitter:{{ matrix_mx_puppet_twitter_appservice_port }}' + +matrix_mx_puppet_twitter_consumer_key: '' +matrix_mx_puppet_twitter_consumer_secret: '' +matrix_mx_puppet_twitter_access_token: '' +matrix_mx_puppet_twitter_access_token_secret: '' +matrix_mx_puppet_twitter_environment: '' +matrix_mx_puppet_twitter_webhook_path: '/twitter/webhook' +matrix_mx_puppet_twitter_webhook_url: 'https://{{ matrix_server_fqn_matrix }}{{ matrix_mx_puppet_twitter_webhook_path }}' + +# "@user:server.com" to allow specific user +# "@.*:yourserver.com" to allow users on a specific homeserver +# "@.*" to allow anyone +matrix_mx_puppet_twitter_provisioning_whitelist: + - "@.*:{{ matrix_domain|regex_escape }}" + +# Leave empty to disable blacklist +# "@user:server.com" disallow a specific user +# "@.*:yourserver.com" disallow users on a specific homeserver +matrix_mx_puppet_twitter_provisioning_blacklist: [] + +# A list of extra arguments to pass to the container +matrix_mx_puppet_twitter_container_extra_arguments: [] + +# List of systemd services that mx-puppet-twitter.service depends on. +matrix_mx_puppet_twitter_systemd_required_services_list: ['docker.service'] + +# List of systemd services that mx-puppet-twitter.service wants +matrix_mx_puppet_twitter_systemd_wanted_services_list: [] + +matrix_mx_puppet_twitter_appservice_token: '' +matrix_mx_puppet_twitter_homeserver_token: '' + +# Can be set to enable automatic double-puppeting via Shared Secret Auth (https://github.com/devture/matrix-synapse-shared-secret-auth). +matrix_mx_puppet_twitter_login_shared_secret: '' + +# Database configuration +matrix_mx_puppet_twitter_database_engine: sqlite + +matrix_mx_puppet_twitter_sqlite_database_path_local: "{{ matrix_mx_puppet_twitter_data_path }}/database.db" +matrix_mx_puppet_twitter_sqlite_database_path_in_container: "/data/database.db" + +matrix_mx_puppet_twitter_database_username: mx_puppet_twitter +matrix_mx_puppet_twitter_database_password: ~ +matrix_mx_puppet_twitter_database_hostname: 'matrix-postgres' +matrix_mx_puppet_twitter_database_port: 5432 +matrix_mx_puppet_twitter_database_name: matrix_mx_puppet_twitter + +matrix_mx_puppet_twitter_database_connection_string: 'postgresql://{{ matrix_mx_puppet_twitter_database_username }}:{{ matrix_mx_puppet_twitter_database_password }}@{{ matrix_mx_puppet_twitter_database_hostname }}:{{ matrix_mx_puppet_twitter_database_port }}/{{ matrix_mx_puppet_twitter_database_name }}?sslmode=disable' + +# Default 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_mx_puppet_twitter_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_mx_puppet_twitter_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_mx_puppet_twitter_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_mx_puppet_twitter_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_mx_puppet_twitter_configuration_yaml`. + +matrix_mx_puppet_twitter_configuration_extension: "{{ matrix_mx_puppet_twitter_configuration_extension_yaml|from_yaml if matrix_mx_puppet_twitter_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_mx_puppet_twitter_configuration_yaml`. +matrix_mx_puppet_twitter_configuration: "{{ matrix_mx_puppet_twitter_configuration_yaml|from_yaml|combine(matrix_mx_puppet_twitter_configuration_extension, recursive=True) }}" + +# The prefix for user IDs and aliases +matrix_mx_puppet_twitter_namespace_prefix: _twitterpuppet_ +matrix_mx_puppet_twitter_bot_localpart: _twitterpuppet_bot + +matrix_mx_puppet_twitter_registration_yaml: | + as_token: "{{ matrix_mx_puppet_twitter_appservice_token }}" + hs_token: "{{ matrix_mx_puppet_twitter_homeserver_token }}" + id: twitter-puppet + namespaces: + users: + - exclusive: true + regex: '@{{ matrix_mx_puppet_twitter_namespace_prefix|regex_escape }}.*:{{ matrix_mx_puppet_twitter_homeserver_domain|regex_escape }}' + rooms: [] + aliases: + - exclusive: true + regex: '#{{ matrix_mx_puppet_twitter_namespace_prefix|regex_escape }}.*:{{ matrix_mx_puppet_twitter_homeserver_domain|regex_escape }}' + protocols: [] + rate_limited: false + sender_localpart: "{{ matrix_mx_puppet_twitter_bot_localpart }}" + url: {{ matrix_mx_puppet_twitter_appservice_address }} + de.sorunome.msc2409.push_ephemeral: true + +matrix_mx_puppet_twitter_registration: "{{ matrix_mx_puppet_twitter_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-mx-puppet-twitter/tasks/init.yml b/roles/matrix-bridge-mx-puppet-twitter/tasks/init.yml new file mode 100644 index 00000000..06cf83fa --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-twitter/tasks/init.yml @@ -0,0 +1,70 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mx_puppet_twitter_container_image_self_build and matrix_mx_puppet_twitter_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mx-puppet-twitter.service'] }}" + when: matrix_mx_puppet_twitter_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_mx_puppet_twitter_config_path }}/registration.yaml,dst=/matrix-mx-puppet-twitter-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-mx-puppet-twitter-registration.yaml"] }} + when: matrix_mx_puppet_twitter_enabled|bool + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append Twitter Appservice's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-mx-puppet-twitter role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate Matrix MX Puppet Twitter proxying configuration for matrix-nginx-proxy + set_fact: + matrix_mx_puppet_twitter_matrix_nginx_proxy_configuration: | + location {{ matrix_mx_puppet_twitter_webhook_path }} { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_mx_puppet_twitter_appservice_address }}"; + proxy_pass $backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_mx_puppet_twitter_appservice_port }}; + {% endif %} + } + + - name: Register Twitter Appservice proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks|default([]) + + + [matrix_mx_puppet_twitter_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_mx_puppet_twitter_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the Matrix Twitter bridge but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ matrix_mx_puppet_twitter_redirect_path }}` + URL endpoint to the matrix-mx-puppet-twitter container. + You can expose the container's port using the `matrix_mx_puppet_twitter_container_http_host_bind_port` variable. + when: "matrix_mx_puppet_twitter_enabled|bool and matrix_nginx_proxy_enabled is not defined" diff --git a/roles/matrix-bridge-mx-puppet-twitter/tasks/main.yml b/roles/matrix-bridge-mx-puppet-twitter/tasks/main.yml new file mode 100644 index 00000000..af355df3 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-twitter/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_mx_puppet_twitter_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-twitter + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_mx_puppet_twitter_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-twitter + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_mx_puppet_twitter_enabled|bool" + tags: + - setup-all + - setup-mx-puppet-twitter diff --git a/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_install.yml b/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_install.yml new file mode 100644 index 00000000..5767ed17 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_install.yml @@ -0,0 +1,128 @@ +--- + +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-bridge-mx-puppet-twitter role needs to execute before the matrix-synapse role. + when: "matrix_synapse_role_executed|default(False)" + +- name: Ensure MX Puppet Twitter paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mx_puppet_twitter_base_path }}", when: true } + - { path: "{{ matrix_mx_puppet_twitter_config_path }}", when: true } + - { path: "{{ matrix_mx_puppet_twitter_data_path }}", when: true } + - { path: "{{ matrix_mx_puppet_twitter_docker_src_files_path }}", when: "{{ matrix_mx_puppet_twitter_container_image_self_build }}" } + when: matrix_mx_puppet_twitter_enabled|bool and item.when|bool + +- name: Check if an old database file already exists + stat: + path: "{{ matrix_mx_puppet_twitter_base_path }}/database.db" + register: matrix_mx_puppet_twitter_stat_database + +- name: (Data relocation) Ensure matrix-mx-puppet-twitter.service is stopped + service: + name: matrix-mx-puppet-twitter + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_mx_puppet_twitter_stat_database.stat.exists" + +- name: (Data relocation) Move mx-puppet-twitter database file to ./data directory + command: "mv {{ matrix_mx_puppet_twitter_base_path }}/database.db {{ matrix_mx_puppet_twitter_data_path }}/database.db" + when: "matrix_mx_puppet_twitter_stat_database.stat.exists" + +- set_fact: + matrix_mx_puppet_twitter_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_mx_puppet_twitter_sqlite_database_path_local }}" + register: matrix_mx_puppet_twitter_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_mx_puppet_twitter_sqlite_database_path_local }}" + dst: "{{ matrix_mx_puppet_twitter_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_mx_puppet_twitter_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-mx-puppet-twitter.service'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_mx_puppet_twitter_requires_restart: true + when: "matrix_mx_puppet_twitter_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_mx_puppet_twitter_database_engine == 'postgres'" + +- name: Ensure MX Puppet Twitter image is pulled + docker_image: + name: "{{ matrix_mx_puppet_twitter_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mx_puppet_twitter_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mx_puppet_twitter_docker_image_force_pull }}" + when: matrix_mx_puppet_twitter_enabled|bool and not matrix_mx_puppet_twitter_container_image_self_build + +- name: Ensure MX Puppet Twitter repository is present on self build + git: + repo: "{{ matrix_mx_puppet_twitter_container_image_self_build_repo }}" + dest: "{{ matrix_mx_puppet_twitter_docker_src_files_path }}" + force: "yes" + register: matrix_mx_puppet_twitter_git_pull_results + when: "matrix_mx_puppet_twitter_enabled|bool and matrix_mx_puppet_twitter_container_image_self_build" + +- name: Ensure MX Puppet Twitter Docker image is built + docker_image: + name: "{{ matrix_mx_puppet_twitter_docker_image }}" + source: build + force_source: "{{ matrix_mx_puppet_twitter_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_mx_puppet_twitter_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mx_puppet_twitter_docker_src_files_path }}" + pull: yes + when: "matrix_mx_puppet_twitter_enabled|bool and matrix_mx_puppet_twitter_container_image_self_build" + +- name: Ensure mx-puppet-twitter config.yaml installed + copy: + content: "{{ matrix_mx_puppet_twitter_configuration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_twitter_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure mx-puppet-twitter twitter-registration.yaml installed + copy: + content: "{{ matrix_mx_puppet_twitter_registration|to_nice_yaml }}" + dest: "{{ matrix_mx_puppet_twitter_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-mx-puppet-twitter.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mx-puppet-twitter.service.j2" + dest: "/etc/systemd/system/matrix-mx-puppet-twitter.service" + mode: 0644 + register: matrix_mx_puppet_twitter_systemd_service_result + +- name: Ensure systemd reloaded after matrix-mx-puppet-twitter.service installation + service: + daemon_reload: yes + when: "matrix_mx_puppet_twitter_systemd_service_result.changed" + +- name: Ensure matrix-mx-puppet-twitter.service restarted, if necessary + service: + name: "matrix-mx-puppet-twitter.service" + state: restarted + when: "matrix_mx_puppet_twitter_requires_restart|bool" diff --git a/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_uninstall.yml b/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_uninstall.yml new file mode 100644 index 00000000..1d663531 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-twitter/tasks/setup_uninstall.yml @@ -0,0 +1,24 @@ +--- + +- name: Check existence of matrix-mx-puppet-twitter service + stat: + path: "/etc/systemd/system/matrix-mx-puppet-twitter.service" + register: matrix_mx_puppet_twitter_service_stat + +- name: Ensure matrix-mx-puppet-twitter is stopped + service: + name: matrix-mx-puppet-twitter + state: stopped + daemon_reload: yes + when: "matrix_mx_puppet_twitter_service_stat.stat.exists" + +- name: Ensure matrix-mx-puppet-twitter.service doesn't exist + file: + path: "/etc/systemd/system/matrix-mx-puppet-twitter.service" + state: absent + when: "matrix_mx_puppet_twitter_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mx-puppet-twitter.service removal + service: + daemon_reload: yes + when: "matrix_mx_puppet_twitter_service_stat.stat.exists" diff --git a/roles/matrix-bridge-mx-puppet-twitter/tasks/validate_config.yml b/roles/matrix-bridge-mx-puppet-twitter/tasks/validate_config.yml new file mode 100644 index 00000000..d13a39e1 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-twitter/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_mx_puppet_twitter_appservice_token" + - "matrix_mx_puppet_twitter_homeserver_token" diff --git a/roles/matrix-bridge-mx-puppet-twitter/templates/config.yaml.j2 b/roles/matrix-bridge-mx-puppet-twitter/templates/config.yaml.j2 new file mode 100644 index 00000000..1d269057 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-twitter/templates/config.yaml.j2 @@ -0,0 +1,79 @@ +#jinja2: lstrip_blocks: "True" +bridge: + # Port to host the bridge on + # Used for communication between the homeserver and the bridge + port: {{ matrix_mx_puppet_twitter_appservice_port }} + # The host connections to the bridge's webserver are allowed from + bindAddress: 0.0.0.0 + # Public domain of the homeserver + domain: {{ matrix_mx_puppet_twitter_homeserver_domain }} + # Reachable URL of the Matrix homeserver + homeserverUrl: {{ matrix_mx_puppet_twitter_homeserver_address }} + {% if matrix_mx_puppet_twitter_login_shared_secret != '' %} + loginSharedSecretMap: + {{ matrix_domain }}: {{ matrix_mx_puppet_twitter_login_shared_secret }} + {% endif %} + +twitter: + consumerKey: "{{ matrix_mx_puppet_twitter_consumer_key }}" + consumerSecret: "{{ matrix_mx_puppet_twitter_consumer_secret }}" + accessToken: "{{ matrix_mx_puppet_twitter_access_token }}" + accessTokenSecret: "{{ matrix_mx_puppet_twitter_access_token_secret }}" + environment: "{{ matrix_mx_puppet_twitter_environment }}" + server: + url: "{{ matrix_mx_puppet_twitter_webhook_url }}" + path: "{{ matrix_mx_puppet_twitter_webhook_path }}" + +presence: + # Bridge Twitter online/offline status + enabled: true + # How often to send status to the homeserver in milliseconds + interval: 500 + +provisioning: + # Regex of Matrix IDs allowed to use the puppet bridge + whitelist: {{ matrix_mx_puppet_twitter_provisioning_whitelist|to_json }} + # Allow a specific user + #- "@user:server\\.com" + # Allow users on a specific homeserver + #- "@.*:yourserver\\.com" + # Allow anyone + #- ".*" + # Regex of Matrix IDs forbidden from using the puppet bridge + #blacklist: + # Disallow a specific user + #- "@user:server\\.com" + # Disallow users on a specific homeserver + #- "@.*:yourserver\\.com" + blacklist: {{ matrix_mx_puppet_twitter_provisioning_blacklist|to_json }} + + # Shared secret for the provisioning API for use by integration managers. + # If this is not set, the provisioning API will not be enabled. + #sharedSecret: random string + # Path prefix for the provisioning API. /v1 will be appended to the prefix automatically. + apiPrefix: /_matrix/provision + +database: +{% if matrix_mx_puppet_twitter_database_engine == 'postgres' %} + # Use Postgres as a database backend + # If set, will be used instead of SQLite3 + # Connection string to connect to the Postgres instance + # with username "user", password "pass", host "localhost" and database name "dbname". + # Modify each value as necessary + connString: {{ matrix_mx_puppet_twitter_database_connection_string|to_json }} +{% else %} + # Use SQLite3 as a database backend + # The name of the database file + filename: {{ matrix_mx_puppet_twitter_sqlite_database_path_in_container|to_json }} +{% endif %} + +logging: + # Log level of console output + # Allowed values starting with most verbose: + # silly, debug, verbose, info, warn, error + console: info + # Date and time formatting + lineDateFormat: MMM-D HH:mm:ss.SSS + # Logging files + # Log files are rotated daily by default + files: [] diff --git a/roles/matrix-bridge-mx-puppet-twitter/templates/systemd/matrix-mx-puppet-twitter.service.j2 b/roles/matrix-bridge-mx-puppet-twitter/templates/systemd/matrix-mx-puppet-twitter.service.j2 new file mode 100644 index 00000000..7e1b1c32 --- /dev/null +++ b/roles/matrix-bridge-mx-puppet-twitter/templates/systemd/matrix-mx-puppet-twitter.service.j2 @@ -0,0 +1,46 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Mx Puppet Twitter bridge +{% for service in matrix_mx_puppet_twitter_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_mx_puppet_twitter_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-twitter 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-twitter 2>/dev/null' + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mx-puppet-twitter \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_mx_puppet_twitter_container_http_host_bind_port %} + -p {{ matrix_mx_puppet_twitter_container_http_host_bind_port }}:{{ matrix_mx_puppet_twitter_appservice_port }} \ + {% endif %} + -e CONFIG_PATH=/config/config.yaml \ + -e REGISTRATION_PATH=/config/registration.yaml \ + -v {{ matrix_mx_puppet_twitter_config_path }}:/config:z \ + -v {{ matrix_mx_puppet_twitter_data_path }}:/data:z \ + {% for arg in matrix_mx_puppet_twitter_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mx_puppet_twitter_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mx-puppet-twitter 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mx-puppet-twitter 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mx-puppet-twitter + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-bridge-sms/defaults/main.yml b/roles/matrix-bridge-sms/defaults/main.yml new file mode 100644 index 00000000..55f99101 --- /dev/null +++ b/roles/matrix-bridge-sms/defaults/main.yml @@ -0,0 +1,125 @@ +# matrix-sms-bridge is a Matrix <-> SMS bridge +# See: https://github.com/benkuly/matrix-sms-bridge + +matrix_sms_bridge_enabled: true + +matrix_sms_bridge_version: 0.5.7 +matrix_sms_bridge_docker_image: "{{ matrix_container_global_registry_prefix }}folivonet/matrix-sms-bridge:{{ matrix_sms_bridge_version }}" + +matrix_sms_bridge_base_path: "{{ matrix_base_data_path }}/matrix-sms-bridge" +matrix_sms_bridge_config_path: "{{ matrix_base_data_path }}/matrix-sms-bridge/config" +matrix_sms_bridge_data_path: "{{ matrix_base_data_path }}/matrix-sms-bridge/data" + +matrix_sms_bridge_appservice_token: '' +matrix_sms_bridge_homeserver_token: '' + +matrix_sms_bridge_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_sms_bridge_container_extra_arguments: [] + +# List of systemd services that service depends on. +matrix_sms_bridge_systemd_required_services_list: ['docker.service'] + +# List of systemd services that service wants +matrix_sms_bridge_systemd_wanted_services_list: [] + +matrix_sms_bridge_appservice_url: 'http://matrix-sms-bridge:8080' +matrix_sms_bridge_homeserver_hostname: 'matrix-synapse' +matrix_sms_bridge_homeserver_port: '8008' + +matrix_sms_bridge_homserver_domain: "{{ matrix_domain }}" +matrix_sms_bridge_default_room: '' +matrix_sms_bridge_default_region: '' +matrix_sms_bridge_default_timezone: '' +matrix_sms_bridge_single_mode_enabled: false + +matrix_sms_bridge_provider_android_baseurl: '' +matrix_sms_bridge_provider_android_username: '' +matrix_sms_bridge_provider_android_password: '' +matrix_sms_bridge_provider_android_truststore_local_path: '' +matrix_sms_bridge_provider_android_truststore_password: '' + + +matrix_sms_bridge_configuration_yaml: | + #jinja2: lstrip_blocks: "True" + + # Database connection + matrix: + bridge: + sms: + # (optional) SMS messages without a valid token a routed to this room. + # Note that you must invite @smsbot:yourHomeServer to this room. + defaultRoomId: "{{ matrix_sms_bridge_default_room }}" + defaultRegion: "{{ matrix_sms_bridge_default_region }}" + defaultTimeZone: "{{ matrix_sms_bridge_default_timezone }}" + singleModeEnabled: "{{ matrix_sms_bridge_single_mode_enabled }}" + provider: + android: + # (optional) default is disabled + enabled: true + # The url to the android-sms-gateway-server + baseUrl: {{ matrix_sms_bridge_provider_android_baseurl }} + # The username of the gateway + username: {{ matrix_sms_bridge_provider_android_username }} + # The password of the gateway + password: {{ matrix_sms_bridge_provider_android_password }} + # (optional) if you use a self signed certificate, you can add the public key here + {% if matrix_sms_bridge_provider_android_truststore_local_path %} + trustStore: + path: /config/matrix-sms-gateway-server.p12 + password: {{ matrix_sms_bridge_provider_android_truststore_password }} + type: PKCS12 + {% endif %} + bot: + # The domain-part of matrix-ids. E. g. example.org when your userIds look like @unicorn:example.org + serverName: {{ matrix_sms_bridge_homserver_domain }} + migration: + url: "jdbc:h2:file:/data/database/db" + username: sa + database: + url: "r2dbc:h2:file:////data/database/db" + username: sa + client: + homeServer: + # The hostname of your Homeserver. + hostname: {{ matrix_sms_bridge_homeserver_hostname }} + # (optional) The port of your Homeserver. Default is 443. + port: {{ matrix_sms_bridge_homeserver_port }} + # (optional) Use http or https. Default is true (so uses https). + secure: false + # The token to authenticate against the Homeserver. + token: {{ matrix_sms_bridge_appservice_token }} + appservice: + # A unique token for Homeservers to use to authenticate requests to this application service. + hsToken: {{ matrix_sms_bridge_homeserver_token }} + +matrix_sms_bridge_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_sms_bridge_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_sms_bridge_configuration_yaml`. + +matrix_sms_bridge_configuration_extension: "{{ matrix_sms_bridge_configuration_extension_yaml|from_yaml if matrix_sms_bridge_configuration_extension_yaml|from_yaml is mapping else {} }}" + +matrix_sms_bridge_configuration: "{{ matrix_sms_bridge_configuration_yaml|from_yaml|combine(matrix_sms_bridge_configuration_extension, recursive=True) }}" + +matrix_sms_bridge_registration_yaml: | + id: sms + as_token: "{{ matrix_sms_bridge_appservice_token }}" + hs_token: "{{ matrix_sms_bridge_homeserver_token }}" + namespaces: + users: + - exclusive: true + regex: '^@sms_.+:{{ matrix_sms_bridge_homserver_domain|regex_escape }}$' + aliases: + - exclusive: true + regex: '^#sms_.+:{{ matrix_sms_bridge_homserver_domain|regex_escape }}$' + url: {{ matrix_sms_bridge_appservice_url }} + sender_localpart: smsbot + rate_limited: false + +matrix_sms_bridge_registration: "{{ matrix_sms_bridge_registration_yaml|from_yaml }}" diff --git a/roles/matrix-bridge-sms/tasks/init.yml b/roles/matrix-bridge-sms/tasks/init.yml new file mode 100644 index 00000000..5979d132 --- /dev/null +++ b/roles/matrix-bridge-sms/tasks/init.yml @@ -0,0 +1,24 @@ +# If the matrix-synapse role is not used, `matrix_synapse_role_executed` won't exist. +# We don't want to fail in such cases. +- name: Fail if matrix-synapse role already executed + fail: + msg: >- + The matrix-sms-bridge role needs to execute before the matrix-synapse role. + when: "matrix_sms_bridge_enabled and matrix_synapse_role_executed|default(False)" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-sms-bridge.service'] }}" + when: matrix_sms_bridge_enabled|bool + +# If the matrix-synapse role is not used, these variables may not exist. +- set_fact: + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_sms_bridge_config_path }}/registration.yaml,dst=/matrix-sms-bridge-registration.yaml,ro"] + + matrix_synapse_app_service_config_files: > + {{ matrix_synapse_app_service_config_files|default([]) }} + + + {{ ["/matrix-sms-bridge-registration.yaml"] }} + when: matrix_sms_bridge_enabled|bool diff --git a/roles/matrix-bridge-sms/tasks/main.yml b/roles/matrix-bridge-sms/tasks/main.yml new file mode 100644 index 00000000..c1c499de --- /dev/null +++ b/roles/matrix-bridge-sms/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_sms_bridge_enabled|bool" + tags: + - setup-all + - setup-matrix-sms-bridge + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_sms_bridge_enabled|bool" + tags: + - setup-all + - setup-matrix-sms-bridge + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_sms_bridge_enabled|bool" + tags: + - setup-all + - setup-matrix-sms-bridge diff --git a/roles/matrix-bridge-sms/tasks/setup_install.yml b/roles/matrix-bridge-sms/tasks/setup_install.yml new file mode 100644 index 00000000..61de923f --- /dev/null +++ b/roles/matrix-bridge-sms/tasks/setup_install.yml @@ -0,0 +1,55 @@ +--- + +- name: Ensure matrix-sms-bridge image is pulled + docker_image: + name: "{{ matrix_sms_bridge_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + +- name: Ensure matrix-sms-bridge paths exist + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_sms_bridge_base_path }}" + - "{{ matrix_sms_bridge_config_path }}" + - "{{ matrix_sms_bridge_data_path }}" + +- name: Ensure matrix-sms-bridge application.yml installed + copy: + content: "{{ matrix_sms_bridge_configuration|to_nice_yaml }}" + dest: "{{ matrix_sms_bridge_config_path }}/application.yml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-sms-bridge registration.yaml installed + copy: + content: "{{ matrix_sms_bridge_registration|to_nice_yaml }}" + dest: "{{ matrix_sms_bridge_config_path }}/registration.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure android-sms-gateway-server cert installed + copy: + src: "{{ matrix_sms_bridge_provider_android_truststore_local_path }}" + dest: "{{ matrix_sms_bridge_config_path }}/matrix-sms-gateway-server.p12" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_sms_bridge_provider_android_truststore_local_path != "" + +- name: Ensure matrix-sms-bridge.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-sms-bridge.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-sms-bridge.service" + mode: 0644 + register: matrix_sms_bridge_systemd_service_result + +- name: Ensure systemd reloaded after matrix-sms-bridge.service installation + service: + daemon_reload: yes + when: matrix_sms_bridge_systemd_service_result.changed \ No newline at end of file diff --git a/roles/matrix-bridge-sms/tasks/setup_uninstall.yml b/roles/matrix-bridge-sms/tasks/setup_uninstall.yml new file mode 100644 index 00000000..03ddaad0 --- /dev/null +++ b/roles/matrix-bridge-sms/tasks/setup_uninstall.yml @@ -0,0 +1,19 @@ +--- + +- name: Check existence of matrix-sms-bridge service + stat: + path: "{{ matrix_systemd_path }}/matrix-sms-bridge.service" + register: matrix_sms_bridge_service_stat + +- name: Ensure matrix-sms-bridge is stopped + service: + name: matrix-sms-bridge + state: stopped + daemon_reload: yes + when: "matrix_sms_bridge_service_stat.stat.exists" + +- name: Ensure matrix-sms-bridge.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-sms-bridge.service" + state: absent + when: "matrix_sms_bridge_service_stat.stat.exists" \ No newline at end of file diff --git a/roles/matrix-bridge-sms/tasks/validate_config.yml b/roles/matrix-bridge-sms/tasks/validate_config.yml new file mode 100644 index 00000000..6dc6ce9c --- /dev/null +++ b/roles/matrix-bridge-sms/tasks/validate_config.yml @@ -0,0 +1,15 @@ +--- + +- name: Fail if required settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_sms_bridge_appservice_token" + - "matrix_sms_bridge_homeserver_token" + - "matrix_sms_bridge_default_region" + - "matrix_sms_bridge_default_timezone" + - "matrix_sms_bridge_provider_android_baseurl" + - "matrix_sms_bridge_provider_android_username" + - "matrix_sms_bridge_provider_android_password" \ No newline at end of file diff --git a/roles/matrix-bridge-sms/templates/systemd/matrix-sms-bridge.service.j2 b/roles/matrix-bridge-sms/templates/systemd/matrix-sms-bridge.service.j2 new file mode 100644 index 00000000..46c3463f --- /dev/null +++ b/roles/matrix-bridge-sms/templates/systemd/matrix-sms-bridge.service.j2 @@ -0,0 +1,45 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix sms bridge +{% for service in matrix_sms_bridge_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_sms_bridge_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-/usr/bin/docker kill matrix-sms-bridge +ExecStartPre=-/usr/bin/docker rm matrix-sms-bridge + +# Intentional delay, so that the homeserver (we likely depend on) can manage to start. +ExecStartPre=/bin/sleep 5 + +ExecStart=/usr/bin/docker run --rm --name matrix-sms-bridge \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_sms_bridge_container_http_host_bind_port %} + -p {{ matrix_sms_bridge_container_http_host_bind_port }}:8080 \ + {% endif %} + -v {{ matrix_sms_bridge_config_path }}:/config:z \ + -v {{ matrix_sms_bridge_data_path }}:/data:z \ + --env SPRING_CONFIG_ADDITIONAL_LOCATION=/config/application.yml \ + {% for arg in matrix_sms_bridge_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_sms_bridge_docker_image }} + +ExecStop=-/usr/bin/docker kill matrix-sms-bridge +ExecStop=-/usr/bin/docker rm matrix-sms-bridge +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-sms-bridge + + [Install] +WantedBy=multi-user.target diff --git a/roles/matrix-client-element/defaults/main.yml b/roles/matrix-client-element/defaults/main.yml new file mode 100644 index 00000000..dd649ffd --- /dev/null +++ b/roles/matrix-client-element/defaults/main.yml @@ -0,0 +1,129 @@ +matrix_client_element_enabled: true + +matrix_client_element_container_image_self_build: false +matrix_client_element_container_image_self_build_repo: "https://github.com/vector-im/riot-web.git" + +matrix_client_element_version: v1.7.34 +matrix_client_element_docker_image: "{{ matrix_client_element_docker_image_name_prefix }}vectorim/element-web:{{ matrix_client_element_version }}" +matrix_client_element_docker_image_name_prefix: "{{ 'localhost/' if matrix_client_element_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_client_element_docker_image_force_pull: "{{ matrix_client_element_docker_image.endswith(':latest') }}" + +matrix_client_element_data_path: "{{ matrix_base_data_path }}/client-element" +matrix_client_element_docker_src_files_path: "{{ matrix_client_element_data_path }}/docker-src" + +# Controls whether the matrix-client-element container exposes its HTTP port (tcp/8080 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8765"), or empty string to not expose. +matrix_client_element_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_client_element_container_extra_arguments: [] + +# List of systemd services that matrix-client-element.service depends on +matrix_client_element_systemd_required_services_list: ['docker.service'] + +# Element config.json customizations +matrix_client_element_default_server_name: "{{ matrix_domain }}" +matrix_client_element_default_hs_url: "" +matrix_client_element_default_is_url: ~ +matrix_client_element_disable_custom_urls: true +matrix_client_element_disable_guests: true +matrix_client_element_integrations_ui_url: "https://scalar.vector.im/" +matrix_client_element_integrations_rest_url: "https://scalar.vector.im/api" +matrix_client_element_integrations_widgets_urls: ["https://scalar.vector.im/api"] +matrix_client_element_integrations_jitsi_widget_url: "https://scalar.vector.im/api/widgets/jitsi.html" +matrix_client_element_permalinkPrefix: "https://matrix.to" +matrix_client_element_bug_report_endpoint_url: "https://element.io/bugreports/submit" +matrix_client_element_showLabsSettings: true +# Element public room directory server(s) +matrix_client_element_roomdir_servers: ['matrix.org'] +matrix_client_element_welcome_user_id: ~ +# Branding of Element +matrix_client_element_brand: "Element" + +# URL to Logo on welcome page +matrix_client_element_welcome_logo: "welcome/images/logo.svg" + +# URL of link on welcome image +matrix_client_element_welcome_logo_link: "https://element.io" + +matrix_client_element_welcome_headline: "_t('Welcome to Element')" +matrix_client_element_welcome_text: "_t('Decentralised, encrypted chat & collaboration powered by [matrix]')" + +# Links, shown in footer of welcome page: +# [{"text": "Link text", "url": "https://link.target"}, {"text": "Other link"}] +matrix_client_element_branding_authFooterLinks: ~ + +# URL to image, shown during Login +matrix_client_element_branding_authHeaderLogoUrl: "{{ matrix_client_element_welcome_logo }}" + +# URL to Wallpaper, shown in background of welcome page +matrix_client_element_branding_welcomeBackgroundUrl: ~ + +matrix_client_element_page_template_welcome_path: "{{ role_path }}/templates/welcome.html.j2" + +# By default, there's no Element homepage (when logged in). If you wish to have one, +# point this to a `home.html` template file on your local filesystem. +matrix_client_element_embedded_pages_home_path: ~ + +matrix_client_element_jitsi_preferredDomain: '' + +# Controls whether the self-check feature should validate SSL certificates. +matrix_client_element_self_check_validate_certificates: true + +# don't show the registration button on welcome page +matrix_client_element_registration_enabled: false + +# Controls whether presence will be enabled +matrix_client_element_enable_presence_by_hs_url: ~ + +# Controls whether custom Element themes will be installed. +# When enabled, all themes found in the `matrix_client_element_themes_repository_url` repository +# will be installed and enabled automatically. +matrix_client_element_themes_enabled: false +matrix_client_element_themes_repository_url: https://github.com/aaronraimist/element-themes + +# Controls the default theme +matrix_client_element_default_theme: 'light' + +# Controls the `settingsDefault.custom_themes` setting of the Element configuration. +# You can use this setting to define custom themes. +# +# Also, look at `matrix_client_element_themes_enabled` for a way to pull in a bunch of custom themes automatically. +# If you define your own themes here and set `matrix_client_element_themes_enabled: true`, your themes will be preserved as well. +# +# Note that for a custom theme to work well, all Element instances that you use must have the same theme installed. +matrix_client_element_settingDefaults_custom_themes: [] + +# Default Element 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_client_element_configuration_extension_json`) +# or completely replace this variable with your own template. +# +# The side-effect of this lookup is that Ansible would even parse the JSON for us, returning a dict. +# This is unlike what it does when looking up YAML template files (no automatic parsing there). +matrix_client_element_configuration_default: "{{ lookup('template', 'templates/config.json.j2') }}" + +# Your custom JSON configuration for Element should go to `matrix_client_element_configuration_extension_json`. +# This configuration extends the default starting configuration (`matrix_client_element_configuration_default`). +# +# 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_client_element_configuration_default`. +# +# Example configuration extension follows: +# +# matrix_client_element_configuration_extension_json: | +# { +# "disable_3pid_login": true, +# "disable_login_language_selector": true +# } +matrix_client_element_configuration_extension_json: '{}' + +matrix_client_element_configuration_extension: "{{ matrix_client_element_configuration_extension_json|from_json if matrix_client_element_configuration_extension_json|from_json is mapping else {} }}" + +# Holds the final Element configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_client_element_configuration_default`. +matrix_client_element_configuration: "{{ matrix_client_element_configuration_default|combine(matrix_client_element_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-client-element/tasks/init.yml b/roles/matrix-client-element/tasks/init.yml new file mode 100644 index 00000000..44fa1544 --- /dev/null +++ b/roles/matrix-client-element/tasks/init.yml @@ -0,0 +1,10 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-client-element.service'] }}" + when: matrix_client_element_enabled|bool + +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_client_element_container_image_self_build and matrix_client_element_enabled" diff --git a/roles/matrix-client-element/tasks/main.yml b/roles/matrix-client-element/tasks/main.yml new file mode 100644 index 00000000..cdc5fa97 --- /dev/null +++ b/roles/matrix-client-element/tasks/main.yml @@ -0,0 +1,34 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_client_element_enabled|bool" + tags: + - setup-all + - setup-client-element + +- import_tasks: "{{ role_path }}/tasks/prepare_themes.yml" + when: run_setup|bool + tags: + - setup-all + - setup-client-element + +- import_tasks: "{{ role_path }}/tasks/migrate_riot_web.yml" + when: run_setup|bool + tags: + - setup-all + - setup-client-element + +- import_tasks: "{{ role_path }}/tasks/setup.yml" + when: run_setup|bool + tags: + - setup-all + - setup-client-element + +- import_tasks: "{{ role_path }}/tasks/self_check.yml" + delegate_to: 127.0.0.1 + become: false + when: "run_self_check|bool and matrix_client_element_enabled|bool" + tags: + - self-check diff --git a/roles/matrix-client-element/tasks/migrate_riot_web.yml b/roles/matrix-client-element/tasks/migrate_riot_web.yml new file mode 100644 index 00000000..dd0eb0f8 --- /dev/null +++ b/roles/matrix-client-element/tasks/migrate_riot_web.yml @@ -0,0 +1,36 @@ +--- + +- name: Check existence of matrix-riot-web.service + stat: + path: "{{ matrix_systemd_path }}/matrix-riot-web.service" + register: matrix_client_riot_web_service_stat + when: "matrix_client_element_enabled|bool" + +- name: Ensure matrix-riot-web is stopped + service: + name: matrix-riot-web + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_client_element_enabled|bool and matrix_client_riot_web_service_stat.stat.exists" + +- name: Ensure matrix-riot-web.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-riot-web.service" + state: absent + when: "matrix_client_element_enabled|bool and matrix_client_riot_web_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-riot-web.service removal + service: + daemon_reload: yes + when: "matrix_client_element_enabled|bool and matrix_client_riot_web_service_stat.stat.exists" + +- name: Check existence of /matrix/riot-web + stat: + path: "/matrix/riot-web" + register: matrix_client_riot_web_dir_stat + when: "matrix_client_element_enabled|bool" + +- name: Relocate /matrix/riot-web to /matrix/client-element + command: "mv /matrix/riot-web /matrix/client-element" + when: "matrix_client_element_enabled|bool and matrix_client_riot_web_dir_stat.stat.exists" diff --git a/roles/matrix-client-element/tasks/prepare_themes.yml b/roles/matrix-client-element/tasks/prepare_themes.yml new file mode 100644 index 00000000..1453e37d --- /dev/null +++ b/roles/matrix-client-element/tasks/prepare_themes.yml @@ -0,0 +1,48 @@ +--- + +# +# Tasks related to setting up Element themes +# + +- block: + - name: Ensure Element themes repository is pulled + git: + repo: "{{ matrix_client_element_themes_repository_url }}" + dest: "{{ role_path }}/files/scratchpad/themes" + + - name: Find all Element theme files + find: + paths: "{{ role_path }}/files/scratchpad/themes" + patterns: "*.json" + recurse: true + register: matrix_client_element_theme_file_list + + - name: Read Element theme + slurp: + path: "{{ item.path }}" + register: "matrix_client_element_theme_file_contents" + with_items: "{{ matrix_client_element_theme_file_list.files }}" + + - name: Load Element theme + set_fact: + matrix_client_element_settingDefaults_custom_themes: "{{ matrix_client_element_settingDefaults_custom_themes + [item['content'] | b64decode | from_json] }}" + with_items: "{{ matrix_client_element_theme_file_contents.results }}" + + run_once: true + delegate_to: 127.0.0.1 + become: false + when: matrix_client_element_themes_enabled|bool + + +# +# Tasks related to getting rid of Element themes (if it was previously enabled) +# + +- name: Ensure Element themes repository is removed + file: + path: "{{ role_path }}/files/scratchpad/themes" + state: absent + run_once: true + delegate_to: 127.0.0.1 + become: false + when: "not matrix_client_element_themes_enabled|bool" diff --git a/roles/matrix-client-element/tasks/self_check.yml b/roles/matrix-client-element/tasks/self_check.yml new file mode 100644 index 00000000..34b6b88b --- /dev/null +++ b/roles/matrix-client-element/tasks/self_check.yml @@ -0,0 +1,22 @@ +--- + +- set_fact: + matrix_client_element_url_endpoint_public: "https://{{ matrix_server_fqn_element }}/config.json" + +- name: Check Element + uri: + url: "{{ matrix_client_element_url_endpoint_public }}" + follow_redirects: none + validate_certs: "{{ matrix_client_element_self_check_validate_certificates }}" + register: matrix_client_element_self_check_result + check_mode: no + ignore_errors: true + +- name: Fail if Element not working + fail: + msg: "Failed checking Element is up at `{{ matrix_server_fqn_element }}` (checked endpoint: `{{ matrix_client_element_url_endpoint_public }}`). Is Element running? Is port 443 open in your firewall? Full error: {{ matrix_client_element_self_check_result }}" + when: "matrix_client_element_self_check_result.failed or 'json' not in matrix_client_element_self_check_result" + +- name: Report working Element + debug: + msg: "Element at `{{ matrix_server_fqn_element }}` is working (checked endpoint: `{{ matrix_client_element_url_endpoint_public }}`)" diff --git a/roles/matrix-client-element/tasks/setup.yml b/roles/matrix-client-element/tasks/setup.yml new file mode 100644 index 00000000..117a09f4 --- /dev/null +++ b/roles/matrix-client-element/tasks/setup.yml @@ -0,0 +1,130 @@ +--- + +# +# Tasks related to setting up Element +# + +- name: Ensure Element paths exists + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_client_element_data_path }}", when: true } + - { path: "{{ matrix_client_element_docker_src_files_path }}", when: "{{ matrix_client_element_container_image_self_build }}" } + when: matrix_client_element_enabled|bool and item.when + +- name: Ensure Element Docker image is pulled + docker_image: + name: "{{ matrix_client_element_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_client_element_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_client_element_docker_image_force_pull }}" + when: matrix_client_element_enabled|bool and not matrix_client_element_container_image_self_build + +- name: Ensure Element repository is present on self-build + git: + repo: "{{ matrix_client_element_container_image_self_build_repo }}" + dest: "{{ matrix_client_element_docker_src_files_path }}" + version: "{{ matrix_client_element_docker_image.split(':')[1] }}" + force: "yes" + register: matrix_client_element_git_pull_results + when: "matrix_client_element_enabled|bool and matrix_client_element_container_image_self_build|bool" + +- name: Ensure Element Docker image is built + docker_image: + name: "{{ matrix_client_element_docker_image }}" + source: build + force_source: "{{ matrix_client_element_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_client_element_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_client_element_docker_src_files_path }}" + pull: yes + when: "matrix_client_element_enabled|bool and matrix_client_element_container_image_self_build|bool" + +- name: Ensure Element configuration installed + copy: + content: "{{ matrix_client_element_configuration|to_nice_json }}" + dest: "{{ matrix_client_element_data_path }}/config.json" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_client_element_enabled|bool + +- name: Ensure Element config files installed + template: + src: "{{ item.src }}" + dest: "{{ matrix_client_element_data_path }}/{{ item.name }}" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - {src: "{{ role_path }}/templates/nginx.conf.j2", name: "nginx.conf"} + - {src: "{{ matrix_client_element_page_template_welcome_path }}", name: "welcome.html"} + - {src: "{{ matrix_client_element_embedded_pages_home_path }}", name: "home.html"} + when: "matrix_client_element_enabled|bool and item.src is not none" + +- name: Ensure Element config files removed + file: + path: "{{ matrix_client_element_data_path }}/{{ item.name }}" + state: absent + with_items: + - {src: "{{ matrix_client_element_embedded_pages_home_path }}", name: "home.html"} + when: "matrix_client_element_enabled|bool and item.src is none" + +- name: Ensure matrix-client-element.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-client-element.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-client-element.service" + mode: 0644 + register: matrix_client_element_systemd_service_result + when: matrix_client_element_enabled|bool + +- name: Ensure systemd reloaded after matrix-client-element.service installation + service: + daemon_reload: yes + when: "matrix_client_element_enabled and matrix_client_element_systemd_service_result.changed" + +# +# Tasks related to getting rid of Element (if it was previously enabled) +# + +- name: Check existence of matrix-client-element.service + stat: + path: "{{ matrix_systemd_path }}/matrix-client-element.service" + register: matrix_client_element_service_stat + when: "not matrix_client_element_enabled|bool" + +- name: Ensure matrix-client-element is stopped + service: + name: matrix-client-element + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_client_element_enabled|bool and matrix_client_element_service_stat.stat.exists" + +- name: Ensure matrix-client-element.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-client-element.service" + state: absent + when: "not matrix_client_element_enabled|bool and matrix_client_element_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-client-element.service removal + service: + daemon_reload: yes + when: "not matrix_client_element_enabled|bool and matrix_client_element_service_stat.stat.exists" + +- name: Ensure Element paths doesn't exist + file: + path: "{{ matrix_client_element_data_path }}" + state: absent + when: "not matrix_client_element_enabled|bool" + +- name: Ensure Element Docker image doesn't exist + docker_image: + name: "{{ matrix_client_element_docker_image }}" + state: absent + when: "not matrix_client_element_enabled|bool" diff --git a/roles/matrix-client-element/tasks/validate_config.yml b/roles/matrix-client-element/tasks/validate_config.yml new file mode 100644 index 00000000..d246b612 --- /dev/null +++ b/roles/matrix-client-element/tasks/validate_config.yml @@ -0,0 +1,20 @@ +--- + +- name: Fail if required Element settings not defined + fail: + msg: > + You need to define a required configuration setting (`{{ item }}`) for using Element. + when: "vars[item] == ''" + with_items: + - "matrix_client_element_default_hs_url" + +- name: (Deprecation) Catch and report riot-web variables + fail: + msg: >- + Riot has been renamed to Element (https://element.io/blog/welcome-to-element/). + The playbook will migrate your existing configuration and data automatically, but you need to adjust variable names. + Please change your configuration (vars.yml) to rename all riot-web variables (`{{ item.old }}` -> `{{ item.new }}`). + Also note that DNS configuration changes may be necessary. + when: "vars | dict2items | selectattr('key', 'match', item.old) | list | items2dict" + with_items: + - {'old': 'matrix_riot_web_.*', 'new': 'matrix_client_element_.*'} diff --git a/roles/matrix-client-element/templates/config.json.j2 b/roles/matrix-client-element/templates/config.json.j2 new file mode 100644 index 00000000..e87907e4 --- /dev/null +++ b/roles/matrix-client-element/templates/config.json.j2 @@ -0,0 +1,45 @@ +{ + "default_server_config": { + "m.homeserver": { + "base_url": {{ matrix_client_element_default_hs_url|string|to_json }}, + "server_name": {{ matrix_client_element_default_server_name|string|to_json }} + }, + "m.identity_server": { + "base_url": {{ matrix_client_element_default_is_url|string|to_json }} + } + }, + "settingDefaults": { + "custom_themes": {{ matrix_client_element_settingDefaults_custom_themes|to_json }} + }, + "default_theme": {{ matrix_client_element_default_theme|string|to_json }}, + "permalinkPrefix": {{ matrix_client_element_permalinkPrefix|string|to_json }}, + "disable_custom_urls": {{ matrix_client_element_disable_custom_urls|to_json }}, + "disable_guests": {{ matrix_client_element_disable_guests|to_json }}, + "brand": {{ matrix_client_element_brand|to_json }}, + "integrations_ui_url": {{ matrix_client_element_integrations_ui_url|string|to_json }}, + "integrations_rest_url": {{ matrix_client_element_integrations_rest_url|string|to_json }}, + "integrations_widgets_urls": {{ matrix_client_element_integrations_widgets_urls|to_json }}, + "integrations_jitsi_widget_url": {{ matrix_client_element_integrations_jitsi_widget_url|string|to_json }}, + "bug_report_endpoint_url": {{ matrix_client_element_bug_report_endpoint_url|to_json }}, + "showLabsSettings": {{ matrix_client_element_showLabsSettings|to_json }}, + "roomDirectory": { + "servers": {{ matrix_client_element_roomdir_servers|to_json }} + }, + "welcomeUserId": {{ matrix_client_element_welcome_user_id|to_json }}, + {% if matrix_client_element_enable_presence_by_hs_url is not none %} + "enable_presence_by_hs_url": {{ matrix_client_element_enable_presence_by_hs_url|to_json }}, + {% endif %} + "embeddedPages": { + "homeUrl": {{ matrix_client_element_embedded_pages_home_url|string|to_json }} + }, + {% if matrix_client_element_jitsi_preferredDomain %} + "jitsi": { + "preferredDomain": {{ matrix_client_element_jitsi_preferredDomain|to_json }} + }, + {% endif %} + "branding": { + "authFooterLinks": {{ matrix_client_element_branding_authFooterLinks|to_json }}, + "authHeaderLogoUrl": {{ matrix_client_element_branding_authHeaderLogoUrl|to_json }}, + "welcomeBackgroundUrl": {{ matrix_client_element_branding_welcomeBackgroundUrl|to_json }} + } +} diff --git a/roles/matrix-client-element/templates/nginx.conf.j2 b/roles/matrix-client-element/templates/nginx.conf.j2 new file mode 100644 index 00000000..fba16bbd --- /dev/null +++ b/roles/matrix-client-element/templates/nginx.conf.j2 @@ -0,0 +1,66 @@ +#jinja2: lstrip_blocks: "True" +# This is a custom nginx configuration file that we use in the container (instead of the default one), +# because it allows us to run nginx with a non-root user. +# +# For this to work, the default vhost file (`/etc/nginx/conf.d/default.conf`) also needs to be removed. +# (mounting `/dev/null` over `/etc/nginx/conf.d/default.conf` works well) +# +# The following changes have been done compared to a default nginx configuration file: +# - default server port is changed (80 -> 8080), so that a non-root user can bind it +# - various temp paths are changed to `/tmp`, so that a non-root user can write to them +# - the `user` directive was removed, as we don't want nginx to switch users + +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /tmp/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + proxy_temp_path /tmp/proxy_temp; + client_body_temp_path /tmp/client_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + server { + listen 8080; + server_name localhost; + + root /usr/share/nginx/html; + + location / { + index index.html index.htm; + } + + location ~* ^/(config(.+)?\.json$|(.+)\.html$|i18n) { + expires -1; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } +} diff --git a/roles/matrix-client-element/templates/systemd/matrix-client-element.service.j2 b/roles/matrix-client-element/templates/systemd/matrix-client-element.service.j2 new file mode 100644 index 00000000..fe2a3a86 --- /dev/null +++ b/roles/matrix-client-element/templates/systemd/matrix-client-element.service.j2 @@ -0,0 +1,45 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Element server +{% for service in matrix_client_element_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-element 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-element 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-client-element \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + {% if matrix_client_element_container_http_host_bind_port %} + -p {{ matrix_client_element_container_http_host_bind_port }}:8080 \ + {% endif %} + --tmpfs=/tmp:rw,noexec,nosuid,size=10m \ + --mount type=bind,src={{ matrix_client_element_data_path }}/nginx.conf,dst=/etc/nginx/nginx.conf,ro \ + --mount type=bind,src={{ matrix_client_element_data_path }}/config.json,dst=/app/config.json,ro \ + --mount type=bind,src={{ matrix_client_element_data_path }}/config.json,dst=/app/config.{{ matrix_server_fqn_element }}.json,ro \ + {% if matrix_client_element_embedded_pages_home_path is not none %} + --mount type=bind,src={{ matrix_client_element_data_path }}/home.html,dst=/app/home.html,ro \ + {% endif %} + --mount type=bind,src={{ matrix_client_element_data_path }}/welcome.html,dst=/app/welcome.html,ro \ + {% for arg in matrix_client_element_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_client_element_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-element 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-element 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-client-element + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-client-element/templates/welcome.html.j2 b/roles/matrix-client-element/templates/welcome.html.j2 new file mode 100644 index 00000000..b45a9766 --- /dev/null +++ b/roles/matrix-client-element/templates/welcome.html.j2 @@ -0,0 +1,205 @@ +#jinja2: lstrip_blocks: "True" + + +
+ + + +

{{ matrix_client_element_welcome_headline }}

+

{{ matrix_client_element_welcome_text }}

+
+
+ +
_t("Sign In")
+
+{% if matrix_client_element_registration_enabled %} + +
_t("Create Account")
+
+{% endif %} +
+{% if matrix_client_element_disable_guests != true %} + + + + +{% endif %} +
+
diff --git a/roles/matrix-client-element/vars/main.yml b/roles/matrix-client-element/vars/main.yml new file mode 100644 index 00000000..8237b511 --- /dev/null +++ b/roles/matrix-client-element/vars/main.yml @@ -0,0 +1,3 @@ +--- + +matrix_client_element_embedded_pages_home_url: "{{ ('' if matrix_client_element_embedded_pages_home_path is none else 'home.html') }}" diff --git a/roles/matrix-client-hydrogen/defaults/main.yml b/roles/matrix-client-hydrogen/defaults/main.yml new file mode 100644 index 00000000..3cc1df2a --- /dev/null +++ b/roles/matrix-client-hydrogen/defaults/main.yml @@ -0,0 +1,68 @@ +matrix_client_hydrogen_enabled: true + +# Self building is used by default because the `config.json` file is only read at build time. +# The pre-built images also were not functional as of 2021-05-15. +matrix_client_hydrogen_container_image_self_build: true +matrix_client_hydrogen_container_image_self_build_repo: "https://github.com/vector-im/hydrogen-web.git" + +matrix_client_hydrogen_version: v0.2.5 +matrix_client_hydrogen_docker_image: "{{ matrix_client_hydrogen_docker_image_name_prefix }}vectorim/hydrogen-web:{{ matrix_client_hydrogen_version }}" +matrix_client_hydrogen_docker_image_name_prefix: "{{ 'localhost/' if matrix_client_hydrogen_container_image_self_build }}" +matrix_client_hydrogen_docker_image_force_pull: "{{ matrix_client_hydrogen_docker_image.endswith(':latest') }}" + +matrix_client_hydrogen_data_path: "{{ matrix_base_data_path }}/client-hydrogen" +matrix_client_hydrogen_docker_src_files_path: "{{ matrix_client_hydrogen_data_path }}/docker-src" + +# Controls whether the container exposes its HTTP port (tcp/8080 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8768"), or empty string to not expose. +matrix_client_hydrogen_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_client_hydrogen_container_extra_arguments: [] + +# List of systemd services that matrix-client-hydrogen.service depends on +matrix_client_hydrogen_systemd_required_services_list: ['docker.service'] + +# Controls whether the self-check feature should validate SSL certificates. +matrix_client_hydrogen_self_check_validate_certificates: true + +# config.json +matrix_client_hydrogen_default_hs_url: "" + +# Default Hydrogen 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_client_hydrogen_configuration_extension_json`) +# or completely replace this variable with your own template. +# +# The side-effect of this lookup is that Ansible would even parse the JSON for us, returning a dict. +# This is unlike what it does when looking up YAML template files (no automatic parsing there). +matrix_client_hydrogen_configuration_default: "{{ lookup('template', 'templates/config.json.j2') }}" + +# Your custom JSON configuration for Hydrogen should go to `matrix_client_hydrogen_configuration_extension_json`. +# This configuration extends the default starting configuration (`matrix_client_hydrogen_configuration_default`). +# +# 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_client_hydrogen_configuration_default`. +# +# Example configuration extension follows: +# +# matrix_client_hydrogen_configuration_extension_json: | +# { +# "push": { +# "appId": "io.element.hydrogen.web", +# "gatewayUrl": "https://matrix.org", +# "applicationServerKey": "BC-gpSdVHEXhvHSHS0AzzWrQoukv2BE7KzpoPO_FfPacqOo3l1pdqz7rSgmB04pZCWaHPz7XRe6fjLaC-WPDopM" +# }, +# "defaultHomeServer": "matrix.org" +# } +matrix_client_hydrogen_configuration_extension_json: '{}' + +matrix_client_hydrogen_configuration_extension: "{{ matrix_client_hydrogen_configuration_extension_json|from_json if matrix_client_hydrogen_configuration_extension_json|from_json is mapping else {} }}" + +# Holds the final Hydrogen configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_client_hydrogen_configuration_default`. +matrix_client_hydrogen_configuration: "{{ matrix_client_hydrogen_configuration_default|combine(matrix_client_hydrogen_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-client-hydrogen/tasks/init.yml b/roles/matrix-client-hydrogen/tasks/init.yml new file mode 100644 index 00000000..8116a003 --- /dev/null +++ b/roles/matrix-client-hydrogen/tasks/init.yml @@ -0,0 +1,10 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Hydrogen image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_client_hydrogen_container_image_self_build and matrix_client_hydrogen_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-client-hydrogen.service'] }}" + when: matrix_client_hydrogen_enabled|bool diff --git a/roles/matrix-client-hydrogen/tasks/main.yml b/roles/matrix-client-hydrogen/tasks/main.yml new file mode 100644 index 00000000..6534db05 --- /dev/null +++ b/roles/matrix-client-hydrogen/tasks/main.yml @@ -0,0 +1,15 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_client_hydrogen_enabled|bool" + tags: + - setup-all + - setup-client-hydrogen + +- import_tasks: "{{ role_path }}/tasks/setup.yml" + when: run_setup|bool + tags: + - setup-all + - setup-client-hydrogen diff --git a/roles/matrix-client-hydrogen/tasks/self_check.yml b/roles/matrix-client-hydrogen/tasks/self_check.yml new file mode 100644 index 00000000..c7407dcd --- /dev/null +++ b/roles/matrix-client-hydrogen/tasks/self_check.yml @@ -0,0 +1,22 @@ +--- + +- set_fact: + matrix_client_hydrogen_url_endpoint_public: "https://{{ matrix_server_fqn_hydrogen }}" + +- name: Check Hydrogen + uri: + url: "{{ matrix_client_hydrogen_url_endpoint_public }}" + follow_redirects: none + validate_certs: "{{ matrix_client_hydrogen_self_check_validate_certificates }}" + register: matrix_client_hydrogen_self_check_result + check_mode: no + ignore_errors: true + +- name: Fail if Hydrogen not working + fail: + msg: "Failed checking Hydrogen is up at `{{ matrix_server_fqn_hydrogen }}` (checked endpoint: `{{ matrix_client_hydrogen_url_endpoint_public }}`). Is Hydrogen running? Is port 443 open in your firewall? Full error: {{ matrix_client_hydrogen_self_check_result }}" + when: "matrix_client_hydrogen_self_check_result.failed or 'json' not in matrix_client_hydrogen_self_check_result" + +- name: Report working Hydrogen + debug: + msg: "Hydrogen at `{{ matrix_server_fqn_hydrogen }}` is working (checked endpoint: `{{ matrix_client_hydrogen_url_endpoint_public }}`)" diff --git a/roles/matrix-client-hydrogen/tasks/setup.yml b/roles/matrix-client-hydrogen/tasks/setup.yml new file mode 100644 index 00000000..205fa3ce --- /dev/null +++ b/roles/matrix-client-hydrogen/tasks/setup.yml @@ -0,0 +1,119 @@ +--- + +# +# Tasks related to setting up Hydrogen +# + +- name: Ensure Hydrogen paths exists + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_client_hydrogen_data_path }}", when: true } + - { path: "{{ matrix_client_hydrogen_docker_src_files_path }}", when: "{{ matrix_client_hydrogen_container_image_self_build }}" } + when: matrix_client_hydrogen_enabled|bool and item.when + +- name: Ensure Hydrogen Docker image is pulled + docker_image: + name: "{{ matrix_client_hydrogen_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_client_hydrogen_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_client_hydrogen_docker_image_force_pull }}" + when: matrix_client_hydrogen_enabled|bool and not matrix_client_hydrogen_container_image_self_build + +- name: Ensure Hydrogen repository is present on self-build + git: + repo: "{{ matrix_client_hydrogen_container_image_self_build_repo }}" + dest: "{{ matrix_client_hydrogen_docker_src_files_path }}" + version: "{{ matrix_client_hydrogen_docker_image.split(':')[1] }}" + force: "yes" + register: matrix_client_hydrogen_git_pull_results + when: "matrix_client_hydrogen_enabled|bool and matrix_client_hydrogen_container_image_self_build|bool" + +- name: Ensure Hydrogen configuration installed + copy: + content: "{{ matrix_client_hydrogen_configuration|to_nice_json }}" + dest: "{{ matrix_client_hydrogen_docker_src_files_path }}/assets/config.json" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: "matrix_client_hydrogen_enabled|bool and matrix_client_hydrogen_container_image_self_build|bool" + +- name: Ensure Hydrogen additional config files installed + template: + src: "{{ item.src }}" + dest: "{{ matrix_client_hydrogen_data_path }}/{{ item.name }}" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - {src: "{{ role_path }}/templates/nginx.conf.j2", name: "nginx.conf"} + when: "matrix_client_hydrogen_enabled|bool and item.src is not none" + +- name: Ensure Hydrogen Docker image is built + docker_image: + name: "{{ matrix_client_hydrogen_docker_image }}" + source: build + force_source: "{{ matrix_client_hydrogen_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_client_hydrogen_docker_src_files_path }}" + pull: yes + when: "matrix_client_hydrogen_enabled|bool and matrix_client_hydrogen_container_image_self_build|bool" + +- name: Ensure matrix-client-hydrogen.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-client-hydrogen.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-client-hydrogen.service" + mode: 0644 + register: matrix_client_hydrogen_systemd_service_result + when: matrix_client_hydrogen_enabled|bool + +- name: Ensure systemd reloaded after matrix-client-hydrogen.service installation + service: + daemon_reload: yes + when: "matrix_client_hydrogen_enabled and matrix_client_hydrogen_systemd_service_result.changed" + +# +# Tasks related to getting rid of Hydrogen (if it was previously enabled) +# + +- name: Check existence of matrix-client-hydrogen.service + stat: + path: "{{ matrix_systemd_path }}/matrix-client-hydrogen.service" + register: matrix_client_hydrogen_service_stat + when: "not matrix_client_hydrogen_enabled|bool" + +- name: Ensure matrix-client-hydrogen is stopped + service: + name: matrix-client-hydrogen + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_client_hydrogen_enabled|bool and matrix_client_hydrogen_service_stat.stat.exists" + +- name: Ensure matrix-client-hydrogen.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-client-hydrogen.service" + state: absent + when: "not matrix_client_hydrogen_enabled|bool and matrix_client_hydrogen_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-client-hydrogen.service removal + service: + daemon_reload: yes + when: "not matrix_client_hydrogen_enabled|bool and matrix_client_hydrogen_service_stat.stat.exists" + +- name: Ensure Hydrogen paths doesn't exist + file: + path: "{{ matrix_client_hydrogen_data_path }}" + state: absent + when: "not matrix_client_hydrogen_enabled|bool" + +- name: Ensure Hydrogen Docker image doesn't exist + docker_image: + name: "{{ matrix_client_hydrogen_docker_image }}" + state: absent + when: "not matrix_client_hydrogen_enabled|bool" diff --git a/roles/matrix-client-hydrogen/tasks/validate_config.yml b/roles/matrix-client-hydrogen/tasks/validate_config.yml new file mode 100644 index 00000000..d3b9a709 --- /dev/null +++ b/roles/matrix-client-hydrogen/tasks/validate_config.yml @@ -0,0 +1,9 @@ +--- + +- name: Fail if required Hydrogen settings not defined + fail: + msg: > + You need to define a required configuration setting (`{{ item }}`) to use Hydrogen. + when: "(vars[item] == '' or vars[item] is none) and matrix_client_hydrogen_container_image_self_build|bool" + with_items: + - "matrix_client_hydrogen_default_hs_url" diff --git a/roles/matrix-client-hydrogen/templates/config.json.j2 b/roles/matrix-client-hydrogen/templates/config.json.j2 new file mode 100644 index 00000000..62a849b0 --- /dev/null +++ b/roles/matrix-client-hydrogen/templates/config.json.j2 @@ -0,0 +1,3 @@ +{ + "defaultHomeServer": {{ matrix_client_hydrogen_default_hs_url|string|to_json }} +} diff --git a/roles/matrix-client-hydrogen/templates/nginx.conf.j2 b/roles/matrix-client-hydrogen/templates/nginx.conf.j2 new file mode 100644 index 00000000..fba16bbd --- /dev/null +++ b/roles/matrix-client-hydrogen/templates/nginx.conf.j2 @@ -0,0 +1,66 @@ +#jinja2: lstrip_blocks: "True" +# This is a custom nginx configuration file that we use in the container (instead of the default one), +# because it allows us to run nginx with a non-root user. +# +# For this to work, the default vhost file (`/etc/nginx/conf.d/default.conf`) also needs to be removed. +# (mounting `/dev/null` over `/etc/nginx/conf.d/default.conf` works well) +# +# The following changes have been done compared to a default nginx configuration file: +# - default server port is changed (80 -> 8080), so that a non-root user can bind it +# - various temp paths are changed to `/tmp`, so that a non-root user can write to them +# - the `user` directive was removed, as we don't want nginx to switch users + +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /tmp/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + proxy_temp_path /tmp/proxy_temp; + client_body_temp_path /tmp/client_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + server { + listen 8080; + server_name localhost; + + root /usr/share/nginx/html; + + location / { + index index.html index.htm; + } + + location ~* ^/(config(.+)?\.json$|(.+)\.html$|i18n) { + expires -1; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } +} diff --git a/roles/matrix-client-hydrogen/templates/systemd/matrix-client-hydrogen.service.j2 b/roles/matrix-client-hydrogen/templates/systemd/matrix-client-hydrogen.service.j2 new file mode 100644 index 00000000..c85aeb97 --- /dev/null +++ b/roles/matrix-client-hydrogen/templates/systemd/matrix-client-hydrogen.service.j2 @@ -0,0 +1,39 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Hydrogen Client +{% for service in matrix_client_hydrogen_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-hydrogen 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-hydrogen 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-client-hydrogen \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + {% if matrix_client_hydrogen_container_http_host_bind_port %} + -p {{ matrix_client_hydrogen_container_http_host_bind_port }}:8080 \ + {% endif %} + --tmpfs=/tmp:rw,noexec,nosuid,size=10m \ + --mount type=bind,src={{ matrix_client_hydrogen_data_path }}/nginx.conf,dst=/etc/nginx/nginx.conf,ro \ + {% for arg in matrix_client_hydrogen_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_client_hydrogen_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-client-hydrogen 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-client-hydrogen 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-client-hydrogen + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-common-after/defaults/main.yml b/roles/matrix-common-after/defaults/main.yml new file mode 100644 index 00000000..8112191a --- /dev/null +++ b/roles/matrix-common-after/defaults/main.yml @@ -0,0 +1,16 @@ +# Specifies how long to wait between starting systemd services and checking if they're started. +# +# A too low value may lead to a failure, as services may not have enough time to start and potentially fail. +# +# A value higher than 30 seconds (or any multiple of that) may also not work well, because a failing systemd service +# auto-restarts after 30 seconds (`RestartSec=30` in systemd service files). +# Checking if a service is running right after it had potentially restarted in such a way will lead us to +# thinking it's running, while it's merely starting again (and likely to fail again, given that it already did once). +# +# All of the services we manage are also started sequentially, which in itself can take a long time. +# There may be a ~10 second (or even larger) interval between starting the first service and starting the last one. +# This makes it even harder to pick a correct value. Such a 10 second gap and a waiting time of 20 seconds will +# put us right at the "dangerous" 30-second mark. +# +# We can try to measure this gap and adjust our waiting time accordingly, but we currently don't. +matrix_common_after_systemd_service_start_wait_for_timeout_seconds: 15 diff --git a/roles/matrix-common-after/tasks/awx_post.yml b/roles/matrix-common-after/tasks/awx_post.yml new file mode 100644 index 00000000..b934104b --- /dev/null +++ b/roles/matrix-common-after/tasks/awx_post.yml @@ -0,0 +1,64 @@ +--- + +- name: Create user account @janitor + command: | + /usr/local/bin/matrix-synapse-register-user janitor {{ matrix_awx_janitor_user_password | quote }} 1 + register: cmd + when: not matrix_awx_janitor_user_created|bool + no_log: True + +- name: Update AWX janitor user created variable + delegate_to: 127.0.0.1 + lineinfile: + path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: 'AWX Settings' + with_dict: + 'matrix_awx_janitor_user_created': 'true' + when: not matrix_awx_janitor_user_created|bool + +- name: Create user account @dimension + command: | + /usr/local/bin/matrix-synapse-register-user dimension {{ matrix_awx_dimension_user_password | quote }} 0 + register: cmd + when: not matrix_awx_dimension_user_created|bool + no_log: True + +- name: Update AWX dimension user created variable + delegate_to: 127.0.0.1 + lineinfile: + path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: 'AWX Settings' + with_dict: + 'matrix_awx_dimension_user_created': 'true' + when: not matrix_awx_dimension_user_created|bool + +- name: Create user account @mjolnir + command: | + /usr/local/bin/matrix-synapse-register-user mjolnir {{ matrix_awx_mjolnir_user_password | quote }} 0 + register: cmd + when: not matrix_awx_mjolnir_user_created|bool + no_log: True + +- name: Update AWX dimension user created variable + delegate_to: 127.0.0.1 + lineinfile: + path: '/var/lib/awx/projects/clients/{{ member_id }}/{{ subscription_id }}/matrix_vars.yml' + regexp: "^#? *{{ item.key | regex_escape() }}:" + line: "{{ item.key }}: {{ item.value }}" + insertafter: 'AWX Settings' + with_dict: + 'matrix_awx_mjolnir_user_created': 'true' + when: not matrix_awx_mjolnir_user_created|bool + +- name: Ensure /chroot/website location has correct permissions + file: + path: /chroot/website + state: directory + owner: matrix + group: matrix + mode: '0770' + when: customise_base_domain_website is defined diff --git a/roles/matrix-common-after/tasks/dump_runtime_results.yml b/roles/matrix-common-after/tasks/dump_runtime_results.yml new file mode 100644 index 00000000..9788bf84 --- /dev/null +++ b/roles/matrix-common-after/tasks/dump_runtime_results.yml @@ -0,0 +1,6 @@ +# Ansible outputs the message in the `item=` field. +# It's unnecessary to output it again in the actual message, so we don't. +- debug: + msg: "" + with_items: "{{ matrix_playbook_runtime_results }}" + when: "matrix_playbook_runtime_results is defined and matrix_playbook_runtime_results|length > 0" diff --git a/roles/matrix-common-after/tasks/main.yml b/roles/matrix-common-after/tasks/main.yml new file mode 100644 index 00000000..b4503ae1 --- /dev/null +++ b/roles/matrix-common-after/tasks/main.yml @@ -0,0 +1,25 @@ + +- import_tasks: "{{ role_path }}/tasks/start.yml" + when: run_start|bool + tags: + - start + +- import_tasks: "{{ role_path }}/tasks/stop.yml" + when: run_stop|bool + tags: + - stop + +- import_tasks: "{{ role_path }}/tasks/dump_runtime_results.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/awx_post.yml" + when: run_setup|bool and matrix_awx_enabled|bool + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/run_docker_prune.yml" + tags: + - run-docker-prune + + diff --git a/roles/matrix-common-after/tasks/run_docker_prune.yml b/roles/matrix-common-after/tasks/run_docker_prune.yml new file mode 100644 index 00000000..a7e35a03 --- /dev/null +++ b/roles/matrix-common-after/tasks/run_docker_prune.yml @@ -0,0 +1,4 @@ +--- + +- name: Run Docker System Prune + command: "{{ matrix_host_command_docker }} system prune -a -f" diff --git a/roles/matrix-common-after/tasks/start.yml b/roles/matrix-common-after/tasks/start.yml new file mode 100644 index 00000000..64ab4d99 --- /dev/null +++ b/roles/matrix-common-after/tasks/start.yml @@ -0,0 +1,73 @@ +--- + +- name: Determine whether we should make services autostart + set_fact: + matrix_services_autostart_enabled_bool: "{{ true if matrix_services_autostart_enabled|default('') == '' else matrix_services_autostart_enabled|bool }}" + +- name: Ensure systemd is reloaded + service: + daemon_reload: yes + +- name: Ensure Matrix services are stopped + service: + name: "{{ item }}" + state: stopped + with_items: "{{ matrix_systemd_services_list }}" + when: not ansible_check_mode + +- name: Ensure Matrix services are started + service: + name: "{{ item }}" + enabled: "{{ matrix_services_autostart_enabled_bool }}" + state: started + with_items: "{{ matrix_systemd_services_list }}" + when: not ansible_check_mode + +# If we check service state immediately, we may succeed, +# because it takes some time for the service to attempt to start and actually fail. +# +# Waiting too long (30s) may not work for a similar reason, +# as we may run into systemd's automatic restart logic retrying the service. +- name: Wait a bit, so that services can start (or fail) + wait_for: + timeout: "{{ matrix_common_after_systemd_service_start_wait_for_timeout_seconds }}" + delegate_to: 127.0.0.1 + become: false + +- block: + - name: Populate service facts + service_facts: + + - name: Fail if service isn't detected to be running + fail: + msg: >- + {{ item }} was not detected to be running. + It's possible that there's a configuration problem or another service on your server interferes with it (uses the same ports, etc.). + Try running `systemctl status {{ item }}` and `journalctl -fu {{ item }}` on the server to investigate. + If you're on a slow or overloaded server, it may be that services take a longer time to start and that this error is a false-positive. + You can consider raising the value of the `matrix_common_after_systemd_service_start_wait_for_timeout_seconds` variable. + See `roles/matrix-common-after/defaults/main.yml` for more details about that. + with_items: "{{ matrix_systemd_services_list }}" + when: + - "item.endswith('.service') and (ansible_facts.services[item]|default(none) is none or ansible_facts.services[item].state != 'running')" + when: " ansible_distribution != 'Archlinux'" + +- block: + # Currently there is a bug in ansible that renders is incompatible with systemd. + # service_facts is not collecting the data successfully. + # Therefore iterating here manually + - name: Fetch systemd information + systemd: + name: "{{ item }}" + register: systemdstatus + with_items: "{{ matrix_systemd_services_list }}" + + - name: Fail if service isn't detected to be running + fail: + msg: >- + {{ item.item }} was not detected to be running. + It's possible that there's a configuration problem or another service on your server interferes with it (uses the same ports, etc.). + Try running `systemctl status {{ item.item }}` and `journalctl -fu {{ item.item }}` on the server to investigate. + with_items: "{{ systemdstatus.results }}" + when: "item.status['ActiveState'] != 'active'" + when: "ansible_distribution == 'Archlinux'" diff --git a/roles/matrix-common-after/tasks/stop.yml b/roles/matrix-common-after/tasks/stop.yml new file mode 100644 index 00000000..5ae0afbb --- /dev/null +++ b/roles/matrix-common-after/tasks/stop.yml @@ -0,0 +1,7 @@ +--- + +- name: Ensure Matrix services stopped + service: + name: "{{ item }}" + state: stopped + with_items: "{{ matrix_systemd_services_list }}" diff --git a/roles/matrix-corporal/defaults/main.yml b/roles/matrix-corporal/defaults/main.yml new file mode 100644 index 00000000..881bee67 --- /dev/null +++ b/roles/matrix-corporal/defaults/main.yml @@ -0,0 +1,106 @@ +# matrix-corporal is a reconciliator and gateway for a managed Matrix server. +# See: https://github.com/devture/matrix-corporal + +matrix_corporal_enabled: true + +matrix_corporal_container_image_self_build: false +matrix_corporal_container_image_self_build_repo: "https://github.com/devture/matrix-corporal.git" + +# Controls whether the matrix-corporal container exposes its gateway HTTP port (tcp/41080 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:41080"), or empty string to not expose. +matrix_corporal_container_http_gateway_host_bind_port: '' + +# Controls whether the matrix-corporal container exposes its API HTTP port (tcp/41081 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:41081"), or empty string to not expose. +matrix_corporal_container_http_api_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_corporal_container_extra_arguments: [] + +# List of systemd services that matrix-corporal.service depends on +matrix_corporal_systemd_required_services_list: ['docker.service'] + +matrix_corporal_version: 2.1.1 +matrix_corporal_docker_image: "{{ matrix_corporal_docker_image_name_prefix }}devture/matrix-corporal:{{ matrix_corporal_docker_image_tag }}" +matrix_corporal_docker_image_name_prefix: "{{ 'localhost/' if matrix_corporal_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_corporal_docker_image_tag: "{{ matrix_corporal_version }}" # for backward-compatibility +matrix_corporal_docker_image_force_pull: "{{ matrix_corporal_docker_image.endswith(':latest') }}" + +matrix_corporal_base_path: "{{ matrix_base_data_path }}/corporal" +matrix_corporal_container_src_files_path: "{{ matrix_corporal_base_path }}/container-src" +matrix_corporal_config_dir_path: "{{ matrix_corporal_base_path }}/config" +matrix_corporal_cache_dir_path: "{{ matrix_corporal_base_path }}/cache" +matrix_corporal_var_dir_path: "{{ matrix_corporal_base_path }}/var" + +matrix_corporal_matrix_homeserver_domain_name: "{{ matrix_domain }}" + +# Controls where matrix-corporal can reach your Synapse server (e.g. "http://matrix-synapse:8008"). +# If Synapse runs on the same machine, you may need to add its service to `matrix_corporal_systemd_required_services_list`. +matrix_corporal_matrix_homeserver_api_endpoint: "" + +# The shared secret between matrix-corporal and Synapse's shared-secret-auth password provider module. +# To use matrix-corporal, the shared-secret-auth password provider needs to be enabled and the secret needs to be identical. +matrix_corporal_matrix_auth_shared_secret: "" + +# The shared secret for registering users with Synapse. +# Needs to be identical to Synapse's `registration_shared_secret` setting. +matrix_corporal_matrix_registration_shared_secret: "" + +matrix_corporal_matrix_timeout_milliseconds: 45000 + +matrix_corporal_reconciliation_retry_interval_milliseconds: 30000 +matrix_corporal_corporal_user_id_local_part: "matrix-corporal" + +matrix_corporal_http_gateway_timeout_milliseconds: 60000 + +# If enabled, matrix-corporal exposes a `POST /_matrix/corporal/_matrix-internal/identity/v1/check_credentials` API +# on the gateway (Client-Server API) server. +# This API can then be used together with the REST Auth password provider by pointing it to matrix-corporal (e.g. `http://matrix-corporal:41080/_matrix/corporal`). +# Doing so allows Interactive Authentication to work. +matrix_corporal_http_gateway_internal_rest_auth_enabled: false + +matrix_corporal_http_api_enabled: false +matrix_corporal_http_api_auth_token: "" +matrix_corporal_http_api_timeout_milliseconds: 15000 + +# Matrix Corporal policy provider configuration (goes directly into the configuration's `PolicyProvider` value) +matrix_corporal_policy_provider_config: "" + +matrix_corporal_debug: false + + +# Default Corporal 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_corporal_configuration_extension_json`) +# or completely replace this variable with your own template. +# +# The side-effect of this lookup is that Ansible would even parse the JSON for us, returning a dict. +# This is unlike what it does when looking up YAML template files (no automatic parsing there). +matrix_corporal_configuration_default: "{{ lookup('template', 'templates/config.json.j2') }}" + +# Your custom JSON configuration for Corporal should go to `matrix_corporal_configuration_extension_json`. +# This configuration extends the default starting configuration (`matrix_corporal_configuration_default`). +# +# 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_corporal_configuration_default`. +# +# Example configuration extension follows: +# +# matrix_corporal_configuration_extension_json: | +# { +# "Matrix": { +# "TimeoutMilliseconds": 60000 +# } +# } +matrix_corporal_configuration_extension_json: '{}' + +matrix_corporal_configuration_extension: "{{ matrix_corporal_configuration_extension_json|from_json if matrix_corporal_configuration_extension_json|from_json is mapping else {} }}" + +# Holds the final Corporal configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_corporal_configuration_default`. +matrix_corporal_configuration: "{{ matrix_corporal_configuration_default|combine(matrix_corporal_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-corporal/tasks/init.yml b/roles/matrix-corporal/tasks/init.yml new file mode 100644 index 00000000..e5062c27 --- /dev/null +++ b/roles/matrix-corporal/tasks/init.yml @@ -0,0 +1,10 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_corporal_container_image_self_build and matrix_corporal_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-corporal.service'] }}" + when: matrix_corporal_enabled|bool diff --git a/roles/matrix-corporal/tasks/main.yml b/roles/matrix-corporal/tasks/main.yml new file mode 100644 index 00000000..90c8105c --- /dev/null +++ b/roles/matrix-corporal/tasks/main.yml @@ -0,0 +1,22 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_corporal_enabled|bool" + tags: + - setup-all + - setup-corporal + +- import_tasks: "{{ role_path }}/tasks/setup_corporal.yml" + when: run_setup|bool + tags: + - setup-all + - setup-corporal + +- import_tasks: "{{ role_path }}/tasks/self_check_corporal.yml" + delegate_to: 127.0.0.1 + become: false + when: "run_self_check|bool and matrix_corporal_enabled|bool" + tags: + - self-check diff --git a/roles/matrix-corporal/tasks/self_check_corporal.yml b/roles/matrix-corporal/tasks/self_check_corporal.yml new file mode 100644 index 00000000..f7c15109 --- /dev/null +++ b/roles/matrix-corporal/tasks/self_check_corporal.yml @@ -0,0 +1,22 @@ +--- + +- set_fact: + corporal_client_api_url_endpoint_public: "https://{{ matrix_server_fqn_matrix }}/_matrix/client/corporal" + +- name: Check Matrix Corporal HTTP gateway + uri: + url: "{{ corporal_client_api_url_endpoint_public }}" + follow_redirects: none + return_content: true + check_mode: no + register: result_corporal_client_api + ignore_errors: true + +- name: Fail if Matrix Corporal HTTP gateway not working + fail: + msg: "Failed checking Matrix Corporal is fronting the Matrix Client API at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ corporal_client_api_url_endpoint_public }}`). Is matrix-corporal running? Is port 443 open in your firewall? Full error: {{ result_corporal_client_api }}" + when: "result_corporal_client_api.failed or 'Matrix Client-Server API protected by Matrix Corporal' not in result_corporal_client_api.content" + +- name: Report working Matrix Corporal HTTP gateway + debug: + msg: "Matrix Corporal is fronting the Matrix Client API at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ corporal_client_api_url_endpoint_public }}`)" diff --git a/roles/matrix-corporal/tasks/setup_corporal.yml b/roles/matrix-corporal/tasks/setup_corporal.yml new file mode 100644 index 00000000..e668de27 --- /dev/null +++ b/roles/matrix-corporal/tasks/setup_corporal.yml @@ -0,0 +1,114 @@ +--- + +# +# Tasks related to setting up matrix-corporal +# + +- name: Ensure Matrix Corporal paths exist + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_corporal_config_dir_path }}" + - "{{ matrix_corporal_cache_dir_path }}" + - "{{ matrix_corporal_var_dir_path }}" + when: matrix_corporal_enabled|bool + +- name: Ensure Matrix Corporal repository is present on self-build + git: + repo: "{{ matrix_corporal_container_image_self_build_repo }}" + dest: "{{ matrix_corporal_container_src_files_path }}" + version: "{{ matrix_corporal_docker_image.split(':')[1] }}" + force: "yes" + register: matrix_corporal_git_pull_results + when: "matrix_corporal_enabled|bool and matrix_corporal_container_image_self_build|bool" + +- name: Ensure Matrix Corporal Docker image is built + docker_image: + name: "{{ matrix_corporal_docker_image }}" + source: build + force_source: "{{ matrix_corporal_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_corporal_git_pull_results.changed }}" + build: + dockerfile: etc/docker/Dockerfile + path: "{{ matrix_corporal_container_src_files_path }}" + pull: yes + when: "matrix_corporal_enabled|bool and matrix_corporal_container_image_self_build|bool" + +- name: Ensure Matrix Corporal Docker image is pulled + docker_image: + name: "{{ matrix_corporal_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_corporal_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_corporal_docker_image_force_pull }}" + when: "matrix_corporal_enabled|bool and not matrix_corporal_container_image_self_build|bool" + +- name: Ensure Matrix Corporal config installed + copy: + content: "{{ matrix_corporal_configuration|to_nice_json }}" + dest: "{{ matrix_corporal_config_dir_path }}/config.json" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_corporal_enabled|bool + +- name: Ensure matrix-corporal.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-corporal.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-corporal.service" + mode: 0644 + register: matrix_corporal_systemd_service_result + when: matrix_corporal_enabled|bool + +- name: Ensure systemd reloaded after matrix-corporal.service installation + service: + daemon_reload: yes + when: "matrix_corporal_enabled|bool and matrix_corporal_systemd_service_result.changed" + + +# +# Tasks related to getting rid of matrix-corporal (if it was previously enabled) +# + +- name: Check existence of matrix-corporal service + stat: + path: "{{ matrix_systemd_path }}/matrix-corporal.service" + register: matrix_corporal_service_stat + when: "not matrix_corporal_enabled|bool" + +- name: Ensure matrix-corporal is stopped + service: + name: matrix-corporal + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_corporal_enabled|bool and matrix_corporal_service_stat.stat.exists" + +- name: Ensure matrix-corporal.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-corporal.service" + state: absent + when: "not matrix_corporal_enabled|bool and matrix_corporal_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-corporal.service removal + service: + daemon_reload: yes + when: "not matrix_corporal_enabled|bool and matrix_corporal_service_stat.stat.exists" + +- name: Ensure matrix-corporal files don't exist + file: + path: "{{ item }}" + state: absent + with_items: + - "{{ matrix_systemd_path }}/matrix-corporal.service" + - "{{ matrix_corporal_config_dir_path }}/config.json" + when: "not matrix_corporal_enabled|bool" + +- name: Ensure Matrix Corporal Docker image doesn't exist + docker_image: + name: "{{ matrix_corporal_docker_image }}" + state: absent + when: "not matrix_corporal_enabled|bool" diff --git a/roles/matrix-corporal/tasks/validate_config.yml b/roles/matrix-corporal/tasks/validate_config.yml new file mode 100644 index 00000000..a8930e7e --- /dev/null +++ b/roles/matrix-corporal/tasks/validate_config.yml @@ -0,0 +1,27 @@ +--- + +- name: Fail if required matrix-corporal settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`) for using matrix-corporal. + when: "vars[item] == ''" + with_items: + - "matrix_corporal_matrix_homeserver_api_endpoint" + - "matrix_corporal_matrix_auth_shared_secret" + - "matrix_corporal_matrix_registration_shared_secret" + - "matrix_corporal_policy_provider_config" + +- name: Fail if HTTP API enabled, but no token set + fail: + msg: "The Matrix Corporal HTTP API is enabled (`matrix_corporal_http_api_enabled`), but no auth token has been set in `matrix_corporal_http_api_auth_token`" + when: "matrix_corporal_http_api_enabled|bool and matrix_corporal_http_api_auth_token == ''" + +- name: (Deprecation) Catch and report renamed corporal variables + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_corporal_container_expose_ports', 'new': ''} + - {'old': 'matrix_corporal_reconciliation_user_id_local_part', 'new': 'matrix_corporal_corporal_user_id_local_part'} diff --git a/roles/matrix-corporal/templates/config.json.j2 b/roles/matrix-corporal/templates/config.json.j2 new file mode 100644 index 00000000..9b4c7414 --- /dev/null +++ b/roles/matrix-corporal/templates/config.json.j2 @@ -0,0 +1,38 @@ +{ + "Matrix": { + "HomeserverDomainName": "{{ matrix_corporal_matrix_homeserver_domain_name }}", + "HomeserverApiEndpoint": "{{ matrix_corporal_matrix_homeserver_api_endpoint }}", + "AuthSharedSecret": "{{ matrix_corporal_matrix_auth_shared_secret }}", + "RegistrationSharedSecret": "{{ matrix_corporal_matrix_registration_shared_secret }}", + "TimeoutMilliseconds": {{ matrix_corporal_matrix_timeout_milliseconds }} + }, + + "Corporal": { + "UserID": "@{{ matrix_corporal_corporal_user_id_local_part }}:{{ matrix_domain }}" + }, + + "Reconciliation": { + "RetryIntervalMilliseconds": {{ matrix_corporal_reconciliation_retry_interval_milliseconds }} + }, + + "HttpGateway": { + "ListenAddress": "0.0.0.0:41080", + "TimeoutMilliseconds": {{ matrix_corporal_http_gateway_timeout_milliseconds }}, + "InternalRESTAuth": { + "Enabled": {{ matrix_corporal_http_gateway_internal_rest_auth_enabled|to_json }} + } + }, + + "HttpApi": { + "Enabled": {{ matrix_corporal_http_api_enabled|to_json }}, + "ListenAddress": "0.0.0.0:41081", + "AuthorizationBearerToken": "{{ matrix_corporal_http_api_auth_token }}", + "TimeoutMilliseconds": {{ matrix_corporal_http_api_timeout_milliseconds }} + }, + + "PolicyProvider": {{ matrix_corporal_policy_provider_config }}, + + "Misc": { + "Debug": {{ matrix_corporal_debug|to_json }} + } +} diff --git a/roles/matrix-corporal/templates/systemd/matrix-corporal.service.j2 b/roles/matrix-corporal/templates/systemd/matrix-corporal.service.j2 new file mode 100644 index 00000000..262e2e77 --- /dev/null +++ b/roles/matrix-corporal/templates/systemd/matrix-corporal.service.j2 @@ -0,0 +1,44 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Corporal +{% for service in matrix_corporal_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-corporal 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-corporal 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-corporal \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + {% if matrix_corporal_container_http_gateway_host_bind_port %} + -p {{ matrix_corporal_container_http_gateway_host_bind_port }}:41080 \ + {% endif %} + {% if matrix_corporal_container_http_api_host_bind_port %} + -p {{ matrix_corporal_container_http_api_host_bind_port }}:41081 \ + {% endif %} + --mount type=bind,src={{ matrix_corporal_config_dir_path }},dst=/etc/matrix-corporal,ro \ + --mount type=bind,src={{ matrix_corporal_cache_dir_path }},dst=/var/cache/matrix-corporal \ + --mount type=bind,src={{ matrix_corporal_var_dir_path }},dst=/var/matrix-corporal \ + {% for arg in matrix_corporal_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_corporal_docker_image }} \ + /matrix-corporal -config=/etc/matrix-corporal/config.json + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-corporal 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-corporal 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-corporal + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-coturn/defaults/main.yml b/roles/matrix-coturn/defaults/main.yml new file mode 100644 index 00000000..45565686 --- /dev/null +++ b/roles/matrix-coturn/defaults/main.yml @@ -0,0 +1,80 @@ +matrix_coturn_enabled: true + +matrix_coturn_container_image_self_build: false +matrix_coturn_container_image_self_build_repo: "https://github.com/coturn/coturn" +matrix_coturn_container_image_self_build_repo_version: "docker/{{ matrix_coturn_version }}" +matrix_coturn_container_image_self_build_repo_dockerfile_path: "docker/coturn/alpine/Dockerfile" + +matrix_coturn_version: 4.5.2-r2 +matrix_coturn_docker_image: "{{ matrix_coturn_docker_image_name_prefix }}coturn/coturn:{{ matrix_coturn_version }}-alpine" +matrix_coturn_docker_image_name_prefix: "{{ 'localhost/' if matrix_coturn_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_coturn_docker_image_force_pull: "{{ matrix_coturn_docker_image.endswith(':latest') }}" + +# The Docker network that Coturn would be put into. +# +# Because Coturn relays traffic to unvalidated IP addresses, +# using a dedicated network, isolated from other Docker (and local) services is preferrable. +# +# Setting up deny/allow rules with `matrix_coturn_allowed_peer_ips`/`matrix_coturn_denied_peer_ips` is also +# possible for achieving such isolation, but is more complicated due to the dynamic nature of Docker networking. +matrix_coturn_docker_network: "matrix-coturn" + +matrix_coturn_base_path: "{{ matrix_base_data_path }}/coturn" +matrix_coturn_docker_src_files_path: "{{ matrix_coturn_base_path }}/docker-src" +matrix_coturn_config_path: "{{ matrix_coturn_base_path }}/turnserver.conf" + +# List of systemd services that matrix-coturn.service depends on +matrix_coturn_systemd_required_services_list: ['docker.service'] + +# A list of additional "volumes" to mount in the container. +# This list gets populated dynamically at runtime. You can provide a different default value, +# if you wish to mount your own files into the container. +# Contains definition objects like this: `{"src": "/outside", "dst": "/inside", "options": "rw|ro|slave|.."} +matrix_coturn_container_additional_volumes: [] + +# A list of extra arguments to pass to the container +matrix_coturn_container_extra_arguments: [] + +# Controls whether the Coturn container exposes its plain STUN port (tcp/3478 and udp/3478 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:3478"), or empty string to not expose. +matrix_coturn_container_stun_plain_host_bind_port: '3478' + +# Controls whether the Coturn container exposes its TLS STUN port (tcp/5349 and udp/5349 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:5349"), or empty string to not expose. +matrix_coturn_container_stun_tls_host_bind_port: '5349' + +# Controls whether the Coturn container exposes its TURN UDP port range and which interface to do it on. +# +# Takes an interface "" (e.g. "127.0.0.1"), or empty string to listen on all interfaces. +# Takes a null/none value (`~`) to prevent listening. +# +# The UDP port-range itself is specified using `matrix_coturn_turn_udp_min_port` and `matrix_coturn_turn_udp_max_port`. +matrix_coturn_container_turn_range_listen_interface: '' + +# UDP port-range to use for TURN +matrix_coturn_turn_udp_min_port: 49152 +matrix_coturn_turn_udp_max_port: 49172 + +# A shared secret (between Synapse and Coturn) used for authentication. +# You can put any string here, but generating a strong one is preferred (e.g. `pwgen -s 64 1`). +matrix_coturn_turn_static_auth_secret: "" + +# The external IP address of the machine where Coturn is. +matrix_coturn_turn_external_ip_address: '' + +matrix_coturn_allowed_peer_ips: [] +matrix_coturn_denied_peer_ips: [] +matrix_coturn_user_quota: null +matrix_coturn_total_quota: null + +# To enable TLS, you need to provide paths to certificates. +# Paths defined in `matrix_coturn_tls_cert_path` and `matrix_coturn_tls_key_path` are in-container paths. +# Files on the host can be mounted into the container using `matrix_coturn_container_additional_volumes`. +matrix_coturn_tls_enabled: false +matrix_coturn_tls_cert_path: ~ +matrix_coturn_tls_key_path: ~ + +matrix_coturn_tls_v1_enabled: false +matrix_coturn_tls_v1_1_enabled: false diff --git a/roles/matrix-coturn/tasks/init.yml b/roles/matrix-coturn/tasks/init.yml new file mode 100644 index 00000000..a7d8a343 --- /dev/null +++ b/roles/matrix-coturn/tasks/init.yml @@ -0,0 +1,14 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_coturn_container_image_self_build and matrix_coturn_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-coturn.service'] }}" + when: matrix_coturn_enabled|bool + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-coturn-reload.timer'] }}" + when: "matrix_coturn_enabled|bool and matrix_coturn_tls_enabled|bool" diff --git a/roles/matrix-coturn/tasks/main.yml b/roles/matrix-coturn/tasks/main.yml new file mode 100644 index 00000000..9794bcb3 --- /dev/null +++ b/roles/matrix-coturn/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_coturn_enabled|bool" + tags: + - setup-all + - setup-coturn + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_coturn_enabled|bool" + tags: + - setup-all + - setup-coturn + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_coturn_enabled|bool" + tags: + - setup-all + - setup-coturn diff --git a/roles/matrix-coturn/tasks/setup_install.yml b/roles/matrix-coturn/tasks/setup_install.yml new file mode 100644 index 00000000..c31406b1 --- /dev/null +++ b/roles/matrix-coturn/tasks/setup_install.yml @@ -0,0 +1,105 @@ +--- + +# This is a cleanup/migration task. It can be removed some time in the future. +- name: (Migration) Remove deprecated cronjob + file: + path: "{{ matrix_cron_path }}/matrix-coturn-ssl-reload" + state: absent + +- name: Ensure Matrix Coturn path exists + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_coturn_docker_src_files_path }}", when: "{{ matrix_coturn_container_image_self_build }}"} + when: "item.when|bool" + +- name: Ensure Coturn image is pulled + docker_image: + name: "{{ matrix_coturn_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_coturn_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_coturn_docker_image_force_pull }}" + when: "not matrix_coturn_container_image_self_build|bool" + +- block: + - name: Ensure Coturn repository is present on self-build + git: + repo: "{{ matrix_coturn_container_image_self_build_repo }}" + dest: "{{ matrix_coturn_docker_src_files_path }}" + version: "{{ matrix_coturn_container_image_self_build_repo_version }}" + force: "yes" + register: matrix_coturn_git_pull_results + + - name: Ensure Coturn Docker image is built + docker_image: + name: "{{ matrix_coturn_docker_image }}" + source: build + force_source: "{{ matrix_coturn_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_coturn_git_pull_results.changed }}" + build: + dockerfile: "{{ matrix_coturn_container_image_self_build_repo_dockerfile_path }}" + path: "{{ matrix_coturn_docker_src_files_path }}" + pull: yes + when: "matrix_coturn_container_image_self_build|bool" + +- name: Ensure Coturn configuration path exists + file: + path: "{{ matrix_coturn_base_path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure turnserver.conf installed + template: + src: "{{ role_path }}/templates/turnserver.conf.j2" + dest: "{{ matrix_coturn_config_path }}" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure Coturn network is created in Docker + docker_network: + name: "{{ matrix_coturn_docker_network }}" + driver: bridge + +- name: Ensure matrix-coturn.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-coturn.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-coturn.service" + mode: 0644 + register: matrix_coturn_systemd_service_change_results + +# This may be unnecessary when more long-lived certificates are used. +# We optimize for the common use-case though (short-lived Let's Encrypt certificates). +# Reloading doesn't hurt anyway, so there's no need to make this more flexible. +- name: Ensure reloading systemd units installed, if necessary + template: + src: "{{ role_path }}/templates/systemd/{{ item }}.j2" + dest: "{{ matrix_systemd_path }}/{{ item }}" + mode: 0644 + register: "matrix_coturn_systemd_service_change_results" + when: "matrix_coturn_tls_enabled|bool" + with_items: + - matrix-coturn-reload.service + - matrix-coturn-reload.timer + +# A similar task exists in `setup_uninstall.yml` +- name: Ensure reloading systemd units uninstalled, if unnecessary + file: + path: "{{ item }}" + state: absent + register: "matrix_coturn_systemd_service_change_results" + when: "not matrix_coturn_tls_enabled|bool" + with_items: + - matrix-coturn-reload.service + - matrix-coturn-reload.timer + +- name: Ensure systemd reloaded if systemd units changed + service: + daemon_reload: yes + when: "matrix_coturn_systemd_service_change_results.changed" diff --git a/roles/matrix-coturn/tasks/setup_uninstall.yml b/roles/matrix-coturn/tasks/setup_uninstall.yml new file mode 100644 index 00000000..4674903f --- /dev/null +++ b/roles/matrix-coturn/tasks/setup_uninstall.yml @@ -0,0 +1,45 @@ +--- + +- name: Check existence of matrix-coturn service + stat: + path: "{{ matrix_systemd_path }}/matrix-coturn.service" + register: matrix_coturn_service_stat + when: "not matrix_coturn_enabled|bool" + +- name: Ensure matrix-coturn is stopped + service: + name: matrix-coturn + state: stopped + daemon_reload: yes + when: "matrix_coturn_service_stat.stat.exists|bool" + +- name: Ensure matrix-coturn-reload.timer is stopped + service: + name: matrix-coturn + state: stopped + daemon_reload: yes + failed_when: false + when: "matrix_coturn_service_stat.stat.exists|bool" + +- name: Ensure systemd units don't exist + file: + path: "{{ matrix_systemd_path }}/{{ item }}" + state: absent + register: matrix_coturn_systemd_unit_uninstallation_result + with_items: + - matrix-coturn.service + - matrix-coturn-reload.service + - matrix-coturn-reload.timer + +- name: Ensure systemd reloaded after unit removal + service: + daemon_reload: yes + when: "matrix_coturn_systemd_unit_uninstallation_result.changed|bool" + +- name: Ensure Matrix coturn paths don't exist + file: + path: "{{ matrix_coturn_base_path }}" + state: absent + +# Intentionally not removing the Docker image when uninstalling. +# We can't be sure it had been pulled by us in the first place. diff --git a/roles/matrix-coturn/tasks/validate_config.yml b/roles/matrix-coturn/tasks/validate_config.yml new file mode 100644 index 00000000..d8276d3a --- /dev/null +++ b/roles/matrix-coturn/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: Fail if required Coturn settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`) for using Coturn. + when: "vars[item] == ''" + with_items: + - "matrix_coturn_turn_external_ip_address" + - "matrix_coturn_turn_static_auth_secret" diff --git a/roles/matrix-coturn/templates/systemd/matrix-coturn-reload.service.j2 b/roles/matrix-coturn/templates/systemd/matrix-coturn-reload.service.j2 new file mode 100644 index 00000000..e006e5a0 --- /dev/null +++ b/roles/matrix-coturn/templates/systemd/matrix-coturn-reload.service.j2 @@ -0,0 +1,6 @@ +[Unit] +Description=Reloads matrix-coturn so that new SSL certificates can kick in + +[Service] +Type=oneshot +ExecStart={{ matrix_host_command_systemctl }} reload matrix-coturn.service diff --git a/roles/matrix-coturn/templates/systemd/matrix-coturn-reload.timer.j2 b/roles/matrix-coturn/templates/systemd/matrix-coturn-reload.timer.j2 new file mode 100644 index 00000000..52894541 --- /dev/null +++ b/roles/matrix-coturn/templates/systemd/matrix-coturn-reload.timer.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Reloads matrix-coturn periodically so that new SSL certificates can kick in + +[Timer] +Unit=matrix-coturn-reload.service +OnCalendar=*-*-* 06:30:00 +RandomizedDelaySec=1h + +[Install] +WantedBy=timers.target diff --git a/roles/matrix-coturn/templates/systemd/matrix-coturn.service.j2 b/roles/matrix-coturn/templates/systemd/matrix-coturn.service.j2 new file mode 100644 index 00000000..a39030af --- /dev/null +++ b/roles/matrix-coturn/templates/systemd/matrix-coturn.service.j2 @@ -0,0 +1,58 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Coturn server +{% for service in matrix_coturn_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-coturn 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-coturn 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-coturn \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --cap-add=NET_BIND_SERVICE \ + --entrypoint=turnserver \ + --read-only \ + --tmpfs=/var/tmp:rw,noexec,nosuid,size=100m \ + --network={{ matrix_coturn_docker_network }} \ + {% if matrix_coturn_container_stun_plain_host_bind_port != '' %} + -p {{ matrix_coturn_container_stun_plain_host_bind_port }}:3478 \ + -p {{ matrix_coturn_container_stun_plain_host_bind_port }}:3478/udp \ + {% endif %} + {% if matrix_coturn_container_stun_tls_host_bind_port != '' %} + -p {{ matrix_coturn_container_stun_tls_host_bind_port }}:5349 \ + -p {{ matrix_coturn_container_stun_tls_host_bind_port }}:5349/udp \ + {% endif %} + {% if matrix_coturn_container_turn_range_listen_interface is not none %} + -p {{ matrix_coturn_container_turn_range_listen_interface }}{{ ':' if matrix_coturn_container_turn_range_listen_interface else '' }}{{ matrix_coturn_turn_udp_min_port }}-{{ matrix_coturn_turn_udp_max_port }}:{{ matrix_coturn_turn_udp_min_port }}-{{ matrix_coturn_turn_udp_max_port }}/udp \ + {% endif %} + --mount type=bind,src={{ matrix_coturn_config_path }},dst=/turnserver.conf,ro \ + {% for volume in matrix_coturn_container_additional_volumes %} + -v {{ volume.src }}:{{ volume.dst }}:{{ volume.options }} \ + {% endfor %} + {% for arg in matrix_coturn_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_coturn_docker_image }} \ + -c /turnserver.conf + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-coturn 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-coturn 2>/dev/null' + +# This only reloads certificates (not other configuration). +# See: https://github.com/coturn/coturn/pull/236 +ExecReload={{ matrix_host_command_docker }} exec matrix-coturn kill -USR2 1 + +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-coturn + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-coturn/templates/turnserver.conf.j2 b/roles/matrix-coturn/templates/turnserver.conf.j2 new file mode 100644 index 00000000..ba662587 --- /dev/null +++ b/roles/matrix-coturn/templates/turnserver.conf.j2 @@ -0,0 +1,45 @@ +#jinja2: lstrip_blocks: "True" +use-auth-secret +static-auth-secret={{ matrix_coturn_turn_static_auth_secret }} +realm=turn.{{ matrix_server_fqn_matrix }} + +min-port={{ matrix_coturn_turn_udp_min_port }} +max-port={{ matrix_coturn_turn_udp_max_port }} +external-ip={{ matrix_coturn_turn_external_ip_address }} + +log-file=stdout +pidfile=/var/tmp/turnserver.pid +userdb=/var/tmp/turnserver.db + +no-cli + +{% if matrix_coturn_tls_enabled %} +cert={{ matrix_coturn_tls_cert_path }} +pkey={{ matrix_coturn_tls_key_path }} +{% if not matrix_coturn_tls_v1_enabled %} +no-tlsv1 +{% endif %} +{% if not matrix_coturn_tls_v1_1_enabled %} +no-tlsv1_1 +{% endif %} +{% else %} +no-tls +no-dtls +{% endif %} + +prod +no-tcp-relay + +{% if matrix_coturn_user_quota != None %} +user-quota={{ matrix_coturn_user_quota }} +{% endif %} +{% if matrix_coturn_total_quota != None %} +total-quota={{ matrix_coturn_total_quota }} +{% endif %} + +{% for ip_range in matrix_coturn_denied_peer_ips %} +denied-peer-ip={{ ip_range }} +{% endfor %} +{% for ip_range in matrix_coturn_allowed_peer_ips %} +allowed-peer-ip={{ ip_range }} +{% endfor %} diff --git a/roles/matrix-dimension/defaults/main.yml b/roles/matrix-dimension/defaults/main.yml new file mode 100644 index 00000000..f7a84ca1 --- /dev/null +++ b/roles/matrix-dimension/defaults/main.yml @@ -0,0 +1,91 @@ +matrix_dimension_enabled: false + +# You are required to specify an access token for Dimension to work. +# For information on how to acquire an access token, visit https://t2bot.io/docs/access_tokens +matrix_dimension_access_token: "" + +# Users in form: ['@user1:domain.com', '@user2:domain.com'] +matrix_dimension_admins: [] + +# Whether to allow Dimension widgets serve websites with invalid or self signed SSL certificates +matrix_dimension_widgets_allow_self_signed_ssl_certificates: false + +matrix_dimension_base_path: "{{ matrix_base_data_path }}/dimension" + +matrix_dimension_version: latest +matrix_dimension_docker_image: "{{ matrix_container_global_registry_prefix }}turt2live/matrix-dimension:{{ matrix_dimension_version }}" +matrix_dimension_docker_image_force_pull: "{{ matrix_dimension_docker_image.endswith(':latest') }}" + +# List of systemd services that matrix-dimension.service depends on. +matrix_dimension_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-dimension.service wants +matrix_dimension_systemd_wanted_services_list: [] + +# The user and group id correspond to the node user in the `turt2live/matrix-dimension` image. +matrix_dimension_user_uid: '1000' +matrix_dimension_user_gid: '1000' + +# Controls whether the matrix-dimension container exposes its HTTP port (tcp/8184 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8184"), or empty string to not expose. +matrix_dimension_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_dimension_container_extra_arguments: [] + +matrix_dimension_integrations_ui_url: "https://{{ matrix_server_fqn_dimension }}/element" +matrix_dimension_integrations_rest_url: "https://{{ matrix_server_fqn_dimension }}/api/v1/scalar" +matrix_dimension_integrations_widgets_urls: ["https://{{ matrix_server_fqn_dimension }}/widgets"] +matrix_dimension_integrations_jitsi_widget_url: "https://{{ matrix_server_fqn_dimension }}/widgets/jitsi" + +matrix_dimension_homeserver_federationUrl: "http://matrix-synapse:8048" + + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_dimension_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_dimension_postgres_*` variables +matrix_dimension_database_engine: 'sqlite' + +matrix_dimension_sqlite_database_path_local: "{{ matrix_dimension_base_path }}/dimension.db" +matrix_dimension_sqlite_database_path_in_container: "dimension.db" + +matrix_dimension_database_username: 'matrix_dimension' +matrix_dimension_database_password: 'some-password' +matrix_dimension_database_hostname: 'matrix-postgres' +matrix_dimension_database_port: 5432 +matrix_dimension_database_name: 'matrix_dimension' + +matrix_dimension_database_connection_string: 'postgres://{{ matrix_dimension_database_username }}:{{ matrix_dimension_database_password }}@{{ matrix_dimension_database_hostname }}:{{ matrix_dimension_database_port }}/{{ matrix_dimension_database_name }}' + + +# Default Dimension 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_dimension_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_dimension_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_dimension_configuration_extension_yaml: | + # Your custom YAML configuration for Dimension goes here. + # This configuration extends the default starting configuration (`matrix_dimension_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_dimension_configuration_yaml`. + # + # Example configuration extension follows: + # telegram: + # botToken: "YourTokenHere" + # + +matrix_dimension_configuration_extension: "{{ matrix_dimension_configuration_extension_yaml|from_yaml if matrix_dimension_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final Dimension configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_dimension_configuration_yaml`. +matrix_dimension_configuration: "{{ matrix_dimension_configuration_yaml|from_yaml|combine(matrix_dimension_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-dimension/tasks/init.yml b/roles/matrix-dimension/tasks/init.yml new file mode 100644 index 00000000..85ca04ea --- /dev/null +++ b/roles/matrix-dimension/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-dimension.service'] }}" + when: matrix_dimension_enabled|bool diff --git a/roles/matrix-dimension/tasks/main.yml b/roles/matrix-dimension/tasks/main.yml new file mode 100644 index 00000000..aad55286 --- /dev/null +++ b/roles/matrix-dimension/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: run_setup|bool + tags: + - setup-all + - setup-dimension + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: run_setup|bool and matrix_dimension_enabled|bool + tags: + - setup-all + - setup-dimension + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: run_setup|bool and not matrix_dimension_enabled|bool + tags: + - setup-all + - setup-dimension diff --git a/roles/matrix-dimension/tasks/setup_install.yml b/roles/matrix-dimension/tasks/setup_install.yml new file mode 100644 index 00000000..804be88d --- /dev/null +++ b/roles/matrix-dimension/tasks/setup_install.yml @@ -0,0 +1,110 @@ +--- + +- set_fact: + matrix_dimension_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_dimension_sqlite_database_path_local }}" + register: matrix_dimension_sqlite_database_path_local_stat_result + + - block: + # pgloader makes a few columns `smallint`, instead of `boolean`. + # We need to fix them up. + - set_fact: + matrix_dimension_pgloader_additional_psql_statements_list: [] + + - set_fact: + matrix_dimension_pgloader_additional_psql_statements_list: | + {{ + matrix_dimension_pgloader_additional_psql_statements_list + + + ([] if item.default == '' else ['ALTER TABLE ' + item.table + ' ALTER COLUMN "' + item.column + '" DROP default;']) + + + (['ALTER TABLE ' + item.table + ' ALTER COLUMN "' + item.column + '" TYPE BOOLEAN USING("' + item.column + '"::text::boolean);']) + + + ([] if item.default == '' else ['ALTER TABLE ' + item.table + ' ALTER COLUMN "' + item.column + '" SET default ' + item.default + ';']) + }} + with_items: + - {'table': 'dimension_widgets', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_widgets', 'column': 'isPublic', 'default': ''} + - {'table': 'dimension_webhook_bridges', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_user_sticker_packs', 'column': 'isSelected', 'default': ''} + - {'table': 'dimension_scalar_tokens', 'column': 'isDimensionToken', 'default': ''} + - {'table': 'dimension_users', 'column': 'isSelfBot', 'default': 'false'} + - {'table': 'dimension_telegram_bridges', 'column': 'allowTgPuppets', 'default': ''} + - {'table': 'dimension_telegram_bridges', 'column': 'allowMxPuppets', 'default': ''} + - {'table': 'dimension_telegram_bridges', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_sticker_packs', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_sticker_packs', 'column': 'isPublic', 'default': ''} + - {'table': 'dimension_slack_bridges', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_neb_integrations', 'column': 'isPublic', 'default': ''} + - {'table': 'dimension_neb_integrations', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_irc_bridges', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_irc_bridge_networks', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_gitter_bridges', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_custom_simple_bots', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_custom_simple_bots', 'column': 'isPublic', 'default': ''} + - {'table': 'dimension_bridges', 'column': 'isEnabled', 'default': ''} + - {'table': 'dimension_bridges', 'column': 'isPublic', 'default': ''} + + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_dimension_sqlite_database_path_local }}" + dst: "{{ matrix_dimension_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_dimension_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-dimension.service'] + pgloader_options: ['--with "quote identifiers"'] + additional_psql_statements_list: "{{ matrix_dimension_pgloader_additional_psql_statements_list }}" + additional_psql_statements_db_name: "{{ matrix_dimension_database_name }}" + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_dimension_requires_restart: true + when: "matrix_dimension_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_dimension_database_engine == 'postgres'" + +- name: Ensure Dimension base path exists + file: + path: "{{ matrix_dimension_base_path }}" + state: directory + mode: 0770 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_dimension_user_gid }}" + +- name: Ensure Dimension config installed + copy: + content: "{{ matrix_dimension_configuration|to_nice_yaml }}" + dest: "{{ matrix_dimension_base_path }}/config.yaml" + mode: 0640 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_dimension_user_gid }}" + +- name: Ensure Dimension image is pulled + docker_image: + name: "{{ matrix_dimension_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_dimension_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_dimension_docker_image_force_pull }}" + +- name: Ensure matrix-dimension.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-dimension.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-dimension.service" + mode: 0644 + register: matrix_dimension_systemd_service_result + +- name: Ensure systemd reloaded after matrix-dimension.service installation + service: + daemon_reload: yes + when: "matrix_dimension_systemd_service_result.changed|bool" + +- name: Ensure matrix-dimension.service restarted, if necessary + service: + name: "matrix-dimension.service" + state: restarted + when: "matrix_dimension_requires_restart|bool" diff --git a/roles/matrix-dimension/tasks/setup_uninstall.yml b/roles/matrix-dimension/tasks/setup_uninstall.yml new file mode 100644 index 00000000..9bc4ac8b --- /dev/null +++ b/roles/matrix-dimension/tasks/setup_uninstall.yml @@ -0,0 +1,35 @@ +--- + +- name: Check existence of matrix-dimension service + stat: + path: "{{ matrix_systemd_path }}/matrix-dimension.service" + register: matrix_dimension_service_stat + +- name: Ensure matrix-dimension is stopped + service: + name: matrix-dimension + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_dimension_service_stat.stat.exists|bool" + +- name: Ensure matrix-dimension.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-dimension.service" + state: absent + when: "matrix_dimension_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-dimension.service removal + service: + daemon_reload: yes + when: "matrix_dimension_service_stat.stat.exists|bool" + +- name: Ensure Dimension base directory doesn't exist + file: + path: "{{ matrix_dimension_base_path }}" + state: absent + +- name: Ensure Dimension Docker image doesn't exist + docker_image: + name: "{{ matrix_dimension_docker_image }}" + state: absent diff --git a/roles/matrix-dimension/tasks/validate_config.yml b/roles/matrix-dimension/tasks/validate_config.yml new file mode 100644 index 00000000..ead8352b --- /dev/null +++ b/roles/matrix-dimension/tasks/validate_config.yml @@ -0,0 +1,16 @@ +- name: Fail if required Dimension settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`) for using Dimension. + with_items: + - "matrix_dimension_access_token" + when: "matrix_dimension_enabled and vars[item] == ''" + +- name: (Deprecation) Catch and report renamed Dimension variables + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_dimension_container_expose_port', 'new': ''} diff --git a/roles/matrix-dimension/templates/config.yaml.j2 b/roles/matrix-dimension/templates/config.yaml.j2 new file mode 100644 index 00000000..39721d71 --- /dev/null +++ b/roles/matrix-dimension/templates/config.yaml.j2 @@ -0,0 +1,85 @@ +#jinja2: lstrip_blocks: True +# The web settings for the service (API and UI). +# It is best to have this run on localhost and use a reverse proxy to access Dimension. +web: + port: 8184 + address: '0.0.0.0' + +# Homeserver configuration +homeserver: + # The domain name of the homeserver. This is used in many places, such as with go-neb + # setups, to identify the homeserver. + name: "{{ matrix_domain }}" + + # The URL that Dimension, go-neb, and other services provisioned by Dimension should + # use to access the homeserver with. + clientServerUrl: "{{ matrix_homeserver_container_url }}" + + # The URL that Dimension should use when trying to communicate with federated APIs on + # the homeserver. If not supplied or left empty Dimension will try to resolve the address + # through the normal federation process. + federationUrl: "{{ matrix_dimension_homeserver_federationUrl }}" + + # The URL that Dimension will redirect media requests to for downloading media such as + # stickers. If not supplied or left empty Dimension will use the clientServerUrl. + mediaUrl: "https://{{ matrix_server_fqn_matrix }}" + + # The access token Dimension should use for miscellaneous access to the homeserver. This + # should be for a user on the configured homeserver: any user will do, however it is + # recommended to use a dedicated user (such as @dimension:t2bot.io). For information on + # how to acquire an access token, visit https://t2bot.io/docs/access_tokens + accessToken: "{{ matrix_dimension_access_token }}" + +# These users can modify the integrations this Dimension supports. +# To access the admin interface, open Dimension in Element and click the settings icon. +admins: {{ matrix_dimension_admins|to_json }} + +# IPs and CIDR ranges listed here will be blocked from being widgets. +# Note: Widgets may still be embedded with restricted content, although not through Dimension directly. +widgetBlacklist: + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + - 127.0.0.0/8 + +# Where the database for Dimension is +database: +{% if matrix_dimension_database_engine == 'sqlite' %} + file: {{ matrix_dimension_sqlite_database_path_in_container|to_json }} +{% elif matrix_dimension_database_engine == 'postgres' %} + uri: {{ matrix_dimension_database_connection_string|to_json }} +{% endif %} + +# Display settings that apply to self-hosted go-neb instances +goneb: + # The avatars to set for each bot. Usually these don't need to be changed, however if your homeserver + # is not able to reach t2bot.io then you should specify your own here. To not use an avatar for a bot, + # make the bot's avatar an empty string. + avatars: + giphy: "mxc://t2bot.io/c5eaab3ef0133c1a61d3c849026deb27" + imgur: "mxc://t2bot.io/6749eaf2b302bb2188ae931b2eeb1513" + github: "mxc://t2bot.io/905b64b3cd8e2347f91a60c5eb0832e1" + wikipedia: "mxc://t2bot.io/7edfb54e9ad9e13fec0df22636feedf1" + travisci: "mxc://t2bot.io/7f4703126906fab8bb27df34a17707a8" + rss: "mxc://t2bot.io/aace4fcbd045f30afc1b4e5f0928f2f3" + google: "mxc://t2bot.io/636ad10742b66c4729bf89881a505142" + guggy: "mxc://t2bot.io/e7ef0ed0ba651aaf907655704f9a7526" + echo: "mxc://t2bot.io/3407ff2db96b4e954fcbf2c6c0415a13" + circleci: "mxc://t2bot.io/cf7d875845a82a6b21f5f66de78f6bee" + jira: "mxc://t2bot.io/f4a38ebcc4280ba5b950163ca3e7c329" + +# Settings for how Dimension is represented to the public +dimension: + # This is where Dimension is accessible from clients. Be sure to set this + # to your own Dimension instance. + publicUrl: "https://{{ matrix_server_fqn_dimension }}" + +# Settings for controlling how logging works +logging: + file: /dev/null + console: true + consoleLevel: verbose + fileLevel: info + rotate: + size: 52428800 # bytes, default is 50mb + count: 5 diff --git a/roles/matrix-dimension/templates/systemd/matrix-dimension.service.j2 b/roles/matrix-dimension/templates/systemd/matrix-dimension.service.j2 new file mode 100644 index 00000000..e27a5558 --- /dev/null +++ b/roles/matrix-dimension/templates/systemd/matrix-dimension.service.j2 @@ -0,0 +1,48 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Dimension +{% for service in matrix_dimension_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_dimension_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dimension 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dimension 2>/dev/null' + +# Fixup database ownership if it got changed somehow (during a server migration, etc.) +{% if matrix_dimension_database_engine == 'sqlite' %} +ExecStartPre=-{{ matrix_host_command_chown }} {{ matrix_dimension_user_uid }}:{{ matrix_dimension_user_gid }} {{ matrix_dimension_sqlite_database_path_local }} +{% endif %} + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-dimension \ + --log-driver=none \ + --user={{ matrix_dimension_user_uid }}:{{ matrix_dimension_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_dimension_widgets_allow_self_signed_ssl_certificates %} + -e NODE_TLS_REJECT_UNAUTHORIZED=0 \ + {% endif %} + {% if matrix_dimension_container_http_host_bind_port %} + -p {{ matrix_dimension_container_http_host_bind_port }}:8184 \ + {% endif %} + --mount type=bind,src={{ matrix_dimension_base_path }},dst=/data \ + {% for arg in matrix_dimension_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_dimension_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dimension 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dimension 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-dimension + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-dimension/vars/main.yml b/roles/matrix-dimension/vars/main.yml new file mode 100644 index 00000000..107bb4fa --- /dev/null +++ b/roles/matrix-dimension/vars/main.yml @@ -0,0 +1,5 @@ +--- + +# Doing `|from_yaml` when the extension contains nothing yields an empty string (""). +# We need to ensure it's a dictionary or `|combine` (when building `matrix_dimension_configuration`) will fail later. +matrix_dimension_configuration_extension: "{{ matrix_dimension_configuration_extension_yaml|from_yaml if matrix_dimension_configuration_extension_yaml|from_yaml else {} }}" \ No newline at end of file diff --git a/roles/matrix-dynamic-dns/defaults/main.yml b/roles/matrix-dynamic-dns/defaults/main.yml new file mode 100644 index 00000000..3411d0f8 --- /dev/null +++ b/roles/matrix-dynamic-dns/defaults/main.yml @@ -0,0 +1,48 @@ +# Whether dynamic dns is enabled +matrix_dynamic_dns_enabled: true + +# The dynamic dns daemon interval +matrix_dynamic_dns_daemon_interval: '300' + +matrix_dynamic_dns_version: v3.9.1-ls45 + +# The docker container to use when in mode +matrix_dynamic_dns_docker_image: "{{ matrix_dynamic_dns_docker_image_name_prefix }}linuxserver/ddclient:{{ matrix_dynamic_dns_version }}" + +matrix_dynamic_dns_docker_image_name_prefix: "{{ 'localhost/' if matrix_dynamic_dns_container_image_self_build else matrix_container_global_registry_prefix }}" + +# The image to force pull +matrix_dynamic_dns_docker_image_force_pull: "{{ matrix_dynamic_dns_docker_image.endswith(':latest') }}" + +# List of extra arguments to pass to the ontainer mode +matrix_dynamic_dns_container_extra_arguments: [] + +# List of wanted services when running in mode +matrix_dynamic_dns_systemd_wanted_services_list: [] + +# List of required services when running in mode +matrix_dynamic_dns_systemd_required_services_list: ['docker.service'] + +# Build the container from source when running in mode +matrix_dynamic_dns_container_image_self_build: false +matrix_dynamic_dns_container_image_self_build_repo: "https://github.com/linuxserver/docker-ddclient.git" + +# Config paths +matrix_dynamic_dns_base_path: "{{ matrix_base_data_path }}/dynamic-dns" +matrix_dynamic_dns_config_path: "{{ matrix_dynamic_dns_base_path }}/config" +matrix_dynamic_dns_docker_src_files_path: "{{ matrix_dynamic_dns_base_path }}/docker-src" + +# Holds the configurations (the domains to update DNS for, the providers they use, etc.) +# +# Example: +# matrix_dynamic_dns_domain_configurations: +# - provider: domains.google.com +# protocol: dyndn2 +# username: XXXXXXXXXXXXXXXX +# password: XXXXXXXXXXXXXXXX +# domain: "{{ matrix_domain }}" +matrix_dynamic_dns_domain_configurations: [] + +# Config options +matrix_dynamic_dns_additional_configuration_blocks: [] +matrix_dynamic_dns_use: "web" diff --git a/roles/matrix-dynamic-dns/tasks/init.yml b/roles/matrix-dynamic-dns/tasks/init.yml new file mode 100644 index 00000000..e7d33ff2 --- /dev/null +++ b/roles/matrix-dynamic-dns/tasks/init.yml @@ -0,0 +1,10 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_dynamic_dns_container_image_self_build and matrix_dynamic_dns_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-dynamic-dns.service'] }}" + when: "matrix_dynamic_dns_enabled|bool" diff --git a/roles/matrix-dynamic-dns/tasks/install.yml b/roles/matrix-dynamic-dns/tasks/install.yml new file mode 100644 index 00000000..ac69ec89 --- /dev/null +++ b/roles/matrix-dynamic-dns/tasks/install.yml @@ -0,0 +1,62 @@ +--- + +- name: Ensure Dynamic DNS image is pulled + docker_image: + name: "{{ matrix_dynamic_dns_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_dynamic_dns_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_dynamic_dns_docker_image_force_pull }}" + when: matrix_dynamic_dns_enabled|bool and not matrix_dynamic_dns_container_image_self_build + +- name: Ensure Dynamic DNS paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0751 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_dynamic_dns_base_path }}", when: true } + - { path: "{{ matrix_dynamic_dns_config_path }}", when: true } + - { path: "{{ matrix_dynamic_dns_docker_src_files_path }}", when: "{{ matrix_dynamic_dns_container_image_self_build }}" } + when: matrix_dynamic_dns_enabled|bool and item.when|bool + +- name: Ensure Dynamic DNS repository is present on self build + git: + repo: "{{ matrix_dynamic_dns_container_image_self_build_repo }}" + dest: "{{ matrix_dynamic_dns_docker_src_files_path }}" + force: "yes" + register: matrix_dynamic_dns_git_pull_results + when: "matrix_dynamic_dns_enabled|bool and matrix_dynamic_dns_container_image_self_build|bool" + +- name: Ensure Dynamic DNS Docker image is built + docker_image: + name: "{{ matrix_dynamic_dns_docker_image }}" + source: build + force_source: "{{ matrix_dynamic_dns_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_dynamic_dns_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_dynamic_dns_docker_src_files_path }}" + pull: yes + when: "matrix_dynamic_dns_enabled|bool and matrix_dynamic_dns_container_image_self_build|bool" + +- name: Ensure Dynamic DNS ddclient.conf installed + template: + src: "{{ role_path }}/templates/ddclient.conf.j2" + dest: "{{ matrix_dynamic_dns_config_path }}/ddclient.conf" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-dynamic-dns.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-dynamic-dns.service.j2" + dest: "/etc/systemd/system/matrix-dynamic-dns.service" + mode: 0644 + register: matrix_dynamic_dns_systemd_service_result + +- name: Ensure systemd reloaded after matrix-dynamic-dns.service installation + service: + daemon_reload: yes + when: "matrix_dynamic_dns_systemd_service_result.changed" diff --git a/roles/matrix-dynamic-dns/tasks/main.yml b/roles/matrix-dynamic-dns/tasks/main.yml new file mode 100644 index 00000000..f9aaab8f --- /dev/null +++ b/roles/matrix-dynamic-dns/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_dynamic_dns_enabled|bool" + tags: + - setup-all + - setup-dynamic-dns + +- import_tasks: "{{ role_path }}/tasks/install.yml" + when: "run_setup|bool and matrix_dynamic_dns_enabled|bool" + tags: + - setup-all + - setup-dynamic-dns + +- import_tasks: "{{ role_path }}/tasks/uninstall.yml" + when: "run_setup|bool and not matrix_dynamic_dns_enabled|bool" + tags: + - setup-all + - setup-dynamic-dns diff --git a/roles/matrix-dynamic-dns/tasks/uninstall.yml b/roles/matrix-dynamic-dns/tasks/uninstall.yml new file mode 100644 index 00000000..f3caba25 --- /dev/null +++ b/roles/matrix-dynamic-dns/tasks/uninstall.yml @@ -0,0 +1,27 @@ +--- + +- name: Check existence of matrix-dynamic-dns service + stat: + path: "{{ matrix_systemd_path }}/matrix-dynamic-dns.service" + register: matrix_dynamic_dns_service_stat + +- name: Ensure matrix-dynamic-dns is stopped + service: + name: matrix-dynamic-dns + state: stopped + daemon_reload: yes + when: "matrix_dynamic_dns_service_stat.stat.exists" + +- name: Ensure matrix-dynamic-dns.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-dynamic-dns.service" + state: absent + when: "matrix_dynamic_dns_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-dynamic-dns.service removal + service: + daemon_reload: yes + when: "matrix_dynamic_dns_service_stat.stat.exists" + +# Intentionally not removing the Docker image when uninstalling. +# We can't be sure it had been pulled by us in the first place. diff --git a/roles/matrix-dynamic-dns/tasks/validate_config.yml b/roles/matrix-dynamic-dns/tasks/validate_config.yml new file mode 100644 index 00000000..8f0001ea --- /dev/null +++ b/roles/matrix-dynamic-dns/tasks/validate_config.yml @@ -0,0 +1,16 @@ +--- + +- name: Fail if no configurations specified + fail: + msg: >- + You need to define at least one configuration in `matrix_dynamic_dns_domain_configurations` for using matrix-dynamic-dns. + when: "matrix_dynamic_dns_domain_configurations|length == 0" + +- name: Fail if required settings not defined in configuration blocks + fail: + msg: >- + One of the configurations in matrix_dynamic_dns_domain_configurations is missing a required key (domain, provider, protocol). + when: "'domain' not in configuration or 'provider' not in configuration or 'protocol' not in configuration" + with_items: "{{ matrix_dynamic_dns_domain_configurations }}" + loop_control: + loop_var: configuration diff --git a/roles/matrix-dynamic-dns/templates/ddclient.conf.j2 b/roles/matrix-dynamic-dns/templates/ddclient.conf.j2 new file mode 100644 index 00000000..1480d834 --- /dev/null +++ b/roles/matrix-dynamic-dns/templates/ddclient.conf.j2 @@ -0,0 +1,26 @@ +daemon={{ matrix_dynamic_dns_daemon_interval }} +syslog=no +pid=/var/run/ddclient/ddclient.pid +ssl=yes +use={{ matrix_dynamic_dns_use }} + +{% for dynamic_dns_domain_configuration in matrix_dynamic_dns_domain_configurations %} +protocol={{ dynamic_dns_domain_configuration.protocol }} +server={{ dynamic_dns_domain_configuration.provider }} {% if 'username' in dynamic_dns_domain_configuration %} +login='{{ dynamic_dns_domain_configuration.username }}' {% endif %} {% if 'password' in dynamic_dns_domain_configuration %} +password='{{ dynamic_dns_domain_configuration.password }}' {% endif %} {% if 'static' in dynamic_dns_domain_configuration %} +static=yes {% endif %} {% if 'custom' in dynamic_dns_domain_configuration %} +custom=yes {% endif %} {% if 'zone' in dynamic_dns_domain_configuration %} +zone={{ dynamic_dns_domain_configuration.zone }} {% endif %} {% if 'ttl' in dynamic_dns_domain_configuration %} +ttl={{ dynamic_dns_domain_configuration.ttl }} {% endif %} {% if 'mx' in dynamic_dns_domain_configuration %} +mx={{ dynamic_dns_domain_configuration.mx }} {% endif %} {% if 'wildcard' in dynamic_dns_domain_configuration %} +wildcard=yes {% endif %} +{{ dynamic_dns_domain_configuration.domain }} + +{% endfor %} + + +{% for matrix_dynamic_dns_additional_configuration in matrix_dynamic_dns_additional_configuration_blocks %} +{{ matrix_dynamic_dns_additional_configuration }} + +{% endfor %} diff --git a/roles/matrix-dynamic-dns/templates/systemd/matrix-dynamic-dns.service.j2 b/roles/matrix-dynamic-dns/templates/systemd/matrix-dynamic-dns.service.j2 new file mode 100644 index 00000000..dfdd2f72 --- /dev/null +++ b/roles/matrix-dynamic-dns/templates/systemd/matrix-dynamic-dns.service.j2 @@ -0,0 +1,36 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Dynamic DNS +{% for service in matrix_dynamic_dns_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_dynamic_dns_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dynamic-dns 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dynamic-dns 2>/dev/null' +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-dynamic-dns \ + --log-driver=none \ + --network={{ matrix_docker_network }} \ + -e PUID={{ matrix_user_uid }} \ + -e PGID={{ matrix_user_gid }} \ + -v {{ matrix_dynamic_dns_config_path }}:/config:z \ + {% for arg in matrix_dynamic_dns_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_dynamic_dns_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-dynamic-dns 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-dynamic-dns 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-dynamic-dns + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-email2matrix/defaults/main.yml b/roles/matrix-email2matrix/defaults/main.yml new file mode 100644 index 00000000..3dfabc1a --- /dev/null +++ b/roles/matrix-email2matrix/defaults/main.yml @@ -0,0 +1,50 @@ +matrix_email2matrix_enabled: true + +matrix_email2matrix_base_path: "{{ matrix_base_data_path }}/email2matrix" +matrix_email2matrix_config_dir_path: "{{ matrix_email2matrix_base_path }}/config" +matrix_email2matrix_docker_src_files_path: "{{ matrix_email2matrix_base_path }}/docker-src" + +matrix_email2matrix_container_image_self_build: false +matrix_email2matrix_container_image_self_build_repo: "https://github.com/devture/email2matrix.git" +matrix_email2matrix_container_image_self_build_branch: "{{ matrix_email2matrix_version }}" + +matrix_email2matrix_version: 1.0.1 +matrix_email2matrix_docker_image_prefix: "{{ 'localhost/' if matrix_email2matrix_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_email2matrix_docker_image: "{{ matrix_email2matrix_docker_image_prefix }}devture/email2matrix:{{ matrix_email2matrix_version }}" +matrix_email2matrix_docker_image_force_pull: "{{ matrix_email2matrix_docker_image.endswith(':latest') }}" + +# A list of extra arguments to pass to the container +matrix_email2matrix_container_extra_arguments: [] + +# List of systemd services that matrix-corporal.service depends on +matrix_email2matrix_systemd_required_services_list: ['docker.service'] + +# Controls where the matrix-email2matrix container exposes the SMTP (tcp/2525 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:2525"). +# +# By default, we listen on port 25 on all of the host's network interfaces. +matrix_email2matrix_smtp_host_bind_port: "25" + +matrix_email2matrix_smtp_hostname: "{{ matrix_server_fqn_matrix }}" + +# A list of mailbox to Matrix mappings. +# +# Example: +# matrix_email2matrix_matrix_mappings: +# - MailboxName: "mailbox1" +# MatrixRoomId: "!bpcwlxIUxVvvgXcbjy:example.com" +# MatrixHomeserverUrl: "{{ matrix_homeserver_url }}" +# MatrixUserId": "@email2matrix:{{ matrix_domain }}" +# MatrixAccessToken": "TOKEN_HERE" +# IgnoreSubject: false +# +# - MailboxName: "mailbox2" +# MatrixRoomId: "!another:example.com" +# MatrixHomeserverUrl: "{{ matrix_homeserver_url }}" +# MatrixUserId": "@email2matrix:{{ matrix_domain }}" +# MatrixAccessToken": "TOKEN_HERE" +# IgnoreSubject: true +matrix_email2matrix_matrix_mappings: [] + +matrix_email2matrix_misc_debug: false diff --git a/roles/matrix-email2matrix/tasks/init.yml b/roles/matrix-email2matrix/tasks/init.yml new file mode 100644 index 00000000..0c8ffc0c --- /dev/null +++ b/roles/matrix-email2matrix/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-email2matrix.service'] }}" + when: matrix_email2matrix_enabled|bool diff --git a/roles/matrix-email2matrix/tasks/main.yml b/roles/matrix-email2matrix/tasks/main.yml new file mode 100644 index 00000000..77be7279 --- /dev/null +++ b/roles/matrix-email2matrix/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_email2matrix_enabled|bool" + tags: + - setup-all + - setup-email2matrix + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_email2matrix_enabled|bool" + tags: + - setup-all + - setup-email2matrix + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_email2matrix_enabled|bool" + tags: + - setup-all + - setup-email2matrix diff --git a/roles/matrix-email2matrix/tasks/setup_install.yml b/roles/matrix-email2matrix/tasks/setup_install.yml new file mode 100644 index 00000000..7805c2c1 --- /dev/null +++ b/roles/matrix-email2matrix/tasks/setup_install.yml @@ -0,0 +1,63 @@ +--- + +- name: Ensure Email2Matrix paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_email2matrix_base_path }}", when: true } + - { path: "{{ matrix_email2matrix_config_dir_path }}", when: true } + - { path: "{{ matrix_email2matrix_docker_src_files_path }}", when: "{{ matrix_email2matrix_container_image_self_build }}"} + when: "item.when|bool" + +- name: Ensure Email2Matrix configuration file created + template: + src: "{{ role_path }}/templates/config.json.j2" + dest: "{{ matrix_email2matrix_config_dir_path }}/config.json" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: 0640 + +- name: Ensure Email2Matrix image is pulled + docker_image: + name: "{{ matrix_email2matrix_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_email2matrix_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_email2matrix_docker_image_force_pull }}" + when: "not matrix_email2matrix_container_image_self_build|bool" + +- name: Ensure Email2Matrix repository is present on self-build + git: + repo: "{{ matrix_email2matrix_container_image_self_build_repo }}" + dest: "{{ matrix_email2matrix_docker_src_files_path }}" + version: "{{ matrix_email2matrix_container_image_self_build_branch }}" + force: "yes" + register: matrix_email2matrix_git_pull_results + when: "matrix_email2matrix_container_image_self_build|bool" + +- name: Ensure Email2Matrix Docker image is built + docker_image: + name: "{{ matrix_email2matrix_docker_image }}" + source: build + force_source: "{{ matrix_email2matrix_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_email2matrix_git_pull_results.changed }}" + build: + dockerfile: etc/docker/Dockerfile + path: "{{ matrix_email2matrix_docker_src_files_path }}" + pull: yes + when: "matrix_email2matrix_container_image_self_build|bool" + +- name: Ensure matrix-email2matrix.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-email2matrix.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-email2matrix.service" + mode: 0644 + register: matrix_email2matrix_systemd_service_result + +- name: Ensure systemd reloaded after matrix-email2matrix.service installation + service: + daemon_reload: yes + when: "matrix_email2matrix_systemd_service_result.changed|bool" diff --git a/roles/matrix-email2matrix/tasks/setup_uninstall.yml b/roles/matrix-email2matrix/tasks/setup_uninstall.yml new file mode 100644 index 00000000..b0b44cca --- /dev/null +++ b/roles/matrix-email2matrix/tasks/setup_uninstall.yml @@ -0,0 +1,35 @@ +--- + +- name: Check existence of matrix-email2matrix service + stat: + path: "{{ matrix_systemd_path }}/matrix-email2matrix.service" + register: matrix_email2matrix_service_stat + +- name: Ensure matrix-email2matrix is stopped + service: + name: matrix-email2matrix + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_email2matrix_service_stat.stat.exists|bool" + +- name: Ensure matrix-email2matrix.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-email2matrix.service" + state: absent + when: "matrix_email2matrix_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-email2matrix.service removal + service: + daemon_reload: yes + when: "matrix_email2matrix_service_stat.stat.exists|bool" + +- name: Ensure Email2Matrix data path doesn't exist + file: + path: "{{ matrix_email2matrix_base_path }}" + state: absent + +- name: Ensure Email2Matrix Docker image doesn't exist + docker_image: + name: "{{ matrix_email2matrix_docker_image }}" + state: absent diff --git a/roles/matrix-email2matrix/tasks/validate_config.yml b/roles/matrix-email2matrix/tasks/validate_config.yml new file mode 100644 index 00000000..d8beecf4 --- /dev/null +++ b/roles/matrix-email2matrix/tasks/validate_config.yml @@ -0,0 +1,7 @@ +--- + +- name: Fail if no mappings + fail: + msg: > + You need to define at least one mapping in `matrix_email2matrix_matrix_mappings` for enabling Email2Matrix. + when: "matrix_email2matrix_matrix_mappings|length == 0" diff --git a/roles/matrix-email2matrix/templates/config.json.j2 b/roles/matrix-email2matrix/templates/config.json.j2 new file mode 100644 index 00000000..c1be97fd --- /dev/null +++ b/roles/matrix-email2matrix/templates/config.json.j2 @@ -0,0 +1,14 @@ +#jinja2: lstrip_blocks: "True" +{ + "Smtp": { + "ListenInterface": "0.0.0.0:2525", + "Hostname": {{ matrix_email2matrix_smtp_hostname|to_json }}, + "Workers": 10 + }, + "Matrix": { + "Mappings": {{ matrix_email2matrix_matrix_mappings|to_nice_json }} + }, + "Misc": { + "Debug": {{ matrix_email2matrix_misc_debug|to_json }} + } +} diff --git a/roles/matrix-email2matrix/templates/systemd/matrix-email2matrix.service.j2 b/roles/matrix-email2matrix/templates/systemd/matrix-email2matrix.service.j2 new file mode 100644 index 00000000..c9226768 --- /dev/null +++ b/roles/matrix-email2matrix/templates/systemd/matrix-email2matrix.service.j2 @@ -0,0 +1,34 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Email2Matrix +After=docker.service +Requires=docker.service +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-email2matrix 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-email2matrix 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-email2matrix \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + -p {{ matrix_email2matrix_smtp_host_bind_port }}:2525 \ + --mount type=bind,src={{ matrix_email2matrix_config_dir_path }}/config.json,dst=/config.json,ro \ + {% for arg in matrix_email2matrix_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_email2matrix_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-email2matrix 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-email2matrix 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-email2matrix + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-etherpad/defaults/main.yml b/roles/matrix-etherpad/defaults/main.yml new file mode 100644 index 00000000..45f8f8b2 --- /dev/null +++ b/roles/matrix-etherpad/defaults/main.yml @@ -0,0 +1,87 @@ +matrix_etherpad_enabled: false + +matrix_etherpad_base_path: "{{ matrix_base_data_path }}/etherpad" + +matrix_etherpad_version: 1.8.12 +matrix_etherpad_docker_image: "{{ matrix_container_global_registry_prefix }}etherpad/etherpad:{{ matrix_etherpad_version }}" +matrix_etherpad_docker_image_force_pull: "{{ matrix_etherpad_docker_image.endswith(':latest') }}" + +# List of systemd services that matrix-etherpad.service depends on. +matrix_etherpad_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-etherpad.service wants +matrix_etherpad_systemd_wanted_services_list: [] + +# Container user has to be able to write to the source file directories until this bug is fixed: +# https://github.com/ether/etherpad-lite/issues/2683 +matrix_etherpad_user_uid: '5001' +matrix_etherpad_user_gid: '5001' + +# Controls whether the matrix-etherpad container exposes its HTTP port (tcp/9001 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9001"), or empty string to not expose. +matrix_etherpad_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_etherpad_container_extra_arguments: [] + +matrix_etherpad_public_endpoint: '/etherpad' + +# By default, the Etherpad app can be accessed within the Dimension domain +matrix_etherpad_base_url: "https://{{ matrix_server_fqn_dimension }}{{ matrix_etherpad_public_endpoint }}" + +# Database-related configuration fields. +# +# Etherpad requires a dedicated database +matrix_etherpad_database_engine: 'postgres' + +matrix_etherpad_database_username: 'matrix_etherpad' +matrix_etherpad_database_password: 'some-password' +matrix_etherpad_database_hostname: 'matrix-postgres' +matrix_etherpad_database_port: 5432 +matrix_etherpad_database_name: 'matrix_etherpad' + +matrix_etherpad_database_connection_string: 'postgres://{{ matrix_etherpad_database_username }}:{{ matrix_etherpad_database_password }}@{{ matrix_etherpad_database_hostname }}:{{ matrix_etherpad_database_port }}/{{ matrix_etherpad_database_name }}' + +# Variables configuring the etherpad +matrix_etherpad_title: 'Etherpad' +matrix_etherpad_default_pad_text: | + Welcome to Etherpad! + + This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents! + + Get involved with Etherpad at https://etherpad.org + +# Default Etherpad 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_etherpad_configuration_extension_json`) +# or completely replace this variable with your own template. +matrix_etherpad_configuration_default: "{{ lookup('template', 'templates/settings.json.j2') }}" + +# Your custom JSON configuration for Etherpad goes here. +# This configuration extends the default starting configuration (`matrix_etherpad_configuration_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_etherpad_configuration_json`. +# +# Example configuration extension follows: +# +# matrix_etherpad_configuration_extension_json: | +# { +# "loadTest": true, +# "commitRateLimiting": { +# "duration": 1, +# "points": 10 +# } +# } +# +matrix_etherpad_configuration_extension_json: '{}' + +matrix_etherpad_configuration_extension: "{{ matrix_etherpad_configuration_extension_json|from_json if matrix_etherpad_configuration_extension_json|from_json is mapping else {} }}" + +# Holds the final Etherpad configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_etherpad_configuration_json`. +matrix_etherpad_configuration: "{{ matrix_etherpad_configuration_default|combine(matrix_etherpad_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-etherpad/tasks/init.yml b/roles/matrix-etherpad/tasks/init.yml new file mode 100644 index 00000000..081d4c23 --- /dev/null +++ b/roles/matrix-etherpad/tasks/init.yml @@ -0,0 +1,62 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-etherpad.service'] }}" + when: matrix_etherpad_enabled|bool + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append Etherpad's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-etherpad role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate Etherpad proxying configuration for matrix-nginx-proxy + set_fact: + matrix_etherpad_matrix_nginx_proxy_configuration: | + rewrite ^{{ matrix_etherpad_public_endpoint }}$ $scheme://$server_name{{ matrix_etherpad_public_endpoint }}/ permanent; + + location {{ matrix_etherpad_public_endpoint }}/ { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + proxy_pass http://matrix-etherpad:9001/; + {# These are proxy directives needed specifically by Etherpad #} + proxy_buffering off; + proxy_http_version 1.1; # recommended with keepalive connections + proxy_pass_header Server; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto $scheme; # for EP to set secure cookie flag when https is used + # WebSocket proxying - from http://nginx.org/en/docs/http/websocket.html + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + {% else %} + {# Generic configuration for use outside of our container setup #} + # A good guide for setting up your Etherpad behind nginx: + # https://docs.gandi.net/en/cloud/tutorials/etherpad_lite.html + proxy_pass http://127.0.0.1:9001/; + {% endif %} + } + + - name: Register Etherpad proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_dimension_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_dimension_additional_server_configuration_blocks|default([]) + + + [matrix_etherpad_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_etherpad_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the Etherpad tool but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ matrix_etherpad_public_endpoint }}` + URL endpoint to the matrix-etherpad container. + You can expose the container's port using the `matrix_etherpad_container_http_host_bind_port` variable. + when: "matrix_etherpad_enabled|bool and matrix_nginx_proxy_enabled is not defined" diff --git a/roles/matrix-etherpad/tasks/main.yml b/roles/matrix-etherpad/tasks/main.yml new file mode 100644 index 00000000..27548aaf --- /dev/null +++ b/roles/matrix-etherpad/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: run_setup|bool and matrix_etherpad_enabled|bool + tags: + - setup-all + - setup-etherpad + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: run_setup|bool and not matrix_etherpad_enabled|bool + tags: + - setup-all + - setup-etherpad + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: run_setup|bool and matrix_etherpad_enabled|bool + tags: + - setup-all + - setup-etherpad diff --git a/roles/matrix-etherpad/tasks/setup_install.yml b/roles/matrix-etherpad/tasks/setup_install.yml new file mode 100644 index 00000000..a93c28de --- /dev/null +++ b/roles/matrix-etherpad/tasks/setup_install.yml @@ -0,0 +1,36 @@ +--- + +- name: Ensure Etherpad base path exists + file: + path: "{{ matrix_etherpad_base_path }}" + state: directory + mode: 0770 + owner: "{{ matrix_etherpad_user_uid }}" + group: "{{ matrix_etherpad_user_gid }}" + +- name: Ensure Etherpad config installed + copy: + content: "{{ matrix_etherpad_configuration|to_nice_json }}" + dest: "{{ matrix_etherpad_base_path }}/settings.json" + mode: 0640 + owner: "{{ matrix_etherpad_user_uid }}" + group: "{{ matrix_etherpad_user_gid }}" + +- name: Ensure Etherpad image is pulled + docker_image: + name: "{{ matrix_etherpad_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_etherpad_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_etherpad_docker_image_force_pull }}" + +- name: Ensure matrix-etherpad.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-etherpad.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-etherpad.service" + mode: 0644 + register: matrix_etherpad_systemd_service_result + +- name: Ensure systemd reloaded after matrix-etherpad.service installation + service: + daemon_reload: yes + when: "matrix_etherpad_systemd_service_result.changed|bool" diff --git a/roles/matrix-etherpad/tasks/setup_uninstall.yml b/roles/matrix-etherpad/tasks/setup_uninstall.yml new file mode 100644 index 00000000..8f40f420 --- /dev/null +++ b/roles/matrix-etherpad/tasks/setup_uninstall.yml @@ -0,0 +1,35 @@ +--- + +- name: Check existence of matrix-etherpad service + stat: + path: "{{ matrix_systemd_path }}/matrix-etherpad.service" + register: matrix_etherpad_service_stat + +- name: Ensure matrix-etherpad is stopped + service: + name: matrix-etherpad + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_etherpad_service_stat.stat.exists|bool" + +- name: Ensure matrix-etherpad.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-etherpad.service" + state: absent + when: "matrix_etherpad_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-etherpad.service removal + service: + daemon_reload: yes + when: "matrix_etherpad_service_stat.stat.exists|bool" + +- name: Ensure Etherpad base directory doesn't exist + file: + path: "{{ matrix_etherpad_base_path }}" + state: absent + +- name: Ensure Etherpad Docker image doesn't exist + docker_image: + name: "{{ matrix_etherpad_docker_image }}" + state: absent diff --git a/roles/matrix-etherpad/tasks/validate_config.yml b/roles/matrix-etherpad/tasks/validate_config.yml new file mode 100644 index 00000000..c76dc3b5 --- /dev/null +++ b/roles/matrix-etherpad/tasks/validate_config.yml @@ -0,0 +1,11 @@ +- name: Fail if Etherpad is enabled without the Dimension integrations manager + fail: + msg: >- + To integrate Etherpad notes with Matrix rooms you need to set "matrix_dimension_enabled" to true + when: "not matrix_dimension_enabled|bool" + +- name: Fail if no database is configured for Etherpad + fail: + msg: >- + Etherpad requires a dedicated Postgres database. Please enable the built in one, or configure an external DB by redefining "matrix_etherpad_database_hostname" + when: matrix_etherpad_database_hostname == "matrix-postgres" and not matrix_postgres_enabled diff --git a/roles/matrix-etherpad/templates/settings.json.j2 b/roles/matrix-etherpad/templates/settings.json.j2 new file mode 100644 index 00000000..377bad98 --- /dev/null +++ b/roles/matrix-etherpad/templates/settings.json.j2 @@ -0,0 +1,105 @@ +{ + "title": {{ matrix_etherpad_title|to_json }}, + "favicon": "favicon.ico", + "skinName": "colibris", + "skinVariants": "super-light-toolbar super-light-editor light-background", + "ip": "::", + "port": 9001, + "showSettingsInAdminPage": true, + "dbType": {{ matrix_etherpad_database_engine|to_json }}, + "dbSettings": { + "database": {{ matrix_etherpad_database_name|to_json }}, + "host": {{ matrix_etherpad_database_hostname|to_json }}, + "password": {{ matrix_etherpad_database_password|to_json }}, + "port": {{ matrix_etherpad_database_port|to_json }}, + "user": {{ matrix_etherpad_database_username|to_json }} + }, + "defaultPadText" : {{ matrix_etherpad_default_pad_text|to_json }}, + "suppressErrorsInPadText": false, + "requireSession": false, + "editOnly": false, + "minify": true, + "maxAge": 21600, + "abiword": null, + "soffice": null, + "tidyHtml": null, + "allowUnknownFileEnds": true, + "requireAuthentication": false, + "requireAuthorization": false, + "trustProxy": true, + "cookie": { + "sameSite": "Lax" + }, + "disableIPlogging": true, + "automaticReconnectionTimeout": 0, + "scrollWhenFocusLineIsOutOfViewport": { + "percentage": { + "editionAboveViewport": 0, + "editionBelowViewport": 0 + }, + "duration": 0, + "scrollWhenCaretIsInTheLastLineOfViewport": false, + "percentageToScrollWhenUserPressesArrowUp": 0 + }, + "socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"], + "socketIo": { + "maxHttpBufferSize": 10000 + }, + "loadTest": false, + "importExportRateLimiting": { + "windowMs": 90000, + "max": 10 + }, + "importMaxFileSize": 52428800, + "commitRateLimiting": { + "duration": 1, + "points": 10 + }, + "exposeVersion": false, + "padOptions": { + "noColors": false, + "showControls": true, + "showChat": false, + "showLineNumbers": true, + "useMonospaceFont": false, + "userName": false, + "userColor": false, + "rtl": false, + "alwaysShowChat": false, + "chatAndUsers": false, + "lang": "en-gb" + }, + "padShortcutEnabled" : { + "altF9": true, + "altC": true, + "cmdShift2": true, + "delete": true, + "return": true, + "esc": true, + "cmdS": true, + "tab": true, + "cmdZ": true, + "cmdY": true, + "cmdI": true, + "cmdB": true, + "cmdU": true, + "cmd5": true, + "cmdShiftL": true, + "cmdShiftN": true, + "cmdShift1": true, + "cmdShiftC": true, + "cmdH": true, + "ctrlHome": true, + "pageUp": true, + "pageDown": true + }, + "loglevel": "INFO", + "logconfig" : + { "appenders": [ + { "type": "console", + "layout": {"type": "messagePassThrough"} + } + ] + }, + "customLocaleStrings": {} +} diff --git a/roles/matrix-etherpad/templates/systemd/matrix-etherpad.service.j2 b/roles/matrix-etherpad/templates/systemd/matrix-etherpad.service.j2 new file mode 100644 index 00000000..b579036b --- /dev/null +++ b/roles/matrix-etherpad/templates/systemd/matrix-etherpad.service.j2 @@ -0,0 +1,44 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Etherpad +{% for service in matrix_etherpad_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_etherpad_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_docker }} kill matrix-etherpad +ExecStartPre=-{{ matrix_host_command_docker }} rm matrix-etherpad + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-etherpad \ + --log-driver=none \ + --user={{ matrix_etherpad_user_uid }}:{{ matrix_etherpad_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_etherpad_container_http_host_bind_port %} + -p {{ matrix_etherpad_container_http_host_bind_port }}:9001 \ + {% endif %} + --mount type=bind,src={{ matrix_etherpad_base_path }},dst=/data \ + {% for arg in matrix_etherpad_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_etherpad_docker_image }} \ + node --experimental-worker src/node/server.js \ + --settings /data/settings.json --credentials /data/credentials.json \ + --sessionkey /data/sessionkey.json --apikey /data/apijey.json + + +ExecStop=-{{ matrix_host_command_docker }} kill matrix-etherpad +ExecStop=-{{ matrix_host_command_docker }} rm matrix-etherpad +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-etherpad + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-grafana/defaults/main.yml b/roles/matrix-grafana/defaults/main.yml new file mode 100644 index 00000000..45bd2d91 --- /dev/null +++ b/roles/matrix-grafana/defaults/main.yml @@ -0,0 +1,59 @@ +# matrix-grafana is open source visualization and analytics software +# See: https://github.com/matrix-org/synapse/blob/master/docs/metrics-howto.md + +matrix_grafana_enabled: false + +matrix_grafana_version: 8.0.6 +matrix_grafana_docker_image: "{{ matrix_container_global_registry_prefix }}grafana/grafana:{{ matrix_grafana_version }}" +matrix_grafana_docker_image_force_pull: "{{ matrix_grafana_docker_image.endswith(':latest') }}" + +# Not conditional, because when someone disables metrics +# they might still want to look at the old existing data. +# So it would be silly to delete the dashboard in such case. +matrix_grafana_dashboard_download_urls: +- "https://raw.githubusercontent.com/matrix-org/synapse/master/contrib/grafana/synapse.json" +- "https://raw.githubusercontent.com/rfrail3/grafana-dashboards/master/prometheus/node-exporter-full.json" + +matrix_grafana_base_path: "{{ matrix_base_data_path }}/grafana" +matrix_grafana_config_path: "{{ matrix_grafana_base_path }}/config" +matrix_grafana_data_path: "{{ matrix_grafana_base_path }}/data" + +# Allow viewing Grafana without logging in +matrix_grafana_anonymous_access: false + +# specify organization name that should be used for unauthenticated users +# if you change this in the Grafana admin panel, this needs to be updated +# to match to keep anonymous logins working +matrix_grafana_anonymous_access_org_name: 'Main Org.' + + +# default admin credentials, you are asked to change these on first login +matrix_grafana_default_admin_user: admin +matrix_grafana_default_admin_password: admin + +# Set to true to add the Content-Security-Policy header to your requests. +# CSP allows to control resources that the user agent can load and helps +# prevent XSS attacks. +# [Content Security Policy](https://grafana.com/docs/grafana/latest/administration/configuration/#content_security_policy) +matrix_grafana_content_security_policy: true + +# specify content security policy template to customized template +# added https: and http: url schemes (ignored by browsers supporting 'strict-dynamic') to be backward compatible with older browsers. +# [Content Security Policy Browser Test] (https://content-security-policy.com/browser-test/) +# [Content Security Policy Reference](https://content-security-policy.com/script-src/) +matrix_grafana_content_security_policy_customized: false +matrix_grafana_content_security_policy_template: "script-src 'self' 'unsafe-eval' 'unsafe-inline' http: https: 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';" + +# A list of extra arguments to pass to the container +matrix_grafana_container_extra_arguments: [] + +# List of systemd services that matrix-grafana.service depends on +matrix_grafana_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-grafana.service wants +matrix_grafana_systemd_wanted_services_list: [] + +# Controls whether the matrix-grafana container exposes its HTTP port (tcp/3000 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:3000"), or empty string to not expose. +matrix_grafana_container_http_host_bind_port: '' diff --git a/roles/matrix-grafana/tasks/init.yml b/roles/matrix-grafana/tasks/init.yml new file mode 100644 index 00000000..8a22e301 --- /dev/null +++ b/roles/matrix-grafana/tasks/init.yml @@ -0,0 +1,5 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-grafana.service'] }}" + when: matrix_grafana_enabled|bool + + diff --git a/roles/matrix-grafana/tasks/main.yml b/roles/matrix-grafana/tasks/main.yml new file mode 100644 index 00000000..fb16c394 --- /dev/null +++ b/roles/matrix-grafana/tasks/main.yml @@ -0,0 +1,14 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_grafana_enabled|bool" + tags: + - setup-all + - setup-grafana + +- import_tasks: "{{ role_path }}/tasks/setup.yml" + tags: + - setup-all + - setup-grafana diff --git a/roles/matrix-grafana/tasks/setup.yml b/roles/matrix-grafana/tasks/setup.yml new file mode 100644 index 00000000..00d2e230 --- /dev/null +++ b/roles/matrix-grafana/tasks/setup.yml @@ -0,0 +1,110 @@ +--- + +# +# Tasks related to setting up matrix-grafana +# + +- name: Ensure matrix-grafana image is pulled + docker_image: + name: "{{ matrix_grafana_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_grafana_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_grafana_docker_image_force_pull }}" + when: "matrix_grafana_enabled|bool" + +- name: Ensure grafana paths exists + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_grafana_base_path }}" + - "{{ matrix_grafana_config_path }}" + - "{{ matrix_grafana_config_path }}/provisioning" + - "{{ matrix_grafana_config_path }}/provisioning/datasources" + - "{{ matrix_grafana_config_path }}/provisioning/dashboards" + - "{{ matrix_grafana_config_path }}/dashboards" + - "{{ matrix_grafana_data_path }}" + when: matrix_grafana_enabled|bool + +- name: Ensure grafana.ini present + template: + src: "{{ role_path }}/templates/grafana.ini.j2" + dest: "{{ matrix_grafana_config_path }}/grafana.ini" + mode: 0440 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_grafana_enabled|bool + +- name: Ensure provisioning/datasources/default.yaml present + template: + src: "{{ role_path }}/templates/datasources.yaml.j2" + dest: "{{ matrix_grafana_config_path }}/provisioning/datasources/default.yaml" + mode: 0440 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_grafana_enabled|bool + +- name: Ensure provisioning/dashboards/default.yaml present + template: + src: "{{ role_path }}/templates/dashboards.yaml.j2" + dest: "{{ matrix_grafana_config_path }}/provisioning/dashboards/default.yaml" + mode: 0440 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_grafana_enabled|bool + +- name: Ensure dashboard(s) downloaded + get_url: + url: "{{ item }}" + dest: "{{ matrix_grafana_config_path }}/dashboards/" + force: true + mode: 0440 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: "{{ matrix_grafana_dashboard_download_urls_all }}" + when: matrix_grafana_enabled|bool + +- name: Ensure matrix-grafana.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-grafana.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-grafana.service" + mode: 0644 + register: matrix_grafana_systemd_service_result + when: matrix_grafana_enabled|bool + +- name: Ensure systemd reloaded after matrix-grafana.service installation + service: + daemon_reload: yes + when: "matrix_grafana_enabled|bool and matrix_grafana_systemd_service_result.changed" + +# +# Tasks related to getting rid of matrix-grafana (if it was previously enabled) +# + +- name: Check existence of matrix-grafana service + stat: + path: "{{ matrix_systemd_path }}/matrix-grafana.service" + register: matrix_grafana_service_stat + +- name: Ensure matrix-grafana is stopped + service: + name: matrix-grafana + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_grafana_enabled|bool and matrix_grafana_service_stat.stat.exists" + +- name: Ensure matrix-grafana.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-grafana.service" + state: absent + when: "not matrix_grafana_enabled|bool and matrix_grafana_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-grafana.service removal + service: + daemon_reload: yes + when: "not matrix_grafana_enabled|bool and matrix_grafana_service_stat.stat.exists" + diff --git a/roles/matrix-grafana/tasks/validate_config.yml b/roles/matrix-grafana/tasks/validate_config.yml new file mode 100644 index 00000000..63d4919a --- /dev/null +++ b/roles/matrix-grafana/tasks/validate_config.yml @@ -0,0 +1,7 @@ +--- + +- name: Fail if Prometheus not enabled + fail: + msg: > + You need to enable `matrix_prometheus_enabled` to use Prometheus as data source for Grafana. + when: "not matrix_prometheus_enabled" diff --git a/roles/matrix-grafana/templates/dashboards.yaml.j2 b/roles/matrix-grafana/templates/dashboards.yaml.j2 new file mode 100644 index 00000000..aae42ba2 --- /dev/null +++ b/roles/matrix-grafana/templates/dashboards.yaml.j2 @@ -0,0 +1,9 @@ +apiVersion: 1 + +providers: + - name: {{ matrix_server_fqn_matrix }} - Dashboards + folder: '' # The folder where to place the dashboards + type: file + allowUiUpdates: true + options: + path: /etc/grafana/dashboards diff --git a/roles/matrix-grafana/templates/datasources.yaml.j2 b/roles/matrix-grafana/templates/datasources.yaml.j2 new file mode 100644 index 00000000..6ccbe374 --- /dev/null +++ b/roles/matrix-grafana/templates/datasources.yaml.j2 @@ -0,0 +1,8 @@ +apiVersion: 1 + +datasources: + - name: {{ matrix_server_fqn_matrix }} - Prometheus + type: prometheus + # Access mode - proxy (server in the UI) or direct (browser in the UI). + access: proxy + url: http://matrix-prometheus:9090 diff --git a/roles/matrix-grafana/templates/grafana.ini.j2 b/roles/matrix-grafana/templates/grafana.ini.j2 new file mode 100644 index 00000000..8f4c88f0 --- /dev/null +++ b/roles/matrix-grafana/templates/grafana.ini.j2 @@ -0,0 +1,31 @@ +[server] +root_url = "https://{{ matrix_server_fqn_grafana }}" + +[security] +# default admin user, created on startup +admin_user = "{{ matrix_grafana_default_admin_user }}" + +# default admin password, can be changed before first start of grafana, or in profile settings +admin_password = """{{ matrix_grafana_default_admin_password }}""" + +# specify content_security_policy to add the Content-Security-Policy header to your requests +content_security_policy = "{{ matrix_grafana_content_security_policy }}" + +# specify content security policy template to customized template +{% if matrix_grafana_content_security_policy_customized %} +content_security_policy_template = """{{ matrix_grafana_content_security_policy_template }}""" +{% endif %} + +[auth.anonymous] +# enable anonymous access +enabled = {{ matrix_grafana_anonymous_access }} + +# specify organization name that should be used for unauthenticated users +org_name = "{{ matrix_grafana_anonymous_access_org_name }}" + +[dashboards] +{% if matrix_synapse_metrics_enabled %} +default_home_dashboard_path = /etc/grafana/dashboards/synapse.json +{% else %} +default_home_dashboard_path = /etc/grafana/dashboards/node-exporter-full.json +{% endif %} diff --git a/roles/matrix-grafana/templates/systemd/matrix-grafana.service.j2 b/roles/matrix-grafana/templates/systemd/matrix-grafana.service.j2 new file mode 100644 index 00000000..a4f81e35 --- /dev/null +++ b/roles/matrix-grafana/templates/systemd/matrix-grafana.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=matrix-grafana +{% for service in matrix_grafana_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_grafana_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-grafana 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-grafana 2>/dev/null' + + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-grafana \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + {% if matrix_grafana_container_http_host_bind_port %} + -p {{ matrix_grafana_container_http_host_bind_port }}:3000 \ + {% endif %} + -v {{ matrix_grafana_config_path }}:/etc/grafana:z \ + -v {{ matrix_grafana_data_path }}:/var/lib/grafana:z \ + {% for arg in matrix_grafana_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_grafana_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-grafana 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-grafana 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-grafana + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-jitsi/defaults/main.yml b/roles/matrix-jitsi/defaults/main.yml new file mode 100644 index 00000000..8c8d9156 --- /dev/null +++ b/roles/matrix-jitsi/defaults/main.yml @@ -0,0 +1,261 @@ +matrix_jitsi_enabled: true + +matrix_jitsi_base_path: "{{ matrix_base_data_path }}/jitsi" + +matrix_jitsi_enable_auth: false +matrix_jitsi_enable_guests: false +matrix_jitsi_enable_recording: false +matrix_jitsi_enable_transcriptions: false +matrix_jitsi_enable_p2p: true + +# Authentication type, must be one of internal, jwt or ldap. Currently only +# internal and ldap are supported by this playbook. +matrix_jitsi_auth_type: internal + +# Configuration options for LDAP authentication. For details see upstream: +# https://github.com/jitsi/docker-jitsi-meet#authentication-using-ldap. +# Defaults are taken from: +# https://github.com/jitsi/docker-jitsi-meet/blob/master/prosody/rootfs/defaults/saslauthd.conf +matrix_jitsi_ldap_url: "" +matrix_jitsi_ldap_base: "" +matrix_jitsi_ldap_binddn: "" +matrix_jitsi_ldap_bindpw: "" +matrix_jitsi_ldap_filter: "uid=%u" +matrix_jitsi_ldap_auth_method: "bind" +matrix_jitsi_ldap_version: "3" +matrix_jitsi_ldap_use_tls: false +matrix_jitsi_ldap_tls_ciphers: "" +matrix_jitsi_ldap_tls_check_peer: false +matrix_jitsi_ldap_tls_cacert_file: "/etc/ssl/certs/ca-certificates.crt" +matrix_jitsi_ldap_tls_cacert_dir: "/etc/ssl/certs" +matrix_jitsi_ldap_start_tls: false + +matrix_jitsi_timezone: UTC + +matrix_jitsi_xmpp_domain: meet.jitsi +matrix_jitsi_xmpp_server: xmpp.meet.jitsi +matrix_jitsi_xmpp_auth_domain: auth.meet.jitsi +matrix_jitsi_xmpp_bosh_url_base: http://{{ matrix_jitsi_xmpp_server }}:5280 +matrix_jitsi_xmpp_guest_domain: guest.meet.jitsi +matrix_jitsi_xmpp_muc_domain: muc.meet.jitsi +matrix_jitsi_xmpp_internal_muc_domain: internal-muc.meet.jitsi +matrix_jitsi_xmpp_modules: '' + +matrix_jitsi_recorder_domain: recorder.meet.jitsi + + +matrix_jitsi_jibri_brewery_muc: jibribrewery +matrix_jitsi_jibri_pending_timeout: 90 +matrix_jitsi_jibri_xmpp_user: jibri +matrix_jitsi_jibri_xmpp_password: '' +matrix_jitsi_jibri_recorder_user: recorder +matrix_jitsi_jibri_recorder_password: '' + +matrix_jitsi_enable_lobby: false + +matrix_jitsi_version: stable-5963 +matrix_jitsi_container_image_tag: "{{ matrix_jitsi_version }}" # for backward-compatibility + +matrix_jitsi_web_docker_image: "{{ matrix_container_global_registry_prefix }}jitsi/web:{{ matrix_jitsi_container_image_tag }}" +matrix_jitsi_web_docker_image_force_pull: "{{ matrix_jitsi_web_docker_image.endswith(':latest') }}" + +matrix_jitsi_web_base_path: "{{ matrix_base_data_path }}/jitsi/web" +matrix_jitsi_web_config_path: "{{ matrix_jitsi_web_base_path }}/config" +matrix_jitsi_web_transcripts_path: "{{ matrix_jitsi_web_base_path }}/transcripts" + +matrix_jitsi_web_public_url: "https://{{ matrix_server_fqn_jitsi }}" + +# STUN servers used in the web UI. Feel free to point them to your own STUN server. +# Addresses need to be prefixed with one of `stun:`, `turn:` or `turns:`. +matrix_jitsi_web_stun_servers: ['stun:meet-jit-si-turnrelay.jitsi.net:443'] + +# Controls whether Etherpad will be available within Jitsi +matrix_jitsi_etherpad_enabled: false + +# Controls whether the matrix-jitsi-web container exposes its HTTP port (tcp/80 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:13080"), or empty string to not expose. +matrix_jitsi_web_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_jitsi_web_container_extra_arguments: [] + +# List of systemd services that matrix-jitsi-web.service depends on +matrix_jitsi_web_systemd_required_services_list: ['docker.service'] + + +# Some variables controlling the interface of Jitsi Web. +# These get applied to `templates/web/interface_config.js.j2`. +# +# Besides this, you can also use `matrix_jitsi_web_custom_interface_config_extension` +# to define any other configuration option. +matrix_jitsi_web_interface_config_lang_detection: false +matrix_jitsi_web_interface_config_show_jitsi_watermark: true +matrix_jitsi_web_interface_config_jitsi_watermark_link: "https://jitsi.org" +matrix_jitsi_web_interface_config_show_brand_watermark: false +matrix_jitsi_web_interface_config_brand_watermark_link: "" +matrix_jitsi_web_interface_config_generate_room_names_on_welcome_page: true +matrix_jitsi_web_interface_config_display_welcome_page_content: true +matrix_jitsi_web_interface_config_app_name: "Jitsi Meet" +matrix_jitsi_web_interface_config_native_app_name: "Jitsi Meet" +matrix_jitsi_web_interface_config_provider_name: "Jitsi" +matrix_jitsi_web_interface_config_show_powered_by: false +matrix_jitsi_web_interface_config_disable_transcription_subtitles: false +matrix_jitsi_web_interface_config_show_deep_linking_image: false + +# Custom configuration to be injected into `interface_config.js`, passed to Jitsi Web. +# This configuration gets appended to the final interface configuration that Jitsi Web uses. +# +# Note: not to be confused with `matrix_jitsi_web_custom_config_extension`. +# +# For interface configuration, the flow is like this: +# - the contents of `templates/web/interface_config.js.j2` is generated (based on various `matrix_jitsi_web_interface_config_*` variables you see in this file) +# - the contents of `matrix_jitsi_web_custom_interface_config_extension` is appended and can define new settings or override defaults. +# +# Example: +# matrix_jitsi_web_custom_interface_config_extension: | +# interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_ENABLED = false; +# interfaceConfig.DISABLE_VIDEO_BACKGROUND = true; +matrix_jitsi_web_custom_interface_config_extension: '' + + +# Controls after which participant audio will be muted. If not specified, defaults to Jitsi's default value (likely 10) +matrix_jitsi_web_config_start_audio_muted_after_nth_participant: ~ +# Controls after which participant video will be muted. If not specified, defaults to Jitsi's default value (likely 10) +matrix_jitsi_web_config_start_video_muted_after_nth_participant: ~ + +matrix_jitsi_web_config_defaultLanguage: 'en' + +# Ideal and also maximum resolution width. If not specified, defaults to Jitsi's default value (likely 1280) +matrix_jitsi_web_config_resolution_width_ideal_and_max: ~ +# Minimum resolution width. If not specified, defaults to Jitsi's default value (likely 320) +matrix_jitsi_web_config_resolution_width_min: ~ +# Ideal and also maximum resolution height. If not specified, defaults to Jitsi's default value (likely 720) +matrix_jitsi_web_config_resolution_height_ideal_and_max: ~ +# Minimum resolution height. If not specified, defaults to Jitsi's default value (likely 180) +matrix_jitsi_web_config_resolution_height_min: ~ + +# Custom configuration to be injected into `custom-config.js`, passed to Jitsi Web. +# This configuration gets appended to the final configuration that Jitsi Web uses. +# +# Note: not to be confused with `matrix_jitsi_web_custom_interface_config_extension`. +# +# The flow is like this: +# - some default configuration is automatically generated based on the environment variables passed to the Jitsi Web container +# - the contents of `custom-config.js` is appended to it (see `templates/web/custom-config.js.j2`) +# - said `custom-config.js` contains your custom contents specified in `matrix_jitsi_web_custom_config_extension`. +# +# Example: +# matrix_jitsi_web_custom_config_extension: | +# if (!config.hasOwnProperty('testing')) config.testing = {}; +# config.testing.p2pTestMode = true +matrix_jitsi_web_custom_config_extension: '' + +# Additional environment variables to pass to the Jitsi Web container. +# You can use this to further influence the default configuration generated by the Jitsi Web container on every startup. +# Besides influencing the final configuration by passing environment variables, you can also inject custom configuration +# by using `matrix_jitsi_web_custom_config_extension`. +# +# Example: +# matrix_jitsi_web_environment_variables_extension: | +# ENABLE_FILE_RECORDING_SERVICE=1 +# DROPBOX_APPKEY=something +# DROPBOX_REDIRECT_URI=something +matrix_jitsi_web_environment_variables_extension: '' + + +matrix_jitsi_prosody_docker_image: "{{ matrix_container_global_registry_prefix }}jitsi/prosody:{{ matrix_jitsi_container_image_tag }}" +matrix_jitsi_prosody_docker_image_force_pull: "{{ matrix_jitsi_prosody_docker_image.endswith(':latest') }}" + +matrix_jitsi_prosody_base_path: "{{ matrix_base_data_path }}/jitsi/prosody" +matrix_jitsi_prosody_config_path: "{{ matrix_jitsi_prosody_base_path }}/config" +matrix_jitsi_prosody_plugins_path: "{{ matrix_jitsi_prosody_base_path }}/prosody-plugins-custom" + +# A list of extra arguments to pass to the container +matrix_jitsi_prosody_container_extra_arguments: [] + +# List of systemd services that matrix-jitsi-prosody.service depends on +matrix_jitsi_prosody_systemd_required_services_list: ['docker.service'] + +# Neccessary Port binding for those disabling the integrated nginx proxy +matrix_jitsi_prosody_container_http_host_bind_port: '' + +matrix_jitsi_jicofo_docker_image: "{{ matrix_container_global_registry_prefix }}jitsi/jicofo:{{ matrix_jitsi_container_image_tag }}" +matrix_jitsi_jicofo_docker_image_force_pull: "{{ matrix_jitsi_jicofo_docker_image.endswith(':latest') }}" + +matrix_jitsi_jicofo_base_path: "{{ matrix_base_data_path }}/jitsi/jicofo" +matrix_jitsi_jicofo_config_path: "{{ matrix_jitsi_jicofo_base_path }}/config" + +# A list of extra arguments to pass to the container +matrix_jitsi_jicofo_container_extra_arguments: [] + +# List of systemd services that matrix-jitsi-jicofo.service depends on +matrix_jitsi_jicofo_systemd_required_services_list: ['docker.service', 'matrix-jitsi-prosody.service'] + +matrix_jitsi_jicofo_component_secret: '' +matrix_jitsi_jicofo_auth_user: focus +matrix_jitsi_jicofo_auth_password: '' + + +matrix_jitsi_jvb_docker_image: "{{ matrix_container_global_registry_prefix }}jitsi/jvb:{{ matrix_jitsi_container_image_tag }}" +matrix_jitsi_jvb_docker_image_force_pull: "{{ matrix_jitsi_jvb_docker_image.endswith(':latest') }}" + +matrix_jitsi_jvb_base_path: "{{ matrix_base_data_path }}/jitsi/jvb" +matrix_jitsi_jvb_config_path: "{{ matrix_jitsi_jvb_base_path }}/config" + +# A list of extra arguments to pass to the container +matrix_jitsi_jvb_container_extra_arguments: [] + +# List of systemd services that matrix-jitsi-jvb.service depends on +matrix_jitsi_jvb_systemd_required_services_list: ['docker.service', 'matrix-jitsi-prosody.service'] + +matrix_jitsi_jvb_auth_user: jvb +matrix_jitsi_jvb_auth_password: '' + +# STUN servers used by JVB on the server-side, so it can discover its own external IP address. +# Pointing this to a STUN server running on the same Docker network may lead to incorrect IP address discovery. +matrix_jitsi_jvb_stun_servers: ['meet-jit-si-turnrelay.jitsi.net:443'] + +matrix_jitsi_jvb_brewery_muc: jvbbrewery +matrix_jitsi_jvb_rtp_udp_port: 10000 +matrix_jitsi_jvb_rtp_tcp_port: 4443 + +# Custom configuration to be injected into `custom-sip-communicator.properties`, passed to Jitsi JVB. +# This configuration gets appended to the final configuration that Jitsi JVB uses. +# +# The flow is like this: +# - some default configuration is automatically generated based on the environment variables passed to the Jitsi JVB container +# - the contents of `custom-sip-communicator.properties` is appended to it (see `templates/jvb/custom-sip-communicator.properties.j2`) +# - said `custom-sip-communicator.properties` contains your custom contents specified in `matrix_jitsi_jvb_custom_config_extension`. +# +# Example: +# matrix_jitsi_jvb_custom_config_extension: | +# org.jitsi.videobridge.xmpp.user.shard.DISABLE_CERTIFICATE_VERIFICATION=false +# org.jitsi.videobridge.ENABLE_STATISTICS=false +matrix_jitsi_jvb_custom_config_extension: '' + +# Additional environment variables to pass to the Jitsi JVB container. +# You can use this to further influence the default configuration generated by the Jitsi JVB container on every startup. +# Besides influencing the final configuration by passing environment variables, you can also inject custom configuration +# by using `matrix_jitsi_jvb_custom_config_extension`. +# +# Example: +# matrix_jitsi_jvb_environment_variables_extension: | +# SOME_VARIABLE=1 +# ANOTHER_VARIABLE=something +matrix_jitsi_jvb_environment_variables_extension: '' + +# Controls whether the matrix-jitsi-jvb container exposes its RTP UDP port (udp/10000 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:10000"), or empty string to not expose. +matrix_jitsi_jvb_container_rtp_udp_host_bind_port: "{{ matrix_jitsi_jvb_rtp_udp_port }}" + +# Controls whether the matrix-jitsi-jvb container exposes its RTP UDP port (udp/4443 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:4443"), or empty string to not expose. +matrix_jitsi_jvb_container_rtp_tcp_host_bind_port: "{{ matrix_jitsi_jvb_rtp_tcp_port }}" + +# Controls whether the matrix-jitsi-jvb container exposes its Colibri WebSocket port (tcp/9090 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:12090"), or empty string to not expose. +matrix_jitsi_jvb_container_colibri_ws_host_bind_port: '' diff --git a/roles/matrix-jitsi/tasks/init.yml b/roles/matrix-jitsi/tasks/init.yml new file mode 100644 index 00000000..1f7a2d1c --- /dev/null +++ b/roles/matrix-jitsi/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-jitsi-web.service', 'matrix-jitsi-prosody.service', 'matrix-jitsi-jicofo.service', 'matrix-jitsi-jvb.service'] }}" + when: matrix_jitsi_enabled|bool diff --git a/roles/matrix-jitsi/tasks/main.yml b/roles/matrix-jitsi/tasks/main.yml new file mode 100644 index 00000000..e4f3508f --- /dev/null +++ b/roles/matrix-jitsi/tasks/main.yml @@ -0,0 +1,39 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_jitsi_enabled|bool" + tags: + - setup-all + - setup-jitsi + +- import_tasks: "{{ role_path }}/tasks/setup_jitsi_base.yml" + when: run_setup|bool + tags: + - setup-all + - setup-jitsi + +- import_tasks: "{{ role_path }}/tasks/setup_jitsi_web.yml" + when: run_setup|bool + tags: + - setup-all + - setup-jitsi + +- import_tasks: "{{ role_path }}/tasks/setup_jitsi_prosody.yml" + when: run_setup|bool + tags: + - setup-all + - setup-jitsi + +- import_tasks: "{{ role_path }}/tasks/setup_jitsi_jicofo.yml" + when: run_setup|bool + tags: + - setup-all + - setup-jitsi + +- import_tasks: "{{ role_path }}/tasks/setup_jitsi_jvb.yml" + when: run_setup|bool + tags: + - setup-all + - setup-jitsi diff --git a/roles/matrix-jitsi/tasks/setup_jitsi_base.yml b/roles/matrix-jitsi/tasks/setup_jitsi_base.yml new file mode 100644 index 00000000..408027ee --- /dev/null +++ b/roles/matrix-jitsi/tasks/setup_jitsi_base.yml @@ -0,0 +1,20 @@ +--- + +# +# Tasks related to setting up jitsi +# + +- name: Ensure Matrix jitsi base path exists + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_jitsi_base_path }}", when: true } + when: matrix_jitsi_enabled|bool and item.when + +# +# Tasks related to getting rid of jitsi (if it was previously enabled) +# diff --git a/roles/matrix-jitsi/tasks/setup_jitsi_jicofo.yml b/roles/matrix-jitsi/tasks/setup_jitsi_jicofo.yml new file mode 100644 index 00000000..dd2a7bd2 --- /dev/null +++ b/roles/matrix-jitsi/tasks/setup_jitsi_jicofo.yml @@ -0,0 +1,93 @@ +--- + +# +# Tasks related to setting up jitsi-jicofo +# + +- name: Ensure Matrix jitsi-jicofo path exists + file: + path: "{{ item.path }}" + state: directory + mode: 0777 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_jitsi_jicofo_base_path }}", when: true } + - { path: "{{ matrix_jitsi_jicofo_config_path }}", when: true } + when: matrix_jitsi_enabled|bool and item.when + +- name: Ensure jitsi-jicofo Docker image is pulled + docker_image: + name: "{{ matrix_jitsi_jicofo_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_jitsi_jicofo_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_jitsi_jicofo_docker_image_force_pull }}" + when: matrix_jitsi_enabled|bool + +- name: Ensure jitsi-jicofo environment variables file created + template: + src: "{{ role_path }}/templates/jicofo/env.j2" + dest: "{{ matrix_jitsi_jicofo_base_path }}/env" + mode: 0640 + when: matrix_jitsi_enabled|bool + +- name: Ensure jitsi-jicofo configuration files created + template: + src: "{{ role_path }}/templates/jicofo/{{ item }}.j2" + dest: "{{ matrix_jitsi_jicofo_config_path }}/{{ item }}" + mode: 0644 + with_items: + - sip-communicator.properties + - logging.properties + when: matrix_jitsi_enabled|bool + +- name: Ensure matrix-jitsi-jicofo.service installed + template: + src: "{{ role_path }}/templates/jicofo/matrix-jitsi-jicofo.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-jitsi-jicofo.service" + mode: 0644 + register: matrix_jitsi_jicofo_systemd_service_result + when: matrix_jitsi_enabled|bool + +- name: Ensure systemd reloaded after matrix-jitsi-jicofo.service installation + service: + daemon_reload: yes + when: "matrix_jitsi_enabled and matrix_jitsi_jicofo_systemd_service_result.changed" + +# +# Tasks related to getting rid of jitsi-jicofo (if it was previously enabled) +# + +- name: Check existence of matrix-jitsi-jicofo service + stat: + path: "{{ matrix_systemd_path }}/matrix-jitsi-jicofo.service" + register: matrix_jitsi_jicofo_service_stat + when: "not matrix_jitsi_enabled|bool" + +- name: Ensure matrix-jitsi-jicofo is stopped + service: + name: matrix-jitsi-jicofo + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_jicofo_service_stat.stat.exists" + +- name: Ensure matrix-jitsi-jicofo.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-jitsi-jicofo.service" + state: absent + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_jicofo_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-jitsi-jicofo.service removal + service: + daemon_reload: yes + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_jicofo_service_stat.stat.exists" + +- name: Ensure Matrix jitsi-jicofo paths doesn't exist + file: + path: "{{ matrix_jitsi_jicofo_base_path }}" + state: absent + when: "not matrix_jitsi_enabled|bool" + +# Intentionally not removing the Docker image when uninstalling. +# We can't be sure it had been pulled by us in the first place. diff --git a/roles/matrix-jitsi/tasks/setup_jitsi_jvb.yml b/roles/matrix-jitsi/tasks/setup_jitsi_jvb.yml new file mode 100644 index 00000000..b73426db --- /dev/null +++ b/roles/matrix-jitsi/tasks/setup_jitsi_jvb.yml @@ -0,0 +1,93 @@ +--- + +# +# Tasks related to setting up jitsi-jvb +# + +- name: Ensure Matrix jitsi-jvb path exists + file: + path: "{{ item.path }}" + state: directory + mode: 0777 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_jitsi_jvb_base_path }}", when: true } + - { path: "{{ matrix_jitsi_jvb_config_path }}", when: true } + when: matrix_jitsi_enabled|bool and item.when + +- name: Ensure jitsi-jvb Docker image is pulled + docker_image: + name: "{{ matrix_jitsi_jvb_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_jitsi_jvb_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_jitsi_jvb_docker_image_force_pull }}" + when: matrix_jitsi_enabled|bool + +- name: Ensure jitsi-jvb configuration files created + template: + src: "{{ role_path }}/templates/jvb/{{ item }}.j2" + dest: "{{ matrix_jitsi_jvb_config_path }}/{{ item }}" + mode: 0644 + with_items: + - custom-sip-communicator.properties + - logging.properties + when: matrix_jitsi_enabled|bool + +- name: Ensure jitsi-jvb environment variables file created + template: + src: "{{ role_path }}/templates/jvb/env.j2" + dest: "{{ matrix_jitsi_jvb_base_path }}/env" + mode: 0640 + when: matrix_jitsi_enabled|bool + +- name: Ensure matrix-jitsi-jvb.service installed + template: + src: "{{ role_path }}/templates/jvb/matrix-jitsi-jvb.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-jitsi-jvb.service" + mode: 0644 + register: matrix_jitsi_jvb_systemd_service_result + when: matrix_jitsi_enabled|bool + +- name: Ensure systemd reloaded after matrix-jitsi-jvb.service installation + service: + daemon_reload: yes + when: "matrix_jitsi_enabled and matrix_jitsi_jvb_systemd_service_result.changed" + +# +# Tasks related to getting rid of jitsi-jvb (if it was previously enabled) +# + +- name: Check existence of matrix-jitsi-jvb service + stat: + path: "{{ matrix_systemd_path }}/matrix-jitsi-jvb.service" + register: matrix_jitsi_jvb_service_stat + when: "not matrix_jitsi_enabled|bool" + +- name: Ensure matrix-jitsi-jvb is stopped + service: + name: matrix-jitsi-jvb + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_jvb_service_stat.stat.exists" + +- name: Ensure matrix-jitsi-jvb.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-jitsi-jvb.service" + state: absent + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_jvb_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-jitsi-jvb.service removal + service: + daemon_reload: yes + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_jvb_service_stat.stat.exists" + +- name: Ensure Matrix jitsi-jvb paths doesn't exist + file: + path: "{{ matrix_jitsi_jvb_base_path }}" + state: absent + when: "not matrix_jitsi_enabled|bool" + +# Intentionally not removing the Docker image when uninstalling. +# We can't be sure it had been pulled by us in the first place. diff --git a/roles/matrix-jitsi/tasks/setup_jitsi_prosody.yml b/roles/matrix-jitsi/tasks/setup_jitsi_prosody.yml new file mode 100644 index 00000000..fd051fda --- /dev/null +++ b/roles/matrix-jitsi/tasks/setup_jitsi_prosody.yml @@ -0,0 +1,84 @@ +--- + +# +# Tasks related to setting up jitsi-prosody +# + +- name: Ensure Matrix jitsi-prosody path exists + file: + path: "{{ item.path }}" + state: directory + mode: 0777 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_jitsi_prosody_base_path }}", when: true } + - { path: "{{ matrix_jitsi_prosody_config_path }}", when: true } + - { path: "{{ matrix_jitsi_prosody_plugins_path }}", when: true } + when: matrix_jitsi_enabled|bool and item.when + +- name: Ensure jitsi-prosody Docker image is pulled + docker_image: + name: "{{ matrix_jitsi_prosody_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_jitsi_prosody_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_jitsi_prosody_docker_image_force_pull }}" + when: matrix_jitsi_enabled|bool + +- name: Ensure jitsi-prosody environment variables file created + template: + src: "{{ role_path }}/templates/prosody/env.j2" + dest: "{{ matrix_jitsi_prosody_base_path }}/env" + mode: 0640 + when: matrix_jitsi_enabled|bool + +- name: Ensure matrix-jitsi-prosody.service installed + template: + src: "{{ role_path }}/templates/prosody/matrix-jitsi-prosody.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-jitsi-prosody.service" + mode: 0644 + register: matrix_jitsi_prosody_systemd_service_result + when: matrix_jitsi_enabled|bool + +- name: Ensure systemd reloaded after matrix-jitsi-prosody.service installation + service: + daemon_reload: yes + when: "matrix_jitsi_enabled and matrix_jitsi_prosody_systemd_service_result.changed" + +# +# Tasks related to getting rid of jitsi-prosody (if it was previously enabled) +# + +- name: Check existence of matrix-jitsi-prosody service + stat: + path: "{{ matrix_systemd_path }}/matrix-jitsi-prosody.service" + register: matrix_jitsi_prosody_service_stat + when: "not matrix_jitsi_enabled|bool" + +- name: Ensure matrix-jitsi-prosody is stopped + service: + name: matrix-jitsi-prosody + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_prosody_service_stat.stat.exists" + +- name: Ensure matrix-jitsi-prosody.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-jitsi-prosody.service" + state: absent + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_prosody_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-jitsi-prosody.service removal + service: + daemon_reload: yes + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_prosody_service_stat.stat.exists" + +- name: Ensure Matrix jitsi-prosody paths doesn't exist + file: + path: "{{ matrix_jitsi_prosody_base_path }}" + state: absent + when: "not matrix_jitsi_enabled|bool" + +# Intentionally not removing the Docker image when uninstalling. +# We can't be sure it had been pulled by us in the first place. diff --git a/roles/matrix-jitsi/tasks/setup_jitsi_web.yml b/roles/matrix-jitsi/tasks/setup_jitsi_web.yml new file mode 100644 index 00000000..2b8a2cd2 --- /dev/null +++ b/roles/matrix-jitsi/tasks/setup_jitsi_web.yml @@ -0,0 +1,95 @@ +--- + +# +# Tasks related to setting up jitsi-web +# + +- name: Ensure Matrix jitsi-web path exists + file: + path: "{{ item.path }}" + state: directory + mode: 0777 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_jitsi_web_base_path }}", when: true } + - { path: "{{ matrix_jitsi_web_config_path }}", when: true } + - { path: "{{ matrix_jitsi_web_transcripts_path }}", when: true } + when: matrix_jitsi_enabled|bool and item.when + +- name: Ensure jitsi-web Docker image is pulled + docker_image: + name: "{{ matrix_jitsi_web_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_jitsi_web_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_jitsi_web_docker_image_force_pull }}" + when: matrix_jitsi_enabled|bool + +- name: Ensure jitsi-web environment variables file created + template: + src: "{{ role_path }}/templates/web/env.j2" + dest: "{{ matrix_jitsi_web_base_path }}/env" + mode: 0640 + when: matrix_jitsi_enabled|bool + +- name: Ensure jitsi-web configuration files created + template: + src: "{{ role_path }}/templates/web/{{ item }}.j2" + dest: "{{ matrix_jitsi_web_config_path }}/{{ item }}" + mode: 0644 + with_items: + - custom-config.js + - interface_config.js + when: matrix_jitsi_enabled|bool + +- name: Ensure matrix-jitsi-web.service installed + template: + src: "{{ role_path }}/templates/web/matrix-jitsi-web.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-jitsi-web.service" + mode: 0644 + register: matrix_jitsi_web_systemd_service_result + when: matrix_jitsi_enabled|bool + +- name: Ensure systemd reloaded after matrix-jitsi-web.service installation + service: + daemon_reload: yes + when: "matrix_jitsi_enabled and matrix_jitsi_web_systemd_service_result.changed" + +# +# Tasks related to getting rid of jitsi-web (if it was previously enabled) +# + +- name: Check existence of matrix-jitsi-web service + stat: + path: "{{ matrix_systemd_path }}/matrix-jitsi-web.service" + register: matrix_jitsi_web_service_stat + when: "not matrix_jitsi_enabled|bool" + +- name: Ensure matrix-jitsi-web is stopped + service: + name: matrix-jitsi-web + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_web_service_stat.stat.exists" + +- name: Ensure matrix-jitsi-web.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-jitsi-web.service" + state: absent + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_web_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-jitsi-web.service removal + service: + daemon_reload: yes + when: "not matrix_jitsi_enabled|bool and matrix_jitsi_web_service_stat.stat.exists" + +- name: Ensure Matrix jitsi-web paths doesn't exist + file: + path: "{{ matrix_jitsi_web_base_path }}" + state: absent + when: "not matrix_jitsi_enabled|bool" + +# Intentionally not removing the Docker image when uninstalling. +# We can't be sure it had been pulled by us in the first place. + diff --git a/roles/matrix-jitsi/tasks/validate_config.yml b/roles/matrix-jitsi/tasks/validate_config.yml new file mode 100644 index 00000000..d2887b12 --- /dev/null +++ b/roles/matrix-jitsi/tasks/validate_config.yml @@ -0,0 +1,42 @@ +--- + +- name: Fail if required Jitsi settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`) for using Jitsi. + + If you're setting up Jitsi for the first time, you may have missed a step. + Refer to our setup instructions (docs/configuring-playbook-jitsi.md). + + If you had setup Jitsi successfully before and it's just now that you're observing this failure, + it means that your installation may be using some default passwords that the playbook used to define until now. + This is not secure and we urge you to rebuild your Jitsi setup. + Refer to the "Rebuilding your Jitsi installation" section in our setup instructions (docs/configuring-playbook-jitsi.md). + when: "vars[item] == ''" + with_items: + - "matrix_jitsi_jibri_xmpp_password" + - "matrix_jitsi_jibri_recorder_password" + - "matrix_jitsi_jicofo_auth_password" + - "matrix_jitsi_jvb_auth_password" + +- name: (Deprecation) Catch and report renamed settings + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_jitsi_web_config_constraints_enabled', 'new': ''} + - {'old': 'matrix_jitsi_web_config_constraints_video_aspectRatio', 'new': ''} + - {'old': 'matrix_jitsi_web_config_constraints_video_height_ideal', 'new': 'matrix_jitsi_web_config_resolution_height_ideal_and_max'} + - {'old': 'matrix_jitsi_web_config_constraints_video_height_max', 'new': 'matrix_jitsi_web_config_resolution_height_ideal_and_max'} + - {'old': 'matrix_jitsi_web_config_constraints_video_height_min', 'new': 'matrix_jitsi_web_config_resolution_height_min'} + - {'old': 'matrix_jitsi_web_config_disableAudioLevels', 'new': ''} + - {'old': 'matrix_jitsi_web_config_enableLayerSuspension', 'new': ''} + - {'old': 'matrix_jitsi_web_config_channelLastN', 'new': ''} + - {'old': 'matrix_jitsi_web_config_testing_p2pTestMode', 'new': ''} + - {'old': 'matrix_jitsi_web_config_start_with_audio_muted', 'new': ''} + - {'old': 'matrix_jitsi_web_config_start_with_video_muted', 'new': ''} + - {'old': 'matrix_jitsi_web_interface_config_show_watermark_for_guests', 'new': ''} + - {'old': 'matrix_jitsi_web_interface_config_invitation_powered_by', 'new': ''} + - {'old': 'matrix_jisti_web_interface_config_show_deep_linking_image', 'new': 'matrix_jitsi_web_interface_config_show_deep_linking_image'} diff --git a/roles/matrix-jitsi/templates/jicofo/env.j2 b/roles/matrix-jitsi/templates/jicofo/env.j2 new file mode 100644 index 00000000..687df714 --- /dev/null +++ b/roles/matrix-jitsi/templates/jicofo/env.j2 @@ -0,0 +1,34 @@ +AUTH_TYPE={{ matrix_jitsi_auth_type }} +BRIDGE_AVG_PARTICIPANT_STRESS +BRIDGE_STRESS_THRESHOLD +ENABLE_AUTH={{ 1 if matrix_jitsi_enable_auth else 0 }} +ENABLE_AUTO_OWNER +ENABLE_CODEC_VP8 +ENABLE_CODEC_VP9 +ENABLE_CODEC_H264 +ENABLE_OCTO +ENABLE_RECORDING +ENABLE_SCTP +JICOFO_AUTH_USER={{ matrix_jitsi_jicofo_auth_user }} +JICOFO_AUTH_PASSWORD={{ matrix_jitsi_jicofo_auth_password }} +JICOFO_ENABLE_BRIDGE_HEALTH_CHECKS +JICOFO_CONF_INITIAL_PARTICIPANT_WAIT_TIMEOUT +JICOFO_CONF_SINGLE_PARTICIPANT_TIMEOUT +JICOFO_ENABLE_HEALTH_CHECKS +JICOFO_SHORT_ID +JICOFO_RESERVATION_ENABLED +JICOFO_RESERVATION_REST_BASE_URL +JIBRI_BREWERY_MUC={{ matrix_jitsi_jibri_brewery_muc }} +JIBRI_REQUEST_RETRIES +JIBRI_PENDING_TIMEOUT={{ matrix_jitsi_jibri_pending_timeout }} +JIGASI_BREWERY_MUC +JIGASI_SIP_URI +JVB_BREWERY_MUC={{ matrix_jitsi_jvb_brewery_muc }} +MAX_BRIDGE_PARTICIPANTS +OCTO_BRIDGE_SELECTION_STRATEGY +TZ={{ matrix_jitsi_timezone }} +XMPP_DOMAIN={{ matrix_jitsi_xmpp_domain }} +XMPP_AUTH_DOMAIN={{ matrix_jitsi_xmpp_auth_domain }} +XMPP_INTERNAL_MUC_DOMAIN={{ matrix_jitsi_xmpp_internal_muc_domain }} +XMPP_MUC_DOMAIN={{ matrix_jitsi_xmpp_muc_domain }} +XMPP_SERVER={{ matrix_jitsi_xmpp_server }} diff --git a/roles/matrix-jitsi/templates/jicofo/logging.properties.j2 b/roles/matrix-jitsi/templates/jicofo/logging.properties.j2 new file mode 100644 index 00000000..7eba95af --- /dev/null +++ b/roles/matrix-jitsi/templates/jicofo/logging.properties.j2 @@ -0,0 +1,20 @@ +handlers= java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter + +net.java.sip.communicator.util.ScLogFormatter.programname=Jicofo + +.level=INFO +net.sf.level=SEVERE +net.java.sip.communicator.plugin.reconnectplugin.level=FINE +org.ice4j.level=SEVERE +org.jitsi.impl.neomedia.level=SEVERE + +# Do not worry about missing strings +net.java.sip.communicator.service.resources.AbstractResourcesService.level=SEVERE + +#net.java.sip.communicator.service.protocol.level=ALL + +# Enable debug packets logging +#org.jitsi.impl.protocol.xmpp.level=FINE diff --git a/roles/matrix-jitsi/templates/jicofo/matrix-jitsi-jicofo.service.j2 b/roles/matrix-jitsi/templates/jicofo/matrix-jitsi-jicofo.service.j2 new file mode 100644 index 00000000..6ecafaa0 --- /dev/null +++ b/roles/matrix-jitsi/templates/jicofo/matrix-jitsi-jicofo.service.j2 @@ -0,0 +1,33 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix jitsi-jicofo server +{% for service in matrix_jitsi_jicofo_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jicofo 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jicofo 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-jicofo \ + --log-driver=none \ + --network={{ matrix_docker_network }} \ + --env-file={{ matrix_jitsi_jicofo_base_path }}/env \ + --mount type=bind,src={{ matrix_jitsi_jicofo_config_path }},dst=/config \ + {% for arg in matrix_jitsi_jicofo_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_jitsi_jicofo_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jicofo 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jicofo 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-jitsi-jicofo + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-jitsi/templates/jicofo/sip-communicator.properties.j2 b/roles/matrix-jitsi/templates/jicofo/sip-communicator.properties.j2 new file mode 100644 index 00000000..c62e04ff --- /dev/null +++ b/roles/matrix-jitsi/templates/jicofo/sip-communicator.properties.j2 @@ -0,0 +1,9 @@ +org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED=true +org.jitsi.jicofo.BRIDGE_MUC={{ matrix_jitsi_jvb_brewery_muc }}@{{ matrix_jitsi_xmpp_internal_muc_domain }} + +org.jitsi.jicofo.jibri.BREWERY={{ matrix_jitsi_jibri_brewery_muc }}@{{ matrix_jitsi_xmpp_internal_muc_domain }} +org.jitsi.jicofo.jibri.PENDING_TIMEOUT=90 + +{% if matrix_jitsi_enable_auth %} +org.jitsi.jicofo.auth.URL=XMPP:{{ matrix_jitsi_xmpp_domain }} +{% endif %} diff --git a/roles/matrix-jitsi/templates/jvb/custom-sip-communicator.properties.j2 b/roles/matrix-jitsi/templates/jvb/custom-sip-communicator.properties.j2 new file mode 100644 index 00000000..44b6b8c2 --- /dev/null +++ b/roles/matrix-jitsi/templates/jvb/custom-sip-communicator.properties.j2 @@ -0,0 +1,7 @@ +org.jitsi.videobridge.xmpp.user.shard.DISABLE_CERTIFICATE_VERIFICATION=true + +org.jitsi.videobridge.ENABLE_STATISTICS=true +org.jitsi.videobridge.STATISTICS_TRANSPORT=muc +org.jitsi.videobridge.STATISTICS_INTERVAL=5000 + +{{ matrix_jitsi_jvb_custom_config_extension }} diff --git a/roles/matrix-jitsi/templates/jvb/env.j2 b/roles/matrix-jitsi/templates/jvb/env.j2 new file mode 100644 index 00000000..a927314e --- /dev/null +++ b/roles/matrix-jitsi/templates/jvb/env.j2 @@ -0,0 +1,25 @@ +ENABLE_COLIBRI_WEBSOCKET +ENABLE_OCTO +DOCKER_HOST_ADDRESS +XMPP_AUTH_DOMAIN={{ matrix_jitsi_xmpp_auth_domain }} +XMPP_INTERNAL_MUC_DOMAIN={{ matrix_jitsi_xmpp_internal_muc_domain }} +XMPP_SERVER={{ matrix_jitsi_xmpp_server }} +JVB_AUTH_USER={{ matrix_jitsi_jvb_auth_user }} +JVB_AUTH_PASSWORD={{ matrix_jitsi_jvb_auth_password }} +JVB_BREWERY_MUC={{ matrix_jitsi_jvb_brewery_muc }} +JVB_PORT={{ matrix_jitsi_jvb_rtp_udp_port }} +JVB_TCP_HARVESTER_DISABLED=true +JVB_TCP_PORT={{ matrix_jitsi_jvb_rtp_tcp_port }} +JVB_TCP_MAPPED_PORT={{ matrix_jitsi_jvb_rtp_tcp_port }} +{% if matrix_jitsi_jvb_stun_servers|length > 0 %} +JVB_STUN_SERVERS={{ matrix_jitsi_jvb_stun_servers|join(',') }} +{% endif %} +JVB_ENABLE_APIS +JVB_WS_DOMAIN +JVB_WS_SERVER_ID +PUBLIC_URL={{ matrix_jitsi_web_public_url }} +JVB_OCTO_BIND_ADDRESS +JVB_OCTO_PUBLIC_ADDRESS +JVB_OCTO_BIND_PORT +JVB_OCTO_REGION +TZ={{ matrix_jitsi_timezone }} \ No newline at end of file diff --git a/roles/matrix-jitsi/templates/jvb/logging.properties.j2 b/roles/matrix-jitsi/templates/jvb/logging.properties.j2 new file mode 100644 index 00000000..48c1e9fa --- /dev/null +++ b/roles/matrix-jitsi/templates/jvb/logging.properties.j2 @@ -0,0 +1,13 @@ +handlers= java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter + +net.java.sip.communicator.util.ScLogFormatter.programname=JVB + +.level=INFO + +org.jitsi.videobridge.xmpp.ComponentImpl.level=FINE + +# All of the INFO level logs from MediaStreamImpl are unnecessary in the context of jitsi-videobridge. +org.jitsi.impl.neomedia.MediaStreamImpl.level=WARNING diff --git a/roles/matrix-jitsi/templates/jvb/matrix-jitsi-jvb.service.j2 b/roles/matrix-jitsi/templates/jvb/matrix-jitsi-jvb.service.j2 new file mode 100644 index 00000000..2785795d --- /dev/null +++ b/roles/matrix-jitsi/templates/jvb/matrix-jitsi-jvb.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix jitsi-jvb server +{% for service in matrix_jitsi_jvb_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jvb 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jvb 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-jvb \ + --log-driver=none \ + --network={{ matrix_docker_network }} \ + --network-alias=jvb.meet.jitsi \ + --env-file={{ matrix_jitsi_jvb_base_path }}/env \ + {% if matrix_jitsi_jvb_container_rtp_udp_host_bind_port %} + -p {{ matrix_jitsi_jvb_container_rtp_udp_host_bind_port }}:{{ matrix_jitsi_jvb_rtp_udp_port }}/udp \ + {% endif %} + {% if matrix_jitsi_jvb_container_rtp_tcp_host_bind_port %} + -p {{ matrix_jitsi_jvb_container_rtp_tcp_host_bind_port }}:{{ matrix_jitsi_jvb_rtp_tcp_port }} \ + {% endif %} + {% if matrix_jitsi_jvb_container_colibri_ws_host_bind_port %} + -p {{ matrix_jitsi_jvb_container_colibri_ws_host_bind_port }}:9090 \ + {% endif %} + --mount type=bind,src={{ matrix_jitsi_jvb_config_path }},dst=/config \ + {% for arg in matrix_jitsi_jvb_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_jitsi_jvb_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-jvb 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-jvb 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-jitsi-jvb + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-jitsi/templates/prosody/env.j2 b/roles/matrix-jitsi/templates/prosody/env.j2 new file mode 100644 index 00000000..3a91463e --- /dev/null +++ b/roles/matrix-jitsi/templates/prosody/env.j2 @@ -0,0 +1,51 @@ +AUTH_TYPE={{ matrix_jitsi_auth_type }} +ENABLE_AUTH={{ 1 if matrix_jitsi_enable_auth else 0 }} +ENABLE_GUESTS={{ 1 if matrix_jitsi_enable_guests else 0 }} +ENABLE_LOBBY={{ 1 if matrix_jitsi_enable_lobby else 0 }} +ENABLE_XMPP_WEBSOCKET +GLOBAL_MODULES +GLOBAL_CONFIG +LDAP_URL={{ matrix_jitsi_ldap_url }} +LDAP_BASE={{ matrix_jitsi_ldap_base }} +LDAP_BINDDN={{ matrix_jitsi_ldap_binddn }} +LDAP_BINDPW={{ matrix_jitsi_ldap_bindpw }} +LDAP_FILTER={{ matrix_jitsi_ldap_filter }} +LDAP_AUTH_METHOD={{ matrix_jitsi_ldap_auth_method }} +LDAP_VERSION={{ matrix_jitsi_ldap_version }} +LDAP_USE_TLS={{ 1 if matrix_jitsi_ldap_use_tls else 0 }} +LDAP_TLS_CIPHERS={{ matrix_jitsi_ldap_tls_ciphers }} +LDAP_TLS_CHECK_PEER={{ 1 if matrix_jitsi_ldap_tls_check_peer else 0 }} +LDAP_TLS_CACERT_FILE={{ matrix_jitsi_ldap_tls_cacert_file }} +LDAP_TLS_CACERT_DIR={{ matrix_jitsi_ldap_tls_cacert_dir }} +LDAP_START_TLS={{ 1 if matrix_jitsi_ldap_start_tls else 0 }} +XMPP_DOMAIN={{ matrix_jitsi_xmpp_domain }} +XMPP_AUTH_DOMAIN={{ matrix_jitsi_xmpp_auth_domain }} +XMPP_GUEST_DOMAIN={{ matrix_jitsi_xmpp_guest_domain }} +XMPP_MUC_DOMAIN={{ matrix_jitsi_xmpp_muc_domain }} +XMPP_INTERNAL_MUC_DOMAIN={{ matrix_jitsi_xmpp_internal_muc_domain }} +XMPP_MODULES={{ matrix_jitsi_xmpp_modules }} +XMPP_MUC_MODULES= +XMPP_INTERNAL_MUC_MODULES= +XMPP_RECORDER_DOMAIN={{ matrix_jitsi_recorder_domain }} +XMPP_CROSS_DOMAIN=true +JICOFO_AUTH_USER={{ matrix_jitsi_jicofo_auth_user }} +JICOFO_AUTH_PASSWORD={{ matrix_jitsi_jicofo_auth_password }} +JVB_AUTH_USER={{ matrix_jitsi_jvb_auth_user }} +JVB_AUTH_PASSWORD={{ matrix_jitsi_jvb_auth_password }} +JIGASI_XMPP_USER= +JIGASI_XMPP_PASSWORD= +JIBRI_XMPP_USER={{ matrix_jitsi_jibri_xmpp_user }} +JIBRI_XMPP_PASSWORD={{ matrix_jitsi_jibri_xmpp_password }} +JIBRI_RECORDER_USER={{ matrix_jitsi_jibri_recorder_user }} +JIBRI_RECORDER_PASSWORD={{ matrix_jitsi_jibri_recorder_password }} +JWT_APP_ID +JWT_APP_SECRET +JWT_ACCEPTED_ISSUERS +JWT_ACCEPTED_AUDIENCES +JWT_ASAP_KEYSERVER +JWT_ALLOW_EMPTY +JWT_AUTH_TYPE +JWT_TOKEN_AUTH_MODULE +LOG_LEVEL +PUBLIC_URL={{ matrix_jitsi_web_public_url }} +TZ={{ matrix_jitsi_timezone }} diff --git a/roles/matrix-jitsi/templates/prosody/matrix-jitsi-prosody.service.j2 b/roles/matrix-jitsi/templates/prosody/matrix-jitsi-prosody.service.j2 new file mode 100644 index 00000000..5a4a81e5 --- /dev/null +++ b/roles/matrix-jitsi/templates/prosody/matrix-jitsi-prosody.service.j2 @@ -0,0 +1,38 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix jitsi-prosody server +{% for service in matrix_jitsi_prosody_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-prosody 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-prosody 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-prosody \ + --log-driver=none \ + --network={{ matrix_docker_network }} \ + --network-alias={{ matrix_jitsi_xmpp_server }} \ + {% if matrix_jitsi_prosody_container_http_host_bind_port %} + -p {{ matrix_jitsi_prosody_container_http_host_bind_port }}:5280 \ + {% endif %} + --env-file={{ matrix_jitsi_prosody_base_path }}/env \ + --mount type=bind,src={{ matrix_jitsi_prosody_config_path }},dst=/config \ + --mount type=bind,src={{ matrix_jitsi_prosody_plugins_path }},dst=/prosody-plugins-custom \ + {% for arg in matrix_jitsi_prosody_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_jitsi_prosody_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-prosody 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-prosody 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-jitsi-prosody + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-jitsi/templates/web/custom-config.js.j2 b/roles/matrix-jitsi/templates/web/custom-config.js.j2 new file mode 100644 index 00000000..bbe85798 --- /dev/null +++ b/roles/matrix-jitsi/templates/web/custom-config.js.j2 @@ -0,0 +1,18 @@ +config.defaultLanguage = {{ matrix_jitsi_web_config_defaultLanguage|to_json }}; + + +if (!config.hasOwnProperty('p2p')) config.p2p = {% raw %}{}{% endraw %}; + +{% if matrix_jitsi_web_stun_servers|length > 0 %} +config.p2p.stunServers = [ + {% for url in matrix_jitsi_web_stun_servers %} + { urls: {{ url|to_json }} }{% if not loop.last %},{% endif %} + {% endfor %} +]; +{% endif %} + +{% if matrix_jitsi_etherpad_enabled %} +config.etherpad_base = {{ (matrix_jitsi_etherpad_base + '/p/') |to_json }} +{% endif %} + +{{ matrix_jitsi_web_custom_config_extension }} diff --git a/roles/matrix-jitsi/templates/web/env.j2 b/roles/matrix-jitsi/templates/web/env.j2 new file mode 100644 index 00000000..c4faa5ba --- /dev/null +++ b/roles/matrix-jitsi/templates/web/env.j2 @@ -0,0 +1,94 @@ +ENABLE_COLIBRI_WEBSOCKET +ENABLE_FLOC=0 +ENABLE_LETSENCRYPT=0 +ENABLE_HTTP_REDIRECT=0 +ENABLE_HSTS=0 +ENABLE_XMPP_WEBSOCKET +DISABLE_HTTPS=0 +DISABLE_DEEP_LINKING +LETSENCRYPT_DOMAIN={{ matrix_server_fqn_jitsi }} +LETSENCRYPT_EMAIL={{ matrix_ssl_lets_encrypt_support_email }} +LETSENCRYPT_USE_STAGING=0 +PUBLIC_URL={{ matrix_jitsi_web_public_url }} +TZ={{ matrix_jitsi_timezone }} +AMPLITUDE_ID +ANALYTICS_SCRIPT_URLS +ANALYTICS_WHITELISTED_EVENTS +CALLSTATS_CUSTOM_SCRIPT_URL +CALLSTATS_ID +CALLSTATS_SECRET +CHROME_EXTENSION_BANNER_JSON +CONFCODE_URL +CONFIG_EXTERNAL_CONNECT +DEFAULT_LANGUAGE +DEPLOYMENTINFO_ENVIRONMENT +DEPLOYMENTINFO_ENVIRONMENT_TYPE +DEPLOYMENTINFO_REGION +DEPLOYMENTINFO_SHARD +DEPLOYMENTINFO_USERREGION +DIALIN_NUMBERS_URL +DIALOUT_AUTH_URL +DIALOUT_CODES_URL +DROPBOX_APPKEY +DROPBOX_REDIRECT_URI +DYNAMIC_BRANDING_URL +ENABLE_AUDIO_PROCESSING +ENABLE_AUTH={{ 1 if matrix_jitsi_enable_auth else 0 }} +ENABLE_CALENDAR +ENABLE_FILE_RECORDING_SERVICE +ENABLE_FILE_RECORDING_SERVICE_SHARING +ENABLE_GUESTS={{ 1 if matrix_jitsi_enable_guests else 0 }} +ENABLE_IPV6 +ENABLE_LIPSYNC +ENABLE_NO_AUDIO_DETECTION +ENABLE_P2P={{ 1 if matrix_jitsi_enable_p2p else 0 }} +ENABLE_PREJOIN_PAGE +ENABLE_WELCOME_PAGE +ENABLE_CLOSE_PAGE +ENABLE_RECORDING={{ 1 if matrix_jitsi_enable_recording else 0 }} +ENABLE_REMB +ENABLE_REQUIRE_DISPLAY_NAME +ENABLE_SIMULCAST +ENABLE_STATS_ID +ENABLE_STEREO +ENABLE_SUBDOMAINS +ENABLE_TALK_WHILE_MUTED +ENABLE_TCC +ENABLE_TRANSCRIPTIONS={{ 1 if matrix_jitsi_enable_transcriptions else 0 }} +ETHERPAD_PUBLIC_URL +ETHERPAD_URL_BASE={{ (matrix_jitsi_etherpad_base + '/') if matrix_jitsi_etherpad_enabled else ''}} +GOOGLE_ANALYTICS_ID +GOOGLE_API_APP_CLIENT_ID +INVITE_SERVICE_URL +JICOFO_AUTH_USER={{ matrix_jitsi_jicofo_auth_user }} +MATOMO_ENDPOINT +MATOMO_SITE_ID +MICROSOFT_API_APP_CLIENT_ID +NGINX_RESOLVER +NGINX_WORKER_PROCESSES +NGINX_WORKER_CONNECTIONS +PEOPLE_SEARCH_URL +RESOLUTION={{ matrix_jitsi_web_config_resolution_height_ideal_and_max }} +RESOLUTION_MIN={{ matrix_jitsi_web_config_resolution_height_min }} +RESOLUTION_WIDTH={{ matrix_jitsi_web_config_resolution_width_ideal_and_max }} +RESOLUTION_WIDTH_MIN={{ matrix_jitsi_web_config_resolution_width_min }} +START_AUDIO_ONLY +START_AUDIO_MUTED={{ matrix_jitsi_web_config_start_audio_muted_after_nth_participant }} +START_WITH_AUDIO_MUTED +START_SILENT +DISABLE_AUDIO_LEVELS +ENABLE_NOISY_MIC_DETECTION +START_BITRATE +DESKTOP_SHARING_FRAMERATE_MIN +DESKTOP_SHARING_FRAMERATE_MAX +START_VIDEO_MUTED={{ matrix_jitsi_web_config_start_video_muted_after_nth_participant }} +START_WITH_VIDEO_MUTED +TESTING_CAP_SCREENSHARE_BITRATE +TESTING_OCTO_PROBABILITY +XMPP_AUTH_DOMAIN={{ matrix_jitsi_xmpp_auth_domain }} +XMPP_BOSH_URL_BASE={{ matrix_jitsi_xmpp_bosh_url_base }} +XMPP_DOMAIN={{ matrix_jitsi_xmpp_domain }} +XMPP_GUEST_DOMAIN={{ matrix_jitsi_xmpp_guest_domain }} +XMPP_MUC_DOMAIN={{ matrix_jitsi_xmpp_muc_domain }} +XMPP_RECORDER_DOMAIN={{ matrix_jitsi_recorder_domain }} +TOKEN_AUTH_URL \ No newline at end of file diff --git a/roles/matrix-jitsi/templates/web/interface_config.js.j2 b/roles/matrix-jitsi/templates/web/interface_config.js.j2 new file mode 100644 index 00000000..08ac02fe --- /dev/null +++ b/roles/matrix-jitsi/templates/web/interface_config.js.j2 @@ -0,0 +1,295 @@ +/* eslint-disable no-unused-vars, no-var, max-len */ +/* eslint sort-keys: ["error", "asc", {"caseSensitive": false}] */ + +var interfaceConfig = { + APP_NAME: {{ matrix_jitsi_web_interface_config_app_name|to_json }}, + AUDIO_LEVEL_PRIMARY_COLOR: 'rgba(255,255,255,0.4)', + AUDIO_LEVEL_SECONDARY_COLOR: 'rgba(255,255,255,0.2)', + + /** + * A UX mode where the last screen share participant is automatically + * pinned. Valid values are the string "remote-only" so remote participants + * get pinned but not local, otherwise any truthy value for all participants, + * and any falsy value to disable the feature. + * + * Note: this mode is experimental and subject to breakage. + */ + AUTO_PIN_LATEST_SCREEN_SHARE: 'remote-only', + BRAND_WATERMARK_LINK: {{ matrix_jitsi_web_interface_config_brand_watermark_link|to_json }}, + + CLOSE_PAGE_GUEST_HINT: false, // A html text to be shown to guests on the close page, false disables it + /** + * Whether the connection indicator icon should hide itself based on + * connection strength. If true, the connection indicator will remain + * displayed while the participant has a weak connection and will hide + * itself after the CONNECTION_INDICATOR_HIDE_TIMEOUT when the connection is + * strong. + * + * @type {boolean} + */ + CONNECTION_INDICATOR_AUTO_HIDE_ENABLED: true, + + /** + * How long the connection indicator should remain displayed before hiding. + * Used in conjunction with CONNECTION_INDICATOR_AUTOHIDE_ENABLED. + * + * @type {number} + */ + CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT: 5000, + + /** + * If true, hides the connection indicators completely. + * + * @type {boolean} + */ + CONNECTION_INDICATOR_DISABLED: false, + + DEFAULT_BACKGROUND: '#474747', + DEFAULT_LOCAL_DISPLAY_NAME: 'me', + DEFAULT_LOGO_URL: 'images/watermark.svg', + DEFAULT_REMOTE_DISPLAY_NAME: 'Fellow Jitster', + DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.svg', + + DISABLE_DOMINANT_SPEAKER_INDICATOR: false, + + DISABLE_FOCUS_INDICATOR: false, + + /** + * If true, notifications regarding joining/leaving are no longer displayed. + */ + DISABLE_JOIN_LEAVE_NOTIFICATIONS: false, + + /** + * If true, presence status: busy, calling, connected etc. is not displayed. + */ + DISABLE_PRESENCE_STATUS: false, + + /** + * Whether the ringing sound in the call/ring overlay is disabled. If + * {@code undefined}, defaults to {@code false}. + * + * @type {boolean} + */ + DISABLE_RINGING: false, + + /** + * Whether the speech to text transcription subtitles panel is disabled. + * If {@code undefined}, defaults to {@code false}. + * + * @type {boolean} + */ + DISABLE_TRANSCRIPTION_SUBTITLES: {{ matrix_jitsi_web_interface_config_disable_transcription_subtitles|to_json }}, + + /** + * Whether or not the blurred video background for large video should be + * displayed on browsers that can support it. + */ + DISABLE_VIDEO_BACKGROUND: false, + + DISPLAY_WELCOME_FOOTER: true, + DISPLAY_WELCOME_PAGE_ADDITIONAL_CARD: false, + DISPLAY_WELCOME_PAGE_CONTENT: {{ matrix_jitsi_web_interface_config_display_welcome_page_content|to_json }}, + DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: false, + + ENABLE_DIAL_OUT: true, + + ENABLE_FEEDBACK_ANIMATION: false, // Enables feedback star animation. + + FILM_STRIP_MAX_HEIGHT: 120, + + GENERATE_ROOMNAMES_ON_WELCOME_PAGE: {{ matrix_jitsi_web_interface_config_generate_room_names_on_welcome_page|to_json }}, + + /** + * Hide the logo on the deep linking pages. + */ + HIDE_DEEP_LINKING_LOGO: false, + + /** + * Hide the invite prompt in the header when alone in the meeting. + */ + HIDE_INVITE_MORE_HEADER: false, + + INITIAL_TOOLBAR_TIMEOUT: 20000, + JITSI_WATERMARK_LINK: {{ matrix_jitsi_web_interface_config_jitsi_watermark_link|to_json }}, + + LANG_DETECTION: {{ matrix_jitsi_web_interface_config_lang_detection|to_json }}, // Allow i18n to detect the system language + LIVE_STREAMING_HELP_LINK: 'https://jitsi.org/live', // Documentation reference for the live streaming feature. + LOCAL_THUMBNAIL_RATIO: 16 / 9, // 16:9 + + /** + * Maximum coefficient of the ratio of the large video to the visible area + * after the large video is scaled to fit the window. + * + * @type {number} + */ + MAXIMUM_ZOOMING_COEFFICIENT: 1.3, + + /** + * Whether the mobile app Jitsi Meet is to be promoted to participants + * attempting to join a conference in a mobile Web browser. If + * {@code undefined}, defaults to {@code true}. + * + * @type {boolean} + */ + MOBILE_APP_PROMO: true, + + /** + * Specify custom URL for downloading android mobile app. + */ + MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet', + + /** + * Specify custom URL for downloading f droid app. + */ + MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/', + + /** + * Specify URL for downloading ios mobile app. + */ + MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905', + + NATIVE_APP_NAME: {{ matrix_jitsi_web_interface_config_native_app_name|to_json }}, + + // Names of browsers which should show a warning stating the current browser + // has a suboptimal experience. Browsers which are not listed as optimal or + // unsupported are considered suboptimal. Valid values are: + // chrome, chromium, edge, electron, firefox, nwjs, opera, safari + OPTIMAL_BROWSERS: [ 'chrome', 'chromium', 'firefox', 'nwjs', 'electron', 'safari' ], + + POLICY_LOGO: null, + PROVIDER_NAME: {{ matrix_jitsi_web_interface_config_provider_name|to_json }}, + + /** + * If true, will display recent list + * + * @type {boolean} + */ + RECENT_LIST_ENABLED: true, + REMOTE_THUMBNAIL_RATIO: 1, // 1:1 + + SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar' ], + SHOW_BRAND_WATERMARK: {{ matrix_jitsi_web_interface_config_show_brand_watermark|to_json }}, + + /** + * Decides whether the chrome extension banner should be rendered on the landing page and during the meeting. + * If this is set to false, the banner will not be rendered at all. If set to true, the check for extension(s) + * being already installed is done before rendering. + */ + SHOW_CHROME_EXTENSION_BANNER: false, + + SHOW_DEEP_LINKING_IMAGE: {{ matrix_jitsi_web_interface_config_show_deep_linking_image|to_json }}, + SHOW_JITSI_WATERMARK: {{ matrix_jitsi_web_interface_config_show_jitsi_watermark|to_json }}, + SHOW_POWERED_BY: {{ matrix_jitsi_web_interface_config_show_powered_by|to_json }}, + SHOW_PROMOTIONAL_CLOSE_PAGE: false, + + /* + * If indicated some of the error dialogs may point to the support URL for + * help. + */ + SUPPORT_URL: 'https://community.jitsi.org/', + + TOOLBAR_ALWAYS_VISIBLE: false, + + /** + * The name of the toolbar buttons to display in the toolbar, including the + * "More actions" menu. If present, the button will display. Exceptions are + * "livestreaming" and "recording" which also require being a moderator and + * some values in config.js to be enabled. Also, the "profile" button will + * not display for users with a JWT. + * Notes: + * - it's impossible to choose which buttons go in the "More actions" menu + * - it's impossible to control the placement of buttons + * - 'desktop' controls the "Share your screen" button + */ + TOOLBAR_BUTTONS: [ + {% if matrix_jitsi_enable_transcriptions %} + 'closedcaptions', + {% endif %} + {% if matrix_jitsi_enable_recording %} + 'recording', + {% endif %} + 'microphone', 'camera', 'desktop', 'embedmeeting', 'fullscreen', + 'fodeviceselection', 'hangup', 'profile', 'chat', + 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand', + 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts', + 'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', 'security' + ], + + TOOLBAR_TIMEOUT: 4000, + + // Browsers, in addition to those which do not fully support WebRTC, that + // are not supported and should show the unsupported browser page. + UNSUPPORTED_BROWSERS: [], + + /** + * Whether to show thumbnails in filmstrip as a column instead of as a row. + */ + VERTICAL_FILMSTRIP: true, + + // Determines how the video would fit the screen. 'both' would fit the whole + // screen, 'height' would fit the original video height to the height of the + // screen, 'width' would fit the original video width to the width of the + // screen respecting ratio. + VIDEO_LAYOUT_FIT: 'both', + + /** + * If true, hides the video quality label indicating the resolution status + * of the current large video. + * + * @type {boolean} + */ + VIDEO_QUALITY_LABEL_DISABLED: false, + + /** + * How many columns the tile view can expand to. The respected range is + * between 1 and 5. + */ + // TILE_VIEW_MAX_COLUMNS: 5, + + /** + * Specify Firebase dynamic link properties for the mobile apps. + */ + // MOBILE_DYNAMIC_LINK: { + // APN: 'org.jitsi.meet', + // APP_CODE: 'w2atb', + // CUSTOM_DOMAIN: undefined, + // IBI: 'com.atlassian.JitsiMeet.ios', + // ISI: '1165103905' + // }, + + /** + * Specify mobile app scheme for opening the app from the mobile browser. + */ + // APP_SCHEME: 'org.jitsi.meet', + + /** + * Specify the Android app package name. + */ + // ANDROID_APP_PACKAGE: 'org.jitsi.meet', + + /** + * Override the behavior of some notifications to remain displayed until + * explicitly dismissed through a user action. The value is how long, in + * milliseconds, those notifications should remain displayed. + */ + // ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT: 15000, + + // List of undocumented settings + /** + INDICATOR_FONT_SIZES + PHONE_NUMBER_REGEX + */ + + // Allow all above example options to include a trailing comma and + // prevent fear when commenting out the last value. + // eslint-disable-next-line sort-keys + makeJsonParserHappy: 'even if last key had a trailing comma' + + // No configuration value should follow this line. +}; + + +{{ matrix_jitsi_web_custom_interface_config_extension }} + + +/* eslint-enable no-unused-vars, no-var, max-len */ diff --git a/roles/matrix-jitsi/templates/web/matrix-jitsi-web.service.j2 b/roles/matrix-jitsi/templates/web/matrix-jitsi-web.service.j2 new file mode 100644 index 00000000..5d386361 --- /dev/null +++ b/roles/matrix-jitsi/templates/web/matrix-jitsi-web.service.j2 @@ -0,0 +1,38 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix jitsi-web server +{% for service in matrix_jitsi_web_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-web 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-web 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-jitsi-web \ + --log-driver=none \ + --network={{ matrix_docker_network }} \ + --network-alias={{ matrix_jitsi_xmpp_domain }} \ + --env-file={{ matrix_jitsi_web_base_path }}/env \ + {% if matrix_jitsi_web_container_http_host_bind_port %} + -p {{ matrix_jitsi_web_container_http_host_bind_port }}:80 \ + {% endif %} + --mount type=bind,src={{ matrix_jitsi_web_config_path }},dst=/config \ + --mount type=bind,src={{ matrix_jitsi_web_transcripts_path }},dst=/usr/share/jitsi-meet/transcripts \ + {% for arg in matrix_jitsi_web_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_jitsi_web_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-jitsi-web 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-jitsi-web 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-jitsi-web + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-ma1sd/defaults/main.yml b/roles/matrix-ma1sd/defaults/main.yml new file mode 100644 index 00000000..f91189f5 --- /dev/null +++ b/roles/matrix-ma1sd/defaults/main.yml @@ -0,0 +1,161 @@ +# ma1sd is a Federated Matrix Identity Server +# See: https://github.com/ma1uta/ma1sd + +matrix_ma1sd_enabled: true + +matrix_ma1sd_container_image_self_build: false +matrix_ma1sd_container_image_self_build_repo: "https://github.com/ma1uta/ma1sd.git" +matrix_ma1sd_container_image_self_build_branch: "{{ matrix_ma1sd_version }}" + +matrix_ma1sd_version: "2.5.0" + +matrix_ma1sd_docker_image: "{{ matrix_ma1sd_docker_image_name_prefix }}ma1uta/ma1sd:{{ matrix_ma1sd_version }}" +matrix_ma1sd_docker_image_name_prefix: "{{ 'localhost/' if matrix_ma1sd_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_ma1sd_docker_image_force_pull: "{{ matrix_ma1sd_docker_image.endswith(':latest') }}" + +matrix_ma1sd_base_path: "{{ matrix_base_data_path }}/ma1sd" +# We need the docker src directory to be named ma1sd. See: https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/588 +matrix_ma1sd_docker_src_files_path: "{{ matrix_ma1sd_base_path }}/docker-src/ma1sd" +matrix_ma1sd_config_path: "{{ matrix_ma1sd_base_path }}/config" +matrix_ma1sd_data_path: "{{ matrix_ma1sd_base_path }}/data" + +# Controls whether the matrix-ma1sd container exposes its HTTP port (tcp/8090 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8090"), or empty string to not expose. +matrix_ma1sd_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_ma1sd_container_extra_arguments: [] + +# List of systemd services that matrix-ma1sd.service depends on +matrix_ma1sd_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-ma1sd.service wants +matrix_ma1sd_systemd_wanted_services_list: [] + +# Your identity server is private by default. +# To ensure maximum discovery, you can make your identity server +# also forward lookups to the central matrix.org Identity server +# (at the cost of potentially leaking all your contacts information). +# Enabling this is discouraged. Learn more here: https://github.com/ma1uta/ma1sd/blob/master/docs/features/identity.md#lookups +matrix_ma1sd_matrixorg_forwarding_enabled: false + + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_ma1sd_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_ma1sd_postgres_*` variables +matrix_ma1sd_database_engine: 'sqlite' + +matrix_ma1sd_sqlite_database_path_local: "{{ matrix_ma1sd_data_path }}/ma1sd.db" +matrix_ma1sd_sqlite_database_path_in_container: "/var/ma1sd/ma1sd.db" + +matrix_ma1sd_database_username: 'matrix_ma1sd' +matrix_ma1sd_database_password: 'some-password' +matrix_ma1sd_database_hostname: 'matrix-postgres' +matrix_ma1sd_database_port: 5432 +matrix_ma1sd_database_name: 'matrix_ma1sd' + +matrix_ma1sd_database_connection_string: 'postgresql://{{ matrix_ma1sd_database_username }}:{{ matrix_ma1sd_database_password }}@{{ matrix_ma1sd_database_hostname }}:{{ matrix_ma1sd_database_port }}/{{ matrix_ma1sd_database_name }}' + + +# ma1sd has serveral supported identity stores. +# One of them is storing identities directly in Synapse's database. +# Learn more here: https://github.com/ma1uta/ma1sd/blob/master/docs/stores/synapse.md +matrix_ma1sd_synapsesql_enabled: false +matrix_ma1sd_synapsesql_type: "" +matrix_ma1sd_synapsesql_connection: "" + +# Setting up email-sending settings is required for using ma1sd. +matrix_ma1sd_threepid_medium_email_identity_from: "matrix@{{ matrix_domain }}" +matrix_ma1sd_threepid_medium_email_connectors_smtp_host: "" +matrix_ma1sd_threepid_medium_email_connectors_smtp_port: 587 +matrix_ma1sd_threepid_medium_email_connectors_smtp_tls: 1 +matrix_ma1sd_threepid_medium_email_connectors_smtp_login: "" +matrix_ma1sd_threepid_medium_email_connectors_smtp_password: "" + +# DNS overwrites are useful for telling ma1sd how it can reach the homeserver directly. +# Useful when reverse-proxying certain URLs (e.g. `/_matrix/client/r0/user_directory/search`) to ma1sd, +# so that ma1sd can rewrite the original URL to one that would reach the homeserver. +matrix_ma1sd_dns_overwrite_enabled: false +matrix_ma1sd_dns_overwrite_homeserver_client_name: "{{ matrix_server_fqn_matrix }}" +matrix_ma1sd_dns_overwrite_homeserver_client_value: "http://matrix-synapse:8008" + +# Override the default session templates +# To use this, fill in the template variables with the full desired template as a multi-line YAML variable +# +# More info: +# https://github.com/ma1uta/ma1sd/blob/master/docs/threepids/session/session-views.md +matrix_ma1sd_view_session_custom_templates_enabled: false +# Defaults to: https://github.com/ma1uta/ma1sd/blob/master/src/main/resources/templates/session/tokenSubmitSuccess.html +matrix_ma1sd_view_session_custom_onTokenSubmit_success_template: "" +# Defaults to: https://github.com/ma1uta/ma1sd/blob/master/src/main/resources/templates/session/tokenSubmitFailure.html +matrix_ma1sd_view_session_custom_onTokenSubmit_failure_template: "" + +# Override the default email templates +# To use this, fill in the template variables with the full desired template as a multi-line YAML variable +# +# More info: +# https://github.com/ma1uta/ma1sd/blob/master/docs/threepids/notification/template-generator.md +# https://github.com/ma1uta/ma1sd/tree/master/src/main/resources/threepids/email +matrix_ma1sd_threepid_medium_email_custom_templates_enabled: false +# Defaults to: https://github.com/ma1uta/ma1sd/blob/master/src/main/resources/threepids/email/invite-template.eml +matrix_ma1sd_threepid_medium_email_custom_invite_template: "" +# Defaults to: https://github.com/ma1uta/ma1sd/blob/master/src/main/resources/threepids/email/validate-template.eml +matrix_ma1sd_threepid_medium_email_custom_session_validation_template: "" +# Defaults to: https://github.com/ma1uta/ma1sd/blob/master/src/main/resources/threepids/email/unbind-notification.eml +matrix_ma1sd_threepid_medium_email_custom_session_unbind_notification_template: "" +# Defaults to: https://github.com/ma1uta/ma1sd/blob/master/src/main/resources/threepids/email/mxid-template.eml +matrix_ma1sd_threepid_medium_email_custom_matrixid_template: "" + +# Controls whether the self-check feature should validate SSL certificates. +matrix_ma1sd_self_check_validate_certificates: true + +# Controls ma1sd logging verbosity for troubleshooting. +# +# According to: https://github.com/ma1uta/ma1sd/blob/master/docs/troubleshooting.md#increase-verbosity +matrix_ma1sd_verbose_logging: false + +# Setting up support for API prefixes +matrix_ma1sd_v1_enabled: true +matrix_ma1sd_v2_enabled: true + +# Fix for missing 3PIDS bug +matrix_ma1sd_hashing_enabled: true + +# Default ma1sd 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_ma1sd_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_ma1sd_configuration_yaml: "{{ lookup('template', 'templates/ma1sd.yaml.j2') }}" + +matrix_ma1sd_configuration_extension_yaml: | + # Your custom YAML configuration for ma1sd goes here. + # This configuration extends the default starting configuration (`matrix_ma1sd_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_ma1sd_configuration_yaml`. + # + # Example configuration extension follows: + # + # ldap: + # enabled: true + # connection: + # host: ldapHostnameOrIp + # tls: false + # port: 389 + # baseDNs: ['OU=Users,DC=example,DC=org'] + # bindDn: CN=My Ma1sd User,OU=Users,DC=example,DC=org + # bindPassword: TheUserPassword + +matrix_ma1sd_configuration_extension: "{{ matrix_ma1sd_configuration_extension_yaml|from_yaml if matrix_ma1sd_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final ma1sd configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_ma1sd_configuration_yaml`. +matrix_ma1sd_configuration: "{{ matrix_ma1sd_configuration_yaml|from_yaml|combine(matrix_ma1sd_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-ma1sd/tasks/init.yml b/roles/matrix-ma1sd/tasks/init.yml new file mode 100644 index 00000000..04cc3a21 --- /dev/null +++ b/roles/matrix-ma1sd/tasks/init.yml @@ -0,0 +1,10 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_ma1sd_container_image_self_build and matrix_ma1sd_enabled|bool" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-ma1sd.service'] }}" + when: matrix_ma1sd_enabled|bool diff --git a/roles/matrix-ma1sd/tasks/main.yml b/roles/matrix-ma1sd/tasks/main.yml new file mode 100644 index 00000000..0b8a114e --- /dev/null +++ b/roles/matrix-ma1sd/tasks/main.yml @@ -0,0 +1,28 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_ma1sd_enabled|bool" + tags: + - setup-all + - setup-ma1sd + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_ma1sd_enabled|bool" + tags: + - setup-all + - setup-ma1sd + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_ma1sd_enabled|bool" + tags: + - setup-all + - setup-ma1sd + +- import_tasks: "{{ role_path }}/tasks/self_check_ma1sd.yml" + delegate_to: 127.0.0.1 + become: false + when: "run_self_check|bool and matrix_ma1sd_enabled|bool" + tags: + - self-check diff --git a/roles/matrix-ma1sd/tasks/migrate_mxisd.yml b/roles/matrix-ma1sd/tasks/migrate_mxisd.yml new file mode 100644 index 00000000..1d966204 --- /dev/null +++ b/roles/matrix-ma1sd/tasks/migrate_mxisd.yml @@ -0,0 +1,72 @@ +--- + +# This task is for migrating existing mxisd data when transitioning to the ma1sd fork. + +- name: Check for existent mxisd data + stat: + path: "{{ matrix_base_data_path }}/mxisd/data" + register: ma1sd_migrate_mxisd_data_dir_stat + +- name: Warn if mxisd data detected + debug: + msg: > + You seem to have an existing mxisd folder in `{{ matrix_base_data_path }}/mxisd`. + We are going to migrate it to ma1sd and rename the folder to mxisd.migrated. + when: "ma1sd_migrate_mxisd_data_dir_stat.stat.exists" + +- name: Check existence of old matrix-mxisd service + stat: + path: "{{ matrix_systemd_path }}/matrix-mxisd.service" + register: matrix_mxisd_service_stat + +- name: Ensure matrix-mxisd is stopped + service: + name: matrix-mxisd + state: stopped + daemon_reload: yes + when: "matrix_mxisd_service_stat.stat.exists" + +- name: Check existence of matrix-ma1sd service + stat: + path: "{{ matrix_systemd_path }}/matrix-ma1sd.service" + register: matrix_ma1sd_service_stat + when: "ma1sd_migrate_mxisd_data_dir_stat.stat.exists" + +- name: Ensure matrix-ma1sd is stopped + service: + name: matrix-ma1sd + state: stopped + daemon_reload: yes + when: "ma1sd_migrate_mxisd_data_dir_stat.stat.exists and matrix_ma1sd_service_stat.stat.exists" + +# We use shell commands for the migration, because the Ansible copy module cannot +# recursively copy remote directories (like `/matrix/mxisd/data/sign.key`) in older versions of Ansible. +- block: + - name: Copy mxisd data files to ma1sd folder + command: "cp -ar {{ matrix_base_data_path }}/mxisd/data {{ matrix_ma1sd_base_path }}" + + - name: Check existence of mxisd.db file + stat: + path: "{{ matrix_ma1sd_data_path }}/mxisd.db" + register: matrix_ma1sd_mxisd_db_stat + + - name: Rename database (mxisd.db -> ma1sd.db) + command: "mv {{ matrix_ma1sd_data_path }}/mxisd.db {{ matrix_ma1sd_data_path }}/ma1sd.db" + when: "matrix_ma1sd_mxisd_db_stat.stat.exists" + + - name: Rename mxisd folder + command: "mv {{ matrix_base_data_path }}/mxisd {{ matrix_base_data_path }}/mxisd.migrated" + when: "ma1sd_migrate_mxisd_data_dir_stat.stat.exists" + +- name: Ensure outdated matrix-mxisd.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mxisd.service" + state: absent + when: "matrix_mxisd_service_stat.stat.exists" + +- name: Ensure systemd reloaded after removing outdated matrix-mxisd.service + service: + daemon_reload: yes + when: "matrix_mxisd_service_stat.stat.exists" + + diff --git a/roles/matrix-ma1sd/tasks/self_check_ma1sd.yml b/roles/matrix-ma1sd/tasks/self_check_ma1sd.yml new file mode 100644 index 00000000..b8a7faaa --- /dev/null +++ b/roles/matrix-ma1sd/tasks/self_check_ma1sd.yml @@ -0,0 +1,22 @@ +--- + +- set_fact: + ma1sd_url_endpoint_public: "https://{{ matrix_server_fqn_matrix }}/_matrix/identity/api/v1" + +- name: Check ma1sd Identity Service + uri: + url: "{{ ma1sd_url_endpoint_public }}" + follow_redirects: none + validate_certs: "{{ matrix_ma1sd_self_check_validate_certificates }}" + check_mode: no + register: result_ma1sd + ignore_errors: true + +- name: Fail if ma1sd Identity Service not working + fail: + msg: "Failed checking ma1sd is up at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ ma1sd_url_endpoint_public }}`). Is ma1sd running? Is port 443 open in your firewall? Full error: {{ result_ma1sd }}" + when: "result_ma1sd.failed or 'json' not in result_ma1sd" + +- name: Report working ma1sd Identity Service + debug: + msg: "ma1sd at `{{ matrix_server_fqn_matrix }}` is working (checked endpoint: `{{ ma1sd_url_endpoint_public }}`)" diff --git a/roles/matrix-ma1sd/tasks/setup_install.yml b/roles/matrix-ma1sd/tasks/setup_install.yml new file mode 100644 index 00000000..3f319eef --- /dev/null +++ b/roles/matrix-ma1sd/tasks/setup_install.yml @@ -0,0 +1,167 @@ +--- + +- name: Ensure ma1sd paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_ma1sd_config_path }}", when: true } + - { path: "{{ matrix_ma1sd_data_path }}", when: true } + - { path: "{{ matrix_ma1sd_docker_src_files_path }}", when: "{{ matrix_ma1sd_container_image_self_build }}"} + when: "item.when|bool" + +- import_tasks: "{{ role_path }}/tasks/migrate_mxisd.yml" + + +# These (SQLite -> Postgres) migration tasks are usually at the top, +# but we'd like to run them after `migrate_mxisd.yml`, which requires the ma1sd paths to exist. +- set_fact: + matrix_ma1sd_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_ma1sd_sqlite_database_path_local }}" + register: matrix_ma1sd_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_ma1sd_sqlite_database_path_local }}" + dst: "{{ matrix_ma1sd_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_ma1sd_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-ma1sd.service'] + pgloader_options: ['--with "quote identifiers"'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_ma1sd_requires_restart: true + when: "matrix_ma1sd_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_ma1sd_database_engine == 'postgres'" + +- name: Ensure ma1sd image is pulled + docker_image: + name: "{{ matrix_ma1sd_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_ma1sd_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_ma1sd_docker_image_force_pull }}" + when: "not matrix_ma1sd_container_image_self_build|bool" + +- block: + - name: Ensure gradle is installed for self-building (Debian) + apt: + name: + - gradle + state: present + update_cache: yes + when: (ansible_os_family == 'Debian') + + - name: Ensure gradle is installed for self-building (CentOS) + fail: + msg: "Installing gradle on CentOS is currently not supported, so self-building ma1sd cannot happen at this time" + when: ansible_distribution == 'CentOS' + + - name: Ensure gradle is installed for self-building (Archlinux) + pacman: + name: + - gradle + state: latest + update_cache: yes + when: ansible_distribution == 'Archlinux' + + - name: Ensure ma1sd repository is present on self-build + git: + repo: "{{ matrix_ma1sd_container_image_self_build_repo }}" + dest: "{{ matrix_ma1sd_docker_src_files_path }}" + version: "{{ matrix_ma1sd_container_image_self_build_branch }}" + force: "yes" + register: matrix_ma1sd_git_pull_results + + - name: Ensure ma1sd Docker image is built + shell: "DOCKER_BUILDKIT=1 ./gradlew dockerBuild" + args: + chdir: "{{ matrix_ma1sd_docker_src_files_path }}" + + - name: Ensure ma1sd Docker image is tagged correctly + docker_image: + # The build script always tags the image with 2 tags: + # - based on the branch/version: e.g. `ma1uta/ma1sd:2.4.0` (when on `2.4.0`) + # or `ma1uta/ma1sd:2.4.0-19-ga71d32b` (when on a given commit for a pre-release) + # - generic one: `ma1uta/ma1sd:latest-dev` + # + # It's hard to predict the first one, so we'll use the latter. + name: "ma1uta/ma1sd:latest-dev" + repository: "{{ matrix_ma1sd_docker_image }}" + force_tag: yes + source: local + when: "matrix_ma1sd_container_image_self_build|bool" + +- name: Ensure ma1sd config installed + copy: + content: "{{ matrix_ma1sd_configuration|to_nice_yaml }}" + dest: "{{ matrix_ma1sd_config_path }}/ma1sd.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure custom view templates are installed, if any + copy: + content: "{{ item.value }}" + dest: "{{ matrix_ma1sd_config_path }}/{{ item.location }}" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - {value: "{{ matrix_ma1sd_view_session_custom_onTokenSubmit_success_template }}", location: 'tokenSubmitSuccess.html'} + - {value: "{{ matrix_ma1sd_view_session_custom_onTokenSubmit_failure_template }}", location: 'tokenSubmitFailure.html'} + when: "matrix_ma1sd_view_session_custom_templates_enabled|bool and item.value" + +- name: Ensure custom email templates are installed, if any + copy: + content: "{{ item.value }}" + dest: "{{ matrix_ma1sd_config_path }}/{{ item.location }}" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - {value: "{{ matrix_ma1sd_threepid_medium_email_custom_invite_template }}", location: 'invite-template.eml'} + - {value: "{{ matrix_ma1sd_threepid_medium_email_custom_session_validation_template }}", location: 'validate-template.eml'} + - {value: "{{ matrix_ma1sd_threepid_medium_email_custom_session_unbind_notification_template }}", location: 'unbind-notification.eml'} + - {value: "{{ matrix_ma1sd_threepid_medium_email_custom_matrixid_template }}", location: 'mxid-template.eml'} + when: "matrix_ma1sd_threepid_medium_email_custom_templates_enabled|bool and item.value" + +# Only cleaning up for people who define the respective templates +- name: (Cleanup) Ensure custom email templates are not in data/ anymore (we've put them in config/) + file: + path: "{{ matrix_ma1sd_data_path }}/{{ item.location }}" + state: absent + with_items: + - {value: "{{ matrix_ma1sd_threepid_medium_email_custom_invite_template }}", location: 'invite-template.eml'} + - {value: "{{ matrix_ma1sd_threepid_medium_email_custom_session_validation_template }}", location: 'validate-template.eml'} + - {value: "{{ matrix_ma1sd_threepid_medium_email_custom_session_unbind_notification_template }}", location: 'unbind-notification.eml'} + - {value: "{{ matrix_ma1sd_threepid_medium_email_custom_matrixid_template }}", location: 'mxid-template.eml'} + when: "matrix_ma1sd_threepid_medium_email_custom_templates_enabled|bool and item.value" + +- name: Ensure matrix-ma1sd.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-ma1sd.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-ma1sd.service" + mode: 0644 + register: matrix_ma1sd_systemd_service_result + +- name: Ensure systemd reloaded after matrix-ma1sd.service installation + service: + daemon_reload: yes + when: "matrix_ma1sd_systemd_service_result.changed|bool" + +- name: Ensure matrix-ma1sd.service restarted, if necessary + service: + name: "matrix-ma1sd.service" + state: restarted + when: "matrix_ma1sd_requires_restart|bool" diff --git a/roles/matrix-ma1sd/tasks/setup_uninstall.yml b/roles/matrix-ma1sd/tasks/setup_uninstall.yml new file mode 100644 index 00000000..b36ab508 --- /dev/null +++ b/roles/matrix-ma1sd/tasks/setup_uninstall.yml @@ -0,0 +1,35 @@ +--- + +- name: Check existence of matrix-ma1sd service + stat: + path: "{{ matrix_systemd_path }}/matrix-ma1sd.service" + register: matrix_ma1sd_service_stat + +- name: Ensure matrix-ma1sd is stopped + service: + name: matrix-ma1sd + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_ma1sd_service_stat.stat.exists|bool" + +- name: Ensure matrix-ma1sd.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-ma1sd.service" + state: absent + when: "matrix_ma1sd_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-ma1sd.service removal + service: + daemon_reload: yes + when: "matrix_ma1sd_service_stat.stat.exists|bool" + +- name: Ensure Matrix ma1sd paths don't exist + file: + path: "{{ matrix_ma1sd_base_path }}" + state: absent + +- name: Ensure ma1sd Docker image doesn't exist + docker_image: + name: "{{ matrix_ma1sd_docker_image }}" + state: absent diff --git a/roles/matrix-ma1sd/tasks/validate_config.yml b/roles/matrix-ma1sd/tasks/validate_config.yml new file mode 100644 index 00000000..4f80b154 --- /dev/null +++ b/roles/matrix-ma1sd/tasks/validate_config.yml @@ -0,0 +1,68 @@ +--- + +- name: (Deprecation) Warn about ma1sd variables that are not used anymore + fail: + msg: > + The `{{ item }}` variable defined in your configuration is not used by this playbook anymore! + You'll need to adapt to the new way of extending ma1sd configuration. + See the CHANGELOG and the `matrix_ma1sd_configuration_extension_yaml` variable for more information and examples. + when: "item in vars" + with_items: + - 'matrix_ma1sd_ldap_enabled' + - 'matrix_ma1sd_ldap_connection_host' + - 'matrix_ma1sd_ldap_connection_tls' + - 'matrix_ma1sd_ldap_connection_port' + - 'matrix_ma1sd_ldap_connection_baseDn' + - 'matrix_ma1sd_ldap_connection_baseDns' + - 'matrix_ma1sd_ldap_connection_bindDn' + - 'matrix_ma1sd_ldap_connection_bindPassword' + - 'matrix_ma1sd_ldap_filter' + - 'matrix_ma1sd_ldap_attribute_uid_type' + - 'matrix_ma1sd_ldap_attribute_uid_value' + - 'matrix_ma1sd_ldap_connection_bindPassword' + - 'matrix_ma1sd_ldap_attribute_name' + - 'matrix_ma1sd_ldap_attribute_threepid_email' + - 'matrix_ma1sd_ldap_attribute_threepid_msisdn' + - 'matrix_ma1sd_ldap_identity_filter' + - 'matrix_ma1sd_ldap_identity_medium' + - 'matrix_ma1sd_ldap_auth_filter' + - 'matrix_ma1sd_ldap_directory_filter' + - 'matrix_ma1sd_template_config' + - 'matrix_ma1sd_architecture' + +- name: Ensure ma1sd configuration does not contain any dot-notation keys + fail: + msg: > + Since version 1.3.0, ma1sd will not accept property-style configuration keys. + You have defined a key (`{{ item.key }}`) which contains a dot. + Instead, use nesting. See: https://github.com/ma1uta/ma1sd/wiki/Upgrade-Notes#v130 + when: "'.' in item.key" + with_dict: "{{ matrix_ma1sd_configuration }}" + +- name: Fail if required ma1sd settings not defined + fail: + msg: > + You need to define a required configuration setting (`{{ item }}`) for using ma1sd. + when: "vars[item] == ''" + with_items: + - "matrix_ma1sd_threepid_medium_email_connectors_smtp_host" + +- name: (Deprecation) Catch and report renamed ma1sd variables + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "vars | dict2items | selectattr('key', 'match', item.old) | list | items2dict" + with_items: + - {'old': 'matrix_ma1sd_container_expose_port', 'new': ''} + - {'old': 'matrix_ma1sd_threepid_medium_email_custom_unbind_fraudulent_template', 'new': 'matrix_ma1sd_threepid_medium_email_custom_session_unbind_notification_template'} + +- name: (Deprecation) Catch and report mxisd variables + fail: + msg: >- + mxisd is deprecated and has been replaced with ma1sd (https://github.com/ma1uta/ma1sd), a compatible fork. + The playbook will migrate your existing mxisd configuration and data automatically, but you need to adjust variable names. + Please change your configuration (vars.yml) to rename all mxisd variables (`{{ item.old }}` -> `{{ item.new }}`). + when: "vars | dict2items | selectattr('key', 'match', item.old) | list | items2dict" + with_items: + - {'old': 'matrix_mxisd_.*', 'new': 'matrix_ma1sd_.*'} diff --git a/roles/matrix-ma1sd/templates/ma1sd.yaml.j2 b/roles/matrix-ma1sd/templates/ma1sd.yaml.j2 new file mode 100644 index 00000000..a4100adc --- /dev/null +++ b/roles/matrix-ma1sd/templates/ma1sd.yaml.j2 @@ -0,0 +1,104 @@ +#jinja2: lstrip_blocks: True +matrix: + domain: {{ matrix_domain }} + v1: {{ matrix_ma1sd_v1_enabled|to_json }} + v2: {{ matrix_ma1sd_v2_enabled|to_json }} + +server: + name: {{ matrix_server_fqn_matrix }} + +key: + path: /var/ma1sd/sign.key + +storage: + {% if matrix_ma1sd_database_engine == 'sqlite' %} + backend: sqlite + provider: + sqlite: + database: {{ matrix_ma1sd_sqlite_database_path_in_container|to_json }} + {% elif matrix_ma1sd_database_engine == 'postgres' %} + backend: postgresql + provider: + postgresql: + database: //{{ matrix_ma1sd_database_hostname }}:{{ matrix_ma1sd_database_port }}/{{ matrix_ma1sd_database_name }} + username: {{ matrix_ma1sd_database_username|to_json }} + password: {{ matrix_ma1sd_database_password|to_json }} + {% endif %} + +{% if matrix_ma1sd_dns_overwrite_enabled %} +dns: + overwrite: + homeserver: + client: + - name: {{ matrix_ma1sd_dns_overwrite_homeserver_client_name }} + value: {{ matrix_ma1sd_dns_overwrite_homeserver_client_value }} +{% endif %} + +{% if matrix_ma1sd_matrixorg_forwarding_enabled %} +forward: + servers: ['matrix-org'] +{% endif %} + +threepid: + medium: + email: + identity: + from: {{ matrix_ma1sd_threepid_medium_email_identity_from }} + connectors: + smtp: + host: {{ matrix_ma1sd_threepid_medium_email_connectors_smtp_host }} + port: {{ matrix_ma1sd_threepid_medium_email_connectors_smtp_port }} + tls: {{ matrix_ma1sd_threepid_medium_email_connectors_smtp_tls }} + login: {{ matrix_ma1sd_threepid_medium_email_connectors_smtp_login }} + password: {{ matrix_ma1sd_threepid_medium_email_connectors_smtp_password }} +{% if matrix_ma1sd_threepid_medium_email_custom_templates_enabled %} + generators: + template: + {% if matrix_ma1sd_threepid_medium_email_custom_invite_template %} + invite: '/etc/ma1sd/invite-template.eml' + {% endif %} + {% if matrix_ma1sd_threepid_medium_email_custom_session_validation_template or matrix_ma1sd_threepid_medium_email_custom_session_unbind_notification_template %} + session: + {% if matrix_ma1sd_threepid_medium_email_custom_session_validation_template %} + validation: '/etc/ma1sd/validate-template.eml' + {% endif %} + {% if matrix_ma1sd_threepid_medium_email_custom_session_unbind_notification_template %} + unbind: + notification: '/etc/ma1sd/unbind-notification.eml' + {% endif %} + {% endif %} + {% if matrix_ma1sd_threepid_medium_email_custom_matrixid_template %} + generic: + matrixId: '/etc/ma1sd/mxid-template.eml' + {% endif %} +{% endif %} + +{% if matrix_ma1sd_view_session_custom_templates_enabled %} +view: + session: + onTokenSubmit: + {% if matrix_ma1sd_view_session_custom_onTokenSubmit_success_template %} + success: '/etc/ma1sd/tokenSubmitSuccess.html' + {% endif %} + {% if matrix_ma1sd_view_session_custom_onTokenSubmit_failure_template %} + failure: '/etc/ma1sd/tokenSubmitFailure.html' + {% endif %} +{% endif %} + +{% if matrix_ma1sd_hashing_enabled %} +hashing: + enabled: true # enable or disable the hash lookup MSC2140 (default is false) + pepperLength: 20 # length of the pepper value (default is 20) + rotationPolicy: per_requests # or `per_seconds` how often the hashes will be updating + hashStorageType: sql # or `in_memory` where the hashes will be stored + algorithms: + - none # the same as v1 bulk lookup + - sha256 # hash the 3PID and pepper. + delay: 2m # how often hashes will be updated if rotation policy = per_seconds (default is 10s) + requests: 10 +{% endif %} + +synapseSql: + enabled: {{ matrix_ma1sd_synapsesql_enabled|to_json }} + type: {{ matrix_ma1sd_synapsesql_type|to_json }} + connection: {{ matrix_ma1sd_synapsesql_connection|to_json }} diff --git a/roles/matrix-ma1sd/templates/systemd/matrix-ma1sd.service.j2 b/roles/matrix-ma1sd/templates/systemd/matrix-ma1sd.service.j2 new file mode 100644 index 00000000..c2adffc0 --- /dev/null +++ b/roles/matrix-ma1sd/templates/systemd/matrix-ma1sd.service.j2 @@ -0,0 +1,48 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix ma1sd Identity server +{% for service in matrix_ma1sd_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_ma1sd_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-ma1sd 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-ma1sd 2>/dev/null' + +# ma1sd writes an SQLite shared library (libsqlitejdbc.so) to /tmp and executes it from there, +# so /tmp needs to be mounted with an exec option. +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-ma1sd \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --tmpfs=/tmp:rw,exec,nosuid,size=10m \ + --network={{ matrix_docker_network }} \ + {% if matrix_ma1sd_container_http_host_bind_port %} + -p {{ matrix_ma1sd_container_http_host_bind_port }}:8090 \ + {% endif %} + {% if matrix_ma1sd_verbose_logging %} + -e MA1SD_LOG_LEVEL=debug \ + {% endif %} + --mount type=bind,src={{ matrix_ma1sd_config_path }},dst=/etc/ma1sd,ro \ + --mount type=bind,src={{ matrix_ma1sd_data_path }},dst=/var/ma1sd \ + {% for arg in matrix_ma1sd_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_ma1sd_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-ma1sd 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-ma1sd 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-ma1sd + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-ma1sd/vars/main.yml b/roles/matrix-ma1sd/vars/main.yml new file mode 100644 index 00000000..b6c97a59 --- /dev/null +++ b/roles/matrix-ma1sd/vars/main.yml @@ -0,0 +1,5 @@ +--- + +# Doing `|from_yaml` when the extension contains nothing yields an empty string (""). +# We need to ensure it's a dictionary or `|combine` (when building `matrix_ma1sd_configuration`) will fail later. +matrix_ma1sd_configuration_extension: "{{ matrix_ma1sd_configuration_extension_yaml|from_yaml if matrix_ma1sd_configuration_extension_yaml|from_yaml else {} }}" diff --git a/roles/matrix-mailer/defaults/main.yml b/roles/matrix-mailer/defaults/main.yml new file mode 100644 index 00000000..8ca1a8a3 --- /dev/null +++ b/roles/matrix-mailer/defaults/main.yml @@ -0,0 +1,31 @@ +matrix_mailer_enabled: true + +matrix_mailer_base_path: "{{ matrix_base_data_path }}/mailer" + +matrix_mailer_container_image_self_build: false +matrix_mailer_container_image_self_build_repository_url: "https://github.com/devture/exim-relay" +matrix_mailer_container_image_self_build_src_files_path: "{{ matrix_mailer_base_path }}/docker-src" +matrix_mailer_container_image_self_build_version: "{{ matrix_mailer_docker_image.split(':')[1] }}" + +matrix_mailer_version: 4.94.2-r0-2 +matrix_mailer_docker_image: "{{ matrix_mailer_docker_image_name_prefix }}devture/exim-relay:{{ matrix_mailer_version }}" +matrix_mailer_docker_image_name_prefix: "{{ 'localhost/' if matrix_mailer_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_mailer_docker_image_force_pull: "{{ matrix_mailer_docker_image.endswith(':latest') }}" + +# The user/group that the container runs with. +# These match the `exim` user/group within the container image. +matrix_mailer_container_user_uid: 100 +matrix_mailer_container_user_gid: 101 + +# A list of extra arguments to pass to the container +matrix_mailer_container_extra_arguments: [] + +matrix_mailer_hostname: "{{ matrix_server_fqn_matrix }}" + +matrix_mailer_sender_address: "matrix@{{ matrix_domain }}" +matrix_mailer_relay_use: false +matrix_mailer_relay_host_name: "mail.example.com" +matrix_mailer_relay_host_port: 587 +matrix_mailer_relay_auth: false +matrix_mailer_relay_auth_username: "" +matrix_mailer_relay_auth_password: "" diff --git a/roles/matrix-mailer/tasks/init.yml b/roles/matrix-mailer/tasks/init.yml new file mode 100644 index 00000000..d07380f0 --- /dev/null +++ b/roles/matrix-mailer/tasks/init.yml @@ -0,0 +1,10 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_mailer_container_image_self_build and matrix_mailer_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-mailer.service'] }}" + when: matrix_mailer_enabled|bool diff --git a/roles/matrix-mailer/tasks/main.yml b/roles/matrix-mailer/tasks/main.yml new file mode 100644 index 00000000..f636614e --- /dev/null +++ b/roles/matrix-mailer/tasks/main.yml @@ -0,0 +1,9 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/setup_mailer.yml" + when: run_setup|bool + tags: + - setup-all + - setup-mailer diff --git a/roles/matrix-mailer/tasks/setup_mailer.yml b/roles/matrix-mailer/tasks/setup_mailer.yml new file mode 100644 index 00000000..251a52da --- /dev/null +++ b/roles/matrix-mailer/tasks/setup_mailer.yml @@ -0,0 +1,107 @@ +--- + +# +# Tasks related to setting up the mailer +# + +- name: Ensure mailer base path exists + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_mailer_base_path }}", when: true } + - { path: "{{ matrix_mailer_container_image_self_build_src_files_path }}", when: "{{ matrix_mailer_container_image_self_build }}" } + when: "matrix_mailer_enabled|bool and item.when" + +- name: Ensure mailer environment variables file created + template: + src: "{{ role_path }}/templates/env-mailer.j2" + dest: "{{ matrix_mailer_base_path }}/env-mailer" + mode: 0640 + when: matrix_mailer_enabled|bool + +- name: Ensure exim-relay repository is present on self-build + git: + repo: "{{ matrix_mailer_container_image_self_build_repository_url }}" + dest: "{{ matrix_mailer_container_image_self_build_src_files_path }}" + version: "{{ matrix_mailer_container_image_self_build_version }}" + force: "yes" + register: matrix_mailer_git_pull_results + when: "matrix_mailer_enabled|bool and matrix_mailer_container_image_self_build|bool" + +- name: Ensure exim-relay Docker image is built + docker_image: + name: "{{ matrix_mailer_docker_image }}" + source: build + force_source: "{{ matrix_mailer_git_pull_results.changed if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mailer_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_mailer_container_image_self_build_src_files_path }}" + pull: yes + when: "matrix_mailer_enabled|bool and matrix_mailer_container_image_self_build|bool" + +- name: Ensure exim-relay image is pulled + docker_image: + name: "{{ matrix_mailer_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_mailer_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_mailer_docker_image_force_pull }}" + when: "matrix_mailer_enabled|bool and not matrix_mailer_container_image_self_build|bool" + +- name: Ensure matrix-mailer.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-mailer.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-mailer.service" + mode: 0644 + register: matrix_mailer_systemd_service_result + when: matrix_mailer_enabled|bool + +- name: Ensure systemd reloaded after matrix-mailer.service installation + service: + daemon_reload: yes + when: "matrix_mailer_enabled|bool and matrix_mailer_systemd_service_result.changed" + +# +# Tasks related to getting rid of the mailer (if it was previously enabled) +# + +- name: Check existence of matrix-mailer service + stat: + path: "{{ matrix_systemd_path }}/matrix-mailer.service" + register: matrix_mailer_service_stat + when: "not matrix_mailer_enabled|bool" + +- name: Ensure matrix-mailer is stopped + service: + name: matrix-mailer + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_mailer_enabled|bool and matrix_mailer_service_stat.stat.exists" + +- name: Ensure matrix-mailer.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-mailer.service" + state: absent + when: "not matrix_mailer_enabled|bool and matrix_mailer_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-mailer.service removal + service: + daemon_reload: yes + when: "not matrix_mailer_enabled|bool and matrix_mailer_service_stat.stat.exists" + +- name: Ensure Matrix mailer environment variables path doesn't exist + file: + path: "{{ matrix_mailer_base_path }}" + state: absent + when: "not matrix_mailer_enabled|bool" + +- name: Ensure mailer Docker image doesn't exist + docker_image: + name: "{{ matrix_mailer_docker_image }}" + state: absent + when: "not matrix_mailer_enabled|bool" diff --git a/roles/matrix-mailer/templates/env-mailer.j2 b/roles/matrix-mailer/templates/env-mailer.j2 new file mode 100644 index 00000000..eb3f8699 --- /dev/null +++ b/roles/matrix-mailer/templates/env-mailer.j2 @@ -0,0 +1,9 @@ +#jinja2: lstrip_blocks: "True" +{% if matrix_mailer_relay_use %} +SMARTHOST={{ matrix_mailer_relay_host_name }}::{{ matrix_mailer_relay_host_port }} +{% endif %} +{% if matrix_mailer_relay_auth %} +SMTP_USERNAME={{ matrix_mailer_relay_auth_username }} +SMTP_PASSWORD={{ matrix_mailer_relay_auth_password }} +{% endif %} +HOSTNAME={{ matrix_mailer_hostname }} diff --git a/roles/matrix-mailer/templates/systemd/matrix-mailer.service.j2 b/roles/matrix-mailer/templates/systemd/matrix-mailer.service.j2 new file mode 100644 index 00000000..bf5a2e42 --- /dev/null +++ b/roles/matrix-mailer/templates/systemd/matrix-mailer.service.j2 @@ -0,0 +1,37 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix mailer +After=docker.service +Requires=docker.service +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mailer 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mailer 2>/dev/null' + +# --hostname gives us a friendlier hostname than the default. +# The real hostname is passed via a `HOSTNAME` environment variable though. +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-mailer \ + --log-driver=none \ + --user={{ matrix_mailer_container_user_uid }}:{{ matrix_mailer_container_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --tmpfs=/var/spool/exim:rw,noexec,nosuid,size=100m \ + --network={{ matrix_docker_network }} \ + --env-file={{ matrix_mailer_base_path }}/env-mailer \ + --hostname=matrix-mailer \ + {% for arg in matrix_mailer_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_mailer_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-mailer 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-mailer 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-mailer + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-nginx-proxy/defaults/main.yml b/roles/matrix-nginx-proxy/defaults/main.yml new file mode 100644 index 00000000..87cbcde1 --- /dev/null +++ b/roles/matrix-nginx-proxy/defaults/main.yml @@ -0,0 +1,492 @@ +matrix_nginx_proxy_enabled: true +matrix_nginx_proxy_version: 1.21.1-alpine + +# We use an official nginx image, which we fix-up to run unprivileged. +# An alternative would be an `nginxinc/nginx-unprivileged` image, but +# that is frequently out of date. +matrix_nginx_proxy_docker_image: "{{ matrix_container_global_registry_prefix }}nginx:{{ matrix_nginx_proxy_version }}" +matrix_nginx_proxy_docker_image_force_pull: "{{ matrix_nginx_proxy_docker_image.endswith(':latest') }}" + +matrix_nginx_proxy_base_path: "{{ matrix_base_data_path }}/nginx-proxy" +matrix_nginx_proxy_data_path: "{{ matrix_nginx_proxy_base_path }}/data" +matrix_nginx_proxy_data_path_in_container: "/nginx-data" +matrix_nginx_proxy_data_path_extension: "/matrix_domain" +matrix_nginx_proxy_confd_path: "{{ matrix_nginx_proxy_base_path }}/conf.d" + +# List of systemd services that matrix-nginx-proxy.service depends on +matrix_nginx_proxy_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-nginx-proxy.service wants +matrix_nginx_proxy_systemd_wanted_services_list: [] + +# A list of additional "volumes" to mount in the container. +# This list gets populated dynamically at runtime. You can provide a different default value, +# if you wish to mount your own files into the container. +# Contains definition objects like this: `{"src": "/outside", "dst": "/inside", "options": "rw|ro|slave|.."} +matrix_nginx_proxy_container_additional_volumes: [] + +# A list of extra arguments to pass to the container +matrix_nginx_proxy_container_extra_arguments: [] + +# Controls whether matrix-nginx-proxy serves its vhosts over HTTPS or HTTP. +# +# If enabled: +# - SSL certificates would be expected to be available (see `matrix_ssl_retrieval_method`) +# - the HTTP vhost would be made a redirect to the HTTPS vhost +# +# If not enabled: +# - you don't need any SSL certificates (you can set `matrix_ssl_retrieval_method: none`) +# - naturally, there's no HTTPS vhost +# - services are served directly from the HTTP vhost +matrix_nginx_proxy_https_enabled: true + +# Controls whether the matrix-nginx-proxy container exposes its HTTP port (tcp/8080 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:80"), or empty string to not expose. +matrix_nginx_proxy_container_http_host_bind_port: '80' + +# Controls whether the matrix-nginx-proxy container exposes its HTTPS port (tcp/8443 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:443"), or empty string to not expose. +# +# This only makes sense and applies if `matrix_nginx_proxy_https_enabled` is set to `true`. +# Otherwise, there are no HTTPS vhosts to expose. +matrix_nginx_proxy_container_https_host_bind_port: '443' + +# Controls whether the matrix-nginx-proxy container exposes the Matrix Federation port (tcp/8448 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8448"), or empty string to not expose. +# +# This only makes sense and applies if `matrix_nginx_proxy_proxy_matrix_federation_api_enabled` is set to `true`. +# Otherwise, there is no Matrix Federation port to expose. +# +# This port can take HTTP or HTTPS traffic, depending on `matrix_nginx_proxy_https_enabled`. +# When HTTPS is disabled, you'd likely want to only expose the port locally, and front it with another HTTPS-enabled reverse-proxy. +matrix_nginx_proxy_container_federation_host_bind_port: '8448' + +# Controls whether matrix-nginx-proxy should serve the base domain. +# +# This is useful for when you only have your Matrix server, but you need to serve +# to serve `/.well-known/matrix/*` files from the base domain for the needs of +# Server-Discovery (Federation) and for Client-Discovery. +# +# Besides serving these Matrix files, a homepage would be served with content +# as specified in the `matrix_nginx_proxy_base_domain_homepage_template` variable. +# You can also put additional files to use for this webpage +# in the `{{ matrix_nginx_proxy_data_path }}/matrix-domain` (`/matrix/nginx-proxy/data/matrix-domain`) directory. +matrix_nginx_proxy_base_domain_serving_enabled: false + +# Controls whether the base domain directory and default index.html file are created. +matrix_nginx_proxy_base_domain_create_directory: true + +matrix_nginx_proxy_base_domain_hostname: "{{ matrix_domain }}" + +# Controls whether `matrix_nginx_proxy_base_domain_homepage_template` would be dumped to an `index.html` file +# in the `/matrix/nginx-proxy/data/matrix-domain` directory. +# +# If you would instead like to serve a static website by yourself, you can disable this. +# When disabled, you're expected to put website files in `/matrix/nginx-proxy/data/matrix-domain` manually +# and can expect that the playbook won't intefere with the `index.html` file. +matrix_nginx_proxy_base_domain_homepage_enabled: true + +matrix_nginx_proxy_base_domain_homepage_template: |- + + + + + Hello from {{ matrix_domain }}! + + + +# Option to disable the access log +matrix_nginx_proxy_access_log_enabled: true + +# Controls whether proxying the riot domain should be done. +matrix_nginx_proxy_proxy_riot_compat_redirect_enabled: false +matrix_nginx_proxy_proxy_riot_compat_redirect_hostname: "riot.{{ matrix_domain }}" + +# Controls whether proxying the Synapse domain should be done. +matrix_nginx_proxy_proxy_synapse_enabled: false +matrix_nginx_proxy_proxy_synapse_hostname: "matrix-nginx-proxy" +matrix_nginx_proxy_proxy_synapse_federation_api_enabled: "{{ matrix_nginx_proxy_proxy_matrix_federation_api_enabled }}" +# The addresses where the Federation API is, when using Synapse. +matrix_nginx_proxy_proxy_synapse_federation_api_addr_with_container: "matrix-synapse:8048" +matrix_nginx_proxy_proxy_synapse_federation_api_addr_sans_container: "localhost:8048" + +# Controls whether proxying the Element domain should be done. +matrix_nginx_proxy_proxy_element_enabled: false +matrix_nginx_proxy_proxy_element_hostname: "{{ matrix_server_fqn_element }}" + +# Controls whether proxying the Hydrogen domain should be done. +matrix_nginx_proxy_proxy_hydrogen_enabled: false +matrix_nginx_proxy_proxy_hydrogen_hostname: "{{ matrix_server_fqn_hydrogen }}" + +# Controls whether proxying the matrix domain should be done. +matrix_nginx_proxy_proxy_matrix_enabled: false +matrix_nginx_proxy_proxy_matrix_hostname: "{{ matrix_server_fqn_matrix }}" +matrix_nginx_proxy_proxy_matrix_federation_hostname: "{{ matrix_nginx_proxy_proxy_matrix_hostname }}" +# The port name used for federation in the nginx configuration. +# This is not necessarily the port that it's actually on, +# as port-mapping happens (`-p ..`) for the `matrix-nginx-proxy` container. +matrix_nginx_proxy_proxy_matrix_federation_port: 8448 + +# Controls whether proxying the dimension domain should be done. +matrix_nginx_proxy_proxy_dimension_enabled: false +matrix_nginx_proxy_proxy_dimension_hostname: "{{ matrix_server_fqn_dimension }}" + +# Controls whether proxying the goneb domain should be done. +matrix_nginx_proxy_proxy_bot_go_neb_enabled: false +matrix_nginx_proxy_proxy_bot_go_neb_hostname: "{{ matrix_server_fqn_bot_go_neb }}" + +# Controls whether proxying the jitsi domain should be done. +matrix_nginx_proxy_proxy_jitsi_enabled: false +matrix_nginx_proxy_proxy_jitsi_hostname: "{{ matrix_server_fqn_jitsi }}" + +# Controls whether proxying the grafana domain should be done. +matrix_nginx_proxy_proxy_grafana_enabled: false +matrix_nginx_proxy_proxy_grafana_hostname: "{{ matrix_server_fqn_grafana }}" + +# Controls whether proxying the sygnal domain should be done. +matrix_nginx_proxy_proxy_sygnal_enabled: false +matrix_nginx_proxy_proxy_sygnal_hostname: "{{ matrix_server_fqn_sygnal }}" + +# Controls whether proxying for the matrix-corporal API (`/_matrix/corporal`) should be done (on the matrix domain) +matrix_nginx_proxy_proxy_matrix_corporal_api_enabled: false +matrix_nginx_proxy_proxy_matrix_corporal_api_addr_with_container: "matrix-corporal:41081" +matrix_nginx_proxy_proxy_matrix_corporal_api_addr_sans_container: "127.0.0.1:41081" + +# Controls whether proxying for the User Directory Search API (`/_matrix/client/r0/user_directory/search`) should be done (on the matrix domain). +# This can be used to forward the API endpoint to another service, augmenting the functionality of Synapse's own User Directory Search. +# To learn more, see: https://github.com/ma1uta/ma1sd/blob/master/docs/features/directory.md +matrix_nginx_proxy_proxy_matrix_user_directory_search_enabled: false +matrix_nginx_proxy_proxy_matrix_user_directory_search_addr_with_container: "matrix-ma1sd:8090" +matrix_nginx_proxy_proxy_matrix_user_directory_search_addr_sans_container: "127.0.0.1:8090" + +# Controls whether proxying for 3PID-based registration (`/_matrix/client/r0/register/(email|msisdn)/requestToken`) should be done (on the matrix domain). +# This allows another service to control registrations involving 3PIDs. +# To learn more, see: https://github.com/ma1uta/ma1sd/blob/master/docs/features/registration.md +matrix_nginx_proxy_proxy_matrix_3pid_registration_enabled: false +matrix_nginx_proxy_proxy_matrix_3pid_registration_addr_with_container: "matrix-ma1sd:8090" +matrix_nginx_proxy_proxy_matrix_3pid_registration_addr_sans_container: "127.0.0.1:8090" + +# Controls whether proxying for the Identity API (`/_matrix/identity`) should be done (on the matrix domain) +matrix_nginx_proxy_proxy_matrix_identity_api_enabled: false +matrix_nginx_proxy_proxy_matrix_identity_api_addr_with_container: "matrix-ma1sd:8090" +matrix_nginx_proxy_proxy_matrix_identity_api_addr_sans_container: "127.0.0.1:8090" + +# Controls whether proxying for metrics (`/_synapse/metrics`) should be done (on the matrix domain) +matrix_nginx_proxy_proxy_synapse_metrics: false +matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled: false +matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_key: "" + +# The addresses where the Matrix Client API is. +# Certain extensions (like matrix-corporal) may override this in order to capture all traffic. +matrix_nginx_proxy_proxy_matrix_client_api_addr_with_container: "matrix-nginx-proxy:12080" +matrix_nginx_proxy_proxy_matrix_client_api_addr_sans_container: "127.0.0.1:12080" + +# The addresses where the Matrix Client API is, when using Synapse. +matrix_nginx_proxy_proxy_synapse_client_api_addr_with_container: "matrix-synapse:8008" +matrix_nginx_proxy_proxy_synapse_client_api_addr_sans_container: "127.0.0.1:8008" + +# This needs to be equal or higher than the maximum upload size accepted by Synapse. +matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size_mb: 50 + + +# Tells whether `/_synapse/client` is forwarded to the Matrix Client API server. +matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_client_api_enabled: true + +# Tells whether `/_synapse/oidc` is forwarded to the Matrix Client API server. +# Enable this if you need OpenID Connect authentication support. +matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_oidc_api_enabled: false + +# Tells whether `/_synapse/admin` is forwarded to the Matrix Client API server. +# Following these recommendations (https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.md), by default, we don't. +matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_admin_api_enabled: false + +# `matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_prefixes` holds +# the location prefixes that get forwarded to the Matrix Client API server. +# These locations get combined into a regex like this `^(/_matrix|/_synapse/client)`. +matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_prefix_regexes: | + {{ + (['/_matrix']) + + + (['/_synapse/client'] if matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_client_api_enabled else []) + + + (['/_synapse/oidc'] if matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_oidc_api_enabled else []) + + + (['/_synapse/admin'] if matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_admin_api_enabled else []) + + + (['/_synapse/metrics'] if matrix_nginx_proxy_proxy_synapse_metrics else []) + }} + +# Specifies where requests for the root URI (`/`) on the `matrix.` domain should be redirected. +# If this has an empty value, they're just passed to the homeserver, which serves a static page. +# If you'd like to make `https://matrix.DOMAIN` redirect to `https://element.DOMAIN` (or something of that sort), specify the domain name here. +# Example value: `element.DOMAIN` (or `{{ matrix_server_fqn_element }}`). +matrix_nginx_proxy_proxy_matrix_client_redirect_root_uri_to_domain: "" + +# Controls whether proxying for the Matrix Federation API should be done. +matrix_nginx_proxy_proxy_matrix_federation_api_enabled: false +matrix_nginx_proxy_proxy_matrix_federation_api_addr_with_container: "matrix-nginx-proxy:12088" +matrix_nginx_proxy_proxy_matrix_federation_api_addr_sans_container: "localhost:12088" +matrix_nginx_proxy_proxy_matrix_federation_api_client_max_body_size_mb: "{{ (matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size_mb | int) * 3 }}" +matrix_nginx_proxy_proxy_matrix_federation_api_ssl_certificate: "{{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_matrix_hostname }}/fullchain.pem" +matrix_nginx_proxy_proxy_matrix_federation_api_ssl_certificate_key: "{{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_matrix_hostname }}/privkey.pem" +matrix_nginx_proxy_proxy_matrix_federation_api_ssl_trusted_certificate: "{{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_matrix_hostname }}/chain.pem" + +# The tmpfs at /tmp needs to be large enough to handle multiple concurrent file uploads. +matrix_nginx_proxy_tmp_directory_size_mb: "{{ (matrix_nginx_proxy_proxy_matrix_federation_api_client_max_body_size_mb | int) * 50 }}" + +# A list of strings containing additional configuration blocks to add to the nginx server configuration (nginx.conf). +# for big matrixservers to enlarge the number of open files to prevent timeouts +# matrix_nginx_proxy_proxy_additional_configuration_blocks: +# - 'worker_rlimit_nofile 30000;' +matrix_nginx_proxy_proxy_additional_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to the nginx event server configuration (nginx.conf). +matrix_nginx_proxy_proxy_event_additional_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to the nginx http's server configuration (nginx-http.conf). +matrix_nginx_proxy_proxy_http_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to the base matrix server configuration (matrix-domain.conf). +matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to the synapse's server configuration (matrix-synapse.conf). +matrix_nginx_proxy_proxy_synapse_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to Riot's server configuration (matrix-riot-web.conf). +matrix_nginx_proxy_proxy_riot_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to Element's server configuration (matrix-client-element.conf). +matrix_nginx_proxy_proxy_element_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to Element's server configuration (matrix-client-element.conf). +matrix_nginx_proxy_proxy_hydrogen_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to Dimension's server configuration (matrix-dimension.conf). +matrix_nginx_proxy_proxy_dimension_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to GoNEB's server configuration (matrix-bot-go-neb.conf). +matrix_nginx_proxy_proxy_bot_go_neb_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to Jitsi's server configuration (matrix-jitsi.conf). +matrix_nginx_proxy_proxy_jitsi_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to Grafana's server configuration (matrix-grafana.conf). +matrix_nginx_proxy_proxy_grafana_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to Sygnal's server configuration (matrix-sygnal.conf). +matrix_nginx_proxy_proxy_sygnal_additional_server_configuration_blocks: [] + +# A list of strings containing additional configuration blocks to add to the base domain server configuration (matrix-base-domain.conf). +matrix_nginx_proxy_proxy_domain_additional_server_configuration_blocks: [] + +# Controls whether to send a "Permissions-Policy interest-cohort=();" header along with all responses for all vhosts meant to be accessed by users. +# +# 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. +matrix_nginx_proxy_floc_optout_enabled: true + +# HSTS Preloading Enable +# +# 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 +matrix_nginx_proxy_hsts_preload_enabled: false + +# X-XSS-Protection Enable +# Stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. +# Note: Not applicable for grafana +# +# 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_nginx_proxy_xss_protection: "1; mode=block" + +# Specifies the SSL configuration that should be used for the SSL protocols and ciphers +# This is based on the Mozilla Server Side TLS Recommended configurations. +# +# The posible values are: +# - "modern" - For Modern clients that support TLS 1.3, with no need for backwards compatibility +# - "intermediate" - Recommended configuration for a general-purpose server +# - "old" - Services accessed by very old clients or libraries, such as Internet Explorer 8 (Windows XP), Java 6, or OpenSSL 0.9.8 +# +# For more information visit: +# - https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations +# - https://ssl-config.mozilla.org/#server=nginx +matrix_nginx_proxy_ssl_preset: "intermediate" + +# Presets are taken from Mozilla's Server Side TLS Recommended configurations +# DO NOT modify these values and use `matrix_nginx_proxy_ssl_protocols`, `matrix_nginx_proxy_ssl_ciphers` and `matrix_nginx_proxy_ssl_ciphers` +# if you wish to use something more custom. +matrix_nginx_proxy_ssl_presets: + modern: + protocols: TLSv1.3 + ciphers: "" + prefer_server_ciphers: "off" + intermediate: + protocols: TLSv1.2 TLSv1.3 + ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + prefer_server_ciphers: "off" + old: + protocols: TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 + ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA + prefer_server_ciphers: "on" + + +# Specifies which *SSL protocols* to use when serving all the various vhosts. +matrix_nginx_proxy_ssl_protocols: "{{ matrix_nginx_proxy_ssl_presets[matrix_nginx_proxy_ssl_preset]['protocols'] }}" + +# Specifies whether to prefer *the client’s choice or the server’s choice* when negotiating ciphers. +matrix_nginx_proxy_ssl_prefer_server_ciphers: "{{ matrix_nginx_proxy_ssl_presets[matrix_nginx_proxy_ssl_preset]['prefer_server_ciphers'] }}" + +# Specifies which *SSL Cipher suites* to use when serving all the various vhosts. +# To see the full list for suportes ciphers run `openssl ciphers` on your server +matrix_nginx_proxy_ssl_ciphers: "{{ matrix_nginx_proxy_ssl_presets[matrix_nginx_proxy_ssl_preset]['ciphers'] }}" + +# Controls whether the self-check feature should validate SSL certificates. +matrix_nginx_proxy_self_check_validate_certificates: true + +# Controls whether redirects will be followed when checking the `/.well-known/matrix/client` resource. +# +# As per the spec (https://matrix.org/docs/spec/client_server/r0.6.0#well-known-uri), it shouldn't be, +# so we default to not following redirects as well. +matrix_nginx_proxy_self_check_well_known_matrix_client_follow_redirects: none + +# For OCSP purposes, we need to define a resolver at the `server{}` level or `http{}` level (we do the latter). +# +# Otherwise, we get warnings like this: +# > [warn] 22#22: no resolver defined to resolve r3.o.lencr.org while requesting certificate status, responder: r3.o.lencr.org, certificate: "/matrix/ssl/config/live/.../fullchain.pem" +# +# We point it to the internal Docker resolver, which likely delegates to nameservers defined in `/etc/resolv.conf`. +# +# When nginx proxy is disabled, our configuration is likely used by non-containerized nginx, so can't use the internal Docker resolver. +# Pointing `resolver` to some public DNS server might be an option, but for now we impose DNS servers on people. +# It might also be that no such warnings occur when not running in a container. +matrix_nginx_proxy_http_level_resolver: "{{ '127.0.0.11' if matrix_nginx_proxy_enabled else '' }}" + +# By default, this playbook automatically retrieves and auto-renews +# free SSL certificates from Let's Encrypt. +# +# The following retrieval methods are supported: +# - "lets-encrypt" - the playbook obtains free SSL certificates from Let's Encrypt +# - "self-signed" - the playbook generates and self-signs certificates +# - "manually-managed" - lets you manage certificates by yourself (manually; see below) +# - "none" - like "manually-managed", but doesn't care if you don't drop certificates in the location it expects +# +# If you decide to manage certificates by yourself (`matrix_ssl_retrieval_method: manually-managed`), +# you'd need to drop them into the directory specified by `matrix_ssl_config_dir_path` +# obeying the following hierarchy: +# - /live//fullchain.pem +# - /live//privkey.pem +# where refers to the domains that you need (usually `matrix_server_fqn_matrix` and `matrix_server_fqn_element`). +# +# The "none" type (`matrix_ssl_retrieval_method: none`), simply means that no certificate retrieval will happen. +# It's useful for when you've disabled the nginx proxy (`matrix_nginx_proxy_enabled: false`) +# and you'll be using another reverse-proxy server (like Apache) with your own certificates, managed by yourself. +# It's also useful if you're using `matrix_nginx_proxy_https_enabled: false` to make this nginx proxy serve +# plain HTTP traffic only (usually, on the loopback interface only) and you'd be terminating SSL using another reverse-proxy. +matrix_ssl_retrieval_method: "lets-encrypt" + +matrix_ssl_architecture: "amd64" + +# The full list of domains that this role will obtain certificates for. +# This variable is likely redefined outside of the role, to include the domains that are necessary (depending on the services that are enabled). +# To add additional domain names, consider using `matrix_ssl_additional_domains_to_obtain_certificates_for` instead. +matrix_ssl_domains_to_obtain_certificates_for: "{{ matrix_ssl_additional_domains_to_obtain_certificates_for }}" + +# A list of additional domain names to obtain certificates for. +matrix_ssl_additional_domains_to_obtain_certificates_for: [] + +# Controls whether to obtain production or staging certificates from Let's Encrypt. +matrix_ssl_lets_encrypt_staging: false +matrix_ssl_lets_encrypt_certbot_docker_image: "{{ matrix_container_global_registry_prefix }}certbot/certbot:{{ matrix_ssl_architecture }}-v1.17.0" +matrix_ssl_lets_encrypt_certbot_docker_image_force_pull: "{{ matrix_ssl_lets_encrypt_certbot_docker_image.endswith(':latest') }}" +matrix_ssl_lets_encrypt_certbot_standalone_http_port: 2402 +matrix_ssl_lets_encrypt_support_email: ~ + +# Tells which interface and port the Let's Encrypt (certbot) container should try to bind to +# when it tries to obtain initial certificates in standalone mode. +# +# This should normally be a public interface and port. +# If you'd like to not bind on all IP addresses, specify one explicitly (e.g. `a.b.c.d:80`) +matrix_ssl_lets_encrypt_container_standalone_http_host_bind_port: '80' + +matrix_ssl_base_path: "{{ matrix_base_data_path }}/ssl" +matrix_ssl_config_dir_path: "{{ matrix_ssl_base_path }}/config" +matrix_ssl_log_dir_path: "{{ matrix_ssl_base_path }}/log" + +# If you'd like to start some service before a certificate is obtained, specify it here. +# This could be something like `matrix-dynamic-dns`, etc. +matrix_ssl_pre_obtaining_required_service_name: ~ +matrix_ssl_pre_obtaining_required_service_start_wait_time_seconds: 60 + +# Nginx Optimize SSL Session +# +# ssl_session_cache: +# - Creating a cache of TLS connection parameters reduces the number of handshakes +# and thus can improve the performance of application. +# - Default session cache is not optimal as it can be used by only one worker process +# and can cause memory fragmentation. It is much better to use shared cache. +# - Learn More: https://nginx.org/en/docs/http/ngx_http_ssl_module.html +# +# ssl_session_timeout: +# - Nginx by default it is set to 5 minutes which is very low. +# should be like 4h or 1d but will require you to increase the size of cache. +# - Learn More: +# https://github.com/certbot/certbot/issues/6903 +# https://github.com/mozilla/server-side-tls/issues/198 +# +# ssl_session_tickets: +# - In case of session tickets, information about session is given to the client. +# Enabling this improve performance also make Perfect Forward Secrecy useless. +# - If you would instead like to use ssl_session_tickets by yourself, you can set +# matrix_nginx_proxy_ssl_session_tickets_off false. +# - Learn More: https://github.com/mozilla/server-side-tls/issues/135 +# +# Presets are taken from Mozilla's Server Side TLS Recommended configurations +matrix_nginx_proxy_ssl_session_cache: "shared:MozSSL:10m" +matrix_nginx_proxy_ssl_session_timeout: "1d" +matrix_nginx_proxy_ssl_session_tickets_off: true + +# OCSP Stapling eliminating the need for clients to contact the CA, with the aim of improving both security and performance. +# OCSP stapling can provide a performance boost of up to 30% +# nginx web server supports OCSP stapling since version 1.3.7. +# +# *warning* Nginx is lazy loading OCSP responses, which means that for the first few web requests it is unable to add the OCSP response. +# set matrix_nginx_proxy_ocsp_stapling_enabled false to disable OCSP Stapling +# +# Learn more about what it is here: +# - https://en.wikipedia.org/wiki/OCSP_stapling +# - https://blog.cloudflare.com/high-reliability-ocsp-stapling/ +# - https://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox/ +matrix_nginx_proxy_ocsp_stapling_enabled: true + +# nginx status page configurations. +matrix_nginx_proxy_proxy_matrix_nginx_status_enabled: false +matrix_nginx_proxy_proxy_matrix_nginx_status_allowed_addresses: ['{{ ansible_default_ipv4.address }}'] + + +# synapse worker activation and endpoint mappings +matrix_nginx_proxy_synapse_workers_enabled: false +matrix_nginx_proxy_synapse_workers_list: [] +matrix_nginx_proxy_synapse_generic_worker_client_server_locations: [] +matrix_nginx_proxy_synapse_generic_worker_federation_locations: [] +matrix_nginx_proxy_synapse_media_repository_locations: [] +matrix_nginx_proxy_synapse_user_dir_locations: [] +matrix_nginx_proxy_synapse_frontend_proxy_locations: [] + +# The amount of worker processes and connections +# Consider increasing these when you are expecting high amounts of traffic +# http://nginx.org/en/docs/ngx_core_module.html#worker_connections +matrix_nginx_proxy_worker_processes: 1 +matrix_nginx_proxy_worker_connections: 1024 diff --git a/roles/matrix-nginx-proxy/tasks/init.yml b/roles/matrix-nginx-proxy/tasks/init.yml new file mode 100644 index 00000000..0161da23 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/init.yml @@ -0,0 +1,8 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-nginx-proxy.service'] }}" + when: matrix_nginx_proxy_enabled|bool + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + [item.name] }}" + when: "item.applicable|bool and item.enableable|bool" + with_items: "{{ matrix_ssl_renewal_systemd_units_list }}" diff --git a/roles/matrix-nginx-proxy/tasks/main.yml b/roles/matrix-nginx-proxy/tasks/main.yml new file mode 100644 index 00000000..ad111951 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/main.yml @@ -0,0 +1,38 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +# Always validating the configuration, even if `matrix_nginx_proxy: false`. +# This role performs actions even if the role is disabled, so we need +# to ensure there's a valid configuration in any case. +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: run_setup|bool + tags: + - setup-all + - setup-nginx-proxy + +- import_tasks: "{{ role_path }}/tasks/ssl/main.yml" + when: run_setup|bool + tags: + - setup-all + - setup-nginx-proxy + - setup-ssl + +- import_tasks: "{{ role_path }}/tasks/setup_nginx_proxy.yml" + when: run_setup|bool + tags: + - setup-all + - setup-nginx-proxy + +- import_tasks: "{{ role_path }}/tasks/self_check_well_known.yml" + delegate_to: 127.0.0.1 + become: false + when: run_self_check|bool + tags: + - self-check + +- name: Mark matrix-nginx-proxy role as executed + set_fact: + matrix_nginx_proxy_role_executed: true + tags: + - always diff --git a/roles/matrix-nginx-proxy/tasks/self_check_well_known.yml b/roles/matrix-nginx-proxy/tasks/self_check_well_known.yml new file mode 100644 index 00000000..be1b6555 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/self_check_well_known.yml @@ -0,0 +1,30 @@ +--- + +- name: Determine well-known files to check (Matrix) + set_fact: + well_known_file_checks: + - path: /.well-known/matrix/client + purpose: Client Discovery + cors: true + follow_redirects: "{{ matrix_nginx_proxy_self_check_well_known_matrix_client_follow_redirects }}" + validate_certs: "{{ matrix_nginx_proxy_self_check_validate_certificates }}" + +- block: + - set_fact: + well_known_file_check_matrix_server: + path: /.well-known/matrix/server + purpose: Server Discovery + cors: false + follow_redirects: safe + validate_certs: "{{ matrix_nginx_proxy_self_check_validate_certificates }}" + + - name: Determine domains that we require certificates for (ma1sd) + set_fact: + well_known_file_checks: "{{ well_known_file_checks + [well_known_file_check_matrix_server] }}" + when: matrix_well_known_matrix_server_enabled|bool + +- name: Perform well-known checks + include_tasks: "{{ role_path }}/tasks/self_check_well_known_file.yml" + with_items: "{{ well_known_file_checks }}" + loop_control: + loop_var: well_known_file_check diff --git a/roles/matrix-nginx-proxy/tasks/self_check_well_known_file.yml b/roles/matrix-nginx-proxy/tasks/self_check_well_known_file.yml new file mode 100644 index 00000000..6f831a29 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/self_check_well_known_file.yml @@ -0,0 +1,73 @@ +--- + +- set_fact: + well_known_url_matrix: "https://{{ matrix_server_fqn_matrix }}{{ well_known_file_check.path }}" + well_known_url_identity: "https://{{ matrix_domain }}{{ well_known_file_check.path }}" + +# These well-known files may be served without a `Content-Type: application/json` header, +# so we can't rely on the uri module's automatic parsing of JSON. +- name: Check .well-known on the matrix hostname + uri: + url: "{{ well_known_url_matrix }}" + follow_redirects: none + return_content: true + validate_certs: "{{ well_known_file_check.validate_certs }}" + headers: + Origin: example.com + check_mode: no + register: result_well_known_matrix + ignore_errors: true + +- name: Fail if .well-known not working on the matrix hostname + fail: + msg: "Failed checking that the well-known file for {{ well_known_file_check.purpose }} is configured at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ well_known_url_matrix }}`). Is port 443 open in your firewall? Full error: {{ result_well_known_matrix }}" + when: "result_well_known_matrix.failed" + +- name: Parse JSON for well-known payload at the matrix hostname + set_fact: + well_known_matrix_payload: "{{ result_well_known_matrix.content|from_json }}" + +- name: Fail if .well-known not CORS-aware on the matrix hostname + fail: + msg: "The well-known file for {{ well_known_file_check.purpose }} on `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ well_known_url_matrix }}`) is not CORS-aware. The file needs to be served with an Access-Control-Allow-Origin header set." + when: "well_known_file_check.cors and 'access_control_allow_origin' not in result_well_known_matrix" + +- name: Report working .well-known on the matrix hostname + debug: + msg: "well-known for {{ well_known_file_check.purpose }} is configured correctly for `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ well_known_url_matrix }}`)" + +- name: Check .well-known on the identity hostname + uri: + url: "{{ well_known_url_identity }}" + follow_redirects: "{{ well_known_file_check.follow_redirects }}" + return_content: true + validate_certs: "{{ well_known_file_check.validate_certs }}" + headers: + Origin: example.com + check_mode: no + register: result_well_known_identity + ignore_errors: true + +- name: Fail if .well-known not working on the identity hostname + fail: + msg: "Failed checking that the well-known file for {{ well_known_file_check.purpose }} is configured at `{{ matrix_domain }}` (checked endpoint: `{{ well_known_url_identity }}`). Is port 443 open in your firewall? Full error: {{ result_well_known_identity }}" + when: "result_well_known_identity.failed" + +- name: Parse JSON for well-known payload at the identity hostname + set_fact: + well_known_identity_payload: "{{ result_well_known_identity.content|from_json }}" + +- name: Fail if .well-known not CORS-aware on the identity hostname + fail: + msg: "The well-known file for {{ well_known_file_check.purpose }} on `{{ matrix_domain }}` (checked endpoint: `{{ well_known_url_identity }}`) is not CORS-aware. The file needs to be served with an Access-Control-Allow-Origin header set. See docs/configuring-well-known.md" + when: "well_known_file_check.cors and 'access_control_allow_origin' not in result_well_known_identity" + +# For people who manually copy the well-known file, try to detect if it's outdated +- name: Fail if well-known is different on matrix hostname and identity hostname + fail: + msg: "The well-known files for {{ well_known_file_check.purpose }} at `{{ matrix_server_fqn_matrix }}` and `{{ matrix_domain }}` are different. Perhaps you copied the file ({{ well_known_file_check.path }}) manually before and now it's outdated?" + when: "well_known_matrix_payload != well_known_identity_payload" + +- name: Report working .well-known on the identity hostname + debug: + msg: "well-known for {{ well_known_file_check.purpose }} ({{ well_known_file_check.path }}) is configured correctly for `{{ matrix_domain }}` (checked endpoint: `{{ well_known_url_identity }}`)" diff --git a/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml b/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml new file mode 100644 index 00000000..149fadab --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/setup_nginx_proxy.yml @@ -0,0 +1,272 @@ +--- + +# +# Generic tasks that we always want to happen, regardless +# if the user wants matrix-nginx-proxy or not. +# +# If the user would set up their own nginx proxy server, +# the config files from matrix-nginx-proxy can be reused. +# +# It doesn't hurt to put them in place, even if they turn out +# to be unnecessary. +# +- name: Ensure Matrix nginx-proxy paths exist + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_nginx_proxy_base_path }}" + - "{{ matrix_nginx_proxy_data_path }}" + - "{{ matrix_nginx_proxy_confd_path }}" + +- name: Ensure Matrix nginx-proxy configured (main config override) + template: + src: "{{ role_path }}/templates/nginx/nginx.conf.j2" + dest: "{{ matrix_nginx_proxy_base_path }}/nginx.conf" + mode: 0644 + when: matrix_nginx_proxy_enabled|bool + +- name: Ensure matrix-synapse-metrics-htpasswd is present (protecting /_synapse/metrics URI) + template: + src: "{{ role_path }}/templates/nginx/matrix-synapse-metrics-htpasswd.j2" + dest: "{{ matrix_nginx_proxy_data_path }}/matrix-synapse-metrics-htpasswd" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: 0400 + when: "matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled|bool and matrix_nginx_proxy_proxy_synapse_metrics|bool" + +- name: Ensure Matrix nginx-proxy configured (generic) + template: + src: "{{ role_path }}/templates/nginx/conf.d/nginx-http.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/nginx-http.conf" + mode: 0644 + when: matrix_nginx_proxy_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for matrix-synapse exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-synapse.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-synapse.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_synapse_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for matrix-synapse deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-synapse.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_synapse_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for Element domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-client-element.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-client-element.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_element_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for riot domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-riot-web.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-riot-web.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_riot_compat_redirect_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for Hydrogen domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-client-hydrogen.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-client-hydrogen.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_hydrogen_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for dimension domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-dimension.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-dimension.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_dimension_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for goneb domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-bot-go-neb.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-bot-go-neb.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_bot_go_neb_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for jitsi domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-jitsi.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-jitsi.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_jitsi_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for grafana domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-grafana.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-grafana.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_grafana_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for sygnal domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-sygnal.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-sygnal.conf" + mode: 0644 + when: matrix_nginx_proxy_proxy_sygnal_enabled|bool + +- name: Ensure Matrix nginx-proxy configuration for Matrix domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-domain.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-domain.conf" + mode: 0644 + +- name: Ensure Matrix nginx-proxy data directory for base domain exists + file: + path: "{{ matrix_nginx_proxy_data_path }}/matrix-domain" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_nginx_proxy_base_domain_serving_enabled|bool and matrix_nginx_proxy_base_domain_create_directory|bool + +- name: Ensure Matrix nginx-proxy homepage for base domain exists + copy: + content: "{{ matrix_nginx_proxy_base_domain_homepage_template }}" + dest: "{{ matrix_nginx_proxy_data_path }}/matrix-domain/index.html" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: matrix_nginx_proxy_base_domain_serving_enabled|bool and matrix_nginx_proxy_base_domain_homepage_enabled|bool and matrix_nginx_proxy_base_domain_create_directory|bool + +- name: Ensure Matrix nginx-proxy configuration for base domain exists + template: + src: "{{ role_path }}/templates/nginx/conf.d/matrix-base-domain.conf.j2" + dest: "{{ matrix_nginx_proxy_confd_path }}/matrix-base-domain.conf" + mode: 0644 + when: matrix_nginx_proxy_base_domain_serving_enabled|bool + +# +# Tasks related to setting up matrix-nginx-proxy +# +- name: Ensure nginx Docker image is pulled + docker_image: + name: "{{ matrix_nginx_proxy_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_nginx_proxy_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_nginx_proxy_docker_image_force_pull }}" + when: matrix_nginx_proxy_enabled|bool + +- name: Ensure matrix-nginx-proxy.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-nginx-proxy.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-nginx-proxy.service" + mode: 0644 + register: matrix_nginx_proxy_systemd_service_result + when: matrix_nginx_proxy_enabled|bool + +- name: Ensure systemd reloaded after matrix-nginx-proxy.service installation + service: + daemon_reload: yes + when: "matrix_nginx_proxy_enabled and matrix_nginx_proxy_systemd_service_result.changed" + + +# +# Tasks related to getting rid of matrix-nginx-proxy (if it was previously enabled) +# + +- name: Check existence of matrix-nginx-proxy service + stat: + path: "{{ matrix_systemd_path }}/matrix-nginx-proxy.service" + register: matrix_nginx_proxy_service_stat + when: "not matrix_nginx_proxy_enabled|bool" + +- name: Ensure matrix-nginx-proxy is stopped + service: + name: matrix-nginx-proxy + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_nginx_proxy_enabled|bool and matrix_nginx_proxy_service_stat.stat.exists" + +- name: Ensure matrix-nginx-proxy.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-nginx-proxy.service" + state: absent + when: "not matrix_nginx_proxy_enabled|bool and matrix_nginx_proxy_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-nginx-proxy.service removal + service: + daemon_reload: yes + when: "not matrix_nginx_proxy_enabled|bool and matrix_nginx_proxy_service_stat.stat.exists" + +- name: Ensure Matrix nginx-proxy configuration for matrix domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-domain.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_matrix_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for riot domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-riot-web.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_riot_compat_redirect_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for Hydrogen domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-client-hydrogen.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_hydrogen_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for dimension domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-dimension.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_dimension_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for goneb domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-bot-go-neb.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_bot_go_neb_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for jitsi domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-jitsi.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_jitsi_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for grafana domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-grafana.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_grafana_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for sygnal domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-sygnal.conf" + state: absent + when: "not matrix_nginx_proxy_proxy_sygnal_enabled|bool" + +- name: Ensure Matrix nginx-proxy homepage for base domain deleted + file: + path: "{{ matrix_nginx_proxy_data_path }}/matrix-domain/index.html" + state: absent + when: "not matrix_nginx_proxy_base_domain_serving_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for base domain deleted + file: + path: "{{ matrix_nginx_proxy_confd_path }}/matrix-base-domain.conf" + state: absent + when: "not matrix_nginx_proxy_base_domain_serving_enabled|bool" + +- name: Ensure Matrix nginx-proxy configuration for main config override deleted + file: + path: "{{ matrix_nginx_proxy_base_path }}/nginx.conf" + state: absent + when: "not matrix_nginx_proxy_enabled|bool" + +- name: Ensure Matrix nginx-proxy htpasswd is deleted (protecting /_synapse/metrics URI) + file: + path: "{{ matrix_nginx_proxy_data_path }}/matrix-synapse-metrics-htpasswd" + state: absent + when: "not matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled|bool or not matrix_nginx_proxy_proxy_synapse_metrics|bool" diff --git a/roles/matrix-nginx-proxy/tasks/setup_well_known.yml b/roles/matrix-nginx-proxy/tasks/setup_well_known.yml new file mode 100644 index 00000000..3e43a8c6 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/setup_well_known.yml @@ -0,0 +1,24 @@ +- set_fact: + matrix_well_known_file_path: "{{ matrix_static_files_base_path }}/.well-known/matrix/client" + +# We need others to be able to read these directories too, +# so that matrix-nginx-proxy's nginx user can access the files. +# +# For running with another webserver, we recommend being part of the `matrix` group. +- name: Ensure Matrix static-files path exists + file: + path: "{{ item }}" + state: directory + mode: 0755 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_static_files_base_path }}/.well-known/matrix" + +- name: Ensure Matrix /.well-known/matrix/client configured + template: + src: "{{ role_path }}/templates/well-known/matrix-client.j2" + dest: "{{ matrix_static_files_base_path }}/.well-known/matrix" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" \ No newline at end of file diff --git a/roles/matrix-nginx-proxy/tasks/ssl/main.yml b/roles/matrix-nginx-proxy/tasks/ssl/main.yml new file mode 100644 index 00000000..6c060818 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/ssl/main.yml @@ -0,0 +1,31 @@ +--- + +- name: Fail if using unsupported SSL certificate retrieval method + fail: + msg: "The `matrix_ssl_retrieval_method` variable contains an unsupported value" + when: "matrix_ssl_retrieval_method not in ['lets-encrypt', 'self-signed', 'manually-managed', 'none']" + + +# Common tasks, required by almost any method below. + +- name: Ensure SSL certificate paths exists + file: + path: "{{ item }}" + state: directory + mode: 0770 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + recurse: true + with_items: + - "{{ matrix_ssl_log_dir_path }}" + - "{{ matrix_ssl_config_dir_path }}" + when: "matrix_ssl_retrieval_method != 'none'" + + +# Method specific tasks follow + +- import_tasks: tasks/ssl/setup_ssl_lets_encrypt.yml + +- import_tasks: tasks/ssl/setup_ssl_self_signed.yml + +- import_tasks: tasks/ssl/setup_ssl_manually_managed.yml diff --git a/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_lets_encrypt.yml b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_lets_encrypt.yml new file mode 100644 index 00000000..bfd25894 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_lets_encrypt.yml @@ -0,0 +1,64 @@ +--- + +# This is a cleanup/migration task, because of to the new way we manage cronjobs (`cron` module) and the new script name. +# This migration task can be removed some time in the future. +- name: (Migration) Remove deprecated Let's Encrypt SSL certificate management files + file: + path: "{{ item }}" + state: absent + with_items: + - "{{ matrix_local_bin_path }}/matrix-ssl-certificates-renew" + - "{{ matrix_cron_path }}/matrix-ssl-certificate-renewal" + - "{{ matrix_cron_path }}/matrix-nginx-proxy-periodic-restarter" + - "/etc/cron.d/matrix-ssl-lets-encrypt" + +# +# Tasks related to setting up Let's Encrypt's management of certificates +# + +- block: + - name: Ensure certbot Docker image is pulled + docker_image: + name: "{{ matrix_ssl_lets_encrypt_certbot_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_ssl_lets_encrypt_certbot_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_ssl_lets_encrypt_certbot_docker_image_force_pull }}" + + - name: Obtain Let's Encrypt certificates + include_tasks: "{{ role_path }}/tasks/ssl/setup_ssl_lets_encrypt_obtain_for_domain.yml" + with_items: "{{ matrix_ssl_domains_to_obtain_certificates_for }}" + loop_control: + loop_var: domain_name + + - name: Ensure Let's Encrypt SSL renewal script installed + template: + src: "{{ role_path }}/templates/usr-local-bin/matrix-ssl-lets-encrypt-certificates-renew.j2" + dest: "{{ matrix_local_bin_path }}/matrix-ssl-lets-encrypt-certificates-renew" + mode: 0755 + + - name: Ensure SSL renewal systemd units installed + template: + src: "{{ role_path }}/templates/systemd/{{ item.name }}.j2" + dest: "{{ matrix_systemd_path }}/{{ item.name }}" + mode: 0644 + when: "item.applicable|bool" + with_items: "{{ matrix_ssl_renewal_systemd_units_list }}" + when: "matrix_ssl_retrieval_method == 'lets-encrypt'" + +# +# Tasks related to getting rid of Let's Encrypt's management of certificates +# + +- block: + - name: Ensure matrix-ssl-lets-encrypt-renew cronjob removed + file: + path: "{{ matrix_systemd_path }}/{{ item.name }}" + state: absent + when: "not item.applicable|bool" + with_items: "{{ matrix_ssl_renewal_systemd_units_list }}" + + - name: Ensure Let's Encrypt SSL renewal script removed + file: + path: "{{ matrix_local_bin_path }}/matrix-ssl-lets-encrypt-certificates-renew" + state: absent + when: "matrix_ssl_retrieval_method != 'lets-encrypt'" diff --git a/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_lets_encrypt_obtain_for_domain.yml b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_lets_encrypt_obtain_for_domain.yml new file mode 100644 index 00000000..4639f122 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_lets_encrypt_obtain_for_domain.yml @@ -0,0 +1,91 @@ +- debug: + msg: "Dealing with SSL certificate retrieval for domain: {{ domain_name }}" + +- set_fact: + domain_name_certificate_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/fullchain.pem" + +- name: Check if a certificate for the domain already exists + stat: + path: "{{ domain_name_certificate_path }}" + register: domain_name_certificate_path_stat + +- set_fact: + domain_name_needs_cert: "{{ not domain_name_certificate_path_stat.stat.exists }}" + +- block: + - name: Ensure required service for obtaining is started + service: + name: "{{ matrix_ssl_pre_obtaining_required_service_name }}" + state: started + register: matrix_ssl_pre_obtaining_required_service_start_result + + - name: Wait some time, so that the required service for obtaining can start + wait_for: + timeout: "{{ matrix_ssl_service_to_start_before_obtaining_start_wait_time_seconds }}" + when: "matrix_ssl_pre_obtaining_required_service_start_result.changed|bool" + when: "domain_name_needs_cert|bool and matrix_ssl_pre_obtaining_required_service_name != ''" + +# This will fail if there is something running on port 80 (like matrix-nginx-proxy). +# We suppress the error, as we'll try another method below. +- name: Attempt initial SSL certificate retrieval with standalone authenticator (directly) + shell: >- + {{ matrix_host_command_docker }} run + --rm + --name=matrix-certbot + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + -p {{ matrix_ssl_lets_encrypt_container_standalone_http_host_bind_port }}:8080 + --mount type=bind,src={{ matrix_ssl_config_dir_path }},dst=/etc/letsencrypt + --mount type=bind,src={{ matrix_ssl_log_dir_path }},dst=/var/log/letsencrypt + {{ matrix_ssl_lets_encrypt_certbot_docker_image }} + certonly + --non-interactive + --work-dir=/tmp + --http-01-port 8080 + {% if matrix_ssl_lets_encrypt_staging %}--staging{% endif %} + --standalone + --preferred-challenges http + --agree-tos + --email={{ matrix_ssl_lets_encrypt_support_email }} + -d {{ domain_name }} + when: domain_name_needs_cert|bool + register: result_certbot_direct + ignore_errors: true + +# If matrix-nginx-proxy is configured from a previous run of this playbook, +# and it's running now, it may be able to proxy requests to `matrix_ssl_lets_encrypt_certbot_standalone_http_port`. +- name: Attempt initial SSL certificate retrieval with standalone authenticator (via proxy) + shell: >- + {{ matrix_host_command_docker }} run + --rm + --name=matrix-certbot + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + -p 127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}:8080 + --network={{ matrix_docker_network }} + --mount type=bind,src={{ matrix_ssl_config_dir_path }},dst=/etc/letsencrypt + --mount type=bind,src={{ matrix_ssl_log_dir_path }},dst=/var/log/letsencrypt + {{ matrix_ssl_lets_encrypt_certbot_docker_image }} + certonly + --non-interactive + --work-dir=/tmp + --http-01-port 8080 + {% if matrix_ssl_lets_encrypt_staging %}--staging{% endif %} + --standalone + --preferred-challenges http + --agree-tos + --email={{ matrix_ssl_lets_encrypt_support_email }} + -d {{ domain_name }} + when: "domain_name_needs_cert and result_certbot_direct.failed" + register: result_certbot_proxy + ignore_errors: true + +- name: Fail if all SSL certificate retrieval attempts failed + fail: + msg: | + Failed to obtain a certificate directly (by listening on port 80) + and also failed to obtain by relying on the server at port 80 to proxy the request. + See above for details. + You may wish to set up proxying of /.well-known/acme-challenge to {{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }} or, + more easily, stop the server on port 80 while this playbook runs. + when: "domain_name_needs_cert and result_certbot_direct.failed and result_certbot_proxy.failed" diff --git a/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_manually_managed.yml b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_manually_managed.yml new file mode 100644 index 00000000..ea39f5e9 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_manually_managed.yml @@ -0,0 +1,8 @@ +--- + +- name: Verify certificates + include_tasks: "{{ role_path }}/tasks/ssl/setup_ssl_manually_managed_verify_for_domain.yml" + with_items: "{{ matrix_ssl_domains_to_obtain_certificates_for }}" + loop_control: + loop_var: domain_name + when: "matrix_ssl_retrieval_method == 'manually-managed'" \ No newline at end of file diff --git a/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_manually_managed_verify_for_domain.yml b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_manually_managed_verify_for_domain.yml new file mode 100644 index 00000000..be0444b1 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_manually_managed_verify_for_domain.yml @@ -0,0 +1,23 @@ +--- + +- set_fact: + matrix_ssl_certificate_verification_cert_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/fullchain.pem" + matrix_ssl_certificate_verification_cert_key_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/privkey.pem" + +- name: Check if SSL certificate file exists + stat: + path: "{{ matrix_ssl_certificate_verification_cert_path }}" + register: matrix_ssl_certificate_verification_cert_path_stat_result + +- fail: + msg: "Failed finding a certificate file (for domain `{{ domain_name }}`) at `{{ matrix_ssl_certificate_verification_cert_path }}`" + when: "not matrix_ssl_certificate_verification_cert_path_stat_result.stat.exists" + +- name: Check if SSL certificate key file exists + stat: + path: "{{ matrix_ssl_certificate_verification_cert_key_path }}" + register: matrix_ssl_certificate_verification_cert_key_path_stat_result + +- fail: + msg: "Failed finding a certificate key file (for domain `{{ domain_name }}`) at `{{ matrix_ssl_certificate_verification_cert_key_path }}`" + when: "not matrix_ssl_certificate_verification_cert_key_path_stat_result.stat.exists" \ No newline at end of file diff --git a/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_self_signed.yml b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_self_signed.yml new file mode 100644 index 00000000..8fa316da --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_self_signed.yml @@ -0,0 +1,32 @@ +--- + +- name: Ensure OpenSSL installed (RedHat) + yum: + name: + - openssl + state: present + update_cache: no + when: "matrix_ssl_retrieval_method == 'self-signed' and ansible_os_family == 'RedHat'" + +- name: Ensure APT usage dependencies are installed (Debian) + apt: + name: + - openssl + state: present + update_cache: no + when: "matrix_ssl_retrieval_method == 'self-signed' and ansible_os_family == 'Debian'" + +- name: Ensure OpenSSL installed (Archlinux) + pacman: + name: + - openssl + state: latest + update_cache: no + when: "matrix_ssl_retrieval_method == 'self-signed' and ansible_distribution == 'Archlinux'" + +- name: Generate self-signed certificates + include_tasks: "{{ role_path }}/tasks/ssl/setup_ssl_self_signed_obtain_for_domain.yml" + with_items: "{{ matrix_ssl_domains_to_obtain_certificates_for }}" + loop_control: + loop_var: domain_name + when: "matrix_ssl_retrieval_method == 'self-signed'" diff --git a/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_self_signed_obtain_for_domain.yml b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_self_signed_obtain_for_domain.yml new file mode 100644 index 00000000..aea17cc0 --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/ssl/setup_ssl_self_signed_obtain_for_domain.yml @@ -0,0 +1,42 @@ +--- + +- set_fact: + matrix_ssl_certificate_csr_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/csr.csr" + matrix_ssl_certificate_cert_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/fullchain.pem" + matrix_ssl_certificate_cert_key_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/privkey.pem" + +- name: Check if SSL certificate file exists + stat: + path: "{{ matrix_ssl_certificate_cert_path }}" + register: matrix_ssl_certificate_cert_path_stat_result + +# In order to do any sort of generation (below), we need to ensure the directory exists first +- name: Ensure SSL certificate directory exists + file: + path: "{{ matrix_ssl_certificate_csr_path|dirname }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: "not matrix_ssl_certificate_cert_path_stat_result.stat.exists" + +# The proper way to do this is by using a sequence of +# `openssl_privatekey`, `openssl_csr` and `openssl_certificate`. +# +# Unfortunately, `openssl_csr` and `openssl_certificate` require `PyOpenSSL>=0.15` to work, +# which is not available on CentOS 7 (at least). +# +# We'll do it in a more manual way. +- name: Generate SSL certificate + command: | + openssl req -x509 \ + -sha256 \ + -newkey rsa:4096 \ + -nodes \ + -subj "/CN={{ domain_name }}" \ + -keyout {{ matrix_ssl_certificate_cert_key_path }} \ + -out {{ matrix_ssl_certificate_cert_path }} \ + -days 3650 + become: true + become_user: "{{ matrix_user_username }}" + when: "not matrix_ssl_certificate_cert_path_stat_result.stat.exists" diff --git a/roles/matrix-nginx-proxy/tasks/validate_config.yml b/roles/matrix-nginx-proxy/tasks/validate_config.yml new file mode 100644 index 00000000..9661ae5e --- /dev/null +++ b/roles/matrix-nginx-proxy/tasks/validate_config.yml @@ -0,0 +1,47 @@ +--- + +- name: (Deprecation) Catch and report renamed settings + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_nginx_proxy_matrix_client_api_addr_with_proxy_container', 'new': 'matrix_nginx_proxy_proxy_matrix_client_api_addr_with_container'} + - {'old': 'matrix_nginx_proxy_matrix_client_api_addr_sans_proxy_container', 'new': 'matrix_nginx_proxy_proxy_matrix_client_api_addr_sans_container'} + # People who configured this to disable Riot, would now wish to be disabling Element. + # We now also have `matrix_nginx_proxy_proxy_riot_compat_redirect_`, but that's something else and is disabled by default. + - {'old': 'matrix_nginx_proxy_proxy_riot_enabled', 'new': 'matrix_nginx_proxy_proxy_element_enabled'} + - {'old': 'matrix_ssl_lets_encrypt_renew_cron_time_definition', 'new': ''} + - {'old': 'matrix_nginx_proxy_reload_cron_time_definition', 'new': ''} + +- name: Fail on unknown matrix_ssl_retrieval_method + fail: + msg: >- + `matrix_ssl_retrieval_method` needs to be set to a known value. + when: "matrix_ssl_retrieval_method not in ['lets-encrypt', 'self-signed', 'manually-managed', 'none']" + +- name: Fail on unknown matrix_nginx_proxy_ssl_config + fail: + msg: >- + `matrix_nginx_proxy_ssl_preset` needs to be set to a known value. + when: "matrix_nginx_proxy_ssl_preset not in ['modern', 'intermediate', 'old']" + +- block: + - name: (Deprecation) Catch and report renamed settings + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + with_items: + - {'old': 'host_specific_matrix_ssl_support_email', 'new': 'matrix_ssl_lets_encrypt_support_email'} + - {'old': 'host_specific_matrix_ssl_lets_encrypt_support_email', 'new': 'matrix_ssl_lets_encrypt_support_email'} + when: "item.old in vars" + + - name: Fail if required variables are undefined + fail: + msg: "The `{{ item }}` variable must be defined and have a non-null value" + with_items: + - "matrix_ssl_lets_encrypt_support_email" + when: "vars[item] == '' or vars[item] is none" + when: "matrix_ssl_retrieval_method == 'lets-encrypt'" diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-base-domain.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-base-domain.conf.j2 new file mode 100644 index 00000000..b0294283 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-base-domain.conf.j2 @@ -0,0 +1,95 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + root {{ matrix_nginx_proxy_data_path_in_container if matrix_nginx_proxy_enabled else matrix_nginx_proxy_data_path }}{{ matrix_nginx_proxy_data_path_extension }}; + + gzip on; + gzip_types text/plain application/json; + + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + + {% for configuration_block in matrix_nginx_proxy_proxy_domain_additional_server_configuration_blocks %} + {{- configuration_block }} + {% endfor %} + + location /.well-known/matrix { + root {{ matrix_static_files_base_path }}; + {# + A somewhat long expires value is used to prevent outages + in case this is unreachable due to network failure. + #} + expires 4h; + default_type application/json; + add_header Access-Control-Allow-Origin *; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + + server_name {{ matrix_nginx_proxy_base_domain_hostname }}; + server_tokens off; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_base_domain_hostname }}; + server_tokens off; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_base_domain_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_base_domain_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != '' %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_base_domain_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-bot-go-neb.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-bot-go-neb.conf.j2 new file mode 100644 index 00000000..6cb5f57a --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-bot-go-neb.conf.j2 @@ -0,0 +1,95 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json application/javascript text/css image/x-icon font/ttf image/gif; + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + add_header X-Content-Type-Options nosniff; + +{% for configuration_block in matrix_nginx_proxy_proxy_bot_go_neb_additional_server_configuration_blocks %} + {{- configuration_block }} +{% endfor %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-bot-go-neb:4050"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:4050; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + server_name {{ matrix_nginx_proxy_proxy_bot_go_neb_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_bot_go_neb_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_bot_go_neb_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_bot_go_neb_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != '' %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_bot_go_neb_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-client-element.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-client-element.conf.j2 new file mode 100644 index 00000000..2f4f4aa1 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-client-element.conf.j2 @@ -0,0 +1,104 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json application/javascript text/css image/x-icon font/ttf image/gif; + + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + add_header X-Frame-Options SAMEORIGIN; + add_header Content-Security-Policy "frame-ancestors 'self'"; + + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + + + {% for configuration_block in matrix_nginx_proxy_proxy_element_additional_server_configuration_blocks %} + {{- configuration_block }} + {% endfor %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-client-element:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:8765; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + + server_name {{ matrix_nginx_proxy_proxy_element_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_element_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_element_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_element_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != "" %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_element_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-client-hydrogen.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-client-hydrogen.conf.j2 new file mode 100644 index 00000000..d9a05926 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-client-hydrogen.conf.j2 @@ -0,0 +1,102 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json application/javascript text/css image/x-icon font/ttf image/gif; + + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options SAMEORIGIN; + add_header Content-Security-Policy "frame-ancestors 'none'"; + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + + {% for configuration_block in matrix_nginx_proxy_proxy_hydrogen_additional_server_configuration_blocks %} + {{- configuration_block }} + {% endfor %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-client-hydrogen:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:8768; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + + server_name {{ matrix_nginx_proxy_proxy_hydrogen_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_hydrogen_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_hydrogen_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_hydrogen_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != "" %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_element_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-dimension.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-dimension.conf.j2 new file mode 100644 index 00000000..ef8ee972 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-dimension.conf.j2 @@ -0,0 +1,98 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json application/javascript text/css image/x-icon font/ttf image/gif; + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + add_header X-Content-Type-Options nosniff; + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + +{% for configuration_block in matrix_nginx_proxy_proxy_dimension_additional_server_configuration_blocks %} + {{- configuration_block }} +{% endfor %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-dimension:8184"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:8184; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + server_name {{ matrix_nginx_proxy_proxy_dimension_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_dimension_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_dimension_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_dimension_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != '' %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_dimension_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-domain.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-domain.conf.j2 new file mode 100644 index 00000000..04a77269 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-domain.conf.j2 @@ -0,0 +1,293 @@ +#jinja2: lstrip_blocks: "True" +{% macro render_nginx_status_location_block(addresses) %} + {# Empty first line to make indentation prettier. #} + + location /nginx_status { + stub_status on; + access_log off; + {% for address in addresses %} + allow {{ address }}; + {% endfor %} + deny all; + } +{% endmacro %} + + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json; + + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + + location /.well-known/matrix { + root {{ matrix_static_files_base_path }}; + {# + A somewhat long expires value is used to prevent outages + in case this is unreachable due to network failure or + due to the base domain's server completely dying. + #} + expires 4h; + default_type application/json; + add_header Access-Control-Allow-Origin *; + } + + {% if matrix_nginx_proxy_proxy_matrix_nginx_status_enabled %} + {{ render_nginx_status_location_block(matrix_nginx_proxy_proxy_matrix_nginx_status_allowed_addresses) }} + {% endif %} + + {% if matrix_nginx_proxy_proxy_matrix_corporal_api_enabled %} + location ^~ /_matrix/corporal { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_matrix_corporal_api_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_matrix_corporal_api_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + {% endif %} + + {% if matrix_nginx_proxy_proxy_matrix_identity_api_enabled %} + location ^~ /_matrix/identity { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_matrix_identity_api_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_matrix_identity_api_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + {% endif %} + + {% if matrix_nginx_proxy_proxy_matrix_user_directory_search_enabled %} + location ^~ /_matrix/client/r0/user_directory/search { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_matrix_user_directory_search_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_matrix_user_directory_search_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } + {% endif %} + + {% if matrix_nginx_proxy_proxy_matrix_3pid_registration_enabled %} + location ~ ^/_matrix/client/r0/register/(email|msisdn)/requestToken$ { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_matrix_3pid_registration_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_matrix_3pid_registration_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + {% endif %} + + {% for configuration_block in matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks %} + {{- configuration_block }} + {% endfor %} + + {# + This handles the Matrix Client API only. + The Matrix Federation API is handled by a separate vhost. + #} + location ~* ^({{ matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_prefix_regexes|join('|') }}) { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_matrix_client_api_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_matrix_client_api_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + + client_body_buffer_size 25M; + client_max_body_size {{ matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size_mb }}M; + proxy_max_temp_file_size 0; + } + + {# + We only handle the root URI for this redirect or homepage serving. + Unhandled URIs (mostly by `matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_prefix_regexes` above) should result in a 404, + instead of causing a redirect. + See: https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1058 + #} + location ~* ^/$ { + {% if matrix_nginx_proxy_proxy_matrix_client_redirect_root_uri_to_domain %} + return 302 $scheme://{{ matrix_nginx_proxy_proxy_matrix_client_redirect_root_uri_to_domain }}$request_uri; + {% else %} + rewrite ^/$ /_matrix/static/ last; + {% endif %} + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + server_name {{ matrix_nginx_proxy_proxy_matrix_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + {% if matrix_nginx_proxy_proxy_matrix_nginx_status_enabled %} + {{ render_nginx_status_location_block(matrix_nginx_proxy_proxy_matrix_nginx_status_allowed_addresses) }} + {% endif %} + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_matrix_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_matrix_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_matrix_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != '' %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_matrix_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} + +{% if matrix_nginx_proxy_proxy_matrix_federation_api_enabled %} +{# + This federation vhost is a little special. + It serves federation over HTTP or HTTPS, depending on `matrix_nginx_proxy_https_enabled`. +#} +server { + {% if matrix_nginx_proxy_https_enabled %} + listen {{ matrix_nginx_proxy_proxy_matrix_federation_port }} ssl http2; + listen [::]:{{ matrix_nginx_proxy_proxy_matrix_federation_port }} ssl http2; + {% else %} + listen {{ matrix_nginx_proxy_proxy_matrix_federation_port }}; + {% endif %} + + server_name {{ matrix_nginx_proxy_proxy_matrix_federation_hostname }}; + server_tokens off; + + root /dev/null; + + gzip on; + gzip_types text/plain application/json; + + {% if matrix_nginx_proxy_https_enabled %} + ssl_certificate {{ matrix_nginx_proxy_proxy_matrix_federation_api_ssl_certificate }}; + ssl_certificate_key {{ matrix_nginx_proxy_proxy_matrix_federation_api_ssl_certificate_key }}; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != '' %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_nginx_proxy_proxy_matrix_federation_api_ssl_trusted_certificate }}; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + {% endif %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_matrix_federation_api_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_matrix_federation_api_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + + client_body_buffer_size 25M; + client_max_body_size {{ matrix_nginx_proxy_proxy_matrix_federation_api_client_max_body_size_mb }}M; + proxy_max_temp_file_size 0; + } +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-grafana.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-grafana.conf.j2 new file mode 100644 index 00000000..0f7c43c5 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-grafana.conf.j2 @@ -0,0 +1,106 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json application/javascript text/css image/x-icon font/ttf image/gif; + + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + # duplicate X-Content-Type-Options & X-Frame-Options header + # Enabled by grafana by default + # add_header X-Content-Type-Options nosniff; + # add_header X-Frame-Options SAMEORIGIN; + add_header Referrer-Policy "strict-origin-when-cross-origin"; + + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + + proxy_cookie_path / "/; HTTPOnly; Secure"; + + {% for configuration_block in matrix_nginx_proxy_proxy_grafana_additional_server_configuration_blocks %} + {{- configuration_block }} + {% endfor %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-grafana:3000"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:3000; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + + server_name {{ matrix_nginx_proxy_proxy_grafana_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_grafana_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_grafana_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_grafana_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != "" %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_grafana_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-jitsi.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-jitsi.conf.j2 new file mode 100644 index 00000000..0ccda7d3 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-jitsi.conf.j2 @@ -0,0 +1,140 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json application/javascript text/css image/x-icon font/ttf image/gif; + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + add_header X-Content-Type-Options nosniff; + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + +{% for configuration_block in matrix_nginx_proxy_proxy_jitsi_additional_server_configuration_blocks %} + {{- configuration_block }} +{% endfor %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-jitsi-web:80"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:13080; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } + + # colibri (JVB) websockets + location ~ ^/colibri-ws/([a-zA-Z0-9-\.]+)/(.*) { + {% if matrix_nginx_proxy_enabled %} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-jitsi-jvb:9090"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:13090; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_http_version 1.1; + + tcp_nodelay on; + } + + # XMPP websocket + location = /xmpp-websocket { + {% if matrix_nginx_proxy_enabled %} + resolver 127.0.0.11 valid=5s; + set $backend {{ matrix_jitsi_xmpp_bosh_url_base }}; + proxy_pass $backend/xmpp-websocket; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:5280; + {% endif %} + proxy_set_header Host $host; + + proxy_http_version 1.1; + proxy_read_timeout 900s; + proxy_set_header Connection "upgrade"; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + tcp_nodelay on; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + server_name {{ matrix_nginx_proxy_proxy_jitsi_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_jitsi_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_jitsi_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_jitsi_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != '' %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_jitsi_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-riot-web.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-riot-web.conf.j2 new file mode 100644 index 00000000..d153d5c2 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-riot-web.conf.j2 @@ -0,0 +1,87 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + {% if matrix_nginx_proxy_floc_optout_enabled %} + add_header Permissions-Policy interest-cohort=() always; + {% endif %} + + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + + {% for configuration_block in matrix_nginx_proxy_proxy_riot_additional_server_configuration_blocks %} + {{- configuration_block }} + {% endfor %} + + location / { + return 301 https://{{ matrix_nginx_proxy_proxy_element_hostname }}$request_uri; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + + server_name {{ matrix_nginx_proxy_proxy_riot_compat_redirect_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_riot_compat_redirect_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_riot_compat_redirect_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_riot_compat_redirect_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != '' %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_riot_compat_redirect_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-sygnal.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-sygnal.conf.j2 new file mode 100644 index 00000000..d5760434 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-sygnal.conf.j2 @@ -0,0 +1,97 @@ +#jinja2: lstrip_blocks: "True" + +{% macro render_vhost_directives() %} + gzip on; + gzip_types text/plain application/json application/javascript text/css image/x-icon font/ttf image/gif; + {% if matrix_nginx_proxy_hsts_preload_enabled %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + {% else %} + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + {% endif %} + add_header X-XSS-Protection "{{ matrix_nginx_proxy_xss_protection }}"; + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options DENY; + +{% for configuration_block in matrix_nginx_proxy_proxy_sygnal_additional_server_configuration_blocks %} + {{- configuration_block }} +{% endfor %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-sygnal:6000"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:6000; + {% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } +{% endmacro %} + +server { + listen {{ 8080 if matrix_nginx_proxy_enabled else 80 }}; + server_name {{ matrix_nginx_proxy_proxy_sygnal_hostname }}; + + server_tokens off; + root /dev/null; + + {% if matrix_nginx_proxy_https_enabled %} + location /.well-known/acme-challenge { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-certbot:8080"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}; + {% endif %} + } + + location / { + return 301 https://$http_host$request_uri; + } + {% else %} + {{ render_vhost_directives() }} + {% endif %} +} + +{% if matrix_nginx_proxy_https_enabled %} +server { + listen {{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + listen [::]:{{ 8443 if matrix_nginx_proxy_enabled else 443 }} ssl http2; + + server_name {{ matrix_nginx_proxy_proxy_sygnal_hostname }}; + + server_tokens off; + root /dev/null; + + ssl_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_sygnal_hostname }}/fullchain.pem; + ssl_certificate_key {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_sygnal_hostname }}/privkey.pem; + + ssl_protocols {{ matrix_nginx_proxy_ssl_protocols }}; + {% if matrix_nginx_proxy_ssl_ciphers != '' %} + ssl_ciphers {{ matrix_nginx_proxy_ssl_ciphers }}; + {% endif %} + ssl_prefer_server_ciphers {{ matrix_nginx_proxy_ssl_prefer_server_ciphers }}; + + {% if matrix_nginx_proxy_ocsp_stapling_enabled %} + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate {{ matrix_ssl_config_dir_path }}/live/{{ matrix_nginx_proxy_proxy_sygnal_hostname }}/chain.pem; + {% endif %} + + {% if matrix_nginx_proxy_ssl_session_tickets_off %} + ssl_session_tickets off; + {% endif %} + ssl_session_cache {{ matrix_nginx_proxy_ssl_session_cache }}; + ssl_session_timeout {{ matrix_nginx_proxy_ssl_session_timeout }}; + + {{ render_vhost_directives() }} +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 new file mode 100644 index 00000000..db111090 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/matrix-synapse.conf.j2 @@ -0,0 +1,231 @@ +#jinja2: lstrip_blocks: "True" + +{% set generic_workers = matrix_nginx_proxy_synapse_workers_list|selectattr('type', 'equalto', 'generic_worker')|list %} +{% set media_repository_workers = matrix_nginx_proxy_synapse_workers_list|selectattr('type', 'equalto', 'media_repository')|list %} +{% set user_dir_workers = matrix_nginx_proxy_synapse_workers_list|selectattr('type', 'equalto', 'user_dir')|list %} +{% set frontend_proxy_workers = matrix_nginx_proxy_synapse_workers_list|selectattr('type', 'equalto', 'frontend_proxy')|list %} +{% if matrix_nginx_proxy_synapse_workers_enabled %} + # Round Robin "upstream" pools for workers + + {% if generic_workers %} + upstream generic_worker_upstream { + # ensures that requests from the same client will always be passed + # to the same server (except when this server is unavailable) + hash $http_x_forwarded_for; + + {% for worker in generic_workers %} + {% if matrix_nginx_proxy_enabled %} + server "matrix-synapse-worker-{{ worker.type }}-{{ worker.instanceId }}:{{ worker.port }}"; + {% else %} + server "127.0.0.1:{{ worker.port }}"; + {% endif %} + {% endfor %} + } + {% endif %} + + {% if frontend_proxy_workers %} + upstream frontend_proxy_upstream { + {% for worker in frontend_proxy_workers %} + {% if matrix_nginx_proxy_enabled %} + server "matrix-synapse-worker-{{ worker.type }}-{{ worker.instanceId }}:{{ worker.port }}"; + {% else %} + server "127.0.0.1:{{ worker.port }}"; + {% endif %} + {% endfor %} + } + {% endif %} + + {% if media_repository_workers %} + upstream media_repository_upstream { + {% for worker in media_repository_workers %} + {% if matrix_nginx_proxy_enabled %} + server "matrix-synapse-worker-{{ worker.type }}-{{ worker.instanceId }}:{{ worker.port }}"; + {% else %} + server "127.0.0.1:{{ worker.port }}"; + {% endif %} + {% endfor %} + } + {% endif %} + + {% if user_dir_workers %} + upstream user_dir_upstream { + {% for worker in user_dir_workers %} + {% if matrix_nginx_proxy_enabled %} + server "matrix-synapse-worker-{{ worker.type }}-{{ worker.instanceId }}:{{ worker.port }}"; + {% else %} + server "127.0.0.1:{{ worker.port }}"; + {% endif %} + {% endfor %} + } + {% endif %} +{% endif %} + +server { + listen 12080; + server_name {{ matrix_nginx_proxy_proxy_synapse_hostname }}; + + server_tokens off; + root /dev/null; + + gzip on; + gzip_types text/plain application/json; + + {% if matrix_nginx_proxy_synapse_workers_enabled %} + {# Workers redirects BEGIN #} + + {% if generic_workers %} + # https://github.com/matrix-org/synapse/blob/master/docs/workers.md#synapseappgeneric_worker + {% for location in matrix_nginx_proxy_synapse_generic_worker_client_server_locations %} + location ~ {{ location }} { + proxy_pass http://generic_worker_upstream$request_uri; + proxy_set_header Host $host; + } + {% endfor %} + {% endif %} + + {% if media_repository_workers %} + # https://github.com/matrix-org/synapse/blob/master/docs/workers.md#synapseappmedia_repository + {% for location in matrix_nginx_proxy_synapse_media_repository_locations %} + location ~ {{ location }} { + proxy_pass http://media_repository_upstream$request_uri; + proxy_set_header Host $host; + + client_body_buffer_size 25M; + client_max_body_size {{ matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size_mb }}M; + proxy_max_temp_file_size 0; + } + {% endfor %} + {% endif %} + + {% if user_dir_workers %} + # FIXME: obsolete if matrix_nginx_proxy_proxy_matrix_user_directory_search_enabled is set + # https://github.com/matrix-org/synapse/blob/master/docs/workers.md#synapseappuser_dir + {% for location in matrix_nginx_proxy_synapse_user_dir_locations %} + location ~ {{ location }} { + proxy_pass http://user_dir_upstream$request_uri; + proxy_set_header Host $host; + } + {% endfor %} + {% endif %} + + {% if frontend_proxy_workers %} + # https://github.com/matrix-org/synapse/blob/master/docs/workers.md#synapseappfrontend_proxy + {% for location in matrix_nginx_proxy_synapse_frontend_proxy_locations %} + location ~ {{ location }} { + proxy_pass http://frontend_proxy_upstream$request_uri; + proxy_set_header Host $host; + } + {% endfor %} + {% if matrix_nginx_proxy_synapse_presence_disabled %} + # FIXME: keep in sync with synapse workers documentation manually + location ~ ^/_matrix/client/(api/v1|r0|unstable)/presence/[^/]+/status { + proxy_pass http://frontend_proxy_upstream$request_uri; + proxy_set_header Host $host; + } + {% endif %} + {% endif %} + {# Workers redirects END #} + {% endif %} + + + {% for configuration_block in matrix_nginx_proxy_proxy_synapse_additional_server_configuration_blocks %} + {{- configuration_block }} + {% endfor %} + + {% if matrix_nginx_proxy_proxy_synapse_metrics %} + location /_synapse/metrics { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_synapse_metrics_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_synapse_metrics_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + + {% if matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_enabled %} + auth_basic "protected"; + auth_basic_user_file /nginx-data/matrix-synapse-metrics-htpasswd; + {% endif %} + } + {% endif %} + + {# Everything else just goes to the API server ##} + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_synapse_client_api_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_synapse_client_api_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + + client_body_buffer_size 25M; + client_max_body_size {{ matrix_nginx_proxy_proxy_matrix_client_api_client_max_body_size_mb }}M; + proxy_max_temp_file_size 0; + } +} + +{% if matrix_nginx_proxy_proxy_synapse_federation_api_enabled %} +server { + listen 12088; + + server_name {{ matrix_nginx_proxy_proxy_synapse_hostname }}; + server_tokens off; + + root /dev/null; + + gzip on; + gzip_types text/plain application/json; + + {% if matrix_nginx_proxy_synapse_workers_enabled %} + {% if generic_workers %} + # https://github.com/matrix-org/synapse/blob/master/docs/workers.md#synapseappgeneric_worker + {% for location in matrix_nginx_proxy_synapse_generic_worker_federation_locations %} + location ~ {{ location }} { + proxy_pass http://generic_worker_upstream$request_uri; + proxy_set_header Host $host; + } + {% endfor %} + {% endif %} + {% if media_repository_workers %} + # https://github.com/matrix-org/synapse/blob/master/docs/workers.md#synapseappmedia_repository + {% for location in matrix_nginx_proxy_synapse_media_repository_locations %} + location ~ {{ location }} { + proxy_pass http://media_repository_upstream$request_uri; + proxy_set_header Host $host; + + client_body_buffer_size 25M; + client_max_body_size {{ matrix_nginx_proxy_proxy_matrix_federation_api_client_max_body_size_mb }}M; + proxy_max_temp_file_size 0; + } + {% endfor %} + {% endif %} + {% endif %} + + location / { + {% if matrix_nginx_proxy_enabled %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "{{ matrix_nginx_proxy_proxy_synapse_federation_api_addr_with_container }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://{{ matrix_nginx_proxy_proxy_synapse_federation_api_addr_sans_container }}; + {% endif %} + + proxy_set_header Host $host; + + client_body_buffer_size 25M; + client_max_body_size {{ matrix_nginx_proxy_proxy_matrix_federation_api_client_max_body_size_mb }}M; + proxy_max_temp_file_size 0; + } +} +{% endif %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/conf.d/nginx-http.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/conf.d/nginx-http.conf.j2 new file mode 100644 index 00000000..beea6afa --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/conf.d/nginx-http.conf.j2 @@ -0,0 +1,14 @@ +#jinja2: lstrip_blocks: "True" +# The default is aligned to the CPU's cache size, +# which can sometimes be too low to handle our 2 vhosts (Synapse and Element). +# +# Thus, we ensure a larger bucket size value is used. +server_names_hash_bucket_size 64; + +{% if matrix_nginx_proxy_http_level_resolver %} + resolver {{ matrix_nginx_proxy_http_level_resolver }}; +{% endif %} + +{% for configuration_block in matrix_nginx_proxy_proxy_http_additional_server_configuration_blocks %} + {{- configuration_block }} +{% endfor %} diff --git a/roles/matrix-nginx-proxy/templates/nginx/matrix-synapse-metrics-htpasswd.j2 b/roles/matrix-nginx-proxy/templates/nginx/matrix-synapse-metrics-htpasswd.j2 new file mode 100644 index 00000000..1a7247ac --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/matrix-synapse-metrics-htpasswd.j2 @@ -0,0 +1,3 @@ +#jinja2: lstrip_blocks: "True" +# User and password for protecting /_synapse/metrics URI +prometheus:{{ matrix_nginx_proxy_proxy_synapse_metrics_basic_auth_key }} diff --git a/roles/matrix-nginx-proxy/templates/nginx/nginx.conf.j2 b/roles/matrix-nginx-proxy/templates/nginx/nginx.conf.j2 new file mode 100644 index 00000000..9ec7fa56 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/nginx/nginx.conf.j2 @@ -0,0 +1,61 @@ +#jinja2: lstrip_blocks: "True" +# This is a custom nginx configuration file that we use in the container (instead of the default one), +# because it allows us to run nginx with a non-root user. +# +# For this to work, the default vhost file (`/etc/nginx/conf.d/default.conf`) also needs to be removed. +# +# The following changes have been done compared to a default nginx configuration file: +# - various temp paths are changed to `/tmp`, so that a non-root user can write to them +# - the `user` directive was removed, as we don't want nginx to switch users + +worker_processes {{ matrix_nginx_proxy_worker_processes }}; +error_log /var/log/nginx/error.log warn; +pid /tmp/nginx.pid; +{% for configuration_block in matrix_nginx_proxy_proxy_additional_configuration_blocks %} + {{- configuration_block }} +{% endfor %} + +events { + worker_connections {{ matrix_nginx_proxy_worker_connections }}; +{% for configuration_block in matrix_nginx_proxy_proxy_event_additional_configuration_blocks %} + {{- configuration_block }} +{% endfor %} +} + + +http { + proxy_temp_path /tmp/proxy_temp; + client_body_temp_path /tmp/client_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + {% if matrix_nginx_proxy_access_log_enabled %} + access_log /var/log/nginx/access.log main; + {% else %} + access_log off; + {% endif %} + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + server_tokens off; + + #gzip on; + {# Map directive needed for proxied WebSocket upgrades #} + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + include /etc/nginx/conf.d/*.conf; +} diff --git a/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 b/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 new file mode 100755 index 00000000..c4000fa9 --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/systemd/matrix-nginx-proxy.service.j2 @@ -0,0 +1,58 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix nginx-proxy server +{% for service in matrix_nginx_proxy_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_nginx_proxy_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-nginx-proxy 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-nginx-proxy 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-nginx-proxy \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --tmpfs=/tmp:rw,noexec,nosuid,size={{ matrix_nginx_proxy_tmp_directory_size_mb }}m \ + --network={{ matrix_docker_network }} \ + {% if matrix_nginx_proxy_container_http_host_bind_port %} + -p {{ matrix_nginx_proxy_container_http_host_bind_port }}:8080 \ + {% endif %} + {% if matrix_nginx_proxy_https_enabled and matrix_nginx_proxy_container_https_host_bind_port %} + -p {{ matrix_nginx_proxy_container_https_host_bind_port }}:8443 \ + {% endif %} + {% if matrix_nginx_proxy_proxy_matrix_federation_api_enabled and matrix_nginx_proxy_container_federation_host_bind_port %} + -p {{ matrix_nginx_proxy_container_federation_host_bind_port }}:{{ matrix_nginx_proxy_proxy_matrix_federation_port }} \ + {% endif %} + --mount type=bind,src={{ matrix_nginx_proxy_base_path }}/nginx.conf,dst=/etc/nginx/nginx.conf,ro \ + --mount type=bind,src={{ matrix_nginx_proxy_data_path }},dst={{ matrix_nginx_proxy_data_path_in_container }},ro \ + --mount type=bind,src={{ matrix_nginx_proxy_confd_path }},dst=/etc/nginx/conf.d,ro \ + {% if matrix_ssl_retrieval_method != 'none' %} + --mount type=bind,src={{ matrix_ssl_config_dir_path }},dst={{ matrix_ssl_config_dir_path }},ro \ + {% endif %} + --mount type=bind,src={{ matrix_static_files_base_path }},dst={{ matrix_static_files_base_path }},ro \ + {% for volume in matrix_nginx_proxy_container_additional_volumes %} + -v {{ volume.src }}:{{ volume.dst }}:{{ volume.options }} \ + {% endfor %} + {% for arg in matrix_nginx_proxy_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_nginx_proxy_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-nginx-proxy 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-nginx-proxy 2>/dev/null' +ExecReload={{ matrix_host_command_docker }} exec matrix-nginx-proxy /usr/sbin/nginx -s reload +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-nginx-proxy + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-lets-encrypt-certificates-renew.service.j2 b/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-lets-encrypt-certificates-renew.service.j2 new file mode 100644 index 00000000..c14905ce --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-lets-encrypt-certificates-renew.service.j2 @@ -0,0 +1,7 @@ +[Unit] +Description=Renews Let's Encrypt SSL certificates + +[Service] +Type=oneshot +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStart={{ matrix_local_bin_path }}/matrix-ssl-lets-encrypt-certificates-renew diff --git a/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-lets-encrypt-certificates-renew.timer.j2 b/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-lets-encrypt-certificates-renew.timer.j2 new file mode 100644 index 00000000..b1e1c21e --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-lets-encrypt-certificates-renew.timer.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Renews Let's Encrypt SSL certificates periodically + +[Timer] +Unit=matrix-ssl-lets-encrypt-certificates-renew.service +OnCalendar=*-*-* 04:00:00 +RandomizedDelaySec=2h + +[Install] +WantedBy=timers.target diff --git a/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-nginx-proxy-reload.service.j2 b/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-nginx-proxy-reload.service.j2 new file mode 100644 index 00000000..851655ba --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-nginx-proxy-reload.service.j2 @@ -0,0 +1,6 @@ +[Unit] +Description=Reloads matrix-nginx-proxy so that new SSL certificates can kick in + +[Service] +Type=oneshot +ExecStart={{ matrix_host_command_systemctl }} reload matrix-nginx-proxy.service diff --git a/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-nginx-proxy-reload.timer.j2 b/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-nginx-proxy-reload.timer.j2 new file mode 100644 index 00000000..09cb6dad --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/systemd/matrix-ssl-nginx-proxy-reload.timer.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Reloads matrix-nginx-proxy periodically so that new SSL certificates can kick in + +[Timer] +Unit=matrix-ssl-nginx-proxy-reload.service +OnCalendar=*-*-* 06:30:00 +RandomizedDelaySec=1h + +[Install] +WantedBy=timers.target diff --git a/roles/matrix-nginx-proxy/templates/usr-local-bin/matrix-ssl-lets-encrypt-certificates-renew.j2 b/roles/matrix-nginx-proxy/templates/usr-local-bin/matrix-ssl-lets-encrypt-certificates-renew.j2 new file mode 100644 index 00000000..bc45e85e --- /dev/null +++ b/roles/matrix-nginx-proxy/templates/usr-local-bin/matrix-ssl-lets-encrypt-certificates-renew.j2 @@ -0,0 +1,31 @@ +#jinja2: lstrip_blocks: "True" +#!/bin/bash + +# For renewal to work, matrix-nginx-proxy (or another webserver, if matrix-nginx-proxy is disabled) +# need to forward requests for `/.well-known/acme-challenge` to the certbot container. +# +# This can happen inside the container network by proxying to `http://matrix-certbot:8080` +# or outside (on the host) by proxying to `http://127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}`. + +docker run \ + --rm \ + --name=matrix-certbot \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network="{{ matrix_docker_network }}" \ + -p 127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}:8080 \ + --mount type=bind,src={{ matrix_ssl_config_dir_path }},dst=/etc/letsencrypt \ + --mount type=bind,src={{ matrix_ssl_log_dir_path }},dst=/var/log/letsencrypt \ + {{ matrix_ssl_lets_encrypt_certbot_docker_image }} \ + renew \ + --non-interactive \ + --work-dir=/tmp \ + --http-01-port 8080 \ + {% if matrix_ssl_lets_encrypt_staging %} + --staging \ + {% endif %} + --standalone \ + --preferred-challenges http \ + --agree-tos \ + --email={{ matrix_ssl_lets_encrypt_support_email }} \ + --no-random-sleep-on-renew diff --git a/roles/matrix-nginx-proxy/vars/main.yml b/roles/matrix-nginx-proxy/vars/main.yml new file mode 100644 index 00000000..5c51fe5b --- /dev/null +++ b/roles/matrix-nginx-proxy/vars/main.yml @@ -0,0 +1,18 @@ +--- + +# Tells whether this role had executed or not. Toggled to `true` during runtime. +matrix_nginx_proxy_role_executed: false + +matrix_ssl_renewal_systemd_units_list: + - name: matrix-ssl-lets-encrypt-certificates-renew.service + applicable: "{{ matrix_ssl_retrieval_method == 'lets-encrypt' }}" + enableable: false + - name: matrix-ssl-lets-encrypt-certificates-renew.timer + applicable: "{{ matrix_ssl_retrieval_method == 'lets-encrypt' }}" + enableable: true + - name: matrix-ssl-nginx-proxy-reload.service + applicable: "{{ matrix_ssl_retrieval_method == 'lets-encrypt' and matrix_nginx_proxy_enabled|bool }}" + enableable: false + - name: matrix-ssl-nginx-proxy-reload.timer + applicable: "{{ matrix_ssl_retrieval_method == 'lets-encrypt' and matrix_nginx_proxy_enabled|bool }}" + enableable: true diff --git a/roles/matrix-postgres-backup/defaults/main.yml b/roles/matrix-postgres-backup/defaults/main.yml new file mode 100644 index 00000000..522764ac --- /dev/null +++ b/roles/matrix-postgres-backup/defaults/main.yml @@ -0,0 +1,40 @@ +matrix_postgres_backup_enabled: false + +matrix_postgres_backup_connection_hostname: "matrix-postgres" +matrix_postgres_backup_connection_port: 5432 +matrix_postgres_backup_connection_username: "matrix" +matrix_postgres_backup_connection_password: "" + +matrix_postgres_backup_extra_opts: "-Z9 --schema=public --blobs" +matrix_postgres_backup_schedule: "@daily" +matrix_postgres_backup_keep_days: 7 +matrix_postgres_backup_keep_weeks: 4 +matrix_postgres_backup_keep_months: 12 +matrix_postgres_backup_healthcheck_port: "8080" +matrix_postgres_backup_databases: [] +matrix_postgres_backup_path: "{{ matrix_base_data_path }}/postgres-backup" + +# Specifies where the Postgres data is. +# We use this to autodetect the Postgres version during playbook runtime (by parsing the `PG_VERSION` file contained there). +# You can leave this empty to prevent auto-detection. +matrix_postgres_backup_postgres_data_path: "" + +matrix_postgres_backup_architecture: amd64 + +# matrix_postgres_docker_image_suffix controls whether we use Alpine-based images (`-alpine`) or the normal Debian-based images. +# Alpine-based Postgres images are smaller and we usually prefer them, but they don't work on ARM32 (tested on a Raspberry Pi 3 running Raspbian 10.7). +# On ARM32, `-alpine` images fail with the following error: +# > LOG: startup process (PID 37) was terminated by signal 11: Segmentation fault +matrix_postgres_backup_docker_image_suffix: "{{ '-alpine' if matrix_postgres_backup_architecture in ['amd64', 'arm64'] else '' }}" + +matrix_postgres_backup_docker_image_v9: "{{ matrix_container_global_registry_prefix }}prodrigestivill/postgres-backup-local:9.6{{ matrix_postgres_backup_docker_image_suffix }}" +matrix_postgres_backup_docker_image_v10: "{{ matrix_container_global_registry_prefix }}prodrigestivill/postgres-backup-local:10{{ matrix_postgres_backup_docker_image_suffix }}" +matrix_postgres_backup_docker_image_v11: "{{ matrix_container_global_registry_prefix }}prodrigestivill/postgres-backup-local:11{{ matrix_postgres_backup_docker_image_suffix }}" +matrix_postgres_backup_docker_image_v12: "{{ matrix_container_global_registry_prefix }}prodrigestivill/postgres-backup-local:12{{ matrix_postgres_backup_docker_image_suffix }}" +matrix_postgres_backup_docker_image_v13: "{{ matrix_container_global_registry_prefix }}prodrigestivill/postgres-backup-local:13{{ matrix_postgres_backup_docker_image_suffix }}" +matrix_postgres_backup_docker_image_latest: "{{ matrix_postgres_backup_docker_image_v13 }}" + +# This variable is assigned at runtime. Overriding its value has no effect. +matrix_postgres_backup_docker_image_to_use: '{{ matrix_postgres_backup_docker_image_latest }}' + +matrix_postgres_backup_docker_image_force_pull: "{{ matrix_postgres_backup_docker_image_to_use.endswith(':latest') }}" diff --git a/roles/matrix-postgres-backup/tasks/init.yml b/roles/matrix-postgres-backup/tasks/init.yml new file mode 100644 index 00000000..c6a9bd7e --- /dev/null +++ b/roles/matrix-postgres-backup/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-postgres-backup.service'] }}" + when: matrix_postgres_backup_enabled|bool diff --git a/roles/matrix-postgres-backup/tasks/main.yml b/roles/matrix-postgres-backup/tasks/main.yml new file mode 100644 index 00000000..19e3db5b --- /dev/null +++ b/roles/matrix-postgres-backup/tasks/main.yml @@ -0,0 +1,17 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_postgres_backup_enabled|bool" + tags: + - setup-all + - setup-postgres-backup + +- import_tasks: "{{ role_path }}/tasks/setup_postgres_backup.yml" + when: run_setup|bool + tags: + - setup-all + - setup-postgres-backup diff --git a/roles/matrix-postgres-backup/tasks/setup_postgres_backup.yml b/roles/matrix-postgres-backup/tasks/setup_postgres_backup.yml new file mode 100644 index 00000000..9761088d --- /dev/null +++ b/roles/matrix-postgres-backup/tasks/setup_postgres_backup.yml @@ -0,0 +1,103 @@ +--- + +# +# Tasks related to setting up an internal postgres server +# +- import_tasks: "{{ role_path }}/tasks/util/detect_existing_postgres_version.yml" + when: 'matrix_postgres_backup_enabled|bool and matrix_postgres_backup_postgres_data_path != ""' + +# If we have found an existing version (installed from before), we use its corresponding Docker image. +# If not, we install using the latest Postgres. +# +# Upgrading is supposed to be performed separately and explicitly (see `upgrade_postgres.yml`). +- set_fact: + matrix_postgres_backup_docker_image_to_use: "{{ matrix_postgres_backup_docker_image_latest if matrix_postgres_backup_detected_version_corresponding_docker_image|default('') == '' else matrix_postgres_backup_detected_version_corresponding_docker_image }}" + when: matrix_postgres_backup_enabled|bool + +- name: Ensure postgres backup Docker image is pulled + docker_image: + name: "{{ matrix_postgres_backup_docker_image_to_use }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_postgres_backup_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_postgres_backup_docker_image_force_pull }}" + when: matrix_postgres_backup_enabled|bool + +- name: Ensure Postgres backup paths exist + file: + path: "{{ item }}" + state: directory + mode: 0700 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_postgres_backup_path }}" + when: matrix_postgres_backup_enabled|bool + +- name: Ensure Postgres environment variables file created + template: + src: "{{ role_path }}/templates/{{ item }}.j2" + dest: "{{ matrix_postgres_backup_path }}/{{ item }}" + mode: 0640 + with_items: + - "env-postgres-backup" + when: matrix_postgres_backup_enabled|bool + +- name: Ensure matrix-postgres-backup.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-postgres-backup.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-postgres-backup.service" + mode: 0644 + register: matrix_postgres_backup_systemd_service_result + when: matrix_postgres_backup_enabled|bool + +- name: Ensure systemd reloaded after matrix-postgres-backup.service installation + service: + daemon_reload: yes + when: "matrix_postgres_backup_enabled|bool and matrix_postgres_backup_systemd_service_result.changed" + +# +# Tasks related to getting rid of the internal postgres backup server (if it was previously enabled) +# + +- name: Check existence of matrix-postgres-backup service + stat: + path: "{{ matrix_systemd_path }}/matrix-postgres-backup.service" + register: matrix_postgres_backup_service_stat + when: "not matrix_postgres_backup_enabled|bool" + +- name: Ensure matrix-postgres-backup is stopped + service: + name: matrix-postgres-backup + state: stopped + daemon_reload: yes + when: "not matrix_postgres_backup_enabled|bool and matrix_postgres_backup_service_stat.stat.exists" + +- name: Ensure matrix-postgres-backup.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-postgres-backup.service" + state: absent + when: "not matrix_postgres_backup_enabled|bool and matrix_postgres_backup_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-postgres-backup.service removal + service: + daemon_reload: yes + when: "not matrix_postgres_backup_enabled|bool and matrix_postgres_backup_service_stat.stat.exists" + +- name: Check existence of matrix-postgres-backup backup path + stat: + path: "{{ matrix_postgres_backup_path }}" + register: matrix_postgres_backup_path_stat + when: "not matrix_postgres_backup_enabled|bool" + +# We just want to notify the user. Deleting data is too destructive. +- name: Inject warning if matrix-postgres backup data remains + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: You are not using the local backup service to backup the PostgreSQL database, but some old data remains from before in `{{ matrix_postgres_backup_path }}`. Feel free to delete it." + ] + }} + when: "not matrix_postgres_backup_enabled|bool and matrix_postgres_backup_path_stat.stat.exists" diff --git a/roles/matrix-postgres-backup/tasks/util/detect_existing_postgres_version.yml b/roles/matrix-postgres-backup/tasks/util/detect_existing_postgres_version.yml new file mode 100644 index 00000000..87a81f74 --- /dev/null +++ b/roles/matrix-postgres-backup/tasks/util/detect_existing_postgres_version.yml @@ -0,0 +1,56 @@ +--- + +# This utility aims to determine if there is some existing Postgres version in use or not. +# If there is, it also tries to detect the Docker image that corresponds to that version. + +- name: Initialize Postgres version determination variables (default to empty) + set_fact: + matrix_postgres_backup_detection_pg_version_path: "{{ matrix_postgres_data_path }}/PG_VERSION" + matrix_postgres_backup_detected_existing: false + matrix_postgres_backup_detected_version: "" + matrix_postgres_backup_detected_version_corresponding_docker_image: "" + +- name: Determine existing Postgres version (check PG_VERSION file) + stat: + path: "{{ matrix_postgres_backup_detection_pg_version_path }}" + register: result_pg_version_stat + +- set_fact: + matrix_postgres_backup_detected_existing: true + when: "result_pg_version_stat.stat.exists" + +- name: Determine existing Postgres version (read PG_VERSION file) + slurp: + src: "{{ matrix_postgres_backup_detection_pg_version_path }}" + register: result_pg_version + when: matrix_postgres_backup_detected_existing|bool + +- name: Determine existing Postgres version (make sense of PG_VERSION file) + set_fact: + matrix_postgres_backup_detected_version: "{{ result_pg_version['content']|b64decode|replace('\n', '') }}" + when: matrix_postgres_backup_detected_existing|bool + +- name: Determine corresponding Docker image to detected version (assume default of latest) + set_fact: + matrix_postgres_backup_detected_version_corresponding_docker_image: "{{ matrix_postgres_backup_docker_image_latest }}" + when: "matrix_postgres_backup_detected_version != ''" + +- name: Determine corresponding Docker image to detected version (use 9.x, if detected) + set_fact: + matrix_postgres_backup_detected_version_corresponding_docker_image: "{{ matrix_postgres_backup_docker_image_v9 }}" + when: "matrix_postgres_backup_detected_version.startswith('9.')" + +- name: Determine corresponding Docker image to detected version (use 10.x, if detected) + set_fact: + matrix_postgres_backup_detected_version_corresponding_docker_image: "{{ matrix_postgres_backup_docker_image_v10 }}" + when: "matrix_postgres_backup_detected_version == '10' or matrix_postgres_backup_detected_version.startswith('10.')" + +- name: Determine corresponding Docker image to detected version (use 11.x, if detected) + set_fact: + matrix_postgres_backup_detected_version_corresponding_docker_image: "{{ matrix_postgres_backup_docker_image_v11 }}" + when: "matrix_postgres_backup_detected_version == '11' or matrix_postgres_backup_detected_version.startswith('11.')" + +- name: Determine corresponding Docker image to detected version (use 12.x, if detected) + set_fact: + matrix_postgres_backup_detected_version_corresponding_docker_image: "{{ matrix_postgres_backup_docker_image_v12 }}" + when: "matrix_postgres_backup_detected_version == '12' or matrix_postgres_backup_detected_version.startswith('12.')" diff --git a/roles/matrix-postgres-backup/tasks/validate_config.yml b/roles/matrix-postgres-backup/tasks/validate_config.yml new file mode 100644 index 00000000..fda9b919 --- /dev/null +++ b/roles/matrix-postgres-backup/tasks/validate_config.yml @@ -0,0 +1,18 @@ +--- + +- name: Fail if required Postgres settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_postgres_backup_connection_hostname" + - "matrix_postgres_backup_connection_username" + - "matrix_postgres_backup_connection_password" + - "matrix_postgres_backup_connection_port" + - "matrix_postgres_backup_schedule" + - "matrix_postgres_backup_keep_days" + - "matrix_postgres_backup_keep_weeks" + - "matrix_postgres_backup_keep_months" + - "matrix_postgres_backup_path" + - "matrix_postgres_backup_databases" diff --git a/roles/matrix-postgres-backup/templates/env-postgres-backup.j2 b/roles/matrix-postgres-backup/templates/env-postgres-backup.j2 new file mode 100644 index 00000000..5a3a1ffd --- /dev/null +++ b/roles/matrix-postgres-backup/templates/env-postgres-backup.j2 @@ -0,0 +1,12 @@ +#jinja2: lstrip_blocks: "True" +POSTGRES_USER={{ matrix_postgres_backup_connection_username }} +POSTGRES_PASSWORD={{ matrix_postgres_backup_connection_password }} +POSTGRES_HOST={{ matrix_postgres_backup_connection_hostname }} +POSTGRES_DB={{ matrix_postgres_backup_databases|join(', ') }} +POSTGRES_EXTRA_OPTS={{ matrix_postgres_backup_extra_opts }} +SCHEDULE={{ matrix_postgres_backup_schedule }} +BACKUP_KEEP_DAYS={{ matrix_postgres_backup_keep_days }} +BACKUP_KEEP_WEEKS={{ matrix_postgres_backup_keep_weeks }} +BACKUP_KEEP_MONTHS={{ matrix_postgres_backup_keep_months }} +HEALTHCHECK_PORT={{ matrix_postgres_backup_healthcheck_port }} +POSTGRES_PORT={{ matrix_postgres_backup_connection_port }} \ No newline at end of file diff --git a/roles/matrix-postgres-backup/templates/systemd/matrix-postgres-backup.service.j2 b/roles/matrix-postgres-backup/templates/systemd/matrix-postgres-backup.service.j2 new file mode 100644 index 00000000..97c9ae7f --- /dev/null +++ b/roles/matrix-postgres-backup/templates/systemd/matrix-postgres-backup.service.j2 @@ -0,0 +1,31 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Automatic Backup of Matrix Postgres server +After=docker.service +Requires=docker.service +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_docker }} stop matrix-postgres-backup +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres-backup 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-postgres-backup \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + --env-file={{ matrix_postgres_backup_path }}/env-postgres-backup \ + --mount type=bind,src={{ matrix_postgres_backup_path }},dst=/backups \ + {{ matrix_postgres_backup_docker_image_to_use }} + +ExecStop=-{{ matrix_host_command_docker }} stop matrix-postgres-backup +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres-backup 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-postgres-backup + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-postgres/defaults/main.yml b/roles/matrix-postgres/defaults/main.yml new file mode 100644 index 00000000..9c1cac9a --- /dev/null +++ b/roles/matrix-postgres/defaults/main.yml @@ -0,0 +1,95 @@ +matrix_postgres_enabled: true + +matrix_postgres_connection_hostname: "matrix-postgres" +matrix_postgres_connection_port: 5432 +matrix_postgres_connection_username: "matrix" +matrix_postgres_connection_password: "" +matrix_postgres_db_name: "matrix" + +matrix_postgres_base_path: "{{ matrix_base_data_path }}/postgres" +matrix_postgres_data_path: "{{ matrix_postgres_base_path }}/data" + +matrix_postgres_architecture: amd64 + +# matrix_postgres_docker_image_suffix controls whether we use Alpine-based images (`-alpine`) or the normal Debian-based images. +# Alpine-based Postgres images are smaller and we usually prefer them, but they don't work on ARM32 (tested on a Raspberry Pi 3 running Raspbian 10.7). +# On ARM32, `-alpine` images fail with the following error: +# > LOG: startup process (PID 37) was terminated by signal 11: Segmentation fault +matrix_postgres_docker_image_suffix: "{{ '-alpine' if matrix_postgres_architecture in ['amd64', 'arm64'] else '' }}" + +matrix_postgres_docker_image_v9: "{{ matrix_container_global_registry_prefix }}postgres:9.6.22{{ matrix_postgres_docker_image_suffix }}" +matrix_postgres_docker_image_v10: "{{ matrix_container_global_registry_prefix }}postgres:10.17{{ matrix_postgres_docker_image_suffix }}" +matrix_postgres_docker_image_v11: "{{ matrix_container_global_registry_prefix }}postgres:11.12{{ matrix_postgres_docker_image_suffix }}" +matrix_postgres_docker_image_v12: "{{ matrix_container_global_registry_prefix }}postgres:12.7{{ matrix_postgres_docker_image_suffix }}" +matrix_postgres_docker_image_v13: "{{ matrix_container_global_registry_prefix }}postgres:13.3{{ matrix_postgres_docker_image_suffix }}" +matrix_postgres_docker_image_latest: "{{ matrix_postgres_docker_image_v13 }}" + +# This variable is assigned at runtime. Overriding its value has no effect. +matrix_postgres_docker_image_to_use: '{{ matrix_postgres_docker_image_latest }}' + +matrix_postgres_docker_image_force_pull: "{{ matrix_postgres_docker_image_to_use.endswith(':latest') }}" + +# A list of extra arguments to pass to the container +matrix_postgres_container_extra_arguments: [] + +# A list of extra arguments to pass to the postgres process +# e.g. "-c 'max_connections=200'" +matrix_postgres_process_extra_arguments: [] + +# Controls whether the matrix-postgres container exposes a port (tcp/5432 in the +# container) that can be used to access the database from outside the container (e.g. with psql) +# +# psql postgresql://username:password@localhost:/database_name +# +# Takes an ":" or "" value (e.g. "127.0.0.1:5432"), or empty string to not expose. +matrix_postgres_container_postgres_bind_port: "" + +# A list of additional (databases and their credentials) to create. +# +# Example: +# matrix_postgres_additional_databases: +# - name: matrix_appservice_discord +# username: matrix_appservice_discord +# password: some_password +# - name: matrix_appservice_slack +# username: matrix_appservice_slack +# password: some_password +matrix_postgres_additional_databases: [] + +# A list of roles/users to avoid creating when importing (or upgrading) the database. +# If a dump file contains the roles and they've also been created beforehand (see `matrix_postgres_additional_databases`), +# importing would fail. +# We either need to not create them or to ignore the `CREATE ROLE` statements in the dump. +matrix_postgres_import_roles_to_ignore: [matrix_postgres_connection_username] + +matrix_postgres_import_roles_ignore_regex: "^CREATE ROLE ({{ matrix_postgres_import_roles_to_ignore|join('|') }});" + +# A list of databases to avoid creating when importing (or upgrading) the database. +# If a dump file contains the databases and they've also been created beforehand (see `matrix_postgres_additional_databases`), +# importing would fail. +# We either need to not create them or to ignore the `CREATE DATABASE` statements in the dump. +matrix_postgres_import_databases_to_ignore: [matrix_postgres_db_name] + +matrix_postgres_import_databases_ignore_regex: "^CREATE DATABASE ({{ matrix_postgres_import_databases_to_ignore|join('|') }})\\s" + +# The number of seconds to wait after starting `matrix-postgres.service` +# and before trying to run queries for creating additional databases/users against it. +# +# For most (subsequent) runs, Postgres would already be running, so no waiting will be happening at all. +# +# On ARM, we wait some more. ARM32 devices are especially known for being slow. +# ARM64 likely don't need such a long delay, but it doesn't hurt too much having it. +matrix_postgres_additional_databases_postgres_start_wait_timeout_seconds: "{{ 45 if matrix_postgres_architecture in ['arm32', 'arm64'] else 15 }}" + + +matrix_postgres_pgloader_container_image_self_build: false +matrix_postgres_pgloader_container_image_self_build_repo: "https://github.com/illagrenan/pgloader-docker.git" +matrix_postgres_pgloader_container_image_self_build_repo_branch: "v{{ matrix_postgres_pgloader_docker_image_tag }}" +matrix_postgres_pgloader_container_image_self_build_src_path: "{{ matrix_postgres_base_path }}/pgloader-container-src" + +# We use illagrenan/pgloader, instead of the more official dimitri/pgloader image, +# because the official one only provides a `latest` tag. +matrix_postgres_pgloader_docker_image: "{{ matrix_postgres_pgloader_docker_image_name_prefix }}illagrenan/pgloader:{{ matrix_postgres_pgloader_docker_image_tag }}" +matrix_postgres_pgloader_docker_image_name_prefix: "{{ 'localhost/' if matrix_postgres_pgloader_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_postgres_pgloader_docker_image_tag: "3.6.2" +matrix_postgres_pgloader_docker_image_force_pull: "{{ matrix_postgres_pgloader_docker_image.endswith(':latest') }}" diff --git a/roles/matrix-postgres/tasks/import_generic_sqlite_db.yml b/roles/matrix-postgres/tasks/import_generic_sqlite_db.yml new file mode 100644 index 00000000..a42c6f55 --- /dev/null +++ b/roles/matrix-postgres/tasks/import_generic_sqlite_db.yml @@ -0,0 +1,97 @@ +--- + +# Pre-checks + +- name: Fail if Postgres not enabled + fail: + msg: "Postgres via the matrix-postgres role is not enabled (`matrix_postgres_enabled`). Cannot import." + when: "not matrix_postgres_enabled|bool" + +- name: Fail if playbook called incorrectly + fail: + msg: "The `sqlite_database_path` variable needs to be provided to this playbook, via --extra-vars" + when: "sqlite_database_path is not defined or sqlite_database_path.startswith('<')" + +- name: Check if the provided SQLite database file exists + stat: + path: "{{ sqlite_database_path }}" + register: sqlite_database_path_stat_result + +- name: Fail if provided SQLite database file doesn't exist + fail: + msg: "File cannot be found on the server at {{ sqlite_database_path }}" + when: "not sqlite_database_path_stat_result.stat.exists" + +# We either expect `postgres_db_connection_string` specifying a full Postgres database connection string, +# or `postgres_connection_string_variable_name`, specifying a name of a variable, which contains a valid connection string. + +- block: + - name: Fail if postgres_connection_string_variable_name points to an undefined variable + fail: msg="postgres_connection_string_variable_name is defined, but there is no variable with the name `{{ postgres_connection_string_variable_name }}`" + when: "postgres_connection_string_variable_name not in vars" + + - name: Get Postgres connection string from variable + set_fact: + postgres_db_connection_string: "{{ lookup('vars', postgres_connection_string_variable_name) }}" + when: 'postgres_connection_string_variable_name is defined' + +- name: Fail if playbook called incorrectly + fail: + msg: >- + Either a `postgres_db_connection_string` variable or a `postgres_connection_string_variable_name` needs to be provided to this playbook, via `--extra-vars`. + Example: `--extra-vars="postgres_db_connection_string=postgresql://username:password@localhost:/database_name"` or `--extra-vars="postgres_connection_string_variable_name=matrix_appservice_discord_database_connString"` + when: "postgres_db_connection_string is not defined or not postgres_db_connection_string.startswith('postgresql://')" + + +# Defaults + +- name: Set postgres_start_wait_time, if not provided + set_fact: + postgres_start_wait_time: 15 + when: "postgres_start_wait_time|default('') == ''" + + +# Actual import work + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + register: matrix_postgres_service_start_result + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + when: "matrix_postgres_service_start_result.changed|bool" + +- name: Import SQLite database from {{ sqlite_database_path }} into Postgres + command: + cmd: >- + {{ matrix_host_command_docker }} run + --rm + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --mount type=bind,src={{ sqlite_database_path }},dst=/in.db,ro + --entrypoint=/bin/sh + {{ matrix_postgres_pgloader_docker_image }} + -c + 'pgloader /in.db {{ postgres_db_connection_string }}' + +- name: Archive SQLite database ({{ sqlite_database_path }} -> {{ sqlite_database_path }}.backup) + command: + cmd: "mv {{ sqlite_database_path }} {{ sqlite_database_path }}.backup" + +- name: Inject result + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: Your SQLite database file has been imported into Postgres. The original file has been moved from `{{ sqlite_database_path }}` to `{{ sqlite_database_path }}.backup`. When you've confirmed that the import went well and everything works, you should be able to safely delete this file." + ] + }} diff --git a/roles/matrix-postgres/tasks/import_postgres.yml b/roles/matrix-postgres/tasks/import_postgres.yml new file mode 100644 index 00000000..b8e93219 --- /dev/null +++ b/roles/matrix-postgres/tasks/import_postgres.yml @@ -0,0 +1,106 @@ +--- + +# Pre-checks + +- name: Fail if Postgres not enabled + fail: + msg: "Postgres via the matrix-postgres role is not enabled (`matrix_postgres_enabled`). Cannot import." + when: "not matrix_postgres_enabled|bool" + +- name: Fail if playbook called incorrectly + fail: + msg: "The `server_path_postgres_dump` variable needs to be provided to this playbook, via --extra-vars" + when: "server_path_postgres_dump is not defined or server_path_postgres_dump.startswith('<')" + +- name: Check if the provided Postgres dump file exists + stat: + path: "{{ server_path_postgres_dump }}" + register: result_server_path_postgres_dump_stat + +- name: Fail if provided Postgres dump file doesn't exists + fail: + msg: "File cannot be found on the server at {{ server_path_postgres_dump }}" + when: "not result_server_path_postgres_dump_stat.stat.exists" + + +# Defaults + +- name: Set postgres_start_wait_time, if not provided + set_fact: + postgres_start_wait_time: 15 + when: "postgres_start_wait_time|default('') == ''" + +- name: Set postgres_import_wait_time, if not provided + set_fact: + postgres_import_wait_time: "{{ 7 * 86400 }}" + when: "postgres_import_wait_time|default('') == ''" + +# By default, we connect and import into the main (`matrix`) database. +# Single-database dumps for Synapse may wish to import into `synapse` instead. +- name: Set postgres_default_import_database, if not provided + set_fact: + postgres_default_import_database: "{{ matrix_postgres_db_name }}" + when: "postgres_default_import_database|default('') == ''" + +# Actual import work + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + +- import_tasks: tasks/util/detect_existing_postgres_version.yml + +- name: Abort, if no existing Postgres version detected + fail: + msg: "Could not find existing Postgres installation" + when: "not matrix_postgres_detected_existing|bool" + +# Starting the database container had automatically created the default +# role (`matrix_postgres_connection_username`) and database (`matrix_postgres_db_name`). +# The dump most likely contains those same entries and would try to re-create them, leading to errors. +# We need to skip over those lines. +- name: Generate Postgres database import command + set_fact: + matrix_postgres_import_command: >- + {{ matrix_host_command_docker }} run --rm --name matrix-postgres-import + --log-driver=none + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql + --mount type=bind,src={{ server_path_postgres_dump }},dst=/{{ server_path_postgres_dump|basename }},ro + --entrypoint=/bin/sh + {{ matrix_postgres_docker_image_latest }} + -c "cat /{{ server_path_postgres_dump|basename }} | + {{ 'gunzip |' if server_path_postgres_dump.endswith('.gz') else '' }} + grep -vE '{{ matrix_postgres_import_roles_ignore_regex }}' | + grep -vE '{{ matrix_postgres_import_databases_ignore_regex }}' | + psql -v ON_ERROR_STOP=1 -h matrix-postgres --dbname={{ postgres_default_import_database }}" + +# This is a hack. +# See: https://ansibledaily.com/print-to-standard-output-without-escaping/ +# +# We want to run `debug: msg=".."`, but that dumps it as JSON and escapes double quotes within it, +# which ruins the command (`matrix_postgres_import_command`) +- name: Note about Postgres importing alternative + set_fact: + dummy: true + with_items: + - >- + Importing Postgres database using the following command: `{{ matrix_postgres_import_command }}`. + If this crashes, you can stop Postgres (`systemctl stop matrix-postgres`), + delete its existing data (`rm -rf {{ matrix_postgres_data_path }}/*`), start it again (`systemctl start matrix-postgres`) + and manually run the above import command directly on the server. + +- name: Perform Postgres database import + command: "{{ matrix_postgres_import_command }}" + async: "{{ postgres_import_wait_time }}" + poll: 10 diff --git a/roles/matrix-postgres/tasks/import_synapse_sqlite_db.yml b/roles/matrix-postgres/tasks/import_synapse_sqlite_db.yml new file mode 100644 index 00000000..ea15c5a8 --- /dev/null +++ b/roles/matrix-postgres/tasks/import_synapse_sqlite_db.yml @@ -0,0 +1,86 @@ +--- + +# Pre-checks + +- name: Fail if Postgres not enabled + fail: + msg: "Postgres via the matrix-postgres role is not enabled (`matrix_postgres_enabled`). Cannot import." + when: "not matrix_postgres_enabled|bool" + +- name: Fail if playbook called incorrectly + fail: + msg: "The `server_path_homeserver_db` variable needs to be provided to this playbook, via --extra-vars" + when: "server_path_homeserver_db is not defined or server_path_homeserver_db.startswith('<')" + +- name: Check if the provided SQLite homeserver.db file exists + stat: + path: "{{ server_path_homeserver_db }}" + register: result_server_path_homeserver_db_stat + +- name: Fail if provided SQLite homeserver.db file doesn't exist + fail: + msg: "File cannot be found on the server at {{ server_path_homeserver_db }}" + when: "not result_server_path_homeserver_db_stat.stat.exists" + + +# Defaults + +- name: Set postgres_start_wait_time, if not provided + set_fact: + postgres_start_wait_time: 15 + when: "postgres_start_wait_time|default('') == ''" + + +# Actual import work + +- name: Ensure matrix-postgres is stopped + service: + name: matrix-postgres + state: stopped + daemon_reload: yes + +- name: Ensure postgres data is wiped out + file: + path: "{{ matrix_postgres_data_path }}" + state: absent + +- name: Ensure postgres data path exists + file: + path: "{{ matrix_postgres_data_path }}" + state: directory + mode: 0700 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: restarted + daemon_reload: yes + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + +# We don't use the `docker_container` module, because using it with `cap_drop` requires +# a very recent version, which is not available for a lot of people yet. +# +# Also, some old `docker_container` versions were buggy and would leave containers behind +# on failure, which we had to work around to allow retries (by re-running the playbook). +- name: Import SQLite database into Postgres + command: | + docker run + --rm + --name=matrix-synapse-migrate + --log-driver=none + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --entrypoint=python + --mount type=bind,src={{ matrix_synapse_config_dir_path }},dst=/data + --mount type=bind,src={{ matrix_synapse_config_dir_path }},dst=/matrix-media-store-parent/media-store + --mount type=bind,src={{ server_path_homeserver_db }},dst=/{{ server_path_homeserver_db|basename }} + {{ matrix_synapse_docker_image }} + /usr/local/bin/synapse_port_db --sqlite-database /{{ server_path_homeserver_db|basename }} --postgres-config /data/homeserver.yaml diff --git a/roles/matrix-postgres/tasks/init.yml b/roles/matrix-postgres/tasks/init.yml new file mode 100644 index 00000000..a0f2ae60 --- /dev/null +++ b/roles/matrix-postgres/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-postgres.service'] }}" + when: matrix_postgres_enabled|bool diff --git a/roles/matrix-postgres/tasks/main.yml b/roles/matrix-postgres/tasks/main.yml new file mode 100644 index 00000000..b9c2ae7c --- /dev/null +++ b/roles/matrix-postgres/tasks/main.yml @@ -0,0 +1,43 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_postgres_enabled|bool" + tags: + - setup-all + - setup-postgres + +- import_tasks: "{{ role_path }}/tasks/setup_postgres.yml" + when: run_setup|bool + tags: + - setup-all + - setup-postgres + +- import_tasks: "{{ role_path }}/tasks/import_postgres.yml" + when: run_postgres_import|bool + tags: + - import-postgres + +# The `run_postgres_import_sqlite_db` variable had better be renamed to be consistent, +# but that's a breaking change which may cause trouble for people. +- import_tasks: "{{ role_path }}/tasks/import_synapse_sqlite_db.yml" + when: run_postgres_import_sqlite_db|bool + tags: + - import-synapse-sqlite-db + +# Perhaps we need a new variable here, instead of `run_postgres_import_sqlite_db`. +- import_tasks: "{{ role_path }}/tasks/import_generic_sqlite_db.yml" + when: run_postgres_import_sqlite_db|bool + tags: + - import-generic-sqlite-db + +- import_tasks: "{{ role_path }}/tasks/upgrade_postgres.yml" + when: run_postgres_upgrade|bool + tags: + - upgrade-postgres + +- import_tasks: "{{ role_path }}/tasks/run_vacuum.yml" + when: run_postgres_vacuum|bool + tags: + - run-postgres-vacuum diff --git a/roles/matrix-postgres/tasks/migrate_postgres_data_directory.yml b/roles/matrix-postgres/tasks/migrate_postgres_data_directory.yml new file mode 100644 index 00000000..ef5fbf47 --- /dev/null +++ b/roles/matrix-postgres/tasks/migrate_postgres_data_directory.yml @@ -0,0 +1,72 @@ +--- + +# We used to store Postgres data directly under `/matrix/postgres` (what is now considered `matrix_postgres_base_path`). +# +# From now on, we expect to store Postgres data one directory below now (`/matrix/postgres/data` - `matrix_postgres_data_path`). +# We wish to use the base directory for other purposes (storing environment variable files, etc.). +# Mixing those with the Postgres data is no good and it leads to Postgres's `initdb` complaining to initialize +# a database in a non-empty directory. +# +# For this reason, we store the Postgres data in `/matrix/postgres/data` and need to relocate any installations +# which still store it in the parent directory (`/matrix/postgres`). + +- name: Check if old Postgres data directory is used + stat: + path: "{{ matrix_postgres_base_path }}/PG_VERSION" + register: result_pg_old_data_dir_stat + +- name: Warn if old Postgres data directory detected + debug: + msg: > + Found that you have Postgres data in `{{ matrix_postgres_base_path }}`. + From now on, Postgres data is supposed to be stored in `{{ matrix_postgres_data_path }}` instead. + We'll stop Postgres and relocate the files there for you. + when: "result_pg_old_data_dir_stat.stat.exists" + +# We should stop Postgres first, before building a list of files, +# as to ignore any `postmaster.pid` files, etc. +- name: Ensure matrix-postgres is stopped + service: + name: matrix-postgres + state: stopped + daemon_reload: yes + when: "result_pg_old_data_dir_stat.stat.exists" + +- name: Find files and directories in old Postgres data path + find: + paths: "{{ matrix_postgres_base_path }}" + file_type: any + excludes: ["data"] + register: "result_pg_old_data_dir_find" + when: "result_pg_old_data_dir_stat.stat.exists" + +- name: Ensure new Postgres data path exists + file: + path: "{{ matrix_postgres_data_path }}" + state: directory + mode: 0700 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: "result_pg_old_data_dir_stat.stat.exists" + +- block: + - name: Relocate Postgres data files from old directory to new + command: "mv {{ item.path }} {{ matrix_postgres_data_path }}/{{ item.path|basename }}" + with_items: "{{ result_pg_old_data_dir_find.files }}" + when: "result_pg_old_data_dir_stat.stat.exists" + +# Intentionally not starting matrix-postgres here. +# It likely needs to be updated to point to the new directory. +# In fact, let's even get rid of the outdated service, to ensure no one will start it +# and have it initialize a new database. + +- name: Ensure outdated matrix-postgres.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-postgres.service" + state: absent + when: "result_pg_old_data_dir_stat.stat.exists" + +- name: Ensure systemd reloaded after getting rid of outdated matrix-postgres.service + service: + daemon_reload: yes + when: "result_pg_old_data_dir_stat.stat.exists" \ No newline at end of file diff --git a/roles/matrix-postgres/tasks/run_vacuum.yml b/roles/matrix-postgres/tasks/run_vacuum.yml new file mode 100644 index 00000000..19a27562 --- /dev/null +++ b/roles/matrix-postgres/tasks/run_vacuum.yml @@ -0,0 +1,90 @@ +--- + +# Pre-checks + +- name: Fail if Postgres not enabled + fail: + msg: "Postgres via the matrix-postgres role is not enabled (`matrix_postgres_enabled`). Cannot run vacuum." + when: "not matrix_postgres_enabled|bool" + + +# Defaults + +- name: Set postgres_start_wait_time, if not provided + set_fact: + postgres_start_wait_time: 15 + when: "postgres_start_wait_time|default('') == ''" + +- name: Set postgres_vacuum_wait_time, if not provided + set_fact: + postgres_vacuum_wait_time: "{{ 7 * 86400 }}" + when: "postgres_vacuum_wait_time|default('') == ''" + + +# Actual vacuuming work + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + +- import_tasks: tasks/util/detect_existing_postgres_version.yml + +- name: Abort, if no existing Postgres version detected + fail: + msg: "Could not find existing Postgres installation" + when: "not matrix_postgres_detected_existing|bool" + +- name: Generate Postgres database vacuum command + set_fact: + matrix_postgres_vacuum_command: >- + {{ matrix_host_command_docker }} run --rm --name matrix-postgres-synapse-vacuum + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql + {{ matrix_postgres_docker_image_latest }} + psql -v ON_ERROR_STOP=1 -h matrix-postgres {{ matrix_synapse_database_database }} -c 'VACUUM FULL VERBOSE' + +- name: Note about Postgres vacuum alternative + debug: + msg: >- + Running vacuum with the following Postgres command: `{{ matrix_postgres_vacuum_command }}`. + If this crashes, you can stop all processes (`systemctl stop matrix-*`), + start Postgres only (`systemctl start matrix-postgres`) + and manually run the above command directly on the server. + +- name: Populate service facts + service_facts: + +- set_fact: + matrix_postgres_synapse_was_running: "{{ ansible_facts.services['matrix-synapse.service']|default(none) is not none and ansible_facts.services['matrix-synapse.service'].state == 'running' }}" + +- name: Ensure matrix-synapse is stopped + service: + name: matrix-synapse + state: stopped + daemon_reload: yes + +- name: Run Postgres vacuum command + command: "{{ matrix_postgres_vacuum_command }}" + async: "{{ postgres_vacuum_wait_time }}" + poll: 10 + register: matrix_postgres_synapse_vacuum_result + +# Intentionally show the results +- debug: var="matrix_postgres_synapse_vacuum_result" + +- name: Ensure matrix-synapse is started, if it previously was + service: + name: matrix-synapse + state: started + daemon_reload: yes + when: "matrix_postgres_synapse_was_running|bool" diff --git a/roles/matrix-postgres/tasks/setup_postgres.yml b/roles/matrix-postgres/tasks/setup_postgres.yml new file mode 100644 index 00000000..4294bc11 --- /dev/null +++ b/roles/matrix-postgres/tasks/setup_postgres.yml @@ -0,0 +1,197 @@ +--- + +# +# Tasks related to setting up an internal postgres server +# + +- import_tasks: "{{ role_path }}/tasks/migrate_postgres_data_directory.yml" + when: matrix_postgres_enabled|bool + +- import_tasks: "{{ role_path }}/tasks/util/detect_existing_postgres_version.yml" + when: matrix_postgres_enabled|bool + +# If we have found an existing version (installed from before), we use its corresponding Docker image. +# If not, we install using the latest Postgres. +# +# Upgrading is supposed to be performed separately and explicitly (see `upgrade_postgres.yml`). +- set_fact: + matrix_postgres_docker_image_to_use: "{{ matrix_postgres_docker_image_latest if matrix_postgres_detected_version_corresponding_docker_image == '' else matrix_postgres_detected_version_corresponding_docker_image }}" + when: matrix_postgres_enabled|bool + +- name: Inject warning if on an old version of Postgres + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: Your setup is on an old Postgres version ({{ matrix_postgres_docker_image_to_use }}), while {{ matrix_postgres_docker_image_latest }} is supported. You can upgrade using --tags=upgrade-postgres" + ] + }} + when: "matrix_postgres_enabled|bool and matrix_postgres_docker_image_to_use != matrix_postgres_docker_image_latest" + +# Even if we don't run the internal server, we still need this for running the CLI +- name: Ensure postgres Docker image is pulled + docker_image: + name: "{{ matrix_postgres_docker_image_to_use }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_postgres_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_postgres_docker_image_force_pull }}" + when: matrix_postgres_enabled|bool + +- name: Ensure Postgres paths exist + file: + path: "{{ item }}" + state: directory + mode: 0700 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_postgres_base_path }}" + - "{{ matrix_postgres_data_path }}" + when: matrix_postgres_enabled|bool + +# We do this as a separate task, because: +# - we'd like to do it for the data path only, not for the base path (which contains root-owned environment variable files we'd like to leave as-is) +# - we need to do it without `mode`, or we risk making certain `.conf` and other files's executable bit to flip to true +- name: Ensure Postgres data path ownership is correct + file: + path: "{{ matrix_postgres_data_path }}" + state: directory + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + recurse: yes + when: matrix_postgres_enabled|bool + +- name: Ensure Postgres environment variables file created + template: + src: "{{ role_path }}/templates/{{ item }}.j2" + dest: "{{ matrix_postgres_base_path }}/{{ item }}" + mode: 0640 + with_items: + - "env-postgres-psql" + - "env-postgres-server" + when: matrix_postgres_enabled|bool + +- name: Ensure matrix-postgres-cli script created + template: + src: "{{ role_path }}/templates/usr-local-bin/matrix-postgres-cli.j2" + dest: "{{ matrix_local_bin_path }}/matrix-postgres-cli" + mode: 0755 + when: matrix_postgres_enabled|bool + +- name: Ensure matrix-change-user-admin-status script created + template: + src: "{{ role_path }}/templates/usr-local-bin/matrix-change-user-admin-status.j2" + dest: "{{ matrix_local_bin_path }}/matrix-change-user-admin-status" + mode: 0755 + when: matrix_postgres_enabled|bool + +- name: (Migration) Ensure old matrix-make-user-admin script deleted + file: + path: "{{ matrix_local_bin_path }}/matrix-make-user-admin" + state: absent + when: matrix_postgres_enabled|bool + +- name: Ensure matrix-postgres-update-user-password-hash script created + template: + src: "{{ role_path }}/templates/usr-local-bin/matrix-postgres-update-user-password-hash.j2" + dest: "{{ matrix_local_bin_path }}/matrix-postgres-update-user-password-hash" + mode: 0755 + when: matrix_postgres_enabled|bool + +- name: Ensure matrix-postgres.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-postgres.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-postgres.service" + mode: 0644 + register: matrix_postgres_systemd_service_result + when: matrix_postgres_enabled|bool + +- name: Ensure systemd reloaded after matrix-postgres.service installation + service: + daemon_reload: yes + when: "matrix_postgres_enabled|bool and matrix_postgres_systemd_service_result.changed" + +- include_tasks: + file: "{{ role_path }}/tasks/util/create_additional_databases.yml" + apply: + tags: + - always + when: "matrix_postgres_enabled|bool and matrix_postgres_additional_databases|length > 0" + +- name: Check existence of matrix-postgres backup data path + stat: + path: "{{ matrix_postgres_data_path }}-auto-upgrade-backup" + register: matrix_postgres_data_backup_path_stat + when: "matrix_postgres_enabled|bool" + +- name: Inject warning if backup data remains + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: You have some Postgres backup data in `{{ matrix_postgres_data_path }}-auto-upgrade-backup`, which was created during the last major Postgres update you ran. If your setup works well after this upgrade, feel free to delete this whole directory." + ] + }} + when: "matrix_postgres_enabled|bool and matrix_postgres_data_backup_path_stat.stat.exists" + + +# +# Tasks related to getting rid of the internal postgres server (if it was previously enabled) +# + +- name: Check existence of matrix-postgres service + stat: + path: "{{ matrix_systemd_path }}/matrix-postgres.service" + register: matrix_postgres_service_stat + when: "not matrix_postgres_enabled|bool" + +- name: Ensure matrix-postgres is stopped + service: + name: matrix-postgres + state: stopped + daemon_reload: yes + when: "not matrix_postgres_enabled|bool and matrix_postgres_service_stat.stat.exists" + +- name: Ensure matrix-postgres.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-postgres.service" + state: absent + when: "not matrix_postgres_enabled|bool and matrix_postgres_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-postgres.service removal + service: + daemon_reload: yes + when: "not matrix_postgres_enabled|bool and matrix_postgres_service_stat.stat.exists" + +- name: Check existence of matrix-postgres local data path + stat: + path: "{{ matrix_postgres_data_path }}" + register: matrix_postgres_data_path_stat + when: "not matrix_postgres_enabled|bool" + +# We just want to notify the user. Deleting data is too destructive. +- name: Inject warning if matrix-postgres local data remains + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: You are not using a local PostgreSQL database, but some old data remains from before in `{{ matrix_postgres_data_path }}`. Feel free to delete it." + ] + }} + when: "not matrix_postgres_enabled|bool and matrix_postgres_data_path_stat.stat.exists" + +- name: Remove Postgres scripts + file: + path: "{{ matrix_local_bin_path }}/{{ item }}" + state: absent + with_items: + - matrix-postgres-cli + - matrix-change-user-admin-status + - matrix-postgres-update-user-password-hash + when: "not matrix_postgres_enabled|bool" diff --git a/roles/matrix-postgres/tasks/upgrade_postgres.yml b/roles/matrix-postgres/tasks/upgrade_postgres.yml new file mode 100644 index 00000000..564265d8 --- /dev/null +++ b/roles/matrix-postgres/tasks/upgrade_postgres.yml @@ -0,0 +1,172 @@ +--- + +- name: Set default postgres_dump_dir, if not provided + set_fact: + postgres_dump_dir: "/tmp" + when: "postgres_dump_dir|default('') == ''" + +- name: Set postgres_dump_name, if not provided + set_fact: + postgres_dump_name: "matrix-postgres-dump.sql.gz" + when: "postgres_dump_name|default('') == ''" + +- name: Set postgres_auto_upgrade_backup_data_path, if not provided + set_fact: + postgres_auto_upgrade_backup_data_path: "{{ matrix_postgres_data_path }}-auto-upgrade-backup" + when: "postgres_auto_upgrade_backup_data_path|default('') == ''" + +- name: Set postgres_start_wait_time, if not provided + set_fact: + postgres_start_wait_time: 15 + when: "postgres_start_wait_time|default('') == ''" + +- name: Set postgres_force_upgrade, if not provided + set_fact: + postgres_force_upgrade: false + when: "postgres_force_upgrade|default('') == ''" + +- name: Fail, if trying to upgrade external Postgres database + fail: + msg: "Your configuration indicates that you're not using Postgres from this role. There is nothing to upgrade." + when: "not matrix_postgres_enabled|bool" + +- name: Check Postgres auto-upgrade backup data directory + stat: + path: "{{ postgres_auto_upgrade_backup_data_path }}" + register: result_auto_upgrade_path + +- name: Abort, if existing Postgres auto-upgrade data path detected + fail: + msg: "Detected that a left-over {{ postgres_auto_upgrade_backup_data_path }} exists. You should rename it to {{ matrix_postgres_data_path }} if the previous upgrade went wrong, or delete it if it went well." + when: "result_auto_upgrade_path.stat.exists" + +- import_tasks: tasks/util/detect_existing_postgres_version.yml + +- name: Abort, if no existing Postgres version detected + fail: + msg: "Could not find existing Postgres installation" + when: "not matrix_postgres_detected_existing|bool" + +- name: Abort, if already at latest Postgres version + fail: + msg: "You are already running the latest Postgres version supported ({{ matrix_postgres_docker_image_latest }}). Nothing to do" + when: "matrix_postgres_detected_version_corresponding_docker_image == matrix_postgres_docker_image_latest and not postgres_force_upgrade" + +- debug: + msg: "Upgrading database from {{ matrix_postgres_detected_version_corresponding_docker_image }} to {{ matrix_postgres_docker_image_latest }}" + +- name: Ensure matrix-synapse is stopped + service: + name: matrix-synapse + state: stopped + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + +# We dump all databases, roles, etc. +# +# Because we'll be importing into a new container which initializes the default +# role (`matrix_postgres_connection_username`) and database (`matrix_postgres_db_name`) by itself on startup, +# we need to remove these from the dump, or we'll get errors saying these already exist. +- name: Perform Postgres database dump + command: >- + {{ matrix_host_command_docker }} run --rm --name matrix-postgres-dump + --log-driver=none + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --network={{ matrix_docker_network }} + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql + --entrypoint=/bin/sh + --mount type=bind,src={{ postgres_dump_dir }},dst=/out + {{ matrix_postgres_detected_version_corresponding_docker_image }} + -c "pg_dumpall -h matrix-postgres + {{ '| gzip -c ' if postgres_dump_name.endswith('.gz') else '' }} + > /out/{{ postgres_dump_name }}" + +- name: Ensure matrix-postgres is stopped + service: + name: matrix-postgres + state: stopped + +- name: Rename existing Postgres data directory + command: "mv {{ matrix_postgres_data_path }} {{ postgres_auto_upgrade_backup_data_path }}" + +- debug: + msg: "NOTE: Your Postgres data directory has been moved from `{{ matrix_postgres_data_path }}` to `{{ postgres_auto_upgrade_backup_data_path }}`. In the event of failure, you can move it back and run the playbook with --tags=setup-postgres to restore operation." + +- import_tasks: tasks/setup_postgres.yml + +- name: Ensure matrix-postgres autoruns and is restarted + service: + name: matrix-postgres + enabled: yes + state: restarted + daemon_reload: yes + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + +# Starting the database container had automatically created the default +# role (`matrix_postgres_connection_username`) and database (`matrix_postgres_db_name`). +# The dump most likely contains those same entries and would try to re-create them, leading to errors. +# We need to skip over those lines. +- name: Generate Postgres database import command + set_fact: + matrix_postgres_import_command: >- + {{ matrix_host_command_docker }} run --rm --name matrix-postgres-import + --log-driver=none + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql + --entrypoint=/bin/sh + --mount type=bind,src={{ postgres_dump_dir }},dst=/in,ro + {{ matrix_postgres_docker_image_latest }} + -c "cat /in/{{ postgres_dump_name }} | + {{ 'gunzip |' if postgres_dump_name.endswith('.gz') else '' }} + grep -vE '{{ matrix_postgres_import_roles_ignore_regex }}' | + grep -vE '{{ matrix_postgres_import_databases_ignore_regex }}' | + psql -v ON_ERROR_STOP=1 -h matrix-postgres" + +# This is a hack. +# See: https://ansibledaily.com/print-to-standard-output-without-escaping/ +# +# We want to run `debug: msg=".."`, but that dumps it as JSON and escapes double quotes within it, +# which ruins the command (`matrix_postgres_import_command`) +- name: Note about Postgres importing + set_fact: + dummy: true + with_items: + - >- + Importing Postgres database using the following command: `{{ matrix_postgres_import_command }}`. + If this crashes, you can stop Postgres (`systemctl stop matrix-postgres`), + delete the new database data (`rm -rf {{ matrix_postgres_data_path }}`) + and restore the automatically-made backup (`mv {{ postgres_auto_upgrade_backup_data_path }} {{ matrix_postgres_data_path }}`). + +- name: Perform Postgres database import + command: "{{ matrix_postgres_import_command }}" + +- name: Delete Postgres database dump file + file: + path: "{{ postgres_dump_dir }}/{{ postgres_dump_name }}" + state: absent + +- name: Ensure matrix-synapse is started + service: + name: matrix-synapse + state: started + daemon_reload: yes + +- debug: + msg: "NOTE: Your old Postgres data directory is preserved at `{{ postgres_auto_upgrade_backup_data_path }}`. You might want to get rid of it once you've confirmed that all is well." diff --git a/roles/matrix-postgres/tasks/util/create_additional_database.yml b/roles/matrix-postgres/tasks/util/create_additional_database.yml new file mode 100644 index 00000000..22b3c9a2 --- /dev/null +++ b/roles/matrix-postgres/tasks/util/create_additional_database.yml @@ -0,0 +1,40 @@ +--- + +# It'd be better if this is belonged to `validate_config.yml`, but it would have to be some loop-within-a-loop there, +# and that's ugly. We also don't expect this to catch errors often. It's more of a defensive last-minute check. +- name: Fail if additional database data appears invalid + fail: + msg: "Additional database definition ({{ additional_db }} lacks a required key: {{ item }}" + when: "item not in additional_db" + with_items: "{{ ['name', 'username', 'password'] }}" + +# The SQL statements that we'll run against Postgres are stored in a file that others can't read. +# This file will be mounted into the container and fed to Postgres. +# This way, we avoid passing sensitive data around in CLI commands that other users on the system can see. +- name: Create additional database initialization SQL file for {{ additional_db.name }} + template: + src: "{{ role_path }}/templates/sql/init-additional-db-user-and-role.sql.j2" + dest: "/tmp/matrix-postgres-init-additional-db-user-and-role.sql" + mode: 0600 + owner: "{{ matrix_user_uid }}" + group: "{{ matrix_user_gid }}" + +- name: Execute Postgres additional database initialization SQL file for {{ additional_db.name }} + command: + cmd: >- + {{ matrix_host_command_docker }} run + --rm + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql + --network {{ matrix_docker_network }} + --mount type=bind,src=/tmp/matrix-postgres-init-additional-db-user-and-role.sql,dst=/matrix-postgres-init-additional-db-user-and-role.sql,ro + --entrypoint=/bin/sh + {{ matrix_postgres_docker_image_to_use }} + -c + 'psql -h {{ matrix_postgres_connection_hostname }} --file=/matrix-postgres-init-additional-db-user-and-role.sql' + +- name: Delete additional database initialization SQL file for {{ additional_db.name }} + file: + path: /tmp/matrix-postgres-init-additional-db-user-and-role.sql + state: absent diff --git a/roles/matrix-postgres/tasks/util/create_additional_databases.yml b/roles/matrix-postgres/tasks/util/create_additional_databases.yml new file mode 100644 index 00000000..0ad460dd --- /dev/null +++ b/roles/matrix-postgres/tasks/util/create_additional_databases.yml @@ -0,0 +1,23 @@ +--- + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + register: matrix_postgres_service_start_result + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ matrix_postgres_additional_databases_postgres_start_wait_timeout_seconds }}" + delegate_to: 127.0.0.1 + become: false + when: "matrix_postgres_service_start_result.changed|bool" + +- name: Create additional Postgres user and database + include_tasks: "{{ role_path }}/tasks/util/create_additional_database.yml" + with_items: "{{ matrix_postgres_additional_databases }}" + loop_control: + loop_var: additional_db + # Suppress logging to avoid dumping the credentials to the shell + no_log: true diff --git a/roles/matrix-postgres/tasks/util/detect_existing_postgres_version.yml b/roles/matrix-postgres/tasks/util/detect_existing_postgres_version.yml new file mode 100644 index 00000000..9032c15e --- /dev/null +++ b/roles/matrix-postgres/tasks/util/detect_existing_postgres_version.yml @@ -0,0 +1,56 @@ +--- + +# This utility aims to determine if there is some existing Postgres version in use or not. +# If there is, it also tries to detect the Docker image that corresponds to that version. + +- name: Initialize Postgres version determination variables (default to empty) + set_fact: + matrix_postgres_detection_pg_version_path: "{{ matrix_postgres_data_path }}/PG_VERSION" + matrix_postgres_detected_existing: false + matrix_postgres_detected_version: "" + matrix_postgres_detected_version_corresponding_docker_image: "" + +- name: Determine existing Postgres version (check PG_VERSION file) + stat: + path: "{{ matrix_postgres_detection_pg_version_path }}" + register: result_pg_version_stat + +- set_fact: + matrix_postgres_detected_existing: true + when: "result_pg_version_stat.stat.exists" + +- name: Determine existing Postgres version (read PG_VERSION file) + slurp: + src: "{{ matrix_postgres_detection_pg_version_path }}" + register: result_pg_version + when: matrix_postgres_detected_existing|bool + +- name: Determine existing Postgres version (make sense of PG_VERSION file) + set_fact: + matrix_postgres_detected_version: "{{ result_pg_version['content']|b64decode|replace('\n', '') }}" + when: matrix_postgres_detected_existing|bool + +- name: Determine corresponding Docker image to detected version (assume default of latest) + set_fact: + matrix_postgres_detected_version_corresponding_docker_image: "{{ matrix_postgres_docker_image_latest }}" + when: "matrix_postgres_detected_version != ''" + +- name: Determine corresponding Docker image to detected version (use 9.x, if detected) + set_fact: + matrix_postgres_detected_version_corresponding_docker_image: "{{ matrix_postgres_docker_image_v9 }}" + when: "matrix_postgres_detected_version.startswith('9.')" + +- name: Determine corresponding Docker image to detected version (use 10.x, if detected) + set_fact: + matrix_postgres_detected_version_corresponding_docker_image: "{{ matrix_postgres_docker_image_v10 }}" + when: "matrix_postgres_detected_version == '10' or matrix_postgres_detected_version.startswith('10.')" + +- name: Determine corresponding Docker image to detected version (use 11.x, if detected) + set_fact: + matrix_postgres_detected_version_corresponding_docker_image: "{{ matrix_postgres_docker_image_v11 }}" + when: "matrix_postgres_detected_version == '11' or matrix_postgres_detected_version.startswith('11.')" + +- name: Determine corresponding Docker image to detected version (use 12.x, if detected) + set_fact: + matrix_postgres_detected_version_corresponding_docker_image: "{{ matrix_postgres_docker_image_v12 }}" + when: "matrix_postgres_detected_version == '12' or matrix_postgres_detected_version.startswith('12.')" diff --git a/roles/matrix-postgres/tasks/util/migrate_db_to_postgres.yml b/roles/matrix-postgres/tasks/util/migrate_db_to_postgres.yml new file mode 100644 index 00000000..cf595ade --- /dev/null +++ b/roles/matrix-postgres/tasks/util/migrate_db_to_postgres.yml @@ -0,0 +1,169 @@ +--- + +- name: Fail if Postgres not enabled + fail: + msg: "Postgres via the matrix-postgres role is not enabled (`matrix_postgres_enabled`). Cannot migrate." + when: "not matrix_postgres_enabled|bool" + +- name: Fail if util called incorrectly (missing matrix_postgres_db_migration_request) + fail: + msg: "The `matrix_postgres_db_migration_request` variable needs to be provided to this util." + when: "matrix_postgres_db_migration_request is not defined" + +- name: Fail if util called incorrectly (invalid matrix_postgres_db_migration_request) + fail: + msg: "The `matrix_postgres_db_migration_request` variable needs to contain `{{ item }}`." + with_items: + - src + - dst + - caller + - engine_variable_name + - systemd_services_to_stop + when: "item not in matrix_postgres_db_migration_request" + +- name: Check if the provided source database file exists + stat: + path: "{{ matrix_postgres_db_migration_request.src }}" + register: matrix_postgres_db_migration_request_src_stat_result + +- name: Fail if provided source database file doesn't exist + fail: + msg: "File cannot be found on the server at {{ matrix_postgres_db_migration_request.src }}" + when: "not matrix_postgres_db_migration_request_src_stat_result.stat.exists" + +- block: + - name: Ensure pgloader repository is present on self-build + git: + repo: "{{ matrix_postgres_pgloader_container_image_self_build_repo }}" + dest: "{{ matrix_postgres_pgloader_container_image_self_build_src_path }}" + version: "{{ matrix_postgres_pgloader_container_image_self_build_repo_branch }}" + force: "yes" + register: matrix_postgres_pgloader_git_pull_results + + # If `stable` is used, we hit an error when processing /opt/src/pgloader/build/quicklisp/dists/quicklisp/software/uax-15-20201220-git/data/CompositionExclusions.txt: + # > the octet sequence #(194) cannot be decoded + # + # The issue is described here and is not getting fixed for months: https://github.com/dimitri/pgloader/pull/1179 + # + # Although we're not using the dimitri/pgloader image, the one we're using suffers from the same problem. + - name: Switch pgloader base image from Debian stable (likely 10.x/Buster) to Bullseye + lineinfile: + path: "{{ matrix_postgres_pgloader_container_image_self_build_src_path }}/Dockerfile" + regexp: "{{ item.match }}" + line: "{{ item.replace }}" + with_items: + - match: '^FROM debian:stable-slim as builder$' + replace: 'FROM debian:bullseye-slim as builder' + - match: '^FROM debian:stable-slim$' + replace: 'FROM debian:bullseye-slim' + + - name: Ensure pgloader Docker image is built + docker_image: + name: "{{ matrix_postgres_pgloader_docker_image }}" + source: build + force_source: "{{ matrix_postgres_pgloader_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_postgres_pgloader_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_postgres_pgloader_container_image_self_build_src_path }}" + pull: yes + when: "matrix_postgres_pgloader_container_image_self_build|bool" + +- name: Ensure pgloader Docker image is pulled + docker_image: + name: "{{ matrix_postgres_pgloader_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_postgres_pgloader_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_postgres_pgloader_docker_image_force_pull }}" + when: "not matrix_postgres_pgloader_container_image_self_build" + +# Defaults + +- name: Set postgres_start_wait_time, if not provided + set_fact: + postgres_start_wait_time: 15 + when: "postgres_start_wait_time|default('') == ''" + +# Actual import work + +# matrix-postgres is most likely started already +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + register: matrix_postgres_service_start_result + +- name: Wait a bit, so that Postgres can start + wait_for: + timeout: "{{ postgres_start_wait_time }}" + delegate_to: 127.0.0.1 + become: false + when: "matrix_postgres_service_start_result.changed|bool" + +# We only stop services here, leaving it to the caller to start them later. +# +# We can't start them, because they probably need to be reconfigured too (changing the configuration from using SQLite to Postgres, etc.), +# before starting. +# +# Since the caller will be starting them, it might make sense to leave stopping to it as well. +# However, we don't do it, because it's simpler having it here, and it also gets to happen only if we'll be doing an import. +# If we bailed out (somewhere above), nothing would have gotten stopped. It's nice to leave this running in such cases. +- name: Ensure systemd services blocking the database import are stopped + service: + name: "{{ item }}" + state: stopped + failed_when: false + with_items: "{{ matrix_postgres_db_migration_request.systemd_services_to_stop }}" + +- name: Import {{ matrix_postgres_db_migration_request.engine_old }} database from {{ matrix_postgres_db_migration_request.src }} into Postgres + command: + cmd: >- + {{ matrix_host_command_docker }} run + --rm + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --mount type=bind,src={{ matrix_postgres_db_migration_request.src }},dst=/in.db,ro + --entrypoint=/bin/sh + {{ matrix_postgres_pgloader_docker_image }} + -c + 'pgloader {{ matrix_postgres_db_migration_request.pgloader_options|default([])|join(' ') }} /in.db {{ matrix_postgres_db_migration_request.dst }}' + +- block: + # We can't use `{{ role_path }}` here, neither with `import_tasks`, nor with `include_tasks`, + # because it refers to the role that included this util, and not to the role this file belongs to. + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/detect_existing_postgres_version.yml" + + - set_fact: + matrix_postgres_docker_image_to_use: "{{ matrix_postgres_docker_image_latest if matrix_postgres_detected_version_corresponding_docker_image == '' else matrix_postgres_detected_version_corresponding_docker_image }}" + + - name: Execute additional Postgres SQL migration statements + command: + cmd: >- + {{ matrix_host_command_docker }} run + --rm + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql + --network={{ matrix_docker_network }} + {{ matrix_postgres_docker_image_to_use }} + psql --host=matrix-postgres --dbname={{ matrix_postgres_db_migration_request.additional_psql_statements_db_name }} --command='{{ item }}' + with_items: "{{ matrix_postgres_db_migration_request.additional_psql_statements_list }}" + + when: "matrix_postgres_db_migration_request.additional_psql_statements_list|default([])|length > 0" + +- name: Archive {{ matrix_postgres_db_migration_request.engine_old }} database ({{ matrix_postgres_db_migration_request.src }} -> {{ matrix_postgres_db_migration_request.src }}.backup) + command: + cmd: "mv {{ matrix_postgres_db_migration_request.src }} {{ matrix_postgres_db_migration_request.src }}.backup" + +- name: Inject result + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [ + "NOTE: Your {{ matrix_postgres_db_migration_request.engine_old }} database file has been imported into Postgres. The original database file has been moved from `{{ matrix_postgres_db_migration_request.src }}` to `{{ matrix_postgres_db_migration_request.src }}.backup`. When you've confirmed that the import went well and everything works, you should be able to safely delete this file." + ] + }} diff --git a/roles/matrix-postgres/tasks/validate_config.yml b/roles/matrix-postgres/tasks/validate_config.yml new file mode 100644 index 00000000..eac4dd5b --- /dev/null +++ b/roles/matrix-postgres/tasks/validate_config.yml @@ -0,0 +1,39 @@ +--- + +- name: (Deprecation) Warn about matrix_postgres_use_external usage + fail: + msg: > + The `matrix_postgres_use_external` variable defined in your configuration is not used by this playbook anymore! + You'll need to adapt to the new way of using an external Postgres server. + It's a combination of `matrix_postgres_enabled: false` and specifying Postgres connection + details in a few `matrix_postgres_connection_` variables. + See the "Using an external PostgreSQL server (optional)" documentation page. + when: "'matrix_postgres_use_external' in vars" + +# This is separate (from the other required variables below), +# because we'd like to have a friendlier message for our existing users. +- name: Fail if matrix_postgres_connection_password not defined + fail: + msg: >- + The playbook no longer has a default Postgres password defined in the `matrix_postgres_connection_password` variable, among lots of other Postgres changes. + You need to perform multiple manual steps to resolve this. + See our changelog for more details: + https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/CHANGELOG.md#breaking-change-postgres-changes-that-require-manual-intervention + when: "matrix_postgres_connection_password == ''" + +- name: Fail if required Postgres settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`). + when: "vars[item] == ''" + with_items: + - "matrix_postgres_connection_hostname" + - "matrix_postgres_connection_port" + - "matrix_postgres_connection_username" + - "matrix_postgres_connection_password" + - "matrix_postgres_db_name" + +- name: Fail if Postgres password length exceeded + fail: + msg: "The maximum `matrix_postgres_connection_password` length is 99 characters" + when: "matrix_postgres_connection_password|length > 99" diff --git a/roles/matrix-postgres/templates/env-postgres-psql.j2 b/roles/matrix-postgres/templates/env-postgres-psql.j2 new file mode 100644 index 00000000..c61927a3 --- /dev/null +++ b/roles/matrix-postgres/templates/env-postgres-psql.j2 @@ -0,0 +1,4 @@ +#jinja2: lstrip_blocks: "True" +PGUSER={{ matrix_postgres_connection_username }} +PGPASSWORD={{ matrix_postgres_connection_password }} +PGDATABASE={{ matrix_postgres_db_name }} \ No newline at end of file diff --git a/roles/matrix-postgres/templates/env-postgres-server.j2 b/roles/matrix-postgres/templates/env-postgres-server.j2 new file mode 100644 index 00000000..06feb82a --- /dev/null +++ b/roles/matrix-postgres/templates/env-postgres-server.j2 @@ -0,0 +1,7 @@ +#jinja2: lstrip_blocks: "True" +POSTGRES_USER={{ matrix_postgres_connection_username }} +POSTGRES_PASSWORD={{ matrix_postgres_connection_password }} +POSTGRES_DB={{ matrix_postgres_db_name }} +# Synapse refuses to run if collation is not C. +# See https://github.com/matrix-org/synapse/issues/6722 +POSTGRES_INITDB_ARGS=--lc-collate C --lc-ctype C --encoding UTF8 diff --git a/roles/matrix-postgres/templates/sql/init-additional-db-user-and-role.sql.j2 b/roles/matrix-postgres/templates/sql/init-additional-db-user-and-role.sql.j2 new file mode 100644 index 00000000..a5a3385b --- /dev/null +++ b/roles/matrix-postgres/templates/sql/init-additional-db-user-and-role.sql.j2 @@ -0,0 +1,19 @@ +-- `CREATE USER` does not support `IF NOT EXISTS`, so we use this workaround to prevent an error and raise a notice instead. +-- Seen here: https://stackoverflow.com/a/49858797 +DO $$ +BEGIN + CREATE USER "{{ additional_db.username }}"; + EXCEPTION WHEN DUPLICATE_OBJECT THEN + RAISE NOTICE 'not creating user "{{ additional_db.username }}", since it already exists'; +END +$$; + +-- This is useful for initial user creation (since we don't assign a password above) and for handling subsequent password changes +-- TODO - we should escape quotes in the password. +ALTER ROLE "{{ additional_db.username }}" PASSWORD '{{ additional_db.password }}'; + +-- This will generate an error on subsequent execution +CREATE DATABASE "{{ additional_db.name }}" WITH LC_CTYPE 'C' LC_COLLATE 'C' OWNER "{{ additional_db.username }}"; + +-- This is useful for changing the database owner subsequently +ALTER DATABASE "{{ additional_db.name }}" OWNER TO "{{ additional_db.username }}"; diff --git a/roles/matrix-postgres/templates/systemd/matrix-postgres.service.j2 b/roles/matrix-postgres/templates/systemd/matrix-postgres.service.j2 new file mode 100644 index 00000000..6d1b1c6f --- /dev/null +++ b/roles/matrix-postgres/templates/systemd/matrix-postgres.service.j2 @@ -0,0 +1,41 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Postgres server +After=docker.service +Requires=docker.service +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-postgres 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-postgres \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --tmpfs=/tmp:rw,noexec,nosuid,size=100m \ + --tmpfs=/run/postgresql:rw,noexec,nosuid,size=100m \ + --network={{ matrix_docker_network }} \ + {% if matrix_postgres_container_postgres_bind_port %} + -p {{ matrix_postgres_container_postgres_bind_port }}:5432 \ + {% endif %} + --env-file={{ matrix_postgres_base_path }}/env-postgres-server \ + --mount type=bind,src={{ matrix_postgres_data_path }},dst=/var/lib/postgresql/data \ + --mount type=bind,src=/etc/passwd,dst=/etc/passwd,ro \ + {% for arg in matrix_postgres_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_postgres_docker_image_to_use }} \ + postgres {{ matrix_postgres_process_extra_arguments|join(' ') }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-postgres 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-postgres 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-postgres + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-postgres/templates/usr-local-bin/matrix-change-user-admin-status.j2 b/roles/matrix-postgres/templates/usr-local-bin/matrix-change-user-admin-status.j2 new file mode 100644 index 00000000..6c3082ef --- /dev/null +++ b/roles/matrix-postgres/templates/usr-local-bin/matrix-change-user-admin-status.j2 @@ -0,0 +1,19 @@ +#jinja2: lstrip_blocks: "True" +#!/bin/bash + +if [ $# -ne 2 ]; then + echo "Usage: "$0" <0/1>" + echo "Usage: 0 = non-admin" + echo "Usage: 1 = admin" + exit 1 +fi + +docker run \ + -it \ + --rm \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql \ + --network {{ matrix_docker_network }} \ + {{ matrix_postgres_docker_image_to_use }} \ + psql -h {{ matrix_postgres_connection_hostname }} --dbname={{ matrix_synapse_database_database }} -c "UPDATE users set admin=$2 WHERE name like '@$1:{{ matrix_domain }}'" diff --git a/roles/matrix-postgres/templates/usr-local-bin/matrix-postgres-cli.j2 b/roles/matrix-postgres/templates/usr-local-bin/matrix-postgres-cli.j2 new file mode 100644 index 00000000..de09a4eb --- /dev/null +++ b/roles/matrix-postgres/templates/usr-local-bin/matrix-postgres-cli.j2 @@ -0,0 +1,13 @@ +#jinja2: lstrip_blocks: "True" +#!/bin/bash + +docker run \ + -it \ + --rm \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql \ + --network {{ matrix_docker_network }} \ + {{ matrix_postgres_docker_image_to_use }} \ + psql -h {{ matrix_postgres_connection_hostname }} \ + "$@" diff --git a/roles/matrix-postgres/templates/usr-local-bin/matrix-postgres-update-user-password-hash.j2 b/roles/matrix-postgres/templates/usr-local-bin/matrix-postgres-update-user-password-hash.j2 new file mode 100644 index 00000000..0fbf4f21 --- /dev/null +++ b/roles/matrix-postgres/templates/usr-local-bin/matrix-postgres-update-user-password-hash.j2 @@ -0,0 +1,16 @@ +#jinja2: lstrip_blocks: "True" +#!/bin/bash + +if [ $# -ne 2 ]; then + echo "Usage: "$0" " + exit 1 +fi + +docker run \ + --rm \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql \ + --network {{ matrix_docker_network }} \ + {{ matrix_postgres_docker_image_to_use }} \ + psql -h {{ matrix_postgres_connection_hostname }} --dbname={{ matrix_synapse_database_database }} -c "UPDATE users set password_hash='$2' WHERE name = '@$1:{{ matrix_domain }}'" diff --git a/roles/matrix-prometheus-node-exporter/defaults/main.yml b/roles/matrix-prometheus-node-exporter/defaults/main.yml new file mode 100644 index 00000000..5c3a6386 --- /dev/null +++ b/roles/matrix-prometheus-node-exporter/defaults/main.yml @@ -0,0 +1,34 @@ +# matrix-prometheus-node-exporter is an Prometheus exporter for machine metrics +# See: https://prometheus.io/docs/guides/node-exporter/ + +matrix_prometheus_node_exporter_enabled: false + +matrix_prometheus_node_exporter_version: v1.2.0 +matrix_prometheus_node_exporter_docker_image: "{{ matrix_container_global_registry_prefix }}prom/node-exporter:{{ matrix_prometheus_node_exporter_version }}" +matrix_prometheus_node_exporter_docker_image_force_pull: "{{ matrix_prometheus_node_exporter_docker_image.endswith(':latest') }}" + +# A list of extra arguments to pass to the container +matrix_prometheus_node_exporter_container_extra_arguments: [] + +# List of systemd services that matrix-prometheus.service depends on +matrix_prometheus_node_exporter_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-prometheus.service wants +matrix_prometheus_node_exporter_systemd_wanted_services_list: [] + +# Controls whether the matrix-prometheus container exposes its HTTP port (tcp/9100 in the container). +# +# Takes an ":" value (e.g. "127.0.0.1:9100"), or empty string to not expose. +# +# Official recommendations are to run this container with `--net=host`, +# but we don't do that, since it: +# - likely exposes the metrics web server way too publicly (before applying https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1008) +# - or listens on a loopback interface only (--net=host and 127.0.0.1:9100), which is not reachable from another container (like `matrix-prometheus`) +# +# Using `--net=host` and binding to Docker's `matrix` bridge network may be a solution to both, +# but that's trickier to accomplish and won't necessarily work (hasn't been tested). +# +# Not using `--net=host` means that our network statistic reports are likely broken (inaccurate), +# because node-exporter can't see all interfaces, etc. +# For now, we'll live with that, until someone develops a better solution. +matrix_prometheus_node_exporter_container_http_host_bind_port: '' diff --git a/roles/matrix-prometheus-node-exporter/tasks/init.yml b/roles/matrix-prometheus-node-exporter/tasks/init.yml new file mode 100644 index 00000000..2894b717 --- /dev/null +++ b/roles/matrix-prometheus-node-exporter/tasks/init.yml @@ -0,0 +1,5 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-prometheus-node-exporter.service'] }}" + when: matrix_prometheus_node_exporter_enabled|bool + + diff --git a/roles/matrix-prometheus-node-exporter/tasks/main.yml b/roles/matrix-prometheus-node-exporter/tasks/main.yml new file mode 100644 index 00000000..172b5721 --- /dev/null +++ b/roles/matrix-prometheus-node-exporter/tasks/main.yml @@ -0,0 +1,8 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/setup.yml" + tags: + - setup-all + - setup-prometheus-node-exporter diff --git a/roles/matrix-prometheus-node-exporter/tasks/setup.yml b/roles/matrix-prometheus-node-exporter/tasks/setup.yml new file mode 100644 index 00000000..34086e6c --- /dev/null +++ b/roles/matrix-prometheus-node-exporter/tasks/setup.yml @@ -0,0 +1,54 @@ +--- + +# +# Tasks related to setting up matrix-prometheus-node-exporter +# + +- name: Ensure matrix-prometheus-node-exporter image is pulled + docker_image: + name: "{{ matrix_prometheus_node_exporter_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_prometheus_node_exporter_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_prometheus_node_exporter_docker_image_force_pull }}" + when: "matrix_prometheus_node_exporter_enabled|bool" + +- name: Ensure matrix-prometheus-node-exporter.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-prometheus-node-exporter.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-prometheus-node-exporter.service" + mode: 0644 + register: matrix_prometheus_node_exporter_systemd_service_result + when: matrix_prometheus_node_exporter_enabled|bool + +- name: Ensure systemd reloaded after matrix-prometheus.service installation + service: + daemon_reload: yes + when: "matrix_prometheus_node_exporter_enabled|bool and matrix_prometheus_node_exporter_systemd_service_result.changed" + +# +# Tasks related to getting rid of matrix-prometheus-node-exporter (if it was previously enabled) +# + +- name: Check existence of matrix-prometheus-node-exporter service + stat: + path: "{{ matrix_systemd_path }}/matrix-prometheus-node-exporter.service" + register: matrix_prometheus_node_exporter_service_stat + +- name: Ensure matrix-prometheus-node-exporter is stopped + service: + name: matrix-prometheus-node-exporter + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_prometheus_node_exporter_enabled|bool and matrix_prometheus_node_exporter_service_stat.stat.exists" + +- name: Ensure matrix-prometheus-node-exporter.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-prometheus-node-exporter.service" + state: absent + when: "not matrix_prometheus_node_exporter_enabled|bool and matrix_prometheus_node_exporter_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-prometheus-node-exporter.service removal + service: + daemon_reload: yes + when: "not matrix_prometheus_node_exporter_enabled|bool and matrix_prometheus_node_exporter_service_stat.stat.exists" diff --git a/roles/matrix-prometheus-node-exporter/templates/systemd/matrix-prometheus-node-exporter.service.j2 b/roles/matrix-prometheus-node-exporter/templates/systemd/matrix-prometheus-node-exporter.service.j2 new file mode 100644 index 00000000..210a0d97 --- /dev/null +++ b/roles/matrix-prometheus-node-exporter/templates/systemd/matrix-prometheus-node-exporter.service.j2 @@ -0,0 +1,44 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=matrix-prometheus-node-exporter +{% for service in matrix_prometheus_node_exporter_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_prometheus_node_exporter_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-node-exporter 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-node-exporter 2>/dev/null' + + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus-node-exporter \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + {% for arg in matrix_prometheus_node_exporter_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + --network={{ matrix_docker_network }} \ + {% if matrix_prometheus_node_exporter_container_http_host_bind_port %} + -p {{ matrix_prometheus_node_exporter_container_http_host_bind_port }}:9100 \ + {% endif %} + --pid=host \ + --mount type=bind,src=/,dst=/host,ro,bind-propagation=rslave \ + {{ matrix_prometheus_node_exporter_docker_image }} \ + --path.rootfs=/host + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-node-exporter 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-node-exporter 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-prometheus-node-exporter + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-prometheus-postgres-exporter/defaults/main.yml b/roles/matrix-prometheus-postgres-exporter/defaults/main.yml new file mode 100644 index 00000000..8aca4576 --- /dev/null +++ b/roles/matrix-prometheus-postgres-exporter/defaults/main.yml @@ -0,0 +1,49 @@ +# matrix-prometheus-postgres-exporter is an Prometheus exporter for postgres metrics +# See: https://github.com/prometheus-community/postgres_exporter + +matrix_prometheus_postgres_exporter_enabled: false + +matrix_prometheus_postgres_exporter_version: v0.9.0 +matrix_prometheus_postgres_exporter_port: 9187 + +matrix_prometheus_postgres_exporter_docker_image: "quay.io/prometheuscommunity/postgres-exporter:{{ matrix_prometheus_postgres_exporter_version }}" +matrix_prometheus_postgres_exporter_docker_image_force_pull: "{{ matrix_prometheus_postgres_exporter_docker_image.endswith(':latest') }}" + +# A list of extra arguments to pass to the container +matrix_prometheus_postgres_exporter_container_extra_arguments: ["-e PG_EXPORTER_AUTO_DISCOVER_DATABASES=true", + "-e PG_EXPORTER_WEB_LISTEN_ADDRESS=\":{{matrix_prometheus_postgres_exporter_port}}\"", + "-e DATA_SOURCE_NAME=\"postgresql://{{matrix_prometheus_postgres_exporter_database_username}}:{{matrix_prometheus_postgres_exporter_database_password}}@{{matrix_prometheus_postgres_exporter_database_hostname}}:5432/{{matrix_prometheus_postgres_exporter_database_name}}?sslmode=disable\"" ] + +# List of systemd services that matrix-prometheus-postgres-exporter.service depends on +matrix_prometheus_postgres_exporter_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-prometheus-postgres-exporter.service wants +matrix_prometheus_postgres_exporter_systemd_wanted_services_list: [] + +# details for connecting to the database +matrix_prometheus_postgres_exporter_database_username: 'matrix_prometheus_postgres_exporter' +matrix_prometheus_postgres_exporter_database_password: 'some-password' +matrix_prometheus_postgres_exporter_database_hostname: 'matrix-postgres' +matrix_prometheus_postgres_exporter_database_port: 5432 +matrix_prometheus_postgres_exporter_database_name: 'matrix_prometheus_postgres_exporter' + + +# Controls whether the matrix-prometheus container exposes its HTTP port (tcp/9100 in the container). +# +# Takes an ":" value (e.g. "127.0.0.1:9100"), or empty string to not expose. +# +# Official recommendations are to run this container with `--net=host`, +# but we don't do that, since it: +# - likely exposes the metrics web server way too publicly (before applying https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/1008) +# - or listens on a loopback interface only (--net=host and 127.0.0.1:9100), which is not reachable from another container (like `matrix-prometheus`) +# +# Using `--net=host` and binding to Docker's `matrix` bridge network may be a solution to both, +# but that's trickier to accomplish and won't necessarily work (hasn't been tested). +# +# Not using `--net=host` means that our network statistic reports are likely broken (inaccurate), +# because node-exporter can't see all interfaces, etc. +# For now, we'll live with that, until someone develops a better solution. +matrix_prometheus_postgres_exporter_container_http_host_bind_port: '' + +matrix_prometheus_postgres_exporter_dashboard_urls: +- "https://grafana.com/api/dashboards/9628/revisions/7/download" \ No newline at end of file diff --git a/roles/matrix-prometheus-postgres-exporter/tasks/init.yml b/roles/matrix-prometheus-postgres-exporter/tasks/init.yml new file mode 100644 index 00000000..2bd6904e --- /dev/null +++ b/roles/matrix-prometheus-postgres-exporter/tasks/init.yml @@ -0,0 +1,5 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-prometheus-postgres-exporter.service'] }}" + when: matrix_prometheus_postgres_exporter_enabled|bool + + diff --git a/roles/matrix-prometheus-postgres-exporter/tasks/main.yml b/roles/matrix-prometheus-postgres-exporter/tasks/main.yml new file mode 100644 index 00000000..e3c364fa --- /dev/null +++ b/roles/matrix-prometheus-postgres-exporter/tasks/main.yml @@ -0,0 +1,8 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/setup.yml" + tags: + - setup-all + - setup-prometheus-postgres-exporter diff --git a/roles/matrix-prometheus-postgres-exporter/tasks/setup.yml b/roles/matrix-prometheus-postgres-exporter/tasks/setup.yml new file mode 100644 index 00000000..076ece1a --- /dev/null +++ b/roles/matrix-prometheus-postgres-exporter/tasks/setup.yml @@ -0,0 +1,54 @@ +--- + +# +# Tasks related to setting up matrix-prometheus-postgres-exporter +# + +- name: Ensure matrix-prometheus-postgres-exporter image is pulled + docker_image: + name: "{{ matrix_prometheus_postgres_exporter_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_prometheus_postgres_exporter_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_prometheus_postgres_exporter_docker_image_force_pull }}" + when: "matrix_prometheus_postgres_exporter_enabled|bool" + +- name: Ensure matrix-prometheus-postgres-exporter.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-prometheus-postgres-exporter.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-prometheus-postgres-exporter.service" + mode: 0644 + register: matrix_prometheus_postgres_exporter_systemd_service_result + when: matrix_prometheus_postgres_exporter_enabled|bool + +- name: Ensure systemd reloaded after matrix-prometheus.service installation + service: + daemon_reload: yes + when: "matrix_prometheus_postgres_exporter_enabled|bool and matrix_prometheus_postgres_exporter_systemd_service_result.changed" + +# +# Tasks related to getting rid of matrix-prometheus-postgres-exporter (if it was previously enabled) +# + +- name: Check existence of matrix-prometheus-postgres-exporter service + stat: + path: "{{ matrix_systemd_path }}/matrix-prometheus-postgres-exporter.service" + register: matrix_prometheus_postgres_exporter_service_stat + +- name: Ensure matrix-prometheus-postgres-exporter is stopped + service: + name: matrix-prometheus-postgres-exporter + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_prometheus_postgres_exporter_enabled|bool and matrix_prometheus_postgres_exporter_service_stat.stat.exists" + +- name: Ensure matrix-prometheus-postgres-exporter.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-prometheus-postgres-exporter.service" + state: absent + when: "not matrix_prometheus_postgres_exporter_enabled|bool and matrix_prometheus_postgres_exporter_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-prometheus-postgres-exporter.service removal + service: + daemon_reload: yes + when: "not matrix_prometheus_postgres_exporter_enabled|bool and matrix_prometheus_postgres_exporter_service_stat.stat.exists" diff --git a/roles/matrix-prometheus-postgres-exporter/templates/systemd/matrix-prometheus-postgres-exporter.service.j2 b/roles/matrix-prometheus-postgres-exporter/templates/systemd/matrix-prometheus-postgres-exporter.service.j2 new file mode 100644 index 00000000..b25cb5de --- /dev/null +++ b/roles/matrix-prometheus-postgres-exporter/templates/systemd/matrix-prometheus-postgres-exporter.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=matrix-prometheus-postgres-exporter +{% for service in matrix_prometheus_postgres_exporter_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_prometheus_postgres_exporter_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-postgres-exporter 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-postgres-exporter 2>/dev/null' + + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus-postgres-exporter \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + {% for arg in matrix_prometheus_postgres_exporter_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + --network={{ matrix_docker_network }} \ + {% if matrix_prometheus_postgres_exporter_container_http_host_bind_port %} + -p {{ matrix_prometheus_postgres_exporter_container_http_host_bind_port }}:{{matrix_prometheus_postgres_exporter_port}} \ + {% endif %} + --pid=host \ + {{ matrix_prometheus_postgres_exporter_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus-postgres-exporter 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus-postgres-exporter 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-prometheus-postgres-exporter + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-prometheus/defaults/main.yml b/roles/matrix-prometheus/defaults/main.yml new file mode 100644 index 00000000..3725993c --- /dev/null +++ b/roles/matrix-prometheus/defaults/main.yml @@ -0,0 +1,67 @@ +# matrix-prometheus is an open-source systems monitoring and alerting toolkit +# See: https://github.com/matrix-org/synapse/blob/master/docs/metrics-howto.md + +matrix_prometheus_enabled: false + +matrix_prometheus_version: v2.28.1 +matrix_prometheus_docker_image: "{{ matrix_container_global_registry_prefix }}prom/prometheus:{{ matrix_prometheus_version }}" +matrix_prometheus_docker_image_force_pull: "{{ matrix_prometheus_docker_image.endswith(':latest') }}" + +matrix_prometheus_base_path: "{{ matrix_base_data_path }}/prometheus" +matrix_prometheus_config_path: "{{ matrix_prometheus_base_path }}/config" +matrix_prometheus_data_path: "{{ matrix_prometheus_base_path }}/data" + +# A list of extra arguments to pass to the container +matrix_prometheus_container_extra_arguments: [] + +# List of systemd services that matrix-prometheus.service depends on +matrix_prometheus_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-prometheus.service wants +matrix_prometheus_systemd_wanted_services_list: [] + +# Controls whether the matrix-prometheus container exposes its HTTP port (tcp/9090 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9090"), or empty string to not expose. +matrix_prometheus_container_http_host_bind_port: '' + +# Tells whether the "synapse" scraper configuration is enabled. +matrix_prometheus_scraper_synapse_enabled: false + +# Tells whether to download and load a Synapse rules file +matrix_prometheus_scraper_synapse_rules_enabled: "{{ matrix_prometheus_scraper_synapse_enabled }}" +matrix_prometheus_scraper_synapse_rules_synapse_tag: "master" +matrix_prometheus_scraper_synapse_rules_download_url: "https://raw.githubusercontent.com/matrix-org/synapse/{{ matrix_prometheus_scraper_synapse_rules_synapse_tag }}/contrib/prometheus/synapse-v2.rules" + +matrix_prometheus_scraper_synapse_targets: [] +matrix_prometheus_scraper_synapse_workers_enabled_list: [] + +# Tells whether the "node" scraper configuration is enabled. +# This configuration aims to scrape the current node (this server). +matrix_prometheus_scraper_node_enabled: false + +# Target addresses for the "node" scraper configuration. +# Unless you define this as a non-empty list, it gets populated at runtime with the IP address of `matrix-prometheus-node-exporter` and port 9100. +matrix_prometheus_scraper_node_targets: [] + +# Default prometheus 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_prometheus_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_prometheus_configuration_yaml: "{{ lookup('template', 'templates/prometheus.yml.j2') }}" + +matrix_prometheus_configuration_extension_yaml: | + # Your custom YAML configuration goes here. + # This configuration extends the default starting configuration (`matrix_prometheus_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_prometheus_configuration_yaml`. + +matrix_prometheus_configuration_extension: "{{ matrix_prometheus_configuration_extension_yaml|from_yaml if matrix_prometheus_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_prometheus_configuration_yaml`. +matrix_prometheus_configuration: "{{ matrix_prometheus_configuration_yaml|from_yaml|combine(matrix_prometheus_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-prometheus/tasks/init.yml b/roles/matrix-prometheus/tasks/init.yml new file mode 100644 index 00000000..12fae831 --- /dev/null +++ b/roles/matrix-prometheus/tasks/init.yml @@ -0,0 +1,5 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-prometheus.service'] }}" + when: matrix_prometheus_enabled|bool + + diff --git a/roles/matrix-prometheus/tasks/main.yml b/roles/matrix-prometheus/tasks/main.yml new file mode 100644 index 00000000..20f18cc3 --- /dev/null +++ b/roles/matrix-prometheus/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_prometheus_enabled|bool" + tags: + - setup-all + - setup-prometheus + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_prometheus_enabled|bool" + tags: + - setup-all + - setup-prometheus + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_prometheus_enabled|bool" + tags: + - setup-all + - setup-prometheus diff --git a/roles/matrix-prometheus/tasks/setup_install.yml b/roles/matrix-prometheus/tasks/setup_install.yml new file mode 100644 index 00000000..15a69279 --- /dev/null +++ b/roles/matrix-prometheus/tasks/setup_install.yml @@ -0,0 +1,50 @@ +--- + +- name: Ensure matrix-prometheus image is pulled + docker_image: + name: "{{ matrix_prometheus_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_prometheus_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_prometheus_docker_image_force_pull }}" + +- name: Ensure Prometheus paths exists + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_prometheus_base_path }}" + - "{{ matrix_prometheus_config_path }}" + - "{{ matrix_prometheus_data_path }}" + +- name: Download synapse-v2.rules + get_url: + url: "{{ matrix_prometheus_scraper_synapse_rules_download_url }}" + dest: "{{ matrix_prometheus_config_path }}/synapse-v2.rules" + force: true + mode: 0440 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: "matrix_prometheus_scraper_synapse_rules_enabled|bool" + +- name: Ensure prometheus.yml installed + copy: + content: "{{ matrix_prometheus_configuration|to_nice_yaml }}" + dest: "{{ matrix_prometheus_config_path }}/prometheus.yml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-prometheus.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-prometheus.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-prometheus.service" + mode: 0644 + register: matrix_prometheus_systemd_service_result + +- name: Ensure systemd reloaded after matrix-prometheus.service installation + service: + daemon_reload: yes + when: "matrix_prometheus_systemd_service_result.changed|bool" diff --git a/roles/matrix-prometheus/tasks/setup_uninstall.yml b/roles/matrix-prometheus/tasks/setup_uninstall.yml new file mode 100644 index 00000000..dd46a222 --- /dev/null +++ b/roles/matrix-prometheus/tasks/setup_uninstall.yml @@ -0,0 +1,25 @@ +--- + +- name: Check existence of matrix-prometheus service + stat: + path: "{{ matrix_systemd_path }}/matrix-prometheus.service" + register: matrix_prometheus_service_stat + +- name: Ensure matrix-prometheus is stopped + service: + name: matrix-prometheus + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_prometheus_service_stat.stat.exists|bool" + +- name: Ensure matrix-prometheus.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-prometheus.service" + state: absent + when: "matrix_prometheus_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-prometheus.service removal + service: + daemon_reload: yes + when: "matrix_prometheus_service_stat.stat.exists|bool" diff --git a/roles/matrix-prometheus/tasks/validate_config.yml b/roles/matrix-prometheus/tasks/validate_config.yml new file mode 100644 index 00000000..9fcfe12b --- /dev/null +++ b/roles/matrix-prometheus/tasks/validate_config.yml @@ -0,0 +1,7 @@ +--- + +- name: Fail if Synapse metrics or Prometheus Node Exporter not enabled + fail: + msg: > + You need to enable `matrix_prometheus_scraper_synapse_enabled` and/or `matrix_prometheus_scraper_node_enabled` for Prometheus grab metrics. + when: "not matrix_prometheus_scraper_synapse_enabled and not matrix_prometheus_scraper_node_enabled" diff --git a/roles/matrix-prometheus/templates/prometheus.yml.j2 b/roles/matrix-prometheus/templates/prometheus.yml.j2 new file mode 100644 index 00000000..869b2da8 --- /dev/null +++ b/roles/matrix-prometheus/templates/prometheus.yml.j2 @@ -0,0 +1,59 @@ +#jinja2: lstrip_blocks: "True" +global: + scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + # scrape_timeout is set to the global default (10s). + +# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. +rule_files: + {% if matrix_prometheus_scraper_synapse_rules_enabled %} + - 'synapse-v2.rules' + {% endif %} + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + - job_name: 'prometheus' + + # Override the global default and scrape targets from this job every 5 seconds. + scrape_interval: 5s + scrape_timeout: 5s + + # metrics_path defaults to '/metrics' + # scheme defaults to 'http'. + + static_configs: + - targets: ['localhost:9090'] + + {% if matrix_prometheus_scraper_synapse_enabled %} + - job_name: 'synapse' + metrics_path: '/_synapse/metrics' + static_configs: + - targets: {{ matrix_prometheus_scraper_synapse_targets|to_json }} + labels: + instance: {{ matrix_domain }} + job: master + index: 0 + {% for worker in matrix_prometheus_scraper_synapse_workers_enabled_list %} + {% if worker.metrics_port != 0 %} + - targets: ['matrix-synapse-worker-{{ worker.type }}-{{ worker.instanceId }}:{{ worker.metrics_port }}'] + labels: + instance: {{ matrix_domain }} + job: {{ worker.type }} + index: {{ worker.instanceId }} + {% endif %} + {% endfor %} + {% endif %} + + {% if matrix_prometheus_scraper_node_enabled %} + - job_name: node + static_configs: + - targets: {{ matrix_prometheus_scraper_node_targets|to_json }} + {% endif %} + + {% if matrix_prometheus_scraper_postgres_enabled %} + - job_name: postgres + static_configs: + - targets: {{ matrix_prometheus_scraper_postgres_targets|to_json }} + {% endif %} diff --git a/roles/matrix-prometheus/templates/systemd/matrix-prometheus.service.j2 b/roles/matrix-prometheus/templates/systemd/matrix-prometheus.service.j2 new file mode 100644 index 00000000..ad75d664 --- /dev/null +++ b/roles/matrix-prometheus/templates/systemd/matrix-prometheus.service.j2 @@ -0,0 +1,43 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=matrix-prometheus +{% for service in matrix_prometheus_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_prometheus_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus 2>/dev/null' + + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-prometheus \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --network={{ matrix_docker_network }} \ + {% if matrix_prometheus_container_http_host_bind_port %} + -p {{ matrix_prometheus_container_http_host_bind_port }}:9090 \ + {% endif %} + -v {{ matrix_prometheus_config_path }}:/etc/prometheus:z \ + -v {{ matrix_prometheus_data_path }}:/prometheus:z \ + {% for arg in matrix_prometheus_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_prometheus_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-prometheus 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-prometheus 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-prometheus + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-redis/defaults/main.yml b/roles/matrix-redis/defaults/main.yml new file mode 100644 index 00000000..409c7926 --- /dev/null +++ b/roles/matrix-redis/defaults/main.yml @@ -0,0 +1,22 @@ +matrix_redis_enabled: true + +matrix_redis_connection_password: "" + +matrix_redis_base_path: "{{ matrix_base_data_path }}/redis" +matrix_redis_data_path: "{{ matrix_redis_base_path }}/data" + +matrix_redis_version: 6.2.4-alpine +matrix_redis_docker_image_v6: "{{ matrix_container_global_registry_prefix }}redis:{{ matrix_redis_version }}" +matrix_redis_docker_image_latest: "{{ matrix_redis_docker_image_v6 }}" +matrix_redis_docker_image_to_use: '{{ matrix_redis_docker_image_latest }}' + +matrix_redis_docker_image_force_pull: "{{ matrix_redis_docker_image_to_use.endswith(':latest') }}" + +# A list of extra arguments to pass to the container +matrix_redis_container_extra_arguments: [] + +# Controls whether the matrix-redis container exposes a port (tcp/6379 in the container) +# that can be used to access redis from outside the container +# +# Takes an ":" or "" value (e.g. "127.0.0.1:6379"), or empty string to not expose. +matrix_redis_container_redis_bind_port: "" diff --git a/roles/matrix-redis/tasks/init.yml b/roles/matrix-redis/tasks/init.yml new file mode 100644 index 00000000..49068851 --- /dev/null +++ b/roles/matrix-redis/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-redis'] }}" + when: matrix_redis_enabled|bool diff --git a/roles/matrix-redis/tasks/main.yml b/roles/matrix-redis/tasks/main.yml new file mode 100644 index 00000000..595b09f5 --- /dev/null +++ b/roles/matrix-redis/tasks/main.yml @@ -0,0 +1,9 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/setup_redis.yml" + when: run_setup|bool + tags: + - setup-all + - setup-redis diff --git a/roles/matrix-redis/tasks/setup_redis.yml b/roles/matrix-redis/tasks/setup_redis.yml new file mode 100644 index 00000000..6f00282b --- /dev/null +++ b/roles/matrix-redis/tasks/setup_redis.yml @@ -0,0 +1,99 @@ +--- + +# +# Tasks related to setting up an internal redis server +# + +- name: Ensure redis Docker image is pulled + docker_image: + name: "{{ matrix_redis_docker_image_to_use }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_redis_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_redis_docker_image_force_pull }}" + when: matrix_redis_enabled|bool + +- name: Ensure redis paths exist + file: + path: "{{ item }}" + state: directory + mode: 0700 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_username }}" + with_items: + - "{{ matrix_redis_base_path }}" + - "{{ matrix_redis_data_path }}" + when: matrix_redis_enabled|bool + +# We do this as a separate task, because: +# - we'd like to do it for the data path only, not for the base path (which contains root-owned environment variable files we'd like to leave as-is) +# - we need to do it without `mode`, or we risk making certain `.conf` and other files's executable bit to flip to true +- name: Ensure redis data path ownership is correct + file: + path: "{{ matrix_redis_data_path }}" + state: directory + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_username }}" + recurse: yes + when: matrix_redis_enabled|bool + +- name: Ensure redis environment variables file created + template: + src: "{{ role_path }}/templates/{{ item }}.j2" + dest: "{{ matrix_redis_base_path }}/{{ item }}" + mode: 0644 + with_items: + - "redis.conf" + when: matrix_redis_enabled|bool + +- name: Ensure matrix-redis.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-redis.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-redis.service" + mode: 0644 + register: matrix_redis_systemd_service_result + when: matrix_redis_enabled|bool + +- name: Ensure systemd reloaded after matrix-redis.service installation + service: + daemon_reload: yes + when: "matrix_redis_enabled|bool and matrix_redis_systemd_service_result.changed" + +# +# Tasks related to getting rid of the internal redis server (if it was previously enabled) +# + +- name: Check existence of matrix-redis service + stat: + path: "{{ matrix_systemd_path }}/matrix-redis.service" + register: matrix_redis_service_stat + when: "not matrix_redis_enabled|bool" + +- name: Ensure matrix-redis is stopped + service: + name: matrix-redis + state: stopped + daemon_reload: yes + when: "not matrix_redis_enabled|bool and matrix_redis_service_stat.stat.exists" + +- name: Ensure matrix-redis.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-redis.service" + state: absent + when: "not matrix_redis_enabled|bool and matrix_redis_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-redis.service removal + service: + daemon_reload: yes + when: "not matrix_redis_enabled|bool and matrix_redis_service_stat.stat.exists" + +- name: Check existence of matrix-redis local data path + stat: + path: "{{ matrix_redis_data_path }}" + register: matrix_redis_data_path_stat + when: "not matrix_redis_enabled|bool" + +# We just want to notify the user. Deleting data is too destructive. +- name: Notify if matrix-redis local data remains + debug: + msg: "Note: You are not using a local redis instance, but some old data remains from before in `{{ matrix_redis_data_path }}`. Feel free to delete it." + when: "not matrix_redis_enabled|bool and matrix_redis_data_path_stat.stat.exists" diff --git a/roles/matrix-redis/templates/redis.conf.j2 b/roles/matrix-redis/templates/redis.conf.j2 new file mode 100644 index 00000000..34371356 --- /dev/null +++ b/roles/matrix-redis/templates/redis.conf.j2 @@ -0,0 +1,4 @@ +#jinja2: lstrip_blocks: "True" +{% if matrix_redis_connection_password %} +requirepass {{ matrix_redis_connection_password }} +{% endif %} diff --git a/roles/matrix-redis/templates/systemd/matrix-redis.service.j2 b/roles/matrix-redis/templates/systemd/matrix-redis.service.j2 new file mode 100644 index 00000000..5f6699f8 --- /dev/null +++ b/roles/matrix-redis/templates/systemd/matrix-redis.service.j2 @@ -0,0 +1,37 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Redis server +After=docker.service +Requires=docker.service + +[Service] +Type=simple +ExecStartPre=-/usr/bin/docker stop matrix-redis +ExecStartPre=-/usr/bin/docker rm matrix-redis + +ExecStart=/usr/bin/docker run --rm --name matrix-redis \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --tmpfs=/tmp:rw,noexec,nosuid,size=100m \ + --network={{ matrix_docker_network }} \ + {% if matrix_redis_container_redis_bind_port %} + -p {{ matrix_redis_container_redis_bind_port }}:6379 \ + {% endif %} + --mount type=bind,src={{ matrix_redis_base_path }}/redis.conf,dst=/usr/local/etc/redis/redis.conf,ro \ + --mount type=bind,src={{ matrix_redis_data_path }},dst=/data \ + {% for arg in matrix_redis_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_redis_docker_image_to_use }} \ + redis-server /usr/local/etc/redis/redis.conf + +ExecStop=-/usr/bin/docker stop matrix-redis +ExecStop=-/usr/bin/docker rm matrix-redis +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-redis + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-registration/defaults/main.yml b/roles/matrix-registration/defaults/main.yml new file mode 100644 index 00000000..e03891b2 --- /dev/null +++ b/roles/matrix-registration/defaults/main.yml @@ -0,0 +1,116 @@ +# matrix-registration is a simple python application to have a token based matrix registration +# See: https://zeratax.github.io/matrix-registration/ + +matrix_registration_enabled: true + +matrix_registration_container_image_self_build: false +matrix_registration_container_image_self_build_repo: "https://github.com/ZerataX/matrix-registration" +matrix_registration_container_image_self_build_branch: "{{ 'master' if matrix_registration_version == 'latest' else matrix_registration_version }}" + +matrix_registration_base_path: "{{ matrix_base_data_path }}/matrix-registration" +matrix_registration_config_path: "{{ matrix_registration_base_path }}/config" +matrix_registration_data_path: "{{ matrix_registration_base_path }}/data" +matrix_registration_docker_src_files_path: "{{ matrix_registration_base_path }}/docker-src" + +matrix_registration_version: "v0.7.2" + +matrix_registration_docker_image: "{{ matrix_registration_docker_image_name_prefix }}zeratax/matrix-registration:{{ matrix_registration_version }}" +matrix_registration_docker_image_name_prefix: "{{ 'localhost/' if matrix_registration_container_image_self_build else matrix_container_global_registry_prefix }}" +matrix_registration_docker_image_force_pull: "{{ matrix_registration_docker_image.endswith(':latest') }}" + +# A list of extra arguments to pass to the container +matrix_registration_container_extra_arguments: [] + +# List of systemd services that matrix-registration.service depends on +matrix_registration_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-registration.service wants +matrix_registration_systemd_wanted_services_list: [] + +# Controls whether the matrix-registration container exposes its HTTP port (tcp/5000 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8767"), or empty string to not expose. +matrix_registration_container_http_host_bind_port: '' + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_registration_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_registration_postgres_*` variables +matrix_registration_database_engine: 'sqlite' + +matrix_registration_sqlite_database_path_local: "{{ matrix_registration_data_path }}/db.sqlite3" +matrix_registration_sqlite_database_path_in_container: "/data/db.sqlite3" + +matrix_registration_database_username: 'matrix_registration' +matrix_registration_database_password: 'some-password' +matrix_registration_database_hostname: 'matrix-postgres' +matrix_registration_database_port: 5432 +matrix_registration_database_name: 'matrix_registration' + +matrix_registration_database_connection_string: 'postgresql://{{ matrix_registration_database_username }}:{{ matrix_registration_database_password }}@{{ matrix_registration_database_hostname }}:{{ matrix_registration_database_port }}/{{ matrix_registration_database_name }}' + +# For some reason, matrix-registraiton expects the `db` field to be like this: `sqlite:////data/db.sqlite3`. +# (seems like one too many slashes, but..) +matrix_registration_db: "{{ + { + 'sqlite': ('sqlite:///' + matrix_registration_sqlite_database_path_in_container), + 'postgres': matrix_registration_database_connection_string, + }[matrix_registration_database_engine] +}}" + + +# The path at which Matrix Registration will be exposed on `matrix.DOMAIN` +# (only applies when matrix-nginx-proxy is used). +matrix_registration_public_endpoint: /matrix-registration + +matrix_registration_base_url: "{{ matrix_registration_public_endpoint }}" + +matrix_registration_api_register_endpoint: "{{ matrix_homeserver_url }}{{ matrix_registration_public_endpoint }}/register" +matrix_registration_api_token_endpoint: "{{ matrix_homeserver_url }}{{ matrix_registration_public_endpoint }}/token" + +matrix_registration_api_validate_certs: true + +# The URL to your homeserver (e.g.: `https://matrix.DOMAIN`). +# A local (in-container address) is preferable. +matrix_registration_server_location: "" + +matrix_registration_server_name: "{{ matrix_domain }}" + +# matrix_registration_shared_secret needs to match the homeserver's registration secret. +# For Synapse, that's the `registration_shared_secret` setting. +matrix_registration_shared_secret: "" + +# matrix_registration_admin_secret is your own admin secret for using matrix-registration (creating new tokens, etc.) +matrix_registration_admin_secret: "" + +matrix_registration_riot_instance: "https://riot.im/app/" + +# Default matrix-registration 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_registration_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_registration_configuration_yaml: "{{ lookup('template', 'templates/config.yaml.j2') }}" + +matrix_registration_configuration_extension_yaml: | + # Your custom YAML configuration for registration goes here. + # This configuration extends the default starting configuration (`matrix_registration_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_registration_configuration_yaml`. + # + # Example configuration extension follows: + # + # password: + # min_length: 12 + +matrix_registration_configuration_extension: "{{ matrix_registration_configuration_extension_yaml|from_yaml if matrix_registration_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final matrix-registration configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_registration_configuration_yaml`. +matrix_registration_configuration: "{{ matrix_registration_configuration_yaml|from_yaml|combine(matrix_registration_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-registration/tasks/generate_token.yml b/roles/matrix-registration/tasks/generate_token.yml new file mode 100644 index 00000000..ae5bdf4c --- /dev/null +++ b/roles/matrix-registration/tasks/generate_token.yml @@ -0,0 +1,50 @@ +- name: Fail if playbook called incorrectly + fail: + msg: "The `one_time` variable needs to be provided to this playbook, via --extra-vars" + when: "one_time is not defined or one_time not in ['yes', 'no']" + +- name: Fail if playbook called incorrectly + fail: + msg: "The `ex_date` variable (expiration date) needs to be provided to this playbook, via --extra-vars" + when: "ex_date is not defined or ex_date == ''" + +- name: Call matrix-registration token creation API + uri: + url: "{{ matrix_registration_api_token_endpoint }}" + follow_redirects: none + validate_certs: "{{ matrix_registration_api_validate_certs }}" + headers: + Content-Type: application/json + Authorization: "SharedSecret {{ matrix_registration_admin_secret }}" + method: POST + body_format: json + body: | + { + "one_time": {{ 'true' if one_time == 'yes' else 'false' }}, + "ex_date": {{ ex_date|to_json }} + } + check_mode: no + register: matrix_registration_api_result + +- set_fact: + matrix_registration_api_result_message: >- + matrix-registration result: + + Direct registration link (with the token prefilled): + + {{ matrix_registration_api_register_endpoint }}?token={{ matrix_registration_api_result.json.name }} + + Full token details are: + + {{ matrix_registration_api_result.json }} + check_mode: no + +- name: Inject result message into matrix_playbook_runtime_results + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [matrix_registration_api_result_message] + }} + check_mode: no diff --git a/roles/matrix-registration/tasks/init.yml b/roles/matrix-registration/tasks/init.yml new file mode 100644 index 00000000..32a35c7d --- /dev/null +++ b/roles/matrix-registration/tasks/init.yml @@ -0,0 +1,68 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_registration_container_image_self_build and matrix_registration_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-registration.service'] }}" + when: matrix_registration_enabled|bool + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append matrix-registration's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-registration role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate matrix-registration proxying configuration for matrix-nginx-proxy + set_fact: + matrix_registration_matrix_nginx_proxy_configuration: | + rewrite ^{{ matrix_registration_public_endpoint }}$ $scheme://$server_name{{ matrix_registration_public_endpoint }}/ permanent; + rewrite ^{{ matrix_registration_public_endpoint }}/$ $scheme://$server_name{{ matrix_registration_public_endpoint }}/register redirect; + + location ~ ^{{ matrix_registration_public_endpoint }}/(.*) { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-registration:5000"; + proxy_pass http://$backend/$1; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:8767/$1; + {% endif %} + + {# + Workaround matrix-registration serving the background image at /static + (see https://github.com/ZerataX/matrix-registration/issues/47) + #} + sub_filter_once off; + sub_filter_types text/css; + sub_filter "/static/" "{{ matrix_registration_public_endpoint }}/static/"; + } + + - name: Register matrix-registration proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks|default([]) + + + [matrix_registration_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_registration_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the matrix-registration tool but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ matrix_registration_public_endpoint }}` + URL endpoint to the matrix-registration container. + You can expose the container's port using the `matrix_registration_container_http_host_bind_port` variable. + when: "matrix_registration_enabled|bool and matrix_nginx_proxy_enabled is not defined" diff --git a/roles/matrix-registration/tasks/list_tokens.yml b/roles/matrix-registration/tasks/list_tokens.yml new file mode 100644 index 00000000..dea3eb31 --- /dev/null +++ b/roles/matrix-registration/tasks/list_tokens.yml @@ -0,0 +1,29 @@ +- name: Call matrix-registration list all tokens API + uri: + url: "{{ matrix_registration_api_token_endpoint }}" + follow_redirects: none + validate_certs: "{{ matrix_registration_api_validate_certs }}" + headers: + Content-Type: application/json + Authorization: "SharedSecret {{ matrix_registration_admin_secret }}" + method: GET + body_format: json + check_mode: no + register: matrix_registration_api_result + +- set_fact: + matrix_registration_api_result_message: >- + matrix-registration result: + + {{ matrix_registration_api_result.json | to_nice_json }} + check_mode: no + +- name: Inject result message into matrix_playbook_runtime_results + set_fact: + matrix_playbook_runtime_results: | + {{ + matrix_playbook_runtime_results|default([]) + + + [matrix_registration_api_result_message] + }} + check_mode: no diff --git a/roles/matrix-registration/tasks/main.yml b/roles/matrix-registration/tasks/main.yml new file mode 100644 index 00000000..3324e083 --- /dev/null +++ b/roles/matrix-registration/tasks/main.yml @@ -0,0 +1,31 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: "run_setup|bool and matrix_registration_enabled|bool" + tags: + - setup-all + - setup-matrix-registration + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: "run_setup|bool and matrix_registration_enabled|bool" + tags: + - setup-all + - setup-matrix-registration + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: "run_setup|bool and not matrix_registration_enabled|bool" + tags: + - setup-all + - setup-matrix-registration + +- import_tasks: "{{ role_path }}/tasks/generate_token.yml" + when: "run_setup|bool and matrix_registration_enabled|bool" + tags: + - generate-matrix-registration-token + +- import_tasks: "{{ role_path }}/tasks/list_tokens.yml" + when: "run_setup|bool and matrix_registration_enabled|bool" + tags: + - list-matrix-registration-tokens diff --git a/roles/matrix-registration/tasks/setup_install.yml b/roles/matrix-registration/tasks/setup_install.yml new file mode 100644 index 00000000..0d7da9ce --- /dev/null +++ b/roles/matrix-registration/tasks/setup_install.yml @@ -0,0 +1,101 @@ +--- + +- set_fact: + matrix_registration_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_registration_sqlite_database_path_local }}" + register: matrix_registration_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_registration_sqlite_database_path_local }}" + dst: "{{ matrix_registration_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_registration_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-registration.service'] + # pgloader makes `ex_date` of type `TIMESTAMP WITH TIMEZONE`, + # which makes matrix-registration choke on it later on when comparing dates. + additional_psql_statements_list: + - ALTER TABLE tokens ALTER COLUMN ex_date TYPE TIMESTAMP WITHOUT TIME ZONE; + additional_psql_statements_db_name: "{{ matrix_registration_database_name }}" + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_registration_requires_restart: true + when: "matrix_registration_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_registration_database_engine == 'postgres'" + +- name: Ensure matrix-registration paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_registration_base_path }}", when: true } + - { path: "{{ matrix_registration_config_path }}", when: true } + - { path: "{{ matrix_registration_data_path }}", when: true } + - { path: "{{ matrix_registration_docker_src_files_path }}", when: "{{ matrix_registration_container_image_self_build }}"} + when: "item.when|bool" + +- name: Ensure matrix-registration image is pulled + docker_image: + name: "{{ matrix_registration_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_registration_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_registration_docker_image_force_pull }}" + when: "not matrix_registration_container_image_self_build|bool" + +- name: Ensure matrix-registration repository is present when self-building + git: + repo: "{{ matrix_registration_container_image_self_build_repo }}" + dest: "{{ matrix_registration_docker_src_files_path }}" + version: "{{ matrix_registration_container_image_self_build_branch }}" + force: "yes" + register: matrix_registration_git_pull_results + when: "matrix_registration_container_image_self_build|bool" + +- name: Ensure matrix-registration Docker image is built + docker_image: + name: "{{ matrix_registration_docker_image }}" + source: build + force_source: "{{ matrix_registration_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_registration_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_registration_docker_src_files_path }}" + pull: yes + when: "matrix_registration_container_image_self_build|bool" + +- name: Ensure matrix-registration config installed + copy: + content: "{{ matrix_registration_configuration|to_nice_yaml }}" + dest: "{{ matrix_registration_config_path }}/config.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-registration.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-registration.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-registration.service" + mode: 0644 + register: matrix_registration_systemd_service_result + +- name: Ensure systemd reloaded after matrix-registration.service installation + service: + daemon_reload: yes + when: "matrix_registration_systemd_service_result.changed|bool" + +- name: Ensure matrix-registration.service restarted, if necessary + service: + name: "matrix-registration.service" + state: restarted + when: "matrix_registration_requires_restart|bool" diff --git a/roles/matrix-registration/tasks/setup_uninstall.yml b/roles/matrix-registration/tasks/setup_uninstall.yml new file mode 100644 index 00000000..573f8170 --- /dev/null +++ b/roles/matrix-registration/tasks/setup_uninstall.yml @@ -0,0 +1,30 @@ +--- + +- name: Check existence of matrix-registration service + stat: + path: "{{ matrix_systemd_path }}/matrix-registration.service" + register: matrix_registration_service_stat + +- name: Ensure matrix-registration is stopped + service: + name: matrix-registration + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_registration_service_stat.stat.exists|bool" + +- name: Ensure matrix-registration.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-registration.service" + state: absent + when: "matrix_registration_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-registration.service removal + service: + daemon_reload: yes + when: "matrix_registration_service_stat.stat.exists|bool" + +- name: Ensure matrix-registration Docker image doesn't exist + docker_image: + name: "{{ matrix_registration_docker_image }}" + state: absent diff --git a/roles/matrix-registration/tasks/validate_config.yml b/roles/matrix-registration/tasks/validate_config.yml new file mode 100644 index 00000000..90466b46 --- /dev/null +++ b/roles/matrix-registration/tasks/validate_config.yml @@ -0,0 +1,20 @@ +--- + +- name: Fail if required matrix-registration settings not defined + fail: + msg: > + You need to define a required configuration setting (`{{ item }}`) for using matrix-registration. + when: "vars[item] == ''" + with_items: + - "matrix_registration_shared_secret" + - "matrix_registration_admin_secret" + - "matrix_registration_server_location" + +- name: (Deprecation) Catch and report renamed settings + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_registration_docker_repo', 'new': 'matrix_registration_container_image_self_build_repo'} diff --git a/roles/matrix-registration/templates/config.yaml.j2 b/roles/matrix-registration/templates/config.yaml.j2 new file mode 100644 index 00000000..39211b24 --- /dev/null +++ b/roles/matrix-registration/templates/config.yaml.j2 @@ -0,0 +1,31 @@ +server_location: {{ matrix_registration_server_location|to_json }} +server_name: {{ matrix_registration_server_name|to_json }} +shared_secret: {{ matrix_registration_shared_secret|to_json }} +admin_secret: {{ matrix_registration_admin_secret|to_json }} +riot_instance: {{ matrix_registration_riot_instance|to_json }} +db: {{ matrix_registration_db|to_json }} +host: '0.0.0.0' +port: 5000 +rate_limit: ["100 per day", "10 per minute"] +allow_cors: false +logging: + disable_existing_loggers: False + version: 1 + root: + level: DEBUG + handlers: [console] + formatters: + brief: + format: '%(name)s - %(levelname)s - %(message)s' + precise: + format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + handlers: + console: + class: logging.StreamHandler + level: INFO + formatter: brief + stream: ext://sys.stdout +# password requirements +password: + min_length: 8 +base_url: {{ matrix_registration_base_url|to_json }} diff --git a/roles/matrix-registration/templates/systemd/matrix-registration.service.j2 b/roles/matrix-registration/templates/systemd/matrix-registration.service.j2 new file mode 100644 index 00000000..e73e3e5f --- /dev/null +++ b/roles/matrix-registration/templates/systemd/matrix-registration.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=matrix-registration +{% for service in matrix_registration_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_registration_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-registration 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-registration 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-registration \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --network={{ matrix_docker_network }} \ + {% if matrix_registration_container_http_host_bind_port %} + -p {{ matrix_registration_container_http_host_bind_port }}:5000 \ + {% endif %} + --mount type=bind,src={{ matrix_registration_config_path }},dst=/config,ro \ + --mount type=bind,src={{ matrix_registration_data_path }},dst=/data \ + {% for arg in matrix_registration_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_registration_docker_image }} \ + serve + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-registration 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-registration 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-registration + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-sygnal/defaults/main.yml b/roles/matrix-sygnal/defaults/main.yml new file mode 100644 index 00000000..476ac2ad --- /dev/null +++ b/roles/matrix-sygnal/defaults/main.yml @@ -0,0 +1,95 @@ +# Sygnal is a reference Push Gateway for Matrix. +# To make use of it for delivering push notificatins, you'll need to develop/build your own Matrix app. +# Learn more here: https://github.com/matrix-org/sygnal +matrix_sygnal_enabled: false + +matrix_sygnal_base_path: "{{ matrix_base_data_path }}/sygnal" +matrix_sygnal_config_path: "{{ matrix_sygnal_base_path }}/config" +matrix_sygnal_data_path: "{{ matrix_sygnal_base_path }}/data" + +matrix_sygnal_version: v0.9.0 +matrix_sygnal_docker_image: "{{ matrix_container_global_registry_prefix }}matrixdotorg/sygnal:{{ matrix_sygnal_version }}" +matrix_sygnal_docker_image_force_pull: "{{ matrix_sygnal_docker_image.endswith(':latest') }}" + +# List of systemd services that matrix-sygnal.service depends on. +matrix_sygnal_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-sygnal.service wants +matrix_sygnal_systemd_wanted_services_list: [] + +# Controls whether the matrix-sygnal container exposes its HTTP port (tcp/6000 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:6000"), or empty string to not expose. +matrix_sygnal_container_http_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_sygnal_container_extra_arguments: [] + +# Database-related configuration fields. +# +# To use SQLite, stick to these defaults. +# +# To use Postgres: +# - change the engine (`matrix_sygnal_database_engine: 'postgres'`) +# - adjust your database credentials via the `matrix_sygnal_postgres_*` variables +matrix_sygnal_database_engine: 'sqlite' + +matrix_sygnal_sqlite_database_path_local: "{{ matrix_sygnal_data_path }}/sygnal.db" +matrix_sygnal_sqlite_database_path_in_container: "/data/sygnal.db" + +matrix_sygnal_database_username: 'matrix_sygnal' +matrix_sygnal_database_password: 'some-password' +matrix_sygnal_database_hostname: 'matrix-postgres' +matrix_sygnal_database_port: 5432 +matrix_sygnal_database_name: 'matrix_sygnal' + +matrix_sygnal_database_connection_string: 'postgres://{{ matrix_sygnal_database_username }}:{{ matrix_sygnal_database_password }}@{{ matrix_sygnal_database_hostname }}:{{ matrix_sygnal_database_port }}/{{ matrix_sygnal_database_name }}' + +# A map (dictionary) of apps instances that this server works with. +# +# Example configuration: +# +# matrix_sygnal_apps: +# com.example.myapp.ios: +# type: apns +# # .. more configuration .. +# com.example.myapp.android: +# type: gcm +# api_key: your_api_key_for_gcm +# # .. more configuration .. +# +# The APNS configuration needs to reference some certificate files. +# One can put these in the `matrix_sygnal_data_path` directory (`/matrix/sygnal/data`), mounted to `/data` in the container. +# The `matrix_sygnal_apps` paths need to use the in-container path (`/data`). +# To install these files via the playbook, one can use the `matrix-aux` role. +# Examples and more details are available in `docs/configuring-playbook-sygnal.md`. +matrix_sygnal_apps: [] + +matrix_sygnal_metrics_prometheus_enabled: false + +# Default Sygnal 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_sygnal_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_sygnal_configuration_yaml: "{{ lookup('template', 'templates/sygnal.yaml.j2') }}" + +matrix_sygnal_configuration_extension_yaml: | + # Your custom YAML configuration for Sygnal goes here. + # This configuration extends the default starting configuration (`matrix_sygnal_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_sygnal_configuration_yaml`. + # + # Example configuration extension follows: + # metrics: + # opentracing: + # enabled: true + +matrix_sygnal_configuration_extension: "{{ matrix_sygnal_configuration_extension_yaml|from_yaml if matrix_sygnal_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final sygnal configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_sygnal_configuration_yaml`. +matrix_sygnal_configuration: "{{ matrix_sygnal_configuration_yaml|from_yaml|combine(matrix_sygnal_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-sygnal/tasks/init.yml b/roles/matrix-sygnal/tasks/init.yml new file mode 100644 index 00000000..559a3681 --- /dev/null +++ b/roles/matrix-sygnal/tasks/init.yml @@ -0,0 +1,3 @@ +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-sygnal.service'] }}" + when: matrix_sygnal_enabled|bool diff --git a/roles/matrix-sygnal/tasks/main.yml b/roles/matrix-sygnal/tasks/main.yml new file mode 100644 index 00000000..c00862a4 --- /dev/null +++ b/roles/matrix-sygnal/tasks/main.yml @@ -0,0 +1,21 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: run_setup|bool + tags: + - setup-all + - setup-sygnal + +- import_tasks: "{{ role_path }}/tasks/setup_install.yml" + when: run_setup|bool and matrix_sygnal_enabled|bool + tags: + - setup-all + - setup-sygnal + +- import_tasks: "{{ role_path }}/tasks/setup_uninstall.yml" + when: run_setup|bool and not matrix_sygnal_enabled|bool + tags: + - setup-all + - setup-sygnal diff --git a/roles/matrix-sygnal/tasks/setup_install.yml b/roles/matrix-sygnal/tasks/setup_install.yml new file mode 100644 index 00000000..afac61c4 --- /dev/null +++ b/roles/matrix-sygnal/tasks/setup_install.yml @@ -0,0 +1,73 @@ +--- + +- set_fact: + matrix_sygnal_requires_restart: false + +- block: + - name: Check if an SQLite database already exists + stat: + path: "{{ matrix_sygnal_sqlite_database_path_local }}" + register: matrix_sygnal_sqlite_database_path_local_stat_result + + - block: + - set_fact: + matrix_postgres_db_migration_request: + src: "{{ matrix_sygnal_sqlite_database_path_local }}" + dst: "{{ matrix_sygnal_database_connection_string }}" + caller: "{{ role_path|basename }}" + engine_variable_name: 'matrix_sygnal_database_engine' + engine_old: 'sqlite' + systemd_services_to_stop: ['matrix-sygnal.service'] + pgloader_options: ['--with "quote identifiers"'] + + - import_tasks: "{{ role_path }}/../matrix-postgres/tasks/util/migrate_db_to_postgres.yml" + + - set_fact: + matrix_sygnal_requires_restart: true + when: "matrix_sygnal_sqlite_database_path_local_stat_result.stat.exists|bool" + when: "matrix_sygnal_database_engine == 'postgres'" + +- name: Ensure Sygnal image is pulled + docker_image: + name: "{{ matrix_sygnal_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_sygnal_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_sygnal_docker_image_force_pull }}" + +- name: Ensure Sygnal paths exists + file: + path: "{{ item }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - "{{ matrix_sygnal_base_path }}" + - "{{ matrix_sygnal_config_path }}" + - "{{ matrix_sygnal_data_path }}" + +- name: Ensure Sygnal config installed + copy: + content: "{{ matrix_sygnal_configuration|to_nice_yaml }}" + dest: "{{ matrix_sygnal_config_path }}/sygnal.yaml" + mode: 0640 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure matrix-sygnal.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-sygnal.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-sygnal.service" + mode: 0644 + register: matrix_sygnal_systemd_service_result + +- name: Ensure systemd reloaded after matrix-sygnal.service installation + service: + daemon_reload: yes + when: "matrix_sygnal_systemd_service_result.changed|bool" + +- name: Ensure matrix-sygnal.service restarted, if necessary + service: + name: "matrix-sygnal.service" + state: restarted + when: "matrix_sygnal_requires_restart|bool" diff --git a/roles/matrix-sygnal/tasks/setup_uninstall.yml b/roles/matrix-sygnal/tasks/setup_uninstall.yml new file mode 100644 index 00000000..dc50078c --- /dev/null +++ b/roles/matrix-sygnal/tasks/setup_uninstall.yml @@ -0,0 +1,35 @@ +--- + +- name: Check existence of matrix-sygnal service + stat: + path: "{{ matrix_systemd_path }}/matrix-sygnal.service" + register: matrix_sygnal_service_stat + +- name: Ensure matrix-sygnal is stopped + service: + name: matrix-sygnal + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_sygnal_service_stat.stat.exists|bool" + +- name: Ensure matrix-sygnal.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-sygnal.service" + state: absent + when: "matrix_sygnal_service_stat.stat.exists|bool" + +- name: Ensure systemd reloaded after matrix-sygnal.service removal + service: + daemon_reload: yes + when: "matrix_sygnal_service_stat.stat.exists|bool" + +- name: Ensure Sygnal base directory doesn't exist + file: + path: "{{ matrix_sygnal_base_path }}" + state: absent + +- name: Ensure Sygnal Docker image doesn't exist + docker_image: + name: "{{ matrix_sygnal_docker_image }}" + state: absent diff --git a/roles/matrix-sygnal/tasks/validate_config.yml b/roles/matrix-sygnal/tasks/validate_config.yml new file mode 100644 index 00000000..efd64104 --- /dev/null +++ b/roles/matrix-sygnal/tasks/validate_config.yml @@ -0,0 +1,13 @@ +- name: Fail if no Sygnal apps defined + fail: + msg: >- + Enabling Sygnal requires that you specify at least one app in `matrix_sygnal_apps` + when: "matrix_sygnal_enabled and matrix_sygnal_apps|length == 0" + +- name: Fail if running on a non-supported architecture + fail: + msg: >- + Sygnal can only be used on the amd64 architecture for now. + Only amd64 container images are pushed for the `docker.io/matrixdotorg/sygnal` container image. + Either use a different image (by redefining `matrix_sygnal_docker_image`) or consider contributing self-building support to this role. + when: "matrix_sygnal_enabled and matrix_architecture != 'amd64' and matrix_sygnal_docker_image.startswith('docker.io/matrixdotorg/sygnal')" diff --git a/roles/matrix-sygnal/templates/sygnal.yaml.j2 b/roles/matrix-sygnal/templates/sygnal.yaml.j2 new file mode 100644 index 00000000..bb8c521d --- /dev/null +++ b/roles/matrix-sygnal/templates/sygnal.yaml.j2 @@ -0,0 +1,288 @@ +## +# This is a configuration for Sygnal, the reference Push Gateway for Matrix +# See: matrix.org +## + +# The 'database' setting defines the database that sygnal uses to store all of +# its data. +# +# 'name' gives the database engine to use: either 'sqlite3' (for SQLite) or +# 'psycopg2' (for PostgreSQL). +# +# 'args' gives options which are passed through to the database engine, +# except for options starting 'cp_', which are used to configure the Twisted +# connection pool. For a reference to valid arguments, see: +# * for sqlite: https://docs.python.org/3/library/sqlite3.html#sqlite3.connect +# * for postgres: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS +# * for the connection pool: https://twistedmatrix.com/documents/current/api/twisted.enterprise.adbapi.ConnectionPool.html#__init__ +# +# +# Example SQLite configuration: +# +#database: +# name: sqlite3 +# args: +# dbfile: /path/to/database.db +# +# +# Example Postgres configuration: +# +#database: +# name: psycopg2 +# args: +# host: localhost +# database: sygnal +# user: sygnal +# password: pass +# cp_min: 1 +# cp_max: 5 +# +{% if matrix_sygnal_database_engine == 'sqlite' %} +database: + name: sqlite3 + args: + dbfile: {{ matrix_sygnal_sqlite_database_path_in_container|to_json }} +{% else %} +database: + name: psycopg2 + args: + host: {{ matrix_sygnal_database_hostname|to_json }} + database: {{ matrix_sygnal_database_name|to_json }} + user: {{ matrix_sygnal_database_username|to_json }} + password: {{ matrix_sygnal_database_password|to_json }} + cp_min: 1 + cp_max: 5 +{% endif %} + +## Logging # +# +log: + # Specify a Python logging 'dictConfig', as described at: + # https://docs.python.org/3.7/library/logging.config.html#logging.config.dictConfig + # + setup: + version: 1 + formatters: + normal: + format: "%(asctime)s [%(process)d] %(levelname)-5s %(name)s %(message)s" + handlers: + # This handler prints to Standard Error + # + stderr: + class: "logging.StreamHandler" + formatter: "normal" + stream: "ext://sys.stderr" + + # This handler prints to Standard Output. + # + stdout: + class: "logging.StreamHandler" + formatter: "normal" + stream: "ext://sys.stdout" + + # This handler demonstrates logging to a text file on the filesystem. + # You can use logrotate(8) to perform log rotation. + # + #file: + # class: "logging.handlers.WatchedFileHandler" + # formatter: "normal" + # filename: "./sygnal.log" + loggers: + # sygnal.access contains the access logging lines. + # Comment out this section if you don't want to give access logging + # any special treatment. + # + sygnal.access: + propagate: false + handlers: ["stdout"] + level: "INFO" + + # sygnal contains log lines from Sygnal itself. + # You can comment out this section to fall back to the root logger. + # + sygnal: + propagate: false + handlers: ["stderr"] + + root: + # Specify the handler(s) to send log messages to. + handlers: ["stderr"] + level: "INFO" + + disable_existing_loggers: false + + + access: + # Specify whether or not to trust the IP address in the `X-Forwarded-For` + # header. In general, you want to enable this if and only if you are using a + # reverse proxy which is configured to emit it. + # + x_forwarded_for: true + +## HTTP Server (Matrix Push Gateway API) # +# +http: + # Specify a list of interface addresses to bind to. + # + # This example listens on the IPv4 loopback device: + #bind_addresses: ['127.0.0.1'] + # This example listens on all IPv4 interfaces: + #bind_addresses: ['0.0.0.0'] + # This example listens on all IPv4 and IPv6 interfaces: + #bind_addresses: ['0.0.0.0', '::'] + bind_addresses: ['::'] + + # Specify the port number to listen on. + # + port: 6000 + +## Proxying for outgoing connections # +# +# Specify the URL of a proxy to use for outgoing traffic +# (e.g. to Apple & Google) if desired. +# Currently only HTTP proxies with CONNECT capability are supported. +# +# If you do not specify a value, the `HTTPS_PROXY` environment variable will +# be used if present. Otherwise, no proxy will be used. +# +# Default is unspecified. +# +#proxy: 'http://user:secret@prox:8080' + +## Metrics # +# +metrics: + ## Prometheus # + # + prometheus: + # Specify whether or not to enable Prometheus. + # + enabled: false + + # Specify an address for the Prometheus HTTP Server to listen on. + # + address: '0.0.0.0' + + # Specify a port for the Prometheus HTTP Server to listen on. + # + port: 8000 + + ## OpenTracing # + # + opentracing: + # Specify whether or not to enable OpenTracing. + # + enabled: false + + # Specify an implementation of OpenTracing to use. Currently only 'jaeger' + # is supported. + # + implementation: jaeger + + # Specify the service name to be reported to the tracer. + # + service_name: sygnal + + # Specify configuration values to pass to jaeger_client. + # + jaeger: + sampler: + type: 'const' + param: 1 +# local_agent: +# reporting_host: '127.0.0.1' +# reporting_port: + logging: true + + ## Sentry # + # + sentry: + # Specify whether or not to enable Sentry. + # + enabled: false + + # Specify your Sentry DSN if you enable Sentry + # + #dsn: "https://@sentry.example.org/" + +## Pushkins/Apps # +# +# Add a section for every push application here. +# Specify the pushkey for the application and also the type. +# For the type, you may specify a fully-qualified Python classname if desired. +# +#apps: + # This is an example APNs push configuration + # + #com.example.myapp.ios: + # type: apns + # + # # Authentication + # # + # # Two methods of authentication to APNs are currently supported. + # # + # # You can authenticate using a key: + # keyfile: my_key.p8 + # key_id: MY_KEY_ID + # team_id: MY_TEAM_ID + # topic: MY_TOPIC + # + # # Or, a certificate can be used instead: + # certfile: com.example.myApp_prod_APNS.pem + # + # # This is the maximum number of in-flight requests *for this pushkin* + # # before additional notifications will be failed. + # # (This is a robustness measure to prevent one pushkin stacking up with + # # queued requests and saturating the inbound connection queue of a load + # # balancer or reverse proxy). + # # Defaults to 512 if unset. + # # + # #inflight_request_limit: 512 + # + # # Specifies whether to use the production or sandbox APNs server. Note that + # # sandbox tokens should only be used with the sandbox server and vice versa. + # # + # # Valid options are: + # # * production + # # * sandbox + # # + # # The default is 'production'. Uncomment to use the sandbox instance. + # #platform: sandbox + + # This is an example GCM/FCM push configuration. + # + #com.example.myapp.android: + # type: gcm + # api_key: your_api_key_for_gcm + # + # # This is the maximum number of connections to GCM servers at any one time + # # the default is 20. + # #max_connections: 20 + # + # # This is the maximum number of in-flight requests *for this pushkin* + # # before additional notifications will be failed. + # # (This is a robustness measure to prevent one pushkin stacking up with + # # queued requests and saturating the inbound connection queue of a load + # # balancer or reverse proxy). + # # Defaults to 512 if unset. + # # + # #inflight_request_limit: 512 + # + # # This allows you to specify additional options to send to Firebase. + # # + # # Of particular interest, admins who wish to support iOS apps using Firebase + # # probably wish to set content_available, and may need to set mutable_content. + # # (content_available allows your iOS app to be woken up by data messages, + # # and mutable_content allows your notification to be modified by a + # # Notification Service app extension). + # # + # # See https://firebase.google.com/docs/cloud-messaging/http-server-ref + # # for the exhaustive list of valid options. + # # + # # Do not specify `data`, `priority`, `to` or `registration_ids` as they may + # # be overwritten or lead to an invalid request. + # # + # #fcm_options: + # # content_available: true + # # mutable_content: true +apps: {{ matrix_sygnal_apps|to_json }} diff --git a/roles/matrix-sygnal/templates/systemd/matrix-sygnal.service.j2 b/roles/matrix-sygnal/templates/systemd/matrix-sygnal.service.j2 new file mode 100644 index 00000000..019ab40c --- /dev/null +++ b/roles/matrix-sygnal/templates/systemd/matrix-sygnal.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Sygnal +{% for service in matrix_sygnal_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_sygnal_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-sygnal 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-sygnal 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-sygnal \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --cap-drop=ALL \ + --env=SYGNAL_CONF=/config/sygnal.yaml \ + --network={{ matrix_docker_network }} \ + {% if matrix_sygnal_container_http_host_bind_port %} + -p {{ matrix_sygnal_container_http_host_bind_port }}:6000 \ + {% endif %} + --mount type=bind,src={{ matrix_sygnal_config_path }},dst=/config \ + --mount type=bind,src={{ matrix_sygnal_data_path }},dst=/data \ + {% for arg in matrix_sygnal_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_sygnal_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-sygnal 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-sygnal 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-sygnal + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-synapse-admin/defaults/main.yml b/roles/matrix-synapse-admin/defaults/main.yml new file mode 100644 index 00000000..069b6279 --- /dev/null +++ b/roles/matrix-synapse-admin/defaults/main.yml @@ -0,0 +1,32 @@ +# matrix-synapse-admin is a web UI for mananging the Synapse Matrix server +# See: https://github.com/Awesome-Technologies/synapse-admin + +matrix_synapse_admin_enabled: true + +matrix_synapse_admin_container_self_build: false +matrix_synapse_admin_container_self_build_repo: "https://github.com/Awesome-Technologies/synapse-admin.git" + +matrix_synapse_admin_docker_src_files_path: "{{ matrix_base_data_path }}/synapse-admin/docker-src" + +matrix_synapse_admin_version: 0.8.1 +matrix_synapse_admin_docker_image: "{{ matrix_synapse_admin_docker_image_name_prefix }}awesometechnologies/synapse-admin:{{ matrix_synapse_admin_version }}" +matrix_synapse_admin_docker_image_name_prefix: "{{ 'localhost/' if matrix_synapse_admin_container_self_build else matrix_container_global_registry_prefix }}" +matrix_synapse_admin_docker_image_force_pull: "{{ matrix_synapse_admin_docker_image.endswith(':latest') }}" + +# A list of extra arguments to pass to the container +matrix_synapse_admin_container_extra_arguments: [] + +# List of systemd services that matrix-synapse-admin.service depends on +matrix_synapse_admin_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-synapse-admin.service wants +matrix_synapse_admin_systemd_wanted_services_list: [] + +# Controls whether the matrix-synapse-admin container exposes its HTTP port (tcp/80 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8766"), or empty string to not expose. +matrix_synapse_admin_container_http_host_bind_port: '' + +# The path at which Synapse Admin will be exposed on `matrix.DOMAIN` +# (only applies when matrix-nginx-proxy is used). +matrix_synapse_admin_public_endpoint: /synapse-admin diff --git a/roles/matrix-synapse-admin/tasks/init.yml b/roles/matrix-synapse-admin/tasks/init.yml new file mode 100644 index 00000000..9e171015 --- /dev/null +++ b/roles/matrix-synapse-admin/tasks/init.yml @@ -0,0 +1,59 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_synapse_admin_container_self_build and matrix_synapse_admin_enabled" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-synapse-admin.service'] }}" + when: matrix_synapse_admin_enabled|bool + +- block: + - name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append Synapse Admin's reverse-proxying configuration to matrix-nginx-proxy, + but it's pointless since the matrix-nginx-proxy role had already executed. + To fix this, please change the order of roles in your plabook, + so that the matrix-nginx-proxy role would run after the matrix-synapse-admin role. + when: matrix_nginx_proxy_role_executed|default(False)|bool + + - name: Generate Synapse Admin proxying configuration for matrix-nginx-proxy + set_fact: + matrix_synapse_admin_matrix_nginx_proxy_configuration: | + rewrite ^{{ matrix_synapse_admin_public_endpoint }}$ $scheme://$server_name{{ matrix_synapse_admin_public_endpoint }}/ permanent; + + location ~ ^{{ matrix_synapse_admin_public_endpoint }}/(.*) { + {% if matrix_nginx_proxy_enabled|default(False) %} + {# Use the embedded DNS resolver in Docker containers to discover the service #} + resolver 127.0.0.11 valid=5s; + set $backend "matrix-synapse-admin:80"; + proxy_pass http://$backend/$1; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:8766/$1; + {% endif %} + } + + - name: Register Synapse Admin proxying configuration with matrix-nginx-proxy + set_fact: + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks: | + {{ + matrix_nginx_proxy_proxy_matrix_additional_server_configuration_blocks|default([]) + + + [matrix_synapse_admin_matrix_nginx_proxy_configuration] + }} + tags: + - always + when: matrix_synapse_admin_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled the Synapse Admin tool but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `{{ matrix_synapse_admin_public_endpoint }}` + URL endpoint to the matrix-synapse-admin container. + You can expose the container's port using the `matrix_synapse_admin_container_http_host_bind_port` variable. + when: "matrix_synapse_admin_enabled|bool and matrix_nginx_proxy_enabled is not defined" diff --git a/roles/matrix-synapse-admin/tasks/main.yml b/roles/matrix-synapse-admin/tasks/main.yml new file mode 100644 index 00000000..b5cb1689 --- /dev/null +++ b/roles/matrix-synapse-admin/tasks/main.yml @@ -0,0 +1,14 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: run_setup|bool + tags: + - setup-all + - setup-synapse-admin + +- import_tasks: "{{ role_path }}/tasks/setup.yml" + tags: + - setup-all + - setup-synapse-admin diff --git a/roles/matrix-synapse-admin/tasks/setup.yml b/roles/matrix-synapse-admin/tasks/setup.yml new file mode 100644 index 00000000..002ff68d --- /dev/null +++ b/roles/matrix-synapse-admin/tasks/setup.yml @@ -0,0 +1,80 @@ +--- + +# +# Tasks related to setting up matrix-synapse-admin +# + +- name: Ensure matrix-synapse-admin image is pulled + docker_image: + name: "{{ matrix_synapse_admin_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_synapse_admin_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_synapse_admin_docker_image_force_pull }}" + when: "matrix_synapse_admin_enabled|bool and not matrix_synapse_admin_container_self_build|bool" + +- name: Ensure matrix-synapse-admin repository is present when self-building + git: + repo: "{{ matrix_synapse_admin_container_self_build_repo }}" + dest: "{{ matrix_synapse_admin_docker_src_files_path }}" + force: "yes" + register: matrix_synapse_admin_git_pull_results + when: "matrix_synapse_admin_enabled|bool and matrix_synapse_admin_container_self_build|bool" + +- name: Ensure matrix-synapse-admin Docker image is built + docker_image: + name: "{{ matrix_synapse_admin_docker_image }}" + source: build + force_source: "{{ matrix_synapse_admin_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_synapse_admin_git_pull_results.changed }}" + build: + dockerfile: Dockerfile + path: "{{ matrix_synapse_admin_docker_src_files_path }}" + pull: yes + when: "matrix_synapse_admin_enabled|bool and matrix_synapse_admin_container_self_build|bool" + +- name: Ensure matrix-synapse-admin.service installed + template: + src: "{{ role_path }}/templates/systemd/matrix-synapse-admin.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-synapse-admin.service" + mode: 0644 + register: matrix_synapse_admin_systemd_service_result + when: matrix_synapse_admin_enabled|bool + +- name: Ensure systemd reloaded after matrix-synapse-admin.service installation + service: + daemon_reload: yes + when: "matrix_synapse_admin_enabled|bool and matrix_synapse_admin_systemd_service_result.changed" + +# +# Tasks related to getting rid of matrix-synapse-admin (if it was previously enabled) +# + +- name: Check existence of matrix-synapse-admin service + stat: + path: "{{ matrix_systemd_path }}/matrix-synapse-admin.service" + register: matrix_synapse_admin_service_stat + +- name: Ensure matrix-synapse-admin is stopped + service: + name: matrix-synapse-admin + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_synapse_admin_enabled|bool and matrix_synapse_admin_service_stat.stat.exists" + +- name: Ensure matrix-synapse-admin.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-synapse-admin.service" + state: absent + when: "not matrix_synapse_admin_enabled|bool and matrix_synapse_admin_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-synapse-admin.service removal + service: + daemon_reload: yes + when: "not matrix_synapse_admin_enabled|bool and matrix_synapse_admin_service_stat.stat.exists" + +- name: Ensure matrix-synapse-admin Docker image doesn't exist + docker_image: + name: "{{ matrix_synapse_admin_docker_image }}" + state: absent + when: "not matrix_synapse_admin_enabled|bool" diff --git a/roles/matrix-synapse-admin/tasks/validate_config.yml b/roles/matrix-synapse-admin/tasks/validate_config.yml new file mode 100644 index 00000000..e08680e0 --- /dev/null +++ b/roles/matrix-synapse-admin/tasks/validate_config.yml @@ -0,0 +1,10 @@ +--- + +- name: (Deprecation) Catch and report renamed settings + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_synapse_admin_docker_repo', 'new': 'matrix_synapse_admin_container_self_build_repo'} diff --git a/roles/matrix-synapse-admin/templates/systemd/matrix-synapse-admin.service.j2 b/roles/matrix-synapse-admin/templates/systemd/matrix-synapse-admin.service.j2 new file mode 100644 index 00000000..4823d89c --- /dev/null +++ b/roles/matrix-synapse-admin/templates/systemd/matrix-synapse-admin.service.j2 @@ -0,0 +1,42 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=matrix-synapse-admin +{% for service in matrix_synapse_admin_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_synapse_admin_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse-admin 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse-admin 2>/dev/null' + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-synapse-admin \ + --log-driver=none \ + --cap-drop=ALL \ + --cap-add=CHOWN \ + --cap-add=NET_BIND_SERVICE \ + --cap-add=SETUID \ + --cap-add=SETGID \ + --network={{ matrix_docker_network }} \ + {% if matrix_synapse_admin_container_http_host_bind_port %} + -p {{ matrix_synapse_admin_container_http_host_bind_port }}:80 \ + {% endif %} + {% for arg in matrix_synapse_admin_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_synapse_admin_docker_image }} + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse-admin 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse-admin 2>/dev/null' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-synapse-admin + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-synapse/defaults/main.yml b/roles/matrix-synapse/defaults/main.yml new file mode 100644 index 00000000..628ea431 --- /dev/null +++ b/roles/matrix-synapse/defaults/main.yml @@ -0,0 +1,612 @@ +# Synapse is a Matrix homeserver +# See: https://github.com/matrix-org/synapse + +matrix_synapse_enabled: true + +matrix_synapse_container_image_self_build: false +matrix_synapse_container_image_self_build_repo: "https://github.com/matrix-org/synapse.git" + +matrix_synapse_docker_image: "{{ matrix_synapse_docker_image_name_prefix }}matrixdotorg/synapse:{{ matrix_synapse_docker_image_tag }}" +matrix_synapse_docker_image_name_prefix: "{{ 'localhost/' if matrix_synapse_container_image_self_build else matrix_container_global_registry_prefix }}" +# The if statement below may look silly at times (leading to the same version being returned), +# but ARM-compatible container images are only released 1-7 hours after a release, +# so we may often be on different versions for different architectures when new Synapse releases come out. +# +# amd64 gets released first. +# arm32 relies on self-building, so the same version can be built immediately. +# arm64 users need to wait for a prebuilt image to become available. +matrix_synapse_version: v1.39.0 +matrix_synapse_version_arm64: v1.39.0 +matrix_synapse_docker_image_tag: "{{ matrix_synapse_version if matrix_architecture in ['arm32', 'amd64'] else matrix_synapse_version_arm64 }}" +matrix_synapse_docker_image_force_pull: "{{ matrix_synapse_docker_image.endswith(':latest') }}" + +matrix_synapse_base_path: "{{ matrix_base_data_path }}/synapse" +matrix_synapse_docker_src_files_path: "{{ matrix_synapse_base_path }}/docker-src" +matrix_synapse_config_dir_path: "{{ matrix_synapse_base_path }}/config" +matrix_synapse_storage_path: "{{ matrix_synapse_base_path }}/storage" +matrix_synapse_media_store_path: "{{ matrix_synapse_storage_path }}/media-store" +matrix_synapse_ext_path: "{{ matrix_synapse_base_path }}/ext" + +# Controls whether the matrix-synapse container exposes the Client/Server API port (tcp/8008 in the container). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8008"), or empty string to not expose. +matrix_synapse_container_client_api_host_bind_port: '' + +# Controls whether the matrix-synapse container exposes the plain (unencrypted) Server/Server (Federation) API port (tcp/8048 in the container). +# +# Takes effect only if federation is enabled (matrix_synapse_federation_enabled). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:8048"), or empty string to not expose. +matrix_synapse_container_federation_api_plain_host_bind_port: '' + +# Controls whether the matrix-synapse container exposes the tls (encrypted) Server/Server (Federation) API port (tcp/8448 in the container). +# +# Takes effect only if federation is enabled (matrix_synapse_federation_enabled) +# and TLS support is enabled (matrix_synapse_tls_federation_listener_enabled). +# +# Takes an ":" or "" value (e.g. "8448"), or empty string to not expose. +matrix_synapse_container_federation_api_tls_host_bind_port: '' + +# Controls whether the matrix-synapse container exposes the metrics port (tcp/9100 in the container). +# +# Takes effect only if metrics are enabled (matrix_synapse_metrics_enabled). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9100"), or empty string to not expose. +matrix_synapse_container_metrics_api_host_bind_port: '' + +# Controls whether the matrix-synapse container exposes the manhole port (tcp/9000 in the container). +# +# Takes effect only if the manhole is enabled (matrix_synapse_manhole_enabled). +# +# Takes an ":" or "" value (e.g. "127.0.0.1:9100"), or empty string to not expose. +matrix_synapse_container_manhole_api_host_bind_port: '' + +# A list of extra arguments to pass to the container +matrix_synapse_container_extra_arguments: [] + +# List of systemd services that matrix-synapse.service depends on +matrix_synapse_systemd_required_services_list: ['docker.service'] + +# List of systemd services that matrix-synapse.service wants +matrix_synapse_systemd_wanted_services_list: [] + +matrix_synapse_in_container_python_packages_path: "/usr/local/lib/python3.8/site-packages" + +# Specifies which template files to use when configuring Synapse. +# If you'd like to have your own different configuration, feel free to copy and paste +# the original files into your inventory (e.g. in `inventory/host_vars//`) +# and then change the specific host's `vars.yaml` file like this: +# matrix_synapse_template_synapse_homeserver: "{{ playbook_dir }}/inventory/host_vars//homeserver.yaml.j2" +matrix_synapse_template_synapse_homeserver: "{{ role_path }}/templates/synapse/homeserver.yaml.j2" +matrix_synapse_template_synapse_log: "{{ role_path }}/templates/synapse/synapse.log.config.j2" + +matrix_synapse_macaroon_secret_key: "" +matrix_synapse_registration_shared_secret: "{{ matrix_synapse_macaroon_secret_key }}" +matrix_synapse_allow_guest_access: false +matrix_synapse_form_secret: "{{ matrix_synapse_macaroon_secret_key }}" + +matrix_synapse_max_upload_size_mb: 50 + +# The tmpfs at /tmp needs to be large enough to handle multiple concurrent file uploads. +matrix_synapse_tmp_directory_size_mb: "{{ matrix_synapse_max_upload_size_mb * 50 }}" + +# Log levels +# Possible options are defined here https://docs.python.org/3/library/logging.html#logging-levels +# warning: setting log level to DEBUG will make synapse log sensitive information such +# as access tokens. +# +# Increasing verbosity may lead to an excessive amount of log messages being generated, +# some of which may get dropped by systemd-journald on certain distributions (like CentOS 7). +# You can work around it by adding `RateLimitInterval=0` and `RateLimitBurst=0` under `[Storage]` in +# `/etc/systemd/journald.conf` and restarting the logging service (`systemctl restart systemd-journald`). +matrix_synapse_log_level: "WARNING" +matrix_synapse_storage_sql_log_level: "WARNING" +matrix_synapse_root_log_level: "WARNING" + +# Rate limits +matrix_synapse_rc_message: + per_second: 0.2 + burst_count: 10 + +matrix_synapse_rc_registration: + per_second: 0.17 + burst_count: 3 + +matrix_synapse_rc_login: + address: + per_second: 0.17 + burst_count: 3 + account: + per_second: 0.17 + burst_count: 3 + failed_attempts: + per_second: 0.17 + burst_count: 3 + +matrix_synapse_rc_admin_redaction: + per_second: 1 + burst_count: 50 + +matrix_synapse_rc_joins: + local: + per_second: 0.1 + burst_count: 3 + remote: + per_second: 0.01 + burst_count: 3 + +matrix_synapse_rc_federation: + window_size: 1000 + sleep_limit: 10 + sleep_delay: 500 + reject_limit: 50 + concurrent: 3 + +matrix_synapse_federation_rr_transactions_per_room_per_second: 50 + +# Controls whether the TLS federation listener is enabled (tcp/8448). +# Only makes sense if federation is enabled (`matrix_synapse_federation_enabled`). +# Note that federation may potentially be enabled as non-TLS on tcp/8048 as well. +# If you're serving Synapse behind an HTTPS-capable reverse-proxy, +# you can disable the TLS listener (`matrix_synapse_tls_federation_listener_enabled: false`). +matrix_synapse_tls_federation_listener_enabled: true +matrix_synapse_tls_certificate_path: "/data/{{ matrix_server_fqn_matrix }}.tls.crt" +matrix_synapse_tls_private_key_path: "/data/{{ matrix_server_fqn_matrix }}.tls.key" + +# Resource names used by the unsecure HTTP listener. Here only the Client API +# is defined, see the homeserver config for a full list of valid resource +# names. +matrix_synapse_http_listener_resource_names: ["client"] + +# Resources served on Synapse's federation port. +# When disabling federation, we may wish to serve the `openid` resource here, +# so that services like Dimension and ma1sd can work. +matrix_synapse_federation_listener_resource_names: "{{ ['federation'] if matrix_synapse_federation_enabled else (['openid'] if matrix_synapse_federation_port_openid_resource_required else []) }}" + +# Enable this to allow Synapse to report utilization statistics about your server to matrix.org +# (things like number of users, number of messages sent, uptime, load, etc.) +matrix_synapse_report_stats: false + +# Controls whether the Matrix server will track presence status (online, offline, unavailable) for users. +# If users participate in large rooms with many other servers, +# disabling this will decrease server load significantly. +matrix_synapse_presence_enabled: true + +# Controls whether accessing the server's public rooms directory can be done without authentication. +# For private servers, you most likely wish to require authentication, +# unless you know what list of rooms you're publishing to the world and explicitly want to do it. +matrix_synapse_allow_public_rooms_without_auth: false + +# Controls whether remote servers can fetch this server's public rooms directory via federation. +# For private servers, you most likely wish to forbid it. +matrix_synapse_allow_public_rooms_over_federation: false + +# Whether to require authentication to retrieve profile data (avatars, +# display names) of other users through the client API. Defaults to +# 'false'. Note that profile data is also available via the federation +# API, so this setting is of limited value if federation is enabled on +# the server. +matrix_synapse_require_auth_for_profile_requests: false + +# Set to true to require a user to share a room with another user in order +# to retrieve their profile information. Only checked on Client-Server +# requests. Profile requests from other servers should be checked by the +# requesting server. Defaults to 'false'. +matrix_synapse_limit_profile_requests_to_users_who_share_rooms: false + +# Set to false to prevent a user's profile data from being retrieved and +# displayed in a room until they have joined it. By default, a user's +# profile data is included in an invite event, regardless of the values +# of the above two settings, and whether or not the users share a server. +# Defaults to 'true'. +matrix_synapse_include_profile_data_on_invite: true + +# Controls whether people with access to the homeserver can register by themselves. +matrix_synapse_enable_registration: false + +# reCAPTCHA API for validating registration attempts +matrix_synapse_enable_registration_captcha: false +matrix_synapse_recaptcha_public_key: '' +matrix_synapse_recaptcha_private_key: '' + +# Allows non-server-admin users to create groups on this server +matrix_synapse_enable_group_creation: false + +# A list of 3PID types which users must supply when registering (possible values: email, msisdn). +matrix_synapse_registrations_require_3pid: [] + +# A list of patterns 3pids must match in order to permit registration, e.g.: +# - medium: email +# pattern: '.*@example\.com' +# - medium: msisdn +# pattern: '\+44' +matrix_synapse_allowed_local_3pids: [] + +# The server to use for email threepid validation. When empty, Synapse does it by itself. +# Otherwise, this should be pointed to an identity server. +matrix_synapse_account_threepid_delegates_email: '' + +# The server to use for phone number threepid validation. When empty, validation cannot happen, as Synapse doesn't support it. +# To make it work, this should be pointed to an identity server. +matrix_synapse_account_threepid_delegates_msisdn: '' + +# Users who register on this homeserver will automatically be joined to these rooms. +# Rooms are to be specified using addresses (e.g. `#address:example.com`) +matrix_synapse_auto_join_rooms: [] + +# Controls whether auto-join rooms (`matrix_synapse_auto_join_rooms`) are to be created +# automatically if they don't already exist. +matrix_synapse_autocreate_auto_join_rooms: true + +# Controls password-peppering for Synapse. Not to be changed after initial setup. +matrix_synapse_password_config_pepper: "" + +# Controls if Synapse allows people to authenticate against its local database. +# It may be useful to disable this if you've configured additional password providers +# and only wish authentication to happen through them. +matrix_synapse_password_config_localdb_enabled: true + +# Controls the number of events that Synapse caches in memory. +matrix_synapse_event_cache_size: "100K" + +# Controls cache sizes for Synapse. +# Raise this to increase cache sizes or lower it to potentially lower memory use. +# To learn more, see: +# - https://github.com/matrix-org/synapse#help-synapse-eats-all-my-ram +# - https://github.com/matrix-org/synapse/issues/3939 +matrix_synapse_caches_global_factor: 0.5 + +# Controls whether Synapse will federate at all. +# Disable this to completely isolate your server from the rest of the Matrix network. +# +# Disabling this still keeps the federation port exposed, because it may be used for other services (`openid`). +# +# Also see: +# - `matrix_synapse_tls_federation_listener_enabled` if you wish to keep federation enabled, +# but want to stop the TLS listener (port 8448). +# - `matrix_synapse_federation_port_enabled` to avoid exposing the federation ports +matrix_synapse_federation_enabled: true + +# Controls whether the federation ports are used at all. +# One may wish to disable federation (`matrix_synapse_federation_enabled: true`), +# but still run other resources (like `openid`) on the federation port +# by enabling them in `matrix_synapse_federation_listener_resource_names`. +matrix_synapse_federation_port_enabled: "{{ matrix_synapse_federation_enabled or matrix_synapse_federation_port_openid_resource_required }}" + +# Controls whether an `openid` listener is to be enabled. Useful when disabling federation, +# but needing the `openid` APIs for Dimension or an identity server like ma1sd. +matrix_synapse_federation_port_openid_resource_required: false + +# A list of domain names that are allowed to federate with the given Synapse server. +# An empty list value (`[]`) will also effectively stop federation, but if that's the desired +# result, it's better to accomplish it by changing `matrix_synapse_federation_enabled`. +matrix_synapse_federation_domain_whitelist: ~ + +# A list of additional "volumes" to mount in the container. +# This list gets populated dynamically based on Synapse extensions that have been enabled. +# Contains definition objects like this: `{"src": "/outside", "dst": "/inside", "options": "rw|ro|slave|.."} +# +# Note: internally, this uses the `-v` flag for mounting the specified volumes. +# It's better (safer) to use the `--mount` flag for mounting volumes. +# To use `--mount`, specify it in `matrix_synapse_container_extra_arguments`. +# Example: `matrix_synapse_container_extra_arguments: ['--mount type=bind,src=/outside,dst=/inside,ro'] +matrix_synapse_container_additional_volumes: [] + +# A list of additional loggers to register in synapse.log.config. +# This list gets populated dynamically based on Synapse extensions that have been enabled. +# Contains definition objects like this: `{"name": "..", "level": "DEBUG"} +matrix_synapse_additional_loggers: [] + +# A list of appservice config files (in-container filesystem paths). +# This list gets populated dynamically based on Synapse extensions that have been enabled. +# You may wish to use this together with `matrix_synapse_container_additional_volumes` or `matrix_synapse_container_extra_arguments`. +matrix_synapse_app_service_config_files: [] + +# This is set dynamically during execution depending on whether +# any password providers have been enabled or not. +matrix_synapse_password_providers_enabled: false + +# Whether clients can request to include message content in push notifications +# sent through third party servers. Setting this to false requires mobile clients +# to load message content directly from the homeserver. +matrix_synapse_push_include_content: true + +# If url previews should be generated. This will cause a request from Synapse to +# URLs shared by users. +matrix_synapse_url_preview_enabled: true + +# Enable exposure of metrics to Prometheus +# See https://github.com/matrix-org/synapse/blob/master/docs/metrics-howto.md +matrix_synapse_metrics_enabled: false +matrix_synapse_metrics_port: 9100 + +# Enable the Synapse manhole +# See https://github.com/matrix-org/synapse/blob/master/docs/manhole.md +matrix_synapse_manhole_enabled: false + +# Enable support for Synapse workers +matrix_synapse_workers_enabled: false + +# Specifies worker configuration that should be used when workers are enabled. +# +# The posible values (as seen in `matrix_synapse_workers_presets`) are: +# - "little-federation-helper" - a very minimal worker configuration to improve federation performance +# - "one-of-each" - one worker of each supported type +# +# You can override `matrix_synapse_workers_presets` to define your own presets, which is ill-advised, because it's fragile. +# To use a more custom configuration, start with one of these presets as a base and configure `matrix_synapse_workers_*_count` variables manually, to suit your liking. +matrix_synapse_workers_preset: one-of-each + +matrix_synapse_workers_presets: + little-federation-helper: + generic_workers_count: 0 + pusher_workers_count: 0 + appservice_workers_count: 0 + federation_sender_workers_count: 1 + media_repository_workers_count: 0 + user_dir_workers_count: 0 + frontend_proxy_workers_count: 0 + one-of-each: + generic_workers_count: 1 + pusher_workers_count: 1 + appservice_workers_count: 1 + federation_sender_workers_count: 1 + media_repository_workers_count: 1 + # Disabled until https://github.com/matrix-org/synapse/issues/8787 is resolved. + user_dir_workers_count: 0 + frontend_proxy_workers_count: 1 + +# Controls whether the matrix-synapse container exposes the various worker ports +# (see `port` and `metrics_port` in `matrix_synapse_workers_enabled_list`) outside of the container. +# +# Takes an "" value (e.g. "127.0.0.1", "0.0.0.0", etc), or empty string to not expose. +# It takes "*" to signify "bind on all interfaces" ("0.0.0.0" is IPv4-only). +matrix_synapse_workers_container_host_bind_address: '' + +matrix_synapse_workers_generic_workers_count: "{{ matrix_synapse_workers_presets[matrix_synapse_workers_preset]['generic_workers_count'] }}" +matrix_synapse_workers_generic_workers_port_range_start: 18111 +matrix_synapse_workers_generic_workers_metrics_range_start: 19111 + +# matrix_synapse_workers_pusher_workers_count can only be 0 or 1 for now. +# More instances are not supported due to a playbook limitation having to do with keeping `pusher_instances` in `homeserver.yaml` updated. +# See https://github.com/matrix-org/synapse/commit/ddfdf945064925eba761ae3748e38f3a1c73c328 +matrix_synapse_workers_pusher_workers_count: "{{ matrix_synapse_workers_presets[matrix_synapse_workers_preset]['pusher_workers_count'] }}" +matrix_synapse_workers_pusher_workers_metrics_range_start: 19200 + +# matrix_synapse_workers_appservice_workers_count can only be 0 or 1. More instances are not supported. +matrix_synapse_workers_appservice_workers_count: "{{ matrix_synapse_workers_presets[matrix_synapse_workers_preset]['appservice_workers_count'] }}" +matrix_synapse_workers_appservice_workers_metrics_range_start: 19300 + +# matrix_synapse_workers_federation_sender_workers_count can only be 0 or 1 for now. +# More instances are not supported due to a playbook limitation having to do with keeping `federation_sender_instances` in `homeserver.yaml` updated. +# See https://github.com/matrix-org/synapse/blob/master/docs/workers.md#synapseappfederation_sender +matrix_synapse_workers_federation_sender_workers_count: "{{ matrix_synapse_workers_presets[matrix_synapse_workers_preset]['federation_sender_workers_count'] }}" +matrix_synapse_workers_federation_sender_workers_metrics_range_start: 19400 + +matrix_synapse_workers_media_repository_workers_count: "{{ matrix_synapse_workers_presets[matrix_synapse_workers_preset]['media_repository_workers_count'] }}" +matrix_synapse_workers_media_repository_workers_port_range_start: 18551 +matrix_synapse_workers_media_repository_workers_metrics_range_start: 19551 + +# Disabled until https://github.com/matrix-org/synapse/issues/8787 is resolved. +matrix_synapse_workers_user_dir_workers_count: "{{ matrix_synapse_workers_presets[matrix_synapse_workers_preset]['user_dir_workers_count'] }}" +matrix_synapse_workers_user_dir_workers_port_range_start: 18661 +matrix_synapse_workers_user_dir_workers_metrics_range_start: 19661 + +matrix_synapse_workers_frontend_proxy_workers_count: "{{ matrix_synapse_workers_presets[matrix_synapse_workers_preset]['frontend_proxy_workers_count'] }}" +matrix_synapse_workers_frontend_proxy_workers_port_range_start: 18771 +matrix_synapse_workers_frontend_proxy_workers_metrics_range_start: 19771 + +# Default list of workers to spawn. +# +# Unless you populate this manually, this list is dynamically generated +# based on other variables above: +# - `matrix_synapse_workers_*_workers_count` +# - `matrix_synapse_workers_*_workers_port_range_start` +# - `matrix_synapse_workers_*_workers_port_metrics_range_start` +# +# We advise that you use those variables and let this list be populated dynamically. +# Doing that is simpler and also protects you from shooting yourself in the foot, +# as certain workers can only be spawned just once. +# +# Each worker instance in the list defines the following fields: +# - `type` - the type of worker (`generic_worker`, etc.) +# - `instanceId` - a string that identifies the worker. The combination of (`type` + `instanceId`) represents the name of the worker and must be unique. +# - `port` - an HTTP port where the worker listens for requests (can be `0` for workers that don't do HTTP request processing) +# - `metrics_port` - an HTTP port where the worker exports Prometheus metrics +# +# Example of what this needs to look like, if you're defining it manually: +# matrix_synapse_workers_enabled_list: +# - { type: generic_worker, instanceId: '18111', port: 18111, metrics_port: 19111 } +# - { type: generic_worker, instanceId: '18112', port: 18112, metrics_port: 19112 } +# - { type: generic_worker, instanceId: '18113', port: 18113, metrics_port: 19113 } +# - { type: generic_worker, instanceId: '18114', port: 18114, metrics_port: 19114 } +# - { type: generic_worker, instanceId: '18115', port: 18115, metrics_port: 19115 } +# - { type: generic_worker, instanceId: '18116', port: 18116, metrics_port: 19116 } +# - { type: pusher, instanceId: '0', port: 0, metrics_port: 19200 } +# - { type: appservice, instanceId: '0', port: 0, metrics_port: 19300 } +# - { type: federation_sender, instanceId: '0', port: 0, metrics_port: 19400 } +# - { type: media_repository, instanceId: '18551', port: 18551, metrics_port: 19551 } +matrix_synapse_workers_enabled_list: [] + +# Redis information +matrix_synapse_redis_enabled: false +matrix_synapse_redis_host: "" +matrix_synapse_redis_port: 6379 +matrix_synapse_redis_password: "" + +# Controls whether Synapse starts a replication listener necessary for workers. +# +# If Redis is available, we prefer to use that, instead of talking over Synapse's custom replication protocol. +# +# matrix_synapse_replication_listener_enabled: "{{ matrix_synapse_workers_enabled and not matrix_redis_enabled }}" +# We force-enable this listener for now until we debug why communication via Redis fails. +matrix_synapse_replication_listener_enabled: true + +# Port used for communication between main synapse process and workers. +# Only gets used if `matrix_synapse_replication_listener_enabled: true` +matrix_synapse_replication_http_port: 9093 + +# Send ERROR logs to sentry.io for easier tracking +# To set this up: go to sentry.io, create a python project, and set +# matrix_synapse_sentry_dsn to the URL it gives you. +# See https://github.com/matrix-org/synapse/issues/4632 for important privacy concerns +matrix_synapse_sentry_dsn: "" + +# Postgres database information +matrix_synapse_database_host: "matrix-postgres" +matrix_synapse_database_port: 5432 +matrix_synapse_database_user: "synapse" +matrix_synapse_database_password: "" +matrix_synapse_database_database: "synapse" + +matrix_synapse_turn_uris: [] +matrix_synapse_turn_shared_secret: "" +matrix_synapse_turn_allow_guests: False + +matrix_synapse_email_enabled: false +matrix_synapse_email_smtp_host: "" +matrix_synapse_email_smtp_port: 587 +matrix_synapse_email_smtp_require_transport_security: false +matrix_synapse_email_notif_from: "Matrix " +matrix_synapse_email_client_base_url: "https://{{ matrix_server_fqn_element }}" + + +# Enable this to activate the REST auth password provider module. +# See: https://github.com/ma1uta/matrix-synapse-rest-password-provider +matrix_synapse_ext_password_provider_rest_auth_enabled: false +matrix_synapse_ext_password_provider_rest_auth_download_url: "https://raw.githubusercontent.com/ma1uta/matrix-synapse-rest-password-provider/ed377fb70513c2e51b42055eb364195af1ccaf33/rest_auth_provider.py" +matrix_synapse_ext_password_provider_rest_auth_endpoint: "" +matrix_synapse_ext_password_provider_rest_auth_registration_enforce_lowercase: false +matrix_synapse_ext_password_provider_rest_auth_registration_profile_name_autofill: true +matrix_synapse_ext_password_provider_rest_auth_login_profile_name_autofill: false + +# Enable this to activate the Shared Secret Auth password provider module. +# See: https://github.com/devture/matrix-synapse-shared-secret-auth +matrix_synapse_ext_password_provider_shared_secret_auth_enabled: false +matrix_synapse_ext_password_provider_shared_secret_auth_download_url: "https://raw.githubusercontent.com/devture/matrix-synapse-shared-secret-auth/1.0.2/shared_secret_authenticator.py" +matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret: "" + +# Enable this to activate LDAP password provider +matrix_synapse_ext_password_provider_ldap_enabled: false +matrix_synapse_ext_password_provider_ldap_uri: "ldap://ldap.mydomain.tld:389" +matrix_synapse_ext_password_provider_ldap_start_tls: true +matrix_synapse_ext_password_provider_ldap_base: "" +matrix_synapse_ext_password_provider_ldap_attributes_uid: "uid" +matrix_synapse_ext_password_provider_ldap_attributes_mail: "mail" +matrix_synapse_ext_password_provider_ldap_attributes_name: "cn" +matrix_synapse_ext_password_provider_ldap_bind_dn: "" +matrix_synapse_ext_password_provider_ldap_bind_password: "" +matrix_synapse_ext_password_provider_ldap_filter: "" +matrix_synapse_ext_password_provider_ldap_active_directory: false +matrix_synapse_ext_password_provider_ldap_default_domain: "" + +# Enable this to activate the Synapse Antispam spam-checker module. +# See: https://github.com/t2bot/synapse-simple-antispam +matrix_synapse_ext_spam_checker_synapse_simple_antispam_enabled: false +matrix_synapse_ext_spam_checker_synapse_simple_antispam_git_repository_url: "https://github.com/t2bot/synapse-simple-antispam" +matrix_synapse_ext_spam_checker_synapse_simple_antispam_git_version: "923ca5c85b08f157181721abbae50dd89c31e4b5" +matrix_synapse_ext_spam_checker_synapse_simple_antispam_config_blocked_homeservers: [] + +# Enable this to activate the Mjolnir Antispam spam-checker module. +# See: https://github.com/matrix-org/mjolnir#synapse-module +matrix_synapse_ext_spam_checker_mjolnir_antispam_enabled: false +matrix_synapse_ext_spam_checker_mjolnir_antispam_git_repository_url: "https://github.com/matrix-org/mjolnir" +matrix_synapse_ext_spam_checker_mjolnir_antispam_git_version: "70f353fbbad0af469b1001080dea194d512b2815" +matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_invites: true +# Flag messages sent by servers/users in the ban lists as spam. Currently +# this means that spammy messages will appear as empty to users. Default +# false. +matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_messages: false +# Remove users from the user directory search by filtering matrix IDs and +# display names by the entries in the user ban list. Default false. +matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_usernames: false +# The room IDs of the ban lists to honour. Unlike other parts of Mjolnir, +# this list cannot be room aliases or permalinks. This server is expected +# to already be joined to the room - Mjolnir will not automatically join +# these rooms. +# ["!roomid:example.org"] +matrix_synapse_ext_spam_checker_mjolnir_antispam_config_ban_lists: [] + + +matrix_s3_media_store_enabled: false +matrix_s3_media_store_custom_endpoint_enabled: false +matrix_s3_goofys_docker_image: "ewoutp/goofys:latest" +matrix_s3_goofys_docker_image_force_pull: "{{ matrix_s3_goofys_docker_image.endswith(':latest') }}" +matrix_s3_media_store_custom_endpoint: "your-custom-endpoint" +matrix_s3_media_store_bucket_name: "your-bucket-name" +matrix_s3_media_store_aws_access_key: "your-aws-access-key" +matrix_s3_media_store_aws_secret_key: "your-aws-secret-key" +matrix_s3_media_store_region: "eu-central-1" +matrix_s3_media_store_path: "{{ matrix_synapse_media_store_path }}" + +# Controls whether the self-check feature should validate SSL certificates. +matrix_synapse_self_check_validate_certificates: true + +# Controls whether searching the public room list is enabled. +matrix_synapse_enable_room_list_search: true + +# Controls who's allowed to create aliases on this server. +matrix_synapse_alias_creation_rules: + - user_id: "*" + alias: "*" + room_id: "*" + action: allow + +# Controls who can publish and which rooms can be published in the public room list. +matrix_synapse_room_list_publication_rules: + - user_id: "*" + alias: "*" + room_id: "*" + action: allow + +matrix_synapse_default_room_version: "6" + +# Controls the Synapse `spam_checker` setting. +# +# If a spam-checker extension is enabled, this variable's value is set automatically by the playbook during runtime. +# If not, you can also control its value manually. +matrix_synapse_spam_checker: [] + +matrix_synapse_trusted_key_servers: + - server_name: "matrix.org" + +matrix_synapse_redaction_retention_period: 7d + +matrix_synapse_user_ips_max_age: 28d + + +matrix_synapse_rust_synapse_compress_state_docker_image: "devture/rust-synapse-compress-state:v0.1.0" +matrix_synapse_rust_synapse_compress_state_docker_image_force_pull: "{{ matrix_synapse_rust_synapse_compress_state_docker_image.endswith(':latest') }}" + +matrix_synapse_rust_synapse_compress_state_base_path: "{{ matrix_base_data_path }}/rust-synapse-compress-state" + + +# Default Synapse 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_synapse_configuration_extension_yaml`) +# or completely replace this variable with your own template. +matrix_synapse_configuration_yaml: "{{ lookup('template', 'templates/synapse/homeserver.yaml.j2') }}" + +matrix_synapse_configuration_extension_yaml: | + # Your custom YAML configuration for Synapse goes here. + # This configuration extends the default starting configuration (`matrix_synapse_configuration_yaml`). + # + # You can override individual variables from the default configuration, or introduce new ones. + # + # If you need something more special, you can take full control by + # completely redefining `matrix_synapse_configuration_yaml`. + # + # Example configuration extension follows: + # + # server_notices: + # system_mxid_localpart: notices + # system_mxid_display_name: "Server Notices" + # system_mxid_avatar_url: "mxc://server.com/oumMVlgDnLYFaPVkExemNVVZ" + # room_name: "Server Notices" + +matrix_synapse_configuration_extension: "{{ matrix_synapse_configuration_extension_yaml|from_yaml if matrix_synapse_configuration_extension_yaml|from_yaml is mapping else {} }}" + +# Holds the final Synapse configuration (a combination of the default and its extension). +# You most likely don't need to touch this variable. Instead, see `matrix_synapse_configuration_yaml`. +matrix_synapse_configuration: "{{ matrix_synapse_configuration_yaml|from_yaml|combine(matrix_synapse_configuration_extension, recursive=True) }}" diff --git a/roles/matrix-synapse/files/workers-doc-to-yaml.awk b/roles/matrix-synapse/files/workers-doc-to-yaml.awk new file mode 100755 index 00000000..d9295e32 --- /dev/null +++ b/roles/matrix-synapse/files/workers-doc-to-yaml.awk @@ -0,0 +1,146 @@ +#!/usr/bin/awk +# Hackish approach to get a machine-readable list of current matrix +# synapse REST API endpoints from the official documentation at +# https://github.com/matrix-org/synapse/raw/master/docs/workers.md +# +# invoke in shell with: +# URL=https://github.com/matrix-org/synapse/raw/master/docs/workers.md +# curl -L ${URL} | awk -f workers-doc-to-yaml.awk - + +function worker_stanza_append(string) { + worker_stanza = worker_stanza string +} + +function line_is_endpoint_url(line) { + # probably API endpoint if it starts with white-space and ^ or / + return (line ~ /^ +[\^\/].*\//) +} + +# Put YAML marker at beginning of file. +BEGIN { + print "---" + endpoint_conditional_comment = " # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually\n" +} + +# Enable further processing after the introductory text. +# Read each synapse worker section as record and its lines as fields. +/Available worker applications/ { + enable_parsing = 1 + # set record separator to markdown section header + RS = "\n### " + # set field separator to newline + FS = "\n" +} + +# Once parsing is active, this will process each section as record. +enable_parsing { + # Each worker section starts with a synapse.app.X headline + if ($1 ~ /synapse\.app\./) { + + # get rid of the backticks and extract worker type from headline + gsub("`", "", $1) + gsub("synapse.app.", "", $1) + worker_type = $1 + + # initialize empty worker stanza + worker_stanza = "" + + # track if any endpoints are mentioned in a specific section + worker_has_urls = 0 + + # some endpoint descriptions contain flag terms + endpoints_seem_conditional = 0 + + # also, collect a list of available workers + workers = (workers ? workers "\n" : "") " - " worker_type + + # loop through the lines (2 - number of fields in record) + for (i = 2; i < NF + 1; i++) { + # copy line for gsub replacements + line = $i + + # end all lines but the last with a linefeed + linefeed = (i < NF - 1) ? "\n" : "" + + # line starts with white-space and a hash: endpoint block headline + if (line ~ /^ +#/) { + + # copy to output verbatim, normalizing white-space + gsub(/^ +/, "", line) + worker_stanza_append(" " line linefeed) + + } else if (line_is_endpoint_url(line)) { + + # mark section for special output formatting + worker_has_urls = 1 + + # remove leading white-space + gsub(/^ +/, "", line) + api_endpoint_regex = line + + # FIXME: https://github.com/matrix-org/synapse/issues/new + # munge inconsistent media_repository endpoint notation + if (api_endpoint_regex == "/_matrix/media/") { + api_endpoint_regex = "^" line + } + + # FIXME: https://github.com/matrix-org/synapse/issues/7530 + # https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/456#issuecomment-719015911 + if (api_endpoint_regex == "^/_matrix/client/(r0|unstable)/auth/.*/fallback/web$") { + worker_stanza_append(" # FIXME: possible bug with SSO and multiple generic workers\n") + worker_stanza_append(" # see https://github.com/matrix-org/synapse/issues/7530\n") + worker_stanza_append(" # " api_endpoint_regex linefeed) + continue + } + + # disable endpoints which specify complications + if (endpoints_seem_conditional) { + # only add notice if previous line didn't match + if (!line_is_endpoint_url($(i - 1))) { + worker_stanza_append(endpoint_conditional_comment) + } + worker_stanza_append(" # " api_endpoint_regex linefeed) + } else { + # output endpoint regex + worker_stanza_append(" - " api_endpoint_regex linefeed) + } + + # white-space only line? + } else if (line ~ /^ *$/) { + + if (i > 3 && i < NF) { + # print white-space lines unless 1st or last line in section + worker_stanza_append(line linefeed) + } + + # nothing of the above: the line is regular documentation text + } else { + + # include this text line as comment + worker_stanza_append(" # " line linefeed) + + # and take note of words hinting at additional conditions to be met + if (line ~ /(^| )[Ii]f |(^| )[Ff]or /) { + endpoints_seem_conditional = 1 + } + } + } + + if (worker_has_urls) { + print "\nmatrix_synapse_workers_" worker_type "_endpoints:" + print worker_stanza + } else { + # include workers without endpoints as well for reference + print "\n# " worker_type " worker (no API endpoints) [" + print worker_stanza + print "# ]" + } + } +} + +END { + print "\nmatrix_synapse_workers_avail_list:" + print workers | "sort" +} + +# vim: tabstop=4 shiftwidth=4 expandtab autoindent diff --git a/roles/matrix-synapse/files/workers-doc-to-yaml.sh b/roles/matrix-synapse/files/workers-doc-to-yaml.sh new file mode 100755 index 00000000..5981523b --- /dev/null +++ b/roles/matrix-synapse/files/workers-doc-to-yaml.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Fetch the synapse worker documentation and extract endpoint URLs +# matrix-org/synapse master branch points to current stable release + +URL=https://github.com/matrix-org/synapse/raw/master/docs/workers.md +curl -L ${URL} | awk -f workers-doc-to-yaml.awk > ../vars/workers.yml diff --git a/roles/matrix-synapse/tasks/ext/ldap-auth/setup.yml b/roles/matrix-synapse/tasks/ext/ldap-auth/setup.yml new file mode 100644 index 00000000..e760626d --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/ldap-auth/setup.yml @@ -0,0 +1,8 @@ +- set_fact: + matrix_synapse_password_providers_enabled: true + + matrix_synapse_additional_loggers: > + {{ matrix_synapse_additional_loggers }} + + + {{ [{'name': 'ldap_auth_provider', 'level': 'INFO'}] }} + when: matrix_synapse_ext_password_provider_ldap_enabled|bool diff --git a/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup.yml b/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup.yml new file mode 100644 index 00000000..6c45f469 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup.yml @@ -0,0 +1,7 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/ext/mjolnir-antispam/setup_install.yml" + when: matrix_synapse_ext_spam_checker_mjolnir_antispam_enabled|bool + +- import_tasks: "{{ role_path }}/tasks/ext/mjolnir-antispam/setup_uninstall.yml" + when: "not matrix_synapse_ext_spam_checker_mjolnir_antispam_enabled|bool" diff --git a/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup_install.yml b/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup_install.yml new file mode 100644 index 00000000..a416e42b --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup_install.yml @@ -0,0 +1,52 @@ +--- + +- name: Ensure git installed (RedHat) + yum: + name: + - git + state: present + update_cache: no + when: "ansible_os_family == 'RedHat'" + +- name: Ensure git installed (Debian) + apt: + name: + - git + state: present + update_cache: no + when: "ansible_os_family == 'Debian'" + +- name: Ensure git installed (Archlinux) + pacman: + name: + - git + state: present + update_cache: no + when: "ansible_distribution == 'Archlinux'" + +- name: Clone mjolnir-antispam git repository + git: + repo: "{{ matrix_synapse_ext_spam_checker_mjolnir_antispam_git_repository_url }}" + version: "{{ matrix_synapse_ext_spam_checker_mjolnir_antispam_git_version }}" + dest: "{{ matrix_synapse_ext_path }}/mjolnir" + become: true + become_user: "{{ matrix_user_username }}" + +- set_fact: + matrix_synapse_spam_checker: > + {{ matrix_synapse_spam_checker }} + + + [{ + "module": "mjolnir.AntiSpam", + "config": { + "block_invites": {{ matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_invites }}, + "block_messages": {{ matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_messages }}, + "block_usernames": {{ matrix_synapse_ext_spam_checker_mjolnir_antispam_config_block_usernames }}, + "ban_lists": {{ matrix_synapse_ext_spam_checker_mjolnir_antispam_config_ban_lists }} + } + }] + + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_synapse_ext_path }}/mjolnir/synapse_antispam/mjolnir,dst={{ matrix_synapse_in_container_python_packages_path }}/mjolnir,ro"] diff --git a/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup_uninstall.yml b/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup_uninstall.yml new file mode 100644 index 00000000..f8439a87 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/mjolnir-antispam/setup_uninstall.yml @@ -0,0 +1,6 @@ +--- + +- name: Ensure mjolnir-antispam doesn't exist + file: + path: "{{ matrix_synapse_ext_path }}/mjolnir" + state: absent diff --git a/roles/matrix-synapse/tasks/ext/rest-auth/setup.yml b/roles/matrix-synapse/tasks/ext/rest-auth/setup.yml new file mode 100644 index 00000000..0270784a --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/rest-auth/setup.yml @@ -0,0 +1,7 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/ext/rest-auth/setup_install.yml" + when: matrix_synapse_ext_password_provider_rest_auth_enabled|bool + +- import_tasks: "{{ role_path }}/tasks/ext/rest-auth/setup_uninstall.yml" + when: "not matrix_synapse_ext_password_provider_rest_auth_enabled|bool" diff --git a/roles/matrix-synapse/tasks/ext/rest-auth/setup_install.yml b/roles/matrix-synapse/tasks/ext/rest-auth/setup_install.yml new file mode 100644 index 00000000..634b1ca5 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/rest-auth/setup_install.yml @@ -0,0 +1,28 @@ +--- + +- name: Fail if REST Auth endpoint not configured + fail: + msg: "You have enabled the REST Auth password provider, but have not configured its endpoint in the `matrix_synapse_ext_password_provider_rest_auth_endpoint` variable. Consult the documentation." + when: "matrix_synapse_ext_password_provider_rest_auth_endpoint == ''" + +- name: Download matrix-synapse-rest-auth + get_url: + url: "{{ matrix_synapse_ext_password_provider_rest_auth_download_url }}" + dest: "{{ matrix_synapse_ext_path }}/rest_auth_provider.py" + force: true + mode: 0440 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- set_fact: + matrix_synapse_password_providers_enabled: true + + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_synapse_ext_path }}/rest_auth_provider.py,dst={{ matrix_synapse_in_container_python_packages_path }}/rest_auth_provider.py,ro"] + + matrix_synapse_additional_loggers: > + {{ matrix_synapse_additional_loggers }} + + + {{ [{'name': 'rest_auth_provider', 'level': 'INFO'}] }} diff --git a/roles/matrix-synapse/tasks/ext/rest-auth/setup_uninstall.yml b/roles/matrix-synapse/tasks/ext/rest-auth/setup_uninstall.yml new file mode 100644 index 00000000..be8ad600 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/rest-auth/setup_uninstall.yml @@ -0,0 +1,6 @@ +--- + +- name: Ensure matrix-synapse-rest-auth doesn't exist + file: + path: "{{ matrix_synapse_ext_path }}/rest_auth_provider.py" + state: absent diff --git a/roles/matrix-synapse/tasks/ext/setup.yml b/roles/matrix-synapse/tasks/ext/setup.yml new file mode 100644 index 00000000..31637fa9 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/setup.yml @@ -0,0 +1,11 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/ext/rest-auth/setup.yml" + +- import_tasks: "{{ role_path }}/tasks/ext/shared-secret-auth/setup.yml" + +- import_tasks: "{{ role_path }}/tasks/ext/ldap-auth/setup.yml" + +- import_tasks: "{{ role_path }}/tasks/ext/synapse-simple-antispam/setup.yml" + +- import_tasks: "{{ role_path }}/tasks/ext/mjolnir-antispam/setup.yml" diff --git a/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup.yml b/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup.yml new file mode 100644 index 00000000..ed8d0197 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup.yml @@ -0,0 +1,7 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/ext/shared-secret-auth/setup_install.yml" + when: matrix_synapse_ext_password_provider_shared_secret_auth_enabled|bool + +- import_tasks: "{{ role_path }}/tasks/ext/shared-secret-auth/setup_uninstall.yml" + when: "not matrix_synapse_ext_password_provider_shared_secret_auth_enabled|bool" diff --git a/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml b/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml new file mode 100644 index 00000000..af92041d --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml @@ -0,0 +1,28 @@ +--- + +- name: Fail if Shared Secret Auth secret not set + fail: + msg: "Shared Secret Auth is enabled, but no secret has been set in matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret" + when: "matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret == ''" + +- name: Download matrix-synapse-shared-secret-auth + get_url: + url: "{{ matrix_synapse_ext_password_provider_shared_secret_auth_download_url }}" + dest: "{{ matrix_synapse_ext_path }}/shared_secret_authenticator.py" + force: true + mode: 0440 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- set_fact: + matrix_synapse_password_providers_enabled: true + + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_synapse_ext_path }}/shared_secret_authenticator.py,dst={{ matrix_synapse_in_container_python_packages_path }}/shared_secret_authenticator.py,ro"] + + matrix_synapse_additional_loggers: > + {{ matrix_synapse_additional_loggers }} + + + {{ [{'name': 'shared_secret_authenticator', 'level': 'INFO'}] }} diff --git a/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_uninstall.yml b/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_uninstall.yml new file mode 100644 index 00000000..e564909e --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/shared-secret-auth/setup_uninstall.yml @@ -0,0 +1,6 @@ +--- + +- name: Ensure matrix-synapse-shared-secret-auth doesn't exist + file: + path: "{{ matrix_synapse_ext_path }}/shared_secret_authenticator.py" + state: absent diff --git a/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup.yml b/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup.yml new file mode 100644 index 00000000..efd4a027 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup.yml @@ -0,0 +1,7 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/ext/synapse-simple-antispam/setup_install.yml" + when: matrix_synapse_ext_spam_checker_synapse_simple_antispam_enabled|bool + +- import_tasks: "{{ role_path }}/tasks/ext/synapse-simple-antispam/setup_uninstall.yml" + when: "not matrix_synapse_ext_spam_checker_synapse_simple_antispam_enabled|bool" diff --git a/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_install.yml b/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_install.yml new file mode 100644 index 00000000..2599e7f1 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_install.yml @@ -0,0 +1,54 @@ +--- + +- name: Fail if Synapse Simple Antispam blocked homeservers is not set + fail: + msg: "Synapse Simple Antispam is enabled, but no blocked homeservers have been set in matrix_synapse_ext_spam_checker_synapse_simple_antispam_config_blocked_homeservers" + when: "matrix_synapse_ext_spam_checker_synapse_simple_antispam_config_blocked_homeservers|length == 0" + +- name: Ensure git installed (RedHat) + yum: + name: + - git + state: present + update_cache: no + when: "ansible_os_family == 'RedHat'" + +- name: Ensure git installed (Debian) + apt: + name: + - git + state: present + update_cache: no + when: "ansible_os_family == 'Debian'" + +- name: Ensure git installed (Archlinux) + pacman: + name: + - git + state: present + update_cache: no + when: "ansible_distribution == 'Archlinux'" + +- name: Clone synapse-simple-antispam git repository + git: + repo: "{{ matrix_synapse_ext_spam_checker_synapse_simple_antispam_git_repository_url }}" + version: "{{ matrix_synapse_ext_spam_checker_synapse_simple_antispam_git_version }}" + dest: "{{ matrix_synapse_ext_path }}/synapse-simple-antispam" + become: true + become_user: "{{ matrix_user_username }}" + +- set_fact: + matrix_synapse_spam_checker: > + {{ matrix_synapse_spam_checker }} + + + [{ + "module": "synapse_simple_antispam.AntiSpamInvites", + "config": { + "blocked_homeservers": {{ matrix_synapse_ext_spam_checker_synapse_simple_antispam_config_blocked_homeservers }} + } + }] + + matrix_synapse_container_extra_arguments: > + {{ matrix_synapse_container_extra_arguments|default([]) }} + + + ["--mount type=bind,src={{ matrix_synapse_ext_path }}/synapse-simple-antispam/synapse_simple_antispam,dst={{ matrix_synapse_in_container_python_packages_path }}/synapse_simple_antispam,ro"] diff --git a/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_uninstall.yml b/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_uninstall.yml new file mode 100644 index 00000000..14cefc72 --- /dev/null +++ b/roles/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_uninstall.yml @@ -0,0 +1,6 @@ +--- + +- name: Ensure synapse-simple-antispam doesn't exist + file: + path: "{{ matrix_synapse_ext_path }}/synapse-simple-antispam" + state: absent diff --git a/roles/matrix-synapse/tasks/goofys/setup.yml b/roles/matrix-synapse/tasks/goofys/setup.yml new file mode 100644 index 00000000..6370408d --- /dev/null +++ b/roles/matrix-synapse/tasks/goofys/setup.yml @@ -0,0 +1,7 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/goofys/setup_install.yml" + when: matrix_s3_media_store_enabled|bool + +- import_tasks: "{{ role_path }}/tasks/goofys/setup_uninstall.yml" + when: "not matrix_s3_media_store_enabled|bool" diff --git a/roles/matrix-synapse/tasks/goofys/setup_install.yml b/roles/matrix-synapse/tasks/goofys/setup_install.yml new file mode 100644 index 00000000..b5e95614 --- /dev/null +++ b/roles/matrix-synapse/tasks/goofys/setup_install.yml @@ -0,0 +1,41 @@ +- name: Ensure Goofys Docker image is pulled + docker_image: + name: "{{ matrix_s3_goofys_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_s3_goofys_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_s3_goofys_docker_image_force_pull }}" + +# This will throw a Permission Denied error if already mounted +- name: Check Matrix Goofys external storage mountpoint path + stat: + path: "{{ matrix_s3_media_store_path }}" + register: local_path_matrix_s3_media_store_path_stat + ignore_errors: yes + +- name: Ensure Matrix Goofys external storage mountpoint exists + file: + path: "{{ matrix_s3_media_store_path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: "not local_path_matrix_s3_media_store_path_stat.failed and not local_path_matrix_s3_media_store_path_stat.stat.exists" + +- name: Ensure goofys environment variables file created + template: + src: "{{ role_path }}/templates/goofys/env-goofys.j2" + dest: "{{ matrix_synapse_config_dir_path }}/env-goofys" + owner: root + mode: 0600 + +- name: Ensure matrix-goofys.service installed + template: + src: "{{ role_path }}/templates/goofys/systemd/matrix-goofys.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-goofys.service" + mode: 0644 + register: matrix_goofys_systemd_service_result + +- name: Ensure systemd reloaded after matrix-goofys.service installation + service: + daemon_reload: yes + when: "matrix_goofys_systemd_service_result.changed" diff --git a/roles/matrix-synapse/tasks/goofys/setup_uninstall.yml b/roles/matrix-synapse/tasks/goofys/setup_uninstall.yml new file mode 100644 index 00000000..91d43456 --- /dev/null +++ b/roles/matrix-synapse/tasks/goofys/setup_uninstall.yml @@ -0,0 +1,33 @@ +- name: Check existence of matrix-goofys service + stat: + path: "{{ matrix_systemd_path }}/matrix-goofys.service" + register: matrix_goofys_service_stat + +- name: Ensure matrix-goofys is stopped + service: + name: matrix-goofys + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_goofys_service_stat.stat.exists" + +- name: Ensure matrix-goofys.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-goofys.service" + state: absent + when: "matrix_goofys_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-goofys.service removal + service: + daemon_reload: yes + when: "matrix_goofys_service_stat.stat.exists" + +- name: Ensure goofys environment variables file doesn't exist + file: + path: "{{ matrix_synapse_config_dir_path }}/env-goofys" + state: absent + +- name: Ensure Goofys Docker image doesn't exist + docker_image: + name: "{{ matrix_s3_goofys_docker_image }}" + state: absent diff --git a/roles/matrix-synapse/tasks/import_media_store.yml b/roles/matrix-synapse/tasks/import_media_store.yml new file mode 100644 index 00000000..487bcb35 --- /dev/null +++ b/roles/matrix-synapse/tasks/import_media_store.yml @@ -0,0 +1,83 @@ +--- + +# Pre-checks + +- name: Fail if playbook called incorrectly + fail: + msg: "The `server_path_media_store` variable needs to be provided to this playbook, via --extra-vars" + when: "server_path_media_store is not defined or server_path_media_store.startswith('<')" + +- name: Fail if media store is on Amazon S3 + fail: + msg: "Your media store is on Amazon S3. Due to technical limitations, restoring is not supported." + when: matrix_s3_media_store_enabled|bool + +- name: Check if the provided media store directory exists + stat: + path: "{{ server_path_media_store }}" + register: server_path_media_store_stat + +- name: Fail if provided media store directory doesn't exist on the server + fail: + msg: "{{ server_path_media_store }} cannot be found on the server" + when: "not server_path_media_store_stat.stat.exists or not server_path_media_store_stat.stat.isdir" + +- name: Check if media store contains local_content + stat: + path: "{{ server_path_media_store }}/local_content" + register: server_path_media_store_local_content_stat + +- name: Check if media store contains remote_content + stat: + path: "{{ server_path_media_store }}/remote_content" + register: server_path_media_store_remote_content_stat + +- name: Fail if media store directory doesn't look okay (lacking remote and local content) + fail: + msg: "{{ server_path_media_store }} contains neither local_content nor remote_content directories. It's most likely a mistake and is not a media store directory." + when: "not server_path_media_store_local_content_stat.stat.exists and not server_path_media_store_remote_content_stat.stat.exists" + + +# Actual import work + +- name: Ensure matrix-synapse is stopped + service: + name: matrix-synapse + state: stopped + daemon_reload: yes + register: stopping_result + +# This can only work with local files, not if the media store is on Amazon S3, +# as it won't be accessible in such a case. +- name: Ensure provided media store directory is synchronized + synchronize: + src: "{{ server_path_media_store }}/" + dest: "{{ matrix_synapse_media_store_path }}" + delete: yes + # It's wasteful to preserve owner/group now. We chown below anyway. + owner: no + group: no + times: yes + delegate_to: "{{ inventory_hostname }}" + +# This is for the generic case and fails in other cases (remote file systems), +# because in such cases the base path (matrix_synapse_media_store_path) is a mount point. +- name: Ensure media store permissions are correct (generic case) + file: + path: "{{ matrix_synapse_media_store_path }}" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + recurse: yes + when: "not matrix_s3_media_store_enabled|bool" + +# We don't chown for Goofys, because due to the way it's mounted, +# all files become owned by whoever needs to own them. + +- name: Ensure Synapse is started (if it previously was) + service: + name: "{{ item }}" + state: started + daemon_reload: yes + when: "stopping_result.changed" + with_items: + - matrix-synapse diff --git a/roles/matrix-synapse/tasks/init.yml b/roles/matrix-synapse/tasks/init.yml new file mode 100644 index 00000000..bc23fc86 --- /dev/null +++ b/roles/matrix-synapse/tasks/init.yml @@ -0,0 +1,26 @@ +# See https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/1070 +# and https://github.com/spantaleev/matrix-docker-ansible-deploy/commit/1ab507349c752042d26def3e95884f6df8886b74#commitcomment-51108407 +- name: Fail if trying to self-build on Ansible < 2.8 + fail: + msg: "To self-build the Element image, you should use Ansible 2.8 or higher. See docs/ansible.md" + when: "ansible_version.major == 2 and ansible_version.minor < 8 and matrix_synapse_container_image_self_build and matrix_synapse_enabled" + +# Unless `matrix_synapse_workers_enabled_list` is explicitly defined, +# we'll generate it dynamically. +- import_tasks: "{{ role_path }}/tasks/synapse/workers/init.yml" + when: "matrix_synapse_enabled and matrix_synapse_workers_enabled and matrix_synapse_workers_enabled_list|length == 0" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-synapse.service'] }}" + when: matrix_synapse_enabled|bool + +- name: Ensure systemd services for workers are injected + include_tasks: "{{ role_path }}/tasks/synapse/workers/util/inject_systemd_services_for_worker.yml" + with_items: "{{ matrix_synapse_workers_enabled_list }}" + loop_control: + loop_var: matrix_synapse_worker_details + when: matrix_synapse_enabled|bool and matrix_synapse_workers_enabled|bool + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-goofys.service'] }}" + when: matrix_s3_media_store_enabled|bool diff --git a/roles/matrix-synapse/tasks/main.yml b/roles/matrix-synapse/tasks/main.yml new file mode 100644 index 00000000..8bf1c563 --- /dev/null +++ b/roles/matrix-synapse/tasks/main.yml @@ -0,0 +1,55 @@ +- import_tasks: "{{ role_path }}/tasks/init.yml" + tags: + - always + +- import_tasks: "{{ role_path }}/tasks/validate_config.yml" + when: run_setup|bool and matrix_synapse_enabled|bool + tags: + - setup-all + - setup-synapse + +- import_tasks: "{{ role_path }}/tasks/setup_synapse.yml" + when: run_setup|bool + tags: + - setup-all + - setup-synapse + +- import_tasks: "{{ role_path }}/tasks/import_media_store.yml" + when: run_synapse_import_media_store|bool + tags: + - import-synapse-media-store + +- import_tasks: "{{ role_path }}/tasks/register_user.yml" + when: run_synapse_register_user|bool + tags: + - register-user + +- import_tasks: "{{ role_path }}/tasks/self_check_client_api.yml" + delegate_to: 127.0.0.1 + become: false + when: run_self_check|bool + tags: + - self-check + +- import_tasks: "{{ role_path }}/tasks/self_check_federation_api.yml" + delegate_to: 127.0.0.1 + become: false + when: run_self_check|bool + tags: + - self-check + +- import_tasks: "{{ role_path }}/tasks/update_user_password.yml" + when: run_synapse_update_user_password|bool + tags: + - update-user-password + +- import_tasks: "{{ role_path }}/tasks/rust-synapse-compress-state/main.yml" + when: run_synapse_rust_synapse_compress_state|bool + tags: + - rust-synapse-compress-state + +- name: Mark matrix-synapse role as executed + set_fact: + matrix_synapse_role_executed: true + tags: + - always diff --git a/roles/matrix-synapse/tasks/register_user.yml b/roles/matrix-synapse/tasks/register_user.yml new file mode 100644 index 00000000..9c2a3ea0 --- /dev/null +++ b/roles/matrix-synapse/tasks/register_user.yml @@ -0,0 +1,31 @@ +--- + +- name: Fail if playbook called incorrectly + fail: + msg: "The `username` variable needs to be provided to this playbook, via --extra-vars" + when: "username is not defined or username == ''" + +- name: Fail if playbook called incorrectly + fail: + msg: "The `password` variable needs to be provided to this playbook, via --extra-vars" + when: "password is not defined or password == ''" + +- name: Fail if playbook called incorrectly + fail: + msg: "The `admin` variable needs to be provided to this playbook, via --extra-vars" + when: "admin is not defined or admin not in ['yes', 'no']" + +- name: Ensure matrix-synapse is started + service: + name: matrix-synapse + state: started + daemon_reload: yes + register: start_result + +- name: Wait a while, so that Synapse can manage to start + pause: + seconds: 7 + when: "start_result.changed" + +- name: Register user + command: "{{ matrix_local_bin_path }}/matrix-synapse-register-user {{ username|quote }} {{ password|quote }} {{ '1' if admin == 'yes' else '0' }}" diff --git a/roles/matrix-synapse/tasks/rust-synapse-compress-state/compress_room.yml b/roles/matrix-synapse/tasks/rust-synapse-compress-state/compress_room.yml new file mode 100644 index 00000000..46cad808 --- /dev/null +++ b/roles/matrix-synapse/tasks/rust-synapse-compress-state/compress_room.yml @@ -0,0 +1,48 @@ +- debug: + msg: "Compressing room `{{ room_details.room_id }}` having {{ room_details.count }} state group rows" + +- name: Generate rust-synapse-compress-state room compression command + set_fact: + matrix_synapse_rust_synapse_compress_state_compress_room_command: >- + {{ matrix_host_command_docker }} run --rm --name matrix-rust-synapse-compress-state-compress-room + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --mount type=bind,src={{ matrix_synapse_rust_synapse_compress_state_base_path }},dst=/work + {{ matrix_synapse_rust_synapse_compress_state_docker_image }} + /synapse-compress-state -t -o /work/state-compressor.sql + -p "host={{ matrix_synapse_database_host }} user={{ matrix_synapse_database_user }} password={{ matrix_synapse_database_password }} dbname={{ matrix_synapse_database_database }}" + -r '{{ room_details.room_id }}' + +- name: Run rust-synapse-compress-state room compression command (SQL generation) + command: "{{ matrix_synapse_rust_synapse_compress_state_compress_room_command }}" + async: "{{ matrix_synapse_rust_synapse_compress_state_compress_room_time }}" + poll: 10 + register: matrix_synapse_rust_synapse_compress_state_compress_room_command_result + +- debug: var="matrix_synapse_rust_synapse_compress_state_compress_room_command_result" + +- name: Generate Postgres compression SQL import command + set_fact: + matrix_synapse_rust_synapse_compress_state_psql_import_command: >- + {{ matrix_host_command_docker }} run --rm --name matrix-rust-synapse-compress-state-psql-import + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql + --mount type=bind,src={{ matrix_synapse_rust_synapse_compress_state_base_path }},dst=/work,ro + --entrypoint=/bin/sh + {{ matrix_postgres_docker_image_latest }} + -c "cat /work/state-compressor.sql | + psql -v ON_ERROR_STOP=1 -h matrix-postgres -d {{ matrix_synapse_database_database }}" + +- name: Import compression SQL into Postgres + command: "{{ matrix_synapse_rust_synapse_compress_state_psql_import_command }}" + async: "{{ matrix_synapse_rust_synapse_compress_state_psql_import_time }}" + poll: 10 + register: matrix_synapse_rust_synapse_compress_state_psql_import_command_result + +- name: Clean up + file: + path: "{{ matrix_synapse_rust_synapse_compress_state_base_path }}/state-compressor.sql" + state: absent diff --git a/roles/matrix-synapse/tasks/rust-synapse-compress-state/main.yml b/roles/matrix-synapse/tasks/rust-synapse-compress-state/main.yml new file mode 100644 index 00000000..106c59d5 --- /dev/null +++ b/roles/matrix-synapse/tasks/rust-synapse-compress-state/main.yml @@ -0,0 +1,118 @@ +# Pre-checks + +- name: Fail if Postgres not enabled + fail: + msg: "Postgres via the matrix-postgres role is not enabled (`matrix_postgres_enabled`). Cannot use rust-synapse-compress-state." + when: "not matrix_postgres_enabled|bool" + + +# Defaults + +- name: Set matrix_synapse_rust_synapse_compress_state_find_rooms_command_wait_time, if not provided + set_fact: + matrix_synapse_rust_synapse_compress_state_find_rooms_command_wait_time: 300 + when: "matrix_synapse_rust_synapse_compress_state_find_rooms_command_wait_time|default('') == ''" + +- name: Set matrix_synapse_rust_synapse_compress_state_compress_room_time, if not provided + set_fact: + matrix_synapse_rust_synapse_compress_state_compress_room_time: 1800 + when: "matrix_synapse_rust_synapse_compress_state_compress_room_time|default('') == ''" + +- name: Set matrix_synapse_rust_synapse_compress_state_psql_import_time, if not provided + set_fact: + matrix_synapse_rust_synapse_compress_state_psql_import_time: 1800 + when: "matrix_synapse_rust_synapse_compress_state_psql_import_time|default('') == ''" + +- name: Set matrix_synapse_rust_synapse_compress_state_min_state_groups_required, if not provided + set_fact: + # The minimum number of state groups we're looking for before we consider a room eligible for compression. + # Rooms with a smaller state groups count will not be compressed. + matrix_synapse_rust_synapse_compress_state_min_state_groups_required: 100000 + when: "matrix_synapse_rust_synapse_compress_state_min_state_groups_required|default('') == ''" + + +# Actual compression work + +- name: Ensure rust-synapse-compress-state paths exist + file: + path: "{{ matrix_synapse_rust_synapse_compress_state_base_path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure rust-synapse-compress-state image is pulled + docker_image: + name: "{{ matrix_synapse_rust_synapse_compress_state_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_synapse_rust_synapse_compress_state_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_synapse_rust_synapse_compress_state_docker_image_force_pull }}" + +- name: Generate rust-synapse-compress-state room find command + set_fact: + matrix_synapse_rust_synapse_compress_state_find_rooms_command: >- + {{ matrix_host_command_docker }} run --rm --name matrix-rust-synapse-compress-state-find-rooms + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --network={{ matrix_docker_network }} + --env-file={{ matrix_postgres_base_path }}/env-postgres-psql + {{ matrix_postgres_docker_image_latest }} + psql -v ON_ERROR_STOP=1 -h matrix-postgres {{ matrix_synapse_database_database }} -c + 'SELECT array_to_json(array_agg(row_to_json (r))) FROM (SELECT room_id, count(*) AS count FROM state_groups_state GROUP BY room_id HAVING count(*) > {{ matrix_synapse_rust_synapse_compress_state_min_state_groups_required }} ORDER BY count DESC) r;' + +- name: Find rooms eligible for compression with rust-synapse-compress-state + command: "{{ matrix_synapse_rust_synapse_compress_state_find_rooms_command }}" + async: "{{ matrix_synapse_rust_synapse_compress_state_find_rooms_command_wait_time }}" + poll: 10 + register: matrix_synapse_rust_synapse_compress_state_find_rooms_command_result + +# We expect the output to be like this: +# +# "stdout_lines": [ +# " array_to_json ", +# "----------------------------------------------------------------------------------------------------------------------------", +# " [{\"room_id\":\"!some-id\",\"count\":2461329},{\"room_id\":\"!another-id\",\"count\":512017}]", +# "(1 row)" +# ] +# +# Row 3 (out of 4) contains the actual result. +# +# Row 3 contains a space when there's no result. + +- block: + - debug: var="matrix_synapse_rust_synapse_compress_state_find_rooms_command_result" + + - name: Fail if room find result is not what we expect + fail: + msg: >- + Expecting 4 lines in the "find rooms" result. + when: "matrix_synapse_rust_synapse_compress_state_find_rooms_command_result.failed or matrix_synapse_rust_synapse_compress_state_find_rooms_command_result.stdout_lines|length != 4" + +- block: + # matrix_synapse_rust_synapse_compress_state_eligible_rooms is a list + # of dictionaries like this: {'room_id': '!some-id', 'count': 2461329} + - set_fact: + matrix_synapse_rust_synapse_compress_state_eligible_rooms: "{{ matrix_synapse_rust_synapse_compress_state_find_rooms_command_result.stdout_lines[2] | from_json }}" + + - name: Display rooms that will be compressed + debug: + msg: >- + The following rooms contain more than {{ matrix_synapse_rust_synapse_compress_state_min_state_groups_required }} state group rows + (configurable via `matrix_synapse_rust_synapse_compress_state_min_state_groups_required`) + and will be compressed: + {{ matrix_synapse_rust_synapse_compress_state_eligible_rooms }} + + - name: Compress room state + include_tasks: "{{ role_path }}/tasks/rust-synapse-compress-state/compress_room.yml" + with_items: "{{ matrix_synapse_rust_synapse_compress_state_eligible_rooms }}" + loop_control: + loop_var: room_details + when: "matrix_synapse_rust_synapse_compress_state_find_rooms_command_result.stdout_lines[2] != ' '" + +- name: Show notice about lack of rooms to compress + debug: + msg: >- + No rooms were found to contain more than {{ matrix_synapse_rust_synapse_compress_state_min_state_groups_required }} state group rows + (configurable via `matrix_synapse_rust_synapse_compress_state_min_state_groups_required`), + so there's nothing to compress. + when: "matrix_synapse_rust_synapse_compress_state_find_rooms_command_result.stdout_lines[2] == ' '" diff --git a/roles/matrix-synapse/tasks/self_check_client_api.yml b/roles/matrix-synapse/tasks/self_check_client_api.yml new file mode 100644 index 00000000..30244d50 --- /dev/null +++ b/roles/matrix-synapse/tasks/self_check_client_api.yml @@ -0,0 +1,21 @@ +--- + +- name: Check Matrix Client API + uri: + url: "{{ matrix_synapse_client_api_url_endpoint_public }}" + follow_redirects: none + validate_certs: "{{ matrix_synapse_self_check_validate_certificates }}" + register: result_matrix_synapse_client_api + ignore_errors: true + check_mode: no + when: matrix_synapse_enabled|bool + +- name: Fail if Matrix Client API not working + fail: + msg: "Failed checking Matrix Client API is up at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ matrix_synapse_client_api_url_endpoint_public }}`). Is Synapse running? Is port 443 open in your firewall? Full error: {{ result_matrix_synapse_client_api }}" + when: "matrix_synapse_enabled|bool and (result_matrix_synapse_client_api.failed or 'json' not in result_matrix_synapse_client_api)" + +- name: Report working Matrix Client API + debug: + msg: "The Matrix Client API at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ matrix_synapse_client_api_url_endpoint_public }}`) is working" + when: matrix_synapse_enabled|bool diff --git a/roles/matrix-synapse/tasks/self_check_federation_api.yml b/roles/matrix-synapse/tasks/self_check_federation_api.yml new file mode 100644 index 00000000..57c9e56b --- /dev/null +++ b/roles/matrix-synapse/tasks/self_check_federation_api.yml @@ -0,0 +1,26 @@ +--- + +- name: Check Matrix Federation API + uri: + url: "{{ matrix_synapse_federation_api_url_endpoint_public }}" + follow_redirects: none + validate_certs: "{{ matrix_synapse_self_check_validate_certificates }}" + register: result_matrix_synapse_federation_api + ignore_errors: true + check_mode: no + when: matrix_synapse_enabled|bool + +- name: Fail if Matrix Federation API not working + fail: + msg: "Failed checking Matrix Federation API is up at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ matrix_synapse_federation_api_url_endpoint_public }}`). Is Synapse running? Is port {{ matrix_federation_public_port }} open in your firewall? Full error: {{ result_matrix_synapse_federation_api }}" + when: "matrix_synapse_enabled|bool and matrix_synapse_federation_enabled|bool and (result_matrix_synapse_federation_api.failed or 'json' not in result_matrix_synapse_federation_api)" + +- name: Fail if Matrix Federation API unexpectedly enabled + fail: + msg: "Matrix Federation API is up at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ matrix_synapse_federation_api_url_endpoint_public }}`) despite being disabled." + when: "matrix_synapse_enabled|bool and not matrix_synapse_federation_enabled|bool and not result_matrix_synapse_federation_api.failed" + +- name: Report working Matrix Federation API + debug: + msg: "The Matrix Federation API at `{{ matrix_server_fqn_matrix }}` (checked endpoint: `{{ matrix_synapse_federation_api_url_endpoint_public }}`) is working" + when: "matrix_synapse_enabled|bool and matrix_synapse_federation_enabled|bool" diff --git a/roles/matrix-synapse/tasks/setup_synapse.yml b/roles/matrix-synapse/tasks/setup_synapse.yml new file mode 100644 index 00000000..f8bc05a1 --- /dev/null +++ b/roles/matrix-synapse/tasks/setup_synapse.yml @@ -0,0 +1,25 @@ +--- + +- name: Ensure Synapse paths exist + file: + path: "{{ item.path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + with_items: + - { path: "{{ matrix_synapse_config_dir_path }}", when: true } + - { path: "{{ matrix_synapse_ext_path }}", when: true } + - { path: "{{ matrix_synapse_docker_src_files_path }}", when: "{{ matrix_synapse_container_image_self_build }}" } + # We handle matrix_synapse_media_store_path elsewhere (in ./synapse/setup_install.yml), + # because if it's using Goofys and it's already mounted (from before), + # trying to chown/chmod it here will cause trouble. + when: "(matrix_synapse_enabled|bool or matrix_s3_media_store_enabled|bool) and item.when" + +- import_tasks: "{{ role_path }}/tasks/ext/setup.yml" + +- import_tasks: "{{ role_path }}/tasks/synapse/workers/setup.yml" + +- import_tasks: "{{ role_path }}/tasks/synapse/setup.yml" + +- import_tasks: "{{ role_path }}/tasks/goofys/setup.yml" diff --git a/roles/matrix-synapse/tasks/synapse/setup.yml b/roles/matrix-synapse/tasks/synapse/setup.yml new file mode 100644 index 00000000..b5d27c36 --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/setup.yml @@ -0,0 +1,7 @@ +--- + +- import_tasks: "{{ role_path }}/tasks/synapse/setup_install.yml" + when: matrix_synapse_enabled|bool + +- import_tasks: "{{ role_path }}/tasks/synapse/setup_uninstall.yml" + when: "not matrix_synapse_enabled|bool" diff --git a/roles/matrix-synapse/tasks/synapse/setup_install.yml b/roles/matrix-synapse/tasks/synapse/setup_install.yml new file mode 100644 index 00000000..b658cfff --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/setup_install.yml @@ -0,0 +1,109 @@ +--- + +# This will throw a Permission Denied error if already mounted using fuse +- name: Check Synapse media store path + stat: + path: "{{ matrix_synapse_media_store_path }}" + register: local_path_media_store_stat + ignore_errors: yes + +# This is separate and conditional, to ensure we don't execute it +# if the path already exists or we failed to check, because it's mounted using fuse. +- name: Ensure Synapse media store path exists + file: + path: "{{ matrix_synapse_media_store_path }}" + state: directory + mode: 0750 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + when: "not local_path_media_store_stat.failed and not local_path_media_store_stat.stat.exists" + +- name: Ensure Synapse repository is present on self-build + git: + repo: "{{ matrix_synapse_container_image_self_build_repo }}" + dest: "{{ matrix_synapse_docker_src_files_path }}" + version: "{{ matrix_synapse_docker_image.split(':')[1] }}" + force: "yes" + register: matrix_synapse_git_pull_results + when: "matrix_synapse_container_image_self_build|bool" + +- name: Ensure Synapse Docker image is built + docker_image: + name: "{{ matrix_synapse_docker_image }}" + source: build + force_source: "{{ matrix_synapse_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_synapse_git_pull_results.changed }}" + build: + dockerfile: docker/Dockerfile + path: "{{ matrix_synapse_docker_src_files_path }}" + pull: yes + when: "matrix_synapse_container_image_self_build|bool" + +- name: Ensure Synapse Docker image is pulled + docker_image: + name: "{{ matrix_synapse_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_synapse_docker_image_force_pull if ansible_version.major > 2 or ansible_version.minor >= 8 else omit }}" + force: "{{ omit if ansible_version.major > 2 or ansible_version.minor >= 8 else matrix_synapse_docker_image_force_pull }}" + when: "not matrix_synapse_container_image_self_build" + +- name: Check if a Synapse signing key exists + stat: + path: "{{ matrix_synapse_config_dir_path }}/{{ matrix_server_fqn_matrix }}.signing.key" + register: matrix_synapse_signing_key_stat + +# We do this so that the signing key would get generated. +# +# This will also generate a default homeserver.yaml configuration file and a log configuration file. +# We don't care about those configuraiton files, as we replace them with our own anyway (see below). +# +# We don't use the `docker_container` module, because using it with `cap_drop` requires +# a very recent version, which is not available for a lot of people yet. +- name: Generate initial Synapse config and signing key + command: | + docker run + --rm + --name=matrix-config + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} + --cap-drop=ALL + --mount type=bind,src={{ matrix_synapse_config_dir_path }},dst=/data + -e UID={{ matrix_user_uid }} + -e GID={{ matrix_user_gid }} + -e SYNAPSE_CONFIG_PATH=/data/homeserver.yaml + -e SYNAPSE_SERVER_NAME={{ matrix_server_fqn_matrix }} + -e SYNAPSE_REPORT_STATS=no + {{ matrix_synapse_docker_image }} + generate + when: "not matrix_synapse_signing_key_stat.stat.exists" + +- name: Ensure Synapse homeserver config installed + copy: + content: "{{ matrix_synapse_configuration|to_nice_yaml }}" + dest: "{{ matrix_synapse_config_dir_path }}/homeserver.yaml" + mode: 0644 + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + +- name: Ensure Synapse log config installed + template: + src: "{{ matrix_synapse_template_synapse_log }}" + dest: "{{ matrix_synapse_config_dir_path }}/{{ matrix_server_fqn_matrix }}.log.config" + mode: 0644 + +- name: Ensure matrix-synapse.service installed + template: + src: "{{ role_path }}/templates/synapse/systemd/matrix-synapse.service.j2" + dest: "{{ matrix_systemd_path }}/matrix-synapse.service" + mode: 0644 + register: matrix_synapse_systemd_service_result + +- name: Ensure systemd reloaded after matrix-synapse.service installation + service: + daemon_reload: yes + when: "matrix_synapse_systemd_service_result.changed" + +- name: Ensure matrix-synapse-register-user script created + template: + src: "{{ role_path }}/templates/synapse/usr-local-bin/matrix-synapse-register-user.j2" + dest: "{{ matrix_local_bin_path }}/matrix-synapse-register-user" + mode: 0755 diff --git a/roles/matrix-synapse/tasks/synapse/setup_uninstall.yml b/roles/matrix-synapse/tasks/synapse/setup_uninstall.yml new file mode 100644 index 00000000..f1cdf167 --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/setup_uninstall.yml @@ -0,0 +1,28 @@ +- name: Check existence of matrix-synapse service + stat: + path: "{{ matrix_systemd_path }}/matrix-synapse.service" + register: matrix_synapse_service_stat + +- name: Ensure matrix-synapse is stopped + service: + name: matrix-synapse + state: stopped + daemon_reload: yes + register: stopping_result + when: "matrix_synapse_service_stat.stat.exists" + +- name: Ensure matrix-synapse.service doesn't exist + file: + path: "{{ matrix_systemd_path }}/matrix-synapse.service" + state: absent + when: "matrix_synapse_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-synapse.service removal + service: + daemon_reload: yes + when: "matrix_synapse_service_stat.stat.exists" + +- name: Ensure Synapse Docker image doesn't exist + docker_image: + name: "{{ matrix_synapse_docker_image }}" + state: absent diff --git a/roles/matrix-synapse/tasks/synapse/workers/init.yml b/roles/matrix-synapse/tasks/synapse/workers/init.yml new file mode 100644 index 00000000..c6fc32c3 --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/workers/init.yml @@ -0,0 +1,86 @@ +# Below is a huge hack for dynamically building a list of workers and finally assigning it to `matrix_synapse_workers_enabled_list`. +# +# set_fact within a loop does not work reliably in Ansible (it only executes on the first iteration for some reason), +# so we're forced to do something much uglier. + +- name: Build generic workers + set_fact: + worker: + type: 'generic_worker' + instanceId: "{{ matrix_synapse_workers_generic_workers_port_range_start + item }}" + port: "{{ matrix_synapse_workers_generic_workers_port_range_start + item }}" + metrics_port: "{{ matrix_synapse_workers_generic_workers_metrics_range_start + item }}" + register: "matrix_synapse_workers_list_results_generic_workers" + loop: "{{ range(0, matrix_synapse_workers_generic_workers_count|int)|list }}" + +- name: Build federation sender workers + set_fact: + worker: + type: 'federation_sender' + instanceId: "{{ item }}" + port: 0 + metrics_port: "{{ matrix_synapse_workers_federation_sender_workers_metrics_range_start + item }}" + register: "matrix_synapse_workers_list_results_federation_sender_workers" + loop: "{{ range(0, matrix_synapse_workers_federation_sender_workers_count|int)|list }}" + +# This type of worker can only have a count of 1, at most +- name: Build pusher workers + set_fact: + worker: + type: 'pusher' + instanceId: "{{ item }}" + port: 0 + metrics_port: "{{ matrix_synapse_workers_pusher_workers_metrics_range_start + item }}" + register: "matrix_synapse_workers_list_results_pusher_workers" + loop: "{{ range(0, matrix_synapse_workers_pusher_workers_count|int)|list }}" + +# This type of worker can only have a count of 1, at most +- name: Build appservice workers + set_fact: + worker: + type: 'appservice' + instanceId: "{{ item }}" + port: 0 + metrics_port: "{{ matrix_synapse_workers_appservice_workers_metrics_range_start + item }}" + register: "matrix_synapse_workers_list_results_appservice_workers" + loop: "{{ range(0, matrix_synapse_workers_appservice_workers_count|int)|list }}" + +- name: Build media_repository workers + set_fact: + worker: + type: 'media_repository' + instanceId: "{{ matrix_synapse_workers_media_repository_workers_port_range_start + item }}" + port: "{{ matrix_synapse_workers_media_repository_workers_port_range_start + item }}" + metrics_port: "{{ matrix_synapse_workers_media_repository_workers_metrics_range_start + item }}" + register: "matrix_synapse_workers_list_results_media_repository_workers" + loop: "{{ range(0, matrix_synapse_workers_media_repository_workers_count|int)|list }}" + +- name: Build frontend_proxy workers + set_fact: + worker: + type: 'frontend_proxy' + instanceId: "{{ matrix_synapse_workers_frontend_proxy_workers_port_range_start + item }}" + port: "{{ matrix_synapse_workers_frontend_proxy_workers_port_range_start + item }}" + metrics_port: "{{ matrix_synapse_workers_frontend_proxy_workers_metrics_range_start + item }}" + register: "matrix_synapse_workers_list_results_frontend_proxy_workers" + loop: "{{ range(0, matrix_synapse_workers_frontend_proxy_workers_count|int)|list }}" + +- set_fact: + matrix_synapse_dynamic_workers_list: "{{ matrix_synapse_dynamic_workers_list|default([]) + [item.ansible_facts.worker] }}" + with_items: | + {{ + matrix_synapse_workers_list_results_generic_workers.results + + + matrix_synapse_workers_list_results_federation_sender_workers.results + + + matrix_synapse_workers_list_results_pusher_workers.results + + + matrix_synapse_workers_list_results_appservice_workers.results + + + matrix_synapse_workers_list_results_media_repository_workers.results + + + matrix_synapse_workers_list_results_frontend_proxy_workers.results + }} + +- set_fact: + matrix_synapse_workers_enabled_list: "{{ matrix_synapse_dynamic_workers_list }}" diff --git a/roles/matrix-synapse/tasks/synapse/workers/setup.yml b/roles/matrix-synapse/tasks/synapse/workers/setup.yml new file mode 100644 index 00000000..ce66a2e4 --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/workers/setup.yml @@ -0,0 +1,21 @@ +--- + +# A previous version of the worker setup used this. +# This is a temporary cleanup for people who ran that version. +- name: Ensure old matrix-synapse.service.wants directory is gone + file: + path: "{{ matrix_systemd_path }}/matrix-synapse.service.wants" + state: absent + +# Same. This was part of a previous version of the worker setup. +# No longer necessary. +- name: Ensure matrix-synapse-worker-write-pid script is removed + file: + path: "{{ matrix_local_bin_path }}/matrix-synapse-worker-write-pid" + state: absent + +- include_tasks: "{{ role_path }}/tasks/synapse/workers/setup_install.yml" + when: "matrix_synapse_enabled|bool and matrix_synapse_workers_enabled|bool" + +- include_tasks: "{{ role_path }}/tasks/synapse/workers/setup_uninstall.yml" + when: "not matrix_synapse_workers_enabled|bool" diff --git a/roles/matrix-synapse/tasks/synapse/workers/setup_install.yml b/roles/matrix-synapse/tasks/synapse/workers/setup_install.yml new file mode 100644 index 00000000..983f1876 --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/workers/setup_install.yml @@ -0,0 +1,42 @@ +--- + +- name: Determine current worker configs + find: + path: "{{ matrix_synapse_config_dir_path }}" + patterns: "worker.*.yaml" + use_regex: true + register: matrix_synapse_workers_current_config_files + +# This also deletes some things which we need. They will be recreated below. +- name: Ensure previous worker configs are cleaned + file: + path: "{{ item.path }}" + state: absent + with_items: "{{ matrix_synapse_workers_current_config_files.files }}" + +- name: Determine current worker systemd services + find: + path: "{{ matrix_systemd_path }}" + patterns: "matrix-synapse-worker.*.service" + use_regex: true + register: matrix_synapse_workers_current_systemd_services + +- name: Ensure unnecessary worker systemd services are stopped and disabled + service: + name: "{{ item.path|basename }}" + state: stopped + enabled: false + with_items: "{{ matrix_synapse_workers_current_systemd_services.files }}" + when: "not ansible_check_mode and item.path|basename not in matrix_systemd_services_list" + +- name: Ensure unnecessary worker systemd services are cleaned + file: + path: "{{ item.path }}" + state: absent + with_items: "{{ matrix_synapse_workers_current_systemd_services.files }}" + +- name: Ensure creation of worker systemd service files and configuration files + include_tasks: "{{ role_path }}/tasks/synapse/workers/util/setup_files_for_worker.yml" + with_items: "{{ matrix_synapse_workers_enabled_list }}" + loop_control: + loop_var: matrix_synapse_worker_details diff --git a/roles/matrix-synapse/tasks/synapse/workers/setup_uninstall.yml b/roles/matrix-synapse/tasks/synapse/workers/setup_uninstall.yml new file mode 100644 index 00000000..4a90bfa6 --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/workers/setup_uninstall.yml @@ -0,0 +1,36 @@ +--- + +- name: Populate service facts + service_facts: + +- name: Ensure any worker services are stopped + service: + name: "{{ item.key }}" + state: stopped + with_dict: "{{ ansible_facts.services|default({})|dict2items|selectattr('key', 'match', 'matrix-synapse-worker-.+\\.service')|list|items2dict }}" + +- name: Find worker configs to be cleaned + find: + path: "{{ matrix_synapse_config_dir_path }}" + patterns: "worker.*.yaml" + use_regex: true + register: matrix_synapse_workers_current_config_files + +- name: Ensure previous worker configs are cleaned + file: + path: "{{ item.path }}" + state: absent + with_items: "{{ matrix_synapse_workers_current_config_files.files }}" + +- name: Find worker systemd services to be cleaned + find: + path: "{{ matrix_systemd_path }}" + patterns: "matrix-synapse-worker.*.service" + use_regex: true + register: matrix_synapse_workers_current_systemd_services + +- name: Ensure previous worker systemd services are cleaned + file: + path: "{{ item.path }}" + state: absent + with_items: "{{ matrix_synapse_workers_current_systemd_services.files }}" diff --git a/roles/matrix-synapse/tasks/synapse/workers/util/inject_systemd_services_for_worker.yml b/roles/matrix-synapse/tasks/synapse/workers/util/inject_systemd_services_for_worker.yml new file mode 100644 index 00000000..62b42625 --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/workers/util/inject_systemd_services_for_worker.yml @@ -0,0 +1,18 @@ +# The tasks below run before `validate_config.yml`. +# To avoid failing with a cryptic error message, we'll do validation here. +# +# This check is mostly relevant to people who explicitly define `matrix_synapse_workers_enabled_list` +# (Synapse Workers users from the earlier days of this PR - https://github.com/spantaleev/matrix-docker-ansible-deploy/pull/456). +# +# In the future, it should be possible to remove this check. +# Our own code which dynamically builds `matrix_synapse_workers_enabled_list` does things right. +- name: Fail if instanceId not defined for worker + fail: + msg: "Synapse workers (like {{ matrix_synapse_worker_details|to_json }}) need to define an instanceId property (type + instanceId must be unique)" + when: "'instanceId' not in matrix_synapse_worker_details" + +- set_fact: + matrix_synapse_worker_systemd_service_name: "matrix-synapse-worker-{{ matrix_synapse_worker_details.type }}-{{ matrix_synapse_worker_details.instanceId }}.service" + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + [matrix_synapse_worker_systemd_service_name] }}" diff --git a/roles/matrix-synapse/tasks/synapse/workers/util/setup_files_for_worker.yml b/roles/matrix-synapse/tasks/synapse/workers/util/setup_files_for_worker.yml new file mode 100644 index 00000000..93ed6575 --- /dev/null +++ b/roles/matrix-synapse/tasks/synapse/workers/util/setup_files_for_worker.yml @@ -0,0 +1,19 @@ +- set_fact: + matrix_synapse_worker_systemd_service_name: "matrix-synapse-worker-{{ matrix_synapse_worker_details.type }}-{{ matrix_synapse_worker_details.instanceId }}" + +- set_fact: + matrix_synapse_worker_container_name: "{{ matrix_synapse_worker_systemd_service_name }}" + +- set_fact: + matrix_synapse_worker_config_file_name: "worker.{{ matrix_synapse_worker_details.type }}_{{ matrix_synapse_worker_details.instanceId }}.yaml" + +- name: Ensure configuration exists for {{ matrix_synapse_worker_systemd_service_name }} + template: + src: "{{ role_path }}/templates/synapse/worker.yaml.j2" + dest: "{{ matrix_synapse_config_dir_path }}/{{ matrix_synapse_worker_config_file_name }}" + +- name: Ensure systemd service exists for {{ matrix_synapse_worker_systemd_service_name }} + template: + src: "{{ role_path }}/templates/synapse/systemd/matrix-synapse-worker.service.j2" + dest: "{{ matrix_systemd_path }}/{{ matrix_synapse_worker_systemd_service_name }}.service" + mode: 0644 diff --git a/roles/matrix-synapse/tasks/update_user_password.yml b/roles/matrix-synapse/tasks/update_user_password.yml new file mode 100644 index 00000000..78136785 --- /dev/null +++ b/roles/matrix-synapse/tasks/update_user_password.yml @@ -0,0 +1,43 @@ +--- + +- name: Fail if playbook called incorrectly + fail: + msg: "The `username` variable needs to be provided to this playbook, via --extra-vars" + when: "username is not defined or username == ''" + +- name: Fail if playbook called incorrectly + fail: + msg: "The `password` variable needs to be provided to this playbook, via --extra-vars" + when: "password is not defined or password == ''" + +- name: Fail if not using matrix-postgres container + fail: + msg: "This command is working only when matrix-postgres container is being used" + when: "not matrix_postgres_enabled|bool" + +- name: Ensure matrix-synapse is started + service: + name: matrix-synapse + state: started + daemon_reload: yes + register: start_result + +- name: Ensure matrix-postgres is started + service: + name: matrix-postgres + state: started + daemon_reload: yes + register: postgres_start_result + + +- name: Wait a while, so that Matrix Synapse can manage to start + pause: + seconds: 7 + when: "start_result.changed or postgres_start_result.changed" + +- name: Generate password hash + shell: "{{ matrix_host_command_docker }} exec matrix-synapse /usr/local/bin/hash_password -c /data/homeserver.yaml -p {{ password|quote }}" + register: password_hash + +- name: Update user password hash + command: "{{ matrix_local_bin_path }}/matrix-postgres-update-user-password-hash {{ username|quote }} {{ password_hash.stdout|quote }}" diff --git a/roles/matrix-synapse/tasks/validate_config.yml b/roles/matrix-synapse/tasks/validate_config.yml new file mode 100644 index 00000000..6dcb50ce --- /dev/null +++ b/roles/matrix-synapse/tasks/validate_config.yml @@ -0,0 +1,59 @@ +--- + +- name: Fail if required Synapse settings not defined + fail: + msg: >- + You need to define a required configuration setting (`{{ item }}`) for using Synapse. + when: "vars[item] == ''" + with_items: + - "matrix_synapse_macaroon_secret_key" + - "matrix_synapse_database_host" + - "matrix_synapse_database_user" + - "matrix_synapse_database_password" + - "matrix_synapse_database_database" + +- name: Fail if asking for more than 1 instance of single-instance workers + fail: + msg: >- + `{{ item }}` cannot be more than 1. This is a single-instance worker. + when: "vars[item]|int > 1" + with_items: + - "matrix_synapse_workers_appservice_workers_count" + - "matrix_synapse_workers_pusher_workers_count" + - "matrix_synapse_workers_federation_sender_workers_count" + +- name: (Deprecation) Catch and report renamed settings + fail: + msg: >- + Your configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in vars" + with_items: + - {'old': 'matrix_synapse_email_riot_base_url', 'new': ''} + - {'old': 'matrix_synapse_container_expose_api_port', 'new': ''} + - {'old': 'matrix_synapse_no_tls', 'new': ''} + - {'old': 'matrix_enable_room_list_search', 'new': 'matrix_synapse_enable_room_list_search'} + - {'old': 'matrix_alias_creation_rules', 'new': 'matrix_synapse_alias_creation_rules'} + - {'old': 'matrix_room_list_publication_rules', 'new': 'matrix_synapse_room_list_publication_rules'} + - {'old': 'matrix_synapse_rc_messages_per_second', 'new': ''} + - {'old': 'matrix_synapse_rc_message_burst_count', 'new': ''} + - {'old': 'matrix_synapse_federation_rc_window_size', 'new': ''} + - {'old': 'matrix_synapse_federation_rc_sleep_limit', 'new': ''} + - {'old': 'matrix_synapse_federation_rc_sleep_delay', 'new': ''} + - {'old': 'matrix_synapse_federation_rc_reject_limit', 'new': ''} + - {'old': 'matrix_synapse_federation_rc_concurrent', 'new': ''} + - {'old': 'matrix_synapse_container_expose_client_api_port', 'new': ''} + - {'old': 'matrix_synapse_container_expose_federation_api_port', 'new': ''} + - {'old': 'matrix_synapse_container_expose_metrics_port', 'new': ''} + - {'old': 'matrix_synapse_cache_factor', 'new': 'matrix_synapse_caches_global_factor'} + - {'old': 'matrix_synapse_trusted_third_party_id_servers', 'new': ''} + - {'old': 'matrix_synapse_use_presence', 'new': 'matrix_synapse_presence_enabled'} + +- name: (Deprecation) Catch and report renamed settings in matrix_synapse_configuration_extension_yaml + fail: + msg: >- + Your matrix_synapse_configuration_extension_yaml configuration contains a variable, which now has a different name. + Please change your configuration to rename the variable (`{{ item.old }}` -> `{{ item.new }}`). + when: "item.old in matrix_synapse_configuration_extension" + with_items: + - {'old': 'federation_ip_range_blacklist', 'new': 'ip_range_blacklist'} diff --git a/roles/matrix-synapse/templates/goofys/env-goofys.j2 b/roles/matrix-synapse/templates/goofys/env-goofys.j2 new file mode 100644 index 00000000..2955efdd --- /dev/null +++ b/roles/matrix-synapse/templates/goofys/env-goofys.j2 @@ -0,0 +1,3 @@ +#jinja2: lstrip_blocks: "True" +AWS_ACCESS_KEY={{ matrix_s3_media_store_aws_access_key }} +AWS_SECRET_KEY={{ matrix_s3_media_store_aws_secret_key }} diff --git a/roles/matrix-synapse/templates/goofys/systemd/matrix-goofys.service.j2 b/roles/matrix-synapse/templates/goofys/systemd/matrix-goofys.service.j2 new file mode 100644 index 00000000..df4a4f23 --- /dev/null +++ b/roles/matrix-synapse/templates/goofys/systemd/matrix-goofys.service.j2 @@ -0,0 +1,39 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Goofys media store +After=docker.service +Requires=docker.service +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_docker }} kill %n +ExecStartPre=-{{ matrix_host_command_docker }} rm %n + +ExecStart={{ matrix_host_command_docker }} run --rm --name %n \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --mount type=bind,src=/etc/passwd,dst=/etc/passwd,ro \ + --mount type=bind,src=/etc/group,dst=/etc/group,ro \ + --mount type=bind,src={{ matrix_s3_media_store_path }},dst=/s3,bind-propagation=shared \ + --security-opt apparmor:unconfined \ + --cap-add mknod \ + --cap-add sys_admin \ + --device=/dev/fuse \ + --env-file={{ matrix_synapse_config_dir_path }}/env-goofys \ + --entrypoint /bin/sh \ + {{ matrix_s3_goofys_docker_image }} \ + -c 'goofys -f{% if not matrix_s3_media_store_custom_endpoint_enabled %} --storage-class=STANDARD_IA{% endif %}{% if matrix_s3_media_store_custom_endpoint_enabled %} --endpoint={{ matrix_s3_media_store_custom_endpoint }}{% endif %} --region {{ matrix_s3_media_store_region }} --stat-cache-ttl 60m0s --type-cache-ttl 60m0s --dir-mode 0700 --file-mode 0700 {{ matrix_s3_media_store_bucket_name }} /s3' + +TimeoutStartSec=5min +ExecStop=-{{ matrix_host_command_docker }} stop %n +ExecStop=-{{ matrix_host_command_docker }} kill %n +ExecStop=-{{ matrix_host_command_docker }} rm %n +ExecStop=-{{ matrix_host_command_fusermount }} -u {{ matrix_s3_media_store_path }} +Restart=always +RestartSec=5 +SyslogIdentifier=matrix-goofys + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 b/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 new file mode 100644 index 00000000..f184cc29 --- /dev/null +++ b/roles/matrix-synapse/templates/synapse/homeserver.yaml.j2 @@ -0,0 +1,2834 @@ +#jinja2: lstrip_blocks: "True" +# Configuration file for Synapse. +# +# This is a YAML file: see [1] for a quick introduction. Note in particular +# that *indentation is important*: all the elements of a list or dictionary +# should have the same indentation. +# +# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html + + +## Modules ## + +# Server admins can expand Synapse's functionality with external modules. +# +# See https://matrix-org.github.io/synapse/develop/modules.html for more +# documentation on how to configure or create custom modules for Synapse. +# +modules: + # - module: my_super_module.MySuperClass + # config: + # do_thing: true + # - module: my_other_super_module.SomeClass + # config: {} + + +## Server ## + +# The public-facing domain of the server +# +# The server_name name will appear at the end of usernames and room addresses +# created on this server. For example if the server_name was example.com, +# usernames on this server would be in the format @user:example.com +# +# In most cases you should avoid using a matrix specific subdomain such as +# matrix.example.com or synapse.example.com as the server_name for the same +# reasons you wouldn't use user@email.example.com as your email address. +# See https://github.com/matrix-org/synapse/blob/master/docs/delegate.md +# for information on how to host Synapse on a subdomain while preserving +# a clean server_name. +# +# The server_name cannot be changed later so it is important to +# configure this correctly before you start Synapse. It should be all +# lowercase and may contain an explicit port. +# Examples: matrix.org, localhost:8080 +# +server_name: "{{ matrix_domain }}" + +# When running as a daemon, the file to store the pid in +# +pid_file: /homeserver.pid + +# The absolute URL to the web client which /_matrix/client will redirect +# to if 'webclient' is configured under the 'listeners' configuration. +# +# This option can be also set to the filesystem path to the web client +# which will be served at /_matrix/client/ if 'webclient' is configured +# under the 'listeners' configuration, however this is a security risk: +# https://github.com/matrix-org/synapse#security-note +# +#web_client_location: https://riot.example.com/ + +# The public-facing base URL that clients use to access this Homeserver (not +# including _matrix/...). This is the same URL a user might enter into the +# 'Custom Homeserver URL' field on their client. If you use Synapse with a +# reverse proxy, this should be the URL to reach Synapse via the proxy. +# Otherwise, it should be the URL to reach Synapse's client HTTP listener (see +# 'listeners' below). +# +public_baseurl: https://{{ matrix_server_fqn_matrix }}/ + +# Set the soft limit on the number of file descriptors synapse can use +# Zero is used to indicate synapse should set the soft limit to the +# hard limit. +# +#soft_file_limit: 0 + +# Presence tracking allows users to see the state (e.g online/offline) +# of other local and remote users. +# +presence: + # Uncomment to disable presence tracking on this homeserver. This option + # replaces the previous top-level 'use_presence' option. + # + enabled: {{ matrix_synapse_presence_enabled|to_json }} + + # Presence routers are third-party modules that can specify additional logic + # to where presence updates from users are routed. + # + presence_router: + # The custom module's class. Uncomment to use a custom presence router module. + # + #module: "my_custom_router.PresenceRouter" + + # Configuration options of the custom module. Refer to your module's + # documentation for available options. + # + #config: + # example_option: 'something' + +# Whether to require authentication to retrieve profile data (avatars, +# display names) of other users through the client API. Defaults to +# 'false'. Note that profile data is also available via the federation +# API, unless allow_profile_lookup_over_federation is set to false. +# +require_auth_for_profile_requests: {{ matrix_synapse_require_auth_for_profile_requests|to_json }} + +# Uncomment to require a user to share a room with another user in order +# to retrieve their profile information. Only checked on Client-Server +# requests. Profile requests from other servers should be checked by the +# requesting server. Defaults to 'false'. +# +limit_profile_requests_to_users_who_share_rooms: {{ matrix_synapse_limit_profile_requests_to_users_who_share_rooms|to_json }} + +# Uncomment to prevent a user's profile data from being retrieved and +# displayed in a room until they have joined it. By default, a user's +# profile data is included in an invite event, regardless of the values +# of the above two settings, and whether or not the users share a server. +# Defaults to 'true'. +# +include_profile_data_on_invite: {{ matrix_synapse_include_profile_data_on_invite|to_json }} + +# If set to 'true', removes the need for authentication to access the server's +# public rooms directory through the client API, meaning that anyone can +# query the room directory. Defaults to 'false'. +# +allow_public_rooms_without_auth: {{ matrix_synapse_allow_public_rooms_without_auth|to_json }} + +# If set to 'true', allows any other homeserver to fetch the server's public +# rooms directory via federation. Defaults to 'false'. +# +allow_public_rooms_over_federation: {{ matrix_synapse_allow_public_rooms_over_federation|to_json }} + +# The default room version for newly created rooms. +# +# Known room versions are listed here: +# https://matrix.org/docs/spec/#complete-list-of-room-versions +# +# For example, for room version 1, default_room_version should be set +# to "1". +# +default_room_version: {{ matrix_synapse_default_room_version|to_json }} + +# The GC threshold parameters to pass to `gc.set_threshold`, if defined +# +#gc_thresholds: [700, 10, 10] + +# The minimum time in seconds between each GC for a generation, regardless of +# the GC thresholds. This ensures that we don't do GC too frequently. +# +# A value of `[1s, 10s, 30s]` indicates that a second must pass between consecutive +# generation 0 GCs, etc. +# +# Defaults to `[1s, 10s, 30s]`. +# +#gc_min_interval: [0.5s, 30s, 1m] + +# Set the limit on the returned events in the timeline in the get +# and sync operations. The default value is 100. -1 means no upper limit. +# +# Uncomment the following to increase the limit to 5000. +# +#filter_timeline_limit: 5000 + +# Whether room invites to users on this server should be blocked +# (except those sent by local server admins). The default is False. +# +#block_non_admin_invites: True + +# Room searching +# +# If disabled, new messages will not be indexed for searching and users +# will receive errors when searching for messages. Defaults to enabled. +# +#enable_search: false + +# Prevent outgoing requests from being sent to the following blacklisted IP address +# CIDR ranges. If this option is not specified then it defaults to private IP +# address ranges (see the example below). +# +# The blacklist applies to the outbound requests for federation, identity servers, +# push servers, and for checking key validity for third-party invite events. +# +# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly +# listed here, since they correspond to unroutable addresses.) +# +# This option replaces federation_ip_range_blacklist in Synapse v1.25.0. +# +#ip_range_blacklist: +# - '127.0.0.0/8' +# - '10.0.0.0/8' +# - '172.16.0.0/12' +# - '192.168.0.0/16' +# - '100.64.0.0/10' +# - '192.0.0.0/24' +# - '169.254.0.0/16' +# - '192.88.99.0/24' +# - '198.18.0.0/15' +# - '192.0.2.0/24' +# - '198.51.100.0/24' +# - '203.0.113.0/24' +# - '224.0.0.0/4' +# - '::1/128' +# - 'fe80::/10' +# - 'fc00::/7' +# - '2001:db8::/32' +# - 'ff00::/8' +# - 'fec0::/10' + +# List of IP address CIDR ranges that should be allowed for federation, +# identity servers, push servers, and for checking key validity for +# third-party invite events. This is useful for specifying exceptions to +# wide-ranging blacklisted target IP ranges - e.g. for communication with +# a push server only visible in your network. +# +# This whitelist overrides ip_range_blacklist and defaults to an empty +# list. +# +#ip_range_whitelist: +# - '192.168.1.1' + +# List of ports that Synapse should listen on, their purpose and their +# configuration. +# +# Options for each listener include: +# +# port: the TCP port to bind to +# +# bind_addresses: a list of local addresses to listen on. The default is +# 'all local interfaces'. +# +# type: the type of listener. Normally 'http', but other valid options are: +# 'manhole' (see docs/manhole.md), +# 'metrics' (see docs/metrics-howto.md), +# 'replication' (see docs/workers.md). +# +# tls: set to true to enable TLS for this listener. Will use the TLS +# key/cert specified in tls_private_key_path / tls_certificate_path. +# +# x_forwarded: Only valid for an 'http' listener. Set to true to use the +# X-Forwarded-For header as the client IP. Useful when Synapse is +# behind a reverse-proxy. +# +# resources: Only valid for an 'http' listener. A list of resources to host +# on this port. Options for each resource are: +# +# names: a list of names of HTTP resources. See below for a list of +# valid resource names. +# +# compress: set to true to enable HTTP compression for this resource. +# +# additional_resources: Only valid for an 'http' listener. A map of +# additional endpoints which should be loaded via dynamic modules. +# +# Valid resource names are: +# +# client: the client-server API (/_matrix/client), and the synapse admin +# API (/_synapse/admin). Also implies 'media' and 'static'. +# +# consent: user consent forms (/_matrix/consent). See +# docs/consent_tracking.md. +# +# federation: the server-server API (/_matrix/federation). Also implies +# 'media', 'keys', 'openid' +# +# keys: the key discovery API (/_matrix/keys). +# +# media: the media API (/_matrix/media). +# +# metrics: the metrics interface. See docs/metrics-howto.md. +# +# openid: OpenID authentication. +# +# replication: the HTTP replication API (/_synapse/replication). See +# docs/workers.md. +# +# static: static resources under synapse/static (/_matrix/static). (Mostly +# useful for 'fallback authentication'.) +# +# webclient: A web client. Requires web_client_location to be set. +# +listeners: +{% if matrix_synapse_metrics_enabled %} + - type: metrics + port: {{ matrix_synapse_metrics_port }} + bind_addresses: + - '0.0.0.0' +{% endif %} + +{% if matrix_synapse_federation_port_enabled and matrix_synapse_tls_federation_listener_enabled %} + # TLS-enabled listener: for when matrix traffic is sent directly to synapse. + - port: 8448 + tls: true + bind_addresses: ['::'] + type: http + x_forwarded: false + + resources: + - names: {{ matrix_synapse_federation_listener_resource_names|to_json }} + compress: false +{% endif %} + + # Unsecure HTTP listener (Client API): for when matrix traffic passes through a reverse proxy + # that unwraps TLS. + - port: 8008 + tls: false + bind_addresses: ['::'] + type: http + x_forwarded: true + + resources: + - names: {{ matrix_synapse_http_listener_resource_names|to_json }} + compress: false + +{% if matrix_synapse_federation_port_enabled %} + # Unsecure HTTP listener (Federation API): for when matrix traffic passes through a reverse proxy + # that unwraps TLS. + - port: 8048 + tls: false + bind_addresses: ['::'] + type: http + x_forwarded: true + + resources: + - names: {{ matrix_synapse_federation_listener_resource_names|to_json }} + compress: false +{% endif %} + +{% if matrix_synapse_manhole_enabled %} + # Turn on the twisted ssh manhole service on localhost on the given + # port. + - port: 9000 + bind_addresses: ['0.0.0.0'] + type: manhole +{% endif %} + +{% if matrix_synapse_workers_enabled %} + +{% if matrix_synapse_replication_listener_enabled %} + # c.f. https://github.com/matrix-org/synapse/tree/master/docs/workers.md + # HTTP replication: for the workers to send data to the main synapse process + - port: {{ matrix_synapse_replication_http_port }} + bind_addresses: ['0.0.0.0'] + type: http + resources: + - names: [replication] +{% endif %} + +# c.f. https://github.com/matrix-org/synapse/tree/master/contrib/systemd-with-workers/README.md +worker_app: synapse.app.homeserver + +# thx https://oznetnerd.com/2017/04/18/jinja2-selectattr-filter/ +# reduce the main worker's offerings to core homeserver business +{% if matrix_synapse_workers_enabled_list|selectattr('type', 'equalto', 'appservice')|list %} +notify_appservices: false +{% endif %} +{% if matrix_synapse_workers_enabled_list|selectattr('type', 'equalto', 'federation_sender')|list %} +send_federation: false +{% endif %} +{% if matrix_synapse_workers_enabled_list|selectattr('type', 'equalto', 'media_repository')|list %} +enable_media_repo: false +{% endif %} +{% if matrix_synapse_workers_enabled_list|selectattr('type', 'equalto', 'pusher')|list %} +start_pushers: false +{% endif %} +{% if matrix_synapse_workers_enabled_list|selectattr('type', 'equalto', 'user_dir')|list %} +update_user_directory: false +{% endif %} + +daemonize: false +{% endif %} + +# Forward extremities can build up in a room due to networking delays between +# homeservers. Once this happens in a large room, calculation of the state of +# that room can become quite expensive. To mitigate this, once the number of +# forward extremities reaches a given threshold, Synapse will send an +# org.matrix.dummy_event event, which will reduce the forward extremities +# in the room. +# +# This setting defines the threshold (i.e. number of forward extremities in the +# room) at which dummy events are sent. The default value is 10. +# +#dummy_events_threshold: 5 + + +## Homeserver blocking ## + +# How to reach the server admin, used in ResourceLimitError +# +#admin_contact: 'mailto:admin@server.com' + +# Global blocking +# +#hs_disabled: False +#hs_disabled_message: 'Human readable reason for why the HS is blocked' +#hs_disabled_limit_type: 'error code(str), to help clients decode reason' + +# Monthly Active User Blocking +# +# Used in cases where the admin or server owner wants to limit to the +# number of monthly active users. +# +# 'limit_usage_by_mau' disables/enables monthly active user blocking. When +# enabled and a limit is reached the server returns a 'ResourceLimitError' +# with error type Codes.RESOURCE_LIMIT_EXCEEDED +# +# 'max_mau_value' is the hard limit of monthly active users above which +# the server will start blocking user actions. +# +# 'mau_trial_days' is a means to add a grace period for active users. It +# means that users must be active for this number of days before they +# can be considered active and guards against the case where lots of users +# sign up in a short space of time never to return after their initial +# session. +# +#limit_usage_by_mau: False +#max_mau_value: 50 +#mau_trial_days: 2 + +# If enabled, the metrics for the number of monthly active users will +# be populated, however no one will be limited. If limit_usage_by_mau +# is true, this is implied to be true. +# +#mau_stats_only: False + +# Sometimes the server admin will want to ensure certain accounts are +# never blocked by mau checking. These accounts are specified here. +# +#mau_limit_reserved_threepids: +# - medium: 'email' +# address: 'reserved_user@example.com' + +# Used by phonehome stats to group together related servers. +#server_context: context + +# Resource-constrained homeserver settings +# +# When this is enabled, the room "complexity" will be checked before a user +# joins a new remote room. If it is above the complexity limit, the server will +# disallow joining, or will instantly leave. +# +# Room complexity is an arbitrary measure based on factors such as the number of +# users in the room. +# +limit_remote_rooms: + # Uncomment to enable room complexity checking. + # + #enabled: true + + # the limit above which rooms cannot be joined. The default is 1.0. + # + #complexity: 0.5 + + # override the error which is returned when the room is too complex. + # + #complexity_error: "This room is too complex." + + # allow server admins to join complex rooms. Default is false. + # + #admins_can_join: true + +# Whether to require a user to be in the room to add an alias to it. +# Defaults to 'true'. +# +#require_membership_for_aliases: false + +# Whether to allow per-room membership profiles through the send of membership +# events with profile information that differ from the target's global profile. +# Defaults to 'true'. +# +#allow_per_room_profiles: false + +# How long to keep redacted events in unredacted form in the database. After +# this period redacted events get replaced with their redacted form in the DB. +# +# Defaults to `7d`. Set to `null` to disable. +# +#redaction_retention_period: 28d + +redaction_retention_period: {{ matrix_synapse_redaction_retention_period }} + +# How long to track users' last seen time and IPs in the database. +# +# Defaults to `28d`. Set to `null` to disable clearing out of old rows. +# +#user_ips_max_age: 14d + +user_ips_max_age: {{ matrix_synapse_user_ips_max_age }} + +# Message retention policy at the server level. +# +# Room admins and mods can define a retention period for their rooms using the +# 'm.room.retention' state event, and server admins can cap this period by setting +# the 'allowed_lifetime_min' and 'allowed_lifetime_max' config options. +# +# If this feature is enabled, Synapse will regularly look for and purge events +# which are older than the room's maximum retention period. Synapse will also +# filter events received over federation so that events that should have been +# purged are ignored and not stored again. +# +retention: + # The message retention policies feature is disabled by default. Uncomment the + # following line to enable it. + # + #enabled: true + + # Default retention policy. If set, Synapse will apply it to rooms that lack the + # 'm.room.retention' state event. Currently, the value of 'min_lifetime' doesn't + # matter much because Synapse doesn't take it into account yet. + # + #default_policy: + # min_lifetime: 1d + # max_lifetime: 1y + + # Retention policy limits. If set, and the state of a room contains a + # 'm.room.retention' event in its state which contains a 'min_lifetime' or a + # 'max_lifetime' that's out of these bounds, Synapse will cap the room's policy + # to these limits when running purge jobs. + # + #allowed_lifetime_min: 1d + #allowed_lifetime_max: 1y + + # Server admins can define the settings of the background jobs purging the + # events which lifetime has expired under the 'purge_jobs' section. + # + # If no configuration is provided, a single job will be set up to delete expired + # events in every room daily. + # + # Each job's configuration defines which range of message lifetimes the job + # takes care of. For example, if 'shortest_max_lifetime' is '2d' and + # 'longest_max_lifetime' is '3d', the job will handle purging expired events in + # rooms whose state defines a 'max_lifetime' that's both higher than 2 days, and + # lower than or equal to 3 days. Both the minimum and the maximum value of a + # range are optional, e.g. a job with no 'shortest_max_lifetime' and a + # 'longest_max_lifetime' of '3d' will handle every room with a retention policy + # which 'max_lifetime' is lower than or equal to three days. + # + # The rationale for this per-job configuration is that some rooms might have a + # retention policy with a low 'max_lifetime', where history needs to be purged + # of outdated messages on a more frequent basis than for the rest of the rooms + # (e.g. every 12h), but not want that purge to be performed by a job that's + # iterating over every room it knows, which could be heavy on the server. + # + # If any purge job is configured, it is strongly recommended to have at least + # a single job with neither 'shortest_max_lifetime' nor 'longest_max_lifetime' + # set, or one job without 'shortest_max_lifetime' and one job without + # 'longest_max_lifetime' set. Otherwise some rooms might be ignored, even if + # 'allowed_lifetime_min' and 'allowed_lifetime_max' are set, because capping a + # room's policy to these values is done after the policies are retrieved from + # Synapse's database (which is done using the range specified in a purge job's + # configuration). + # + #purge_jobs: + # - longest_max_lifetime: 3d + # interval: 12h + # - shortest_max_lifetime: 3d + # interval: 1d + +# Inhibits the /requestToken endpoints from returning an error that might leak +# information about whether an e-mail address is in use or not on this +# homeserver. +# Note that for some endpoints the error situation is the e-mail already being +# used, and for others the error is entering the e-mail being unused. +# If this option is enabled, instead of returning an error, these endpoints will +# act as if no error happened and return a fake session ID ('sid') to clients. +# +#request_token_inhibit_3pid_errors: true + +# A list of domains that the domain portion of 'next_link' parameters +# must match. +# +# This parameter is optionally provided by clients while requesting +# validation of an email or phone number, and maps to a link that +# users will be automatically redirected to after validation +# succeeds. Clients can make use this parameter to aid the validation +# process. +# +# The whitelist is applied whether the homeserver or an +# identity server is handling validation. +# +# The default value is no whitelist functionality; all domains are +# allowed. Setting this value to an empty list will instead disallow +# all domains. +# +#next_link_domain_whitelist: ["matrix.org"] + + +## TLS ## + +# PEM-encoded X509 certificate for TLS. +# This certificate, as of Synapse 1.0, will need to be a valid and verifiable +# certificate, signed by a recognised Certificate Authority. +# +# Be sure to use a `.pem` file that includes the full certificate chain including +# any intermediate certificates (for instance, if using certbot, use +# `fullchain.pem` as your certificate, not `cert.pem`). +# +tls_certificate_path: {{ matrix_synapse_tls_certificate_path|to_json }} + +# PEM-encoded private key for TLS +# +tls_private_key_path: {{ matrix_synapse_tls_private_key_path|to_json }} + +# Whether to verify TLS server certificates for outbound federation requests. +# +# Defaults to `true`. To disable certificate verification, uncomment the +# following line. +# +#federation_verify_certificates: false + +# The minimum TLS version that will be used for outbound federation requests. +# +# Defaults to `1`. Configurable to `1`, `1.1`, `1.2`, or `1.3`. Note +# that setting this value higher than `1.2` will prevent federation to most +# of the public Matrix network: only configure it to `1.3` if you have an +# entirely private federation setup and you can ensure TLS 1.3 support. +# +#federation_client_minimum_tls_version: 1.2 + +# Skip federation certificate verification on the following whitelist +# of domains. +# +# This setting should only be used in very specific cases, such as +# federation over Tor hidden services and similar. For private networks +# of homeservers, you likely want to use a private CA instead. +# +# Only effective if federation_verify_certicates is `true`. +# +#federation_certificate_verification_whitelist: +# - lon.example.com +# - *.domain.com +# - *.onion + +# List of custom certificate authorities for federation traffic. +# +# This setting should only normally be used within a private network of +# homeservers. +# +# Note that this list will replace those that are provided by your +# operating environment. Certificates must be in PEM format. +# +#federation_custom_ca_list: +# - myCA1.pem +# - myCA2.pem +# - myCA3.pem + + +## Federation ## + +# Restrict federation to the following whitelist of domains. +# N.B. we recommend also firewalling your federation listener to limit +# inbound federation traffic as early as possible, rather than relying +# purely on this application-layer restriction. If not specified, the +# default is to whitelist everything. +# +#federation_domain_whitelist: +# - lon.example.com +# - nyc.example.com +# - syd.example.com +{% if matrix_synapse_federation_domain_whitelist is not none %} +{# Cannot use `|to_nice_yaml` here, as an empty list does not get serialized properly by it. #} +federation_domain_whitelist: {{ matrix_synapse_federation_domain_whitelist|to_json }} +{% endif %} + +# Report prometheus metrics on the age of PDUs being sent to and received from +# the following domains. This can be used to give an idea of "delay" on inbound +# and outbound federation, though be aware that any delay can be due to problems +# at either end or with the intermediate network. +# +# By default, no domains are monitored in this way. +# +#federation_metrics_domains: +# - matrix.org +# - example.com + +# Uncomment to disable profile lookup over federation. By default, the +# Federation API allows other homeservers to obtain profile data of any user +# on this homeserver. Defaults to 'true'. +# +#allow_profile_lookup_over_federation: false + +# Uncomment to disable device display name lookup over federation. By default, the +# Federation API allows other homeservers to obtain device display names of any user +# on this homeserver. Defaults to 'true'. +# +#allow_device_name_lookup_over_federation: false + + +## Caching ## + +# Caching can be configured through the following options. +# +# A cache 'factor' is a multiplier that can be applied to each of +# Synapse's caches in order to increase or decrease the maximum +# number of entries that can be stored. + +# The number of events to cache in memory. Not affected by +# caches.global_factor. +# +event_cache_size: "{{ matrix_synapse_event_cache_size }}" + +caches: + # Controls the global cache factor, which is the default cache factor + # for all caches if a specific factor for that cache is not otherwise + # set. + # + # This can also be set by the "SYNAPSE_CACHE_FACTOR" environment + # variable. Setting by environment variable takes priority over + # setting through the config file. + # + # Defaults to 0.5, which will half the size of all caches. + # + global_factor: {{ matrix_synapse_caches_global_factor }} + + # A dictionary of cache name to cache factor for that individual + # cache. Overrides the global cache factor for a given cache. + # + # These can also be set through environment variables comprised + # of "SYNAPSE_CACHE_FACTOR_" + the name of the cache in capital + # letters and underscores. Setting by environment variable + # takes priority over setting through the config file. + # Ex. SYNAPSE_CACHE_FACTOR_GET_USERS_WHO_SHARE_ROOM_WITH_USER=2.0 + # + # Some caches have '*' and other characters that are not + # alphanumeric or underscores. These caches can be named with or + # without the special characters stripped. For example, to specify + # the cache factor for `*stateGroupCache*` via an environment + # variable would be `SYNAPSE_CACHE_FACTOR_STATEGROUPCACHE=2.0`. + # + per_cache_factors: + #get_users_who_share_room_with_user: 2.0 + + +## Database ## + +database: + # The database engine name + name: "psycopg2" + args: + user: {{ matrix_synapse_database_user|string|to_json }} + password: {{ matrix_synapse_database_password|string|to_json }} + database: "{{ matrix_synapse_database_database }}" + host: "{{ matrix_synapse_database_host }}" + port: {{ matrix_synapse_database_port }} + cp_min: 5 + cp_max: 10 + + +## Logging ## + +# A yaml python logging config file as described by +# https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema +# +log_config: "/data/{{ matrix_server_fqn_matrix }}.log.config" + + +## Ratelimiting ## + +# Ratelimiting settings for client actions (registration, login, messaging). +# +# Each ratelimiting configuration is made of two parameters: +# - per_second: number of requests a client can send per second. +# - burst_count: number of requests a client can send before being throttled. +# +# Synapse currently uses the following configurations: +# - one for messages that ratelimits sending based on the account the client +# is using +# - one for registration that ratelimits registration requests based on the +# client's IP address. +# - one for login that ratelimits login requests based on the client's IP +# address. +# - one for login that ratelimits login requests based on the account the +# client is attempting to log into. +# - one for login that ratelimits login requests based on the account the +# client is attempting to log into, based on the amount of failed login +# attempts for this account. +# - one for ratelimiting redactions by room admins. If this is not explicitly +# set then it uses the same ratelimiting as per rc_message. This is useful +# to allow room admins to deal with abuse quickly. +# - two for ratelimiting number of rooms a user can join, "local" for when +# users are joining rooms the server is already in (this is cheap) vs +# "remote" for when users are trying to join rooms not on the server (which +# can be more expensive) +# - one for ratelimiting how often a user or IP can attempt to validate a 3PID. +# - two for ratelimiting how often invites can be sent in a room or to a +# specific user. +# +# The defaults are as shown below. +# +#rc_message: +# per_second: 0.2 +# burst_count: 10 +rc_message: {{ matrix_synapse_rc_message|to_json }} +# +#rc_registration: +# per_second: 0.17 +# burst_count: 3 +rc_registration: {{ matrix_synapse_rc_registration|to_json }} +# +#rc_login: +# address: +# per_second: 0.17 +# burst_count: 3 +# account: +# per_second: 0.17 +# burst_count: 3 +# failed_attempts: +# per_second: 0.17 +# burst_count: 3 +rc_login: {{ matrix_synapse_rc_login|to_json }} +# +#rc_admin_redaction: +# per_second: 1 +# burst_count: 50 +rc_admin_redaction: {{ matrix_synapse_rc_admin_redaction|to_json }} +# +#rc_joins: +# local: +# per_second: 0.1 +# burst_count: 10 +# remote: +# per_second: 0.01 +# burst_count: 10 +rc_joins: {{ matrix_synapse_rc_joins|to_json }} +# +#rc_3pid_validation: +# per_second: 0.003 +# burst_count: 5 +# +#rc_invites: +# per_room: +# per_second: 0.3 +# burst_count: 10 +# per_user: +# per_second: 0.003 +# burst_count: 5 + +# Ratelimiting settings for incoming federation +# +# The rc_federation configuration is made up of the following settings: +# - window_size: window size in milliseconds +# - sleep_limit: number of federation requests from a single server in +# a window before the server will delay processing the request. +# - sleep_delay: duration in milliseconds to delay processing events +# from remote servers by if they go over the sleep limit. +# - reject_limit: maximum number of concurrent federation requests +# allowed from a single server +# - concurrent: number of federation requests to concurrently process +# from a single server +# +# The defaults are as shown below. +# +#rc_federation: +# window_size: 1000 +# sleep_limit: 10 +# sleep_delay: 500 +# reject_limit: 50 +# concurrent: 3 +rc_federation: {{ matrix_synapse_rc_federation|to_json }} + +# Target outgoing federation transaction frequency for sending read-receipts, +# per-room. +# +# If we end up trying to send out more read-receipts, they will get buffered up +# into fewer transactions. +# +#federation_rr_transactions_per_room_per_second: 50 +federation_rr_transactions_per_room_per_second: {{ matrix_synapse_federation_rr_transactions_per_room_per_second }} + + + +## Media Store ## + +# Enable the media store service in the Synapse master. Uncomment the +# following if you are using a separate media store worker. +# +#enable_media_repo: false + +# Directory where uploaded images and attachments are stored. +# +media_store_path: "/matrix-media-store-parent/{{ matrix_synapse_media_store_directory_name }}" + +# Media storage providers allow media to be stored in different +# locations. +# +#media_storage_providers: +# - module: file_system +# # Whether to store newly uploaded local files +# store_local: false +# # Whether to store newly downloaded remote files +# store_remote: false +# # Whether to wait for successful storage for local uploads +# store_synchronous: false +# config: +# directory: /mnt/some/other/directory + +# The largest allowed upload size in bytes +# +# If you are using a reverse proxy you may also need to set this value in +# your reverse proxy's config. Notably Nginx has a small max body size by default. +# See https://matrix-org.github.io/synapse/develop/reverse_proxy.html. +# +max_upload_size: "{{ matrix_synapse_max_upload_size_mb }}M" + +# Maximum number of pixels that will be thumbnailed +# +#max_image_pixels: 32M + +# Whether to generate new thumbnails on the fly to precisely match +# the resolution requested by the client. If true then whenever +# a new resolution is requested by the client the server will +# generate a new thumbnail. If false the server will pick a thumbnail +# from a precalculated list. +# +#dynamic_thumbnails: false + +# List of thumbnails to precalculate when an image is uploaded. +# +#thumbnail_sizes: +# - width: 32 +# height: 32 +# method: crop +# - width: 96 +# height: 96 +# method: crop +# - width: 320 +# height: 240 +# method: scale +# - width: 640 +# height: 480 +# method: scale +# - width: 800 +# height: 600 +# method: scale + +# Is the preview URL API enabled? +# +# 'false' by default: uncomment the following to enable it (and specify a +# url_preview_ip_range_blacklist blacklist). +# +url_preview_enabled: {{ matrix_synapse_url_preview_enabled|to_json }} + +# List of IP address CIDR ranges that the URL preview spider is denied +# from accessing. There are no defaults: you must explicitly +# specify a list for URL previewing to work. You should specify any +# internal services in your network that you do not want synapse to try +# to connect to, otherwise anyone in any Matrix room could cause your +# synapse to issue arbitrary GET requests to your internal services, +# causing serious security issues. +# +# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly +# listed here, since they correspond to unroutable addresses.) +# +# This must be specified if url_preview_enabled is set. It is recommended that +# you uncomment the following list as a starting point. +# +url_preview_ip_range_blacklist: + - '127.0.0.0/8' + - '10.0.0.0/8' + - '172.16.0.0/12' + - '192.168.0.0/16' + - '100.64.0.0/10' + - '192.0.0.0/24' + - '169.254.0.0/16' + - '192.88.99.0/24' + - '198.18.0.0/15' + - '192.0.2.0/24' + - '198.51.100.0/24' + - '203.0.113.0/24' + - '224.0.0.0/4' + - '::1/128' + - 'fe80::/10' + - 'fc00::/7' + - '2001:db8::/32' + - 'ff00::/8' + - 'fec0::/10' + +# List of IP address CIDR ranges that the URL preview spider is allowed +# to access even if they are specified in url_preview_ip_range_blacklist. +# This is useful for specifying exceptions to wide-ranging blacklisted +# target IP ranges - e.g. for enabling URL previews for a specific private +# website only visible in your network. +# +#url_preview_ip_range_whitelist: +# - '192.168.1.1' + +# Optional list of URL matches that the URL preview spider is +# denied from accessing. You should use url_preview_ip_range_blacklist +# in preference to this, otherwise someone could define a public DNS +# entry that points to a private IP address and circumvent the blacklist. +# This is more useful if you know there is an entire shape of URL that +# you know that will never want synapse to try to spider. +# +# Each list entry is a dictionary of url component attributes as returned +# by urlparse.urlsplit as applied to the absolute form of the URL. See +# https://docs.python.org/2/library/urlparse.html#urlparse.urlsplit +# The values of the dictionary are treated as an filename match pattern +# applied to that component of URLs, unless they start with a ^ in which +# case they are treated as a regular expression match. If all the +# specified component matches for a given list item succeed, the URL is +# blacklisted. +# +#url_preview_url_blacklist: +# # blacklist any URL with a username in its URI +# - username: '*' +# +# # blacklist all *.google.com URLs +# - netloc: 'google.com' +# - netloc: '*.google.com' +# +# # blacklist all plain HTTP URLs +# - scheme: 'http' +# +# # blacklist http(s)://www.acme.com/foo +# - netloc: 'www.acme.com' +# path: '/foo' +# +# # blacklist any URL with a literal IPv4 address +# - netloc: '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' + +# The largest allowed URL preview spidering size in bytes +# +max_spider_size: 10M + +# A list of values for the Accept-Language HTTP header used when +# downloading webpages during URL preview generation. This allows +# Synapse to specify the preferred languages that URL previews should +# be in when communicating with remote servers. +# +# Each value is a IETF language tag; a 2-3 letter identifier for a +# language, optionally followed by subtags separated by '-', specifying +# a country or region variant. +# +# Multiple values can be provided, and a weight can be added to each by +# using quality value syntax (;q=). '*' translates to any language. +# +# Defaults to "en". +# +# Example: +# +# url_preview_accept_language: +# - en-UK +# - en-US;q=0.9 +# - fr;q=0.8 +# - *;q=0.7 +# +url_preview_accept_language: +# - en + + +## Captcha ## +# See docs/CAPTCHA_SETUP.md for full details of configuring this. + +# This homeserver's ReCAPTCHA public key. Must be specified if +# enable_registration_captcha is enabled. +# +recaptcha_public_key: {{ matrix_synapse_recaptcha_public_key|to_json }} + +# This homeserver's ReCAPTCHA private key. Must be specified if +# enable_registration_captcha is enabled. +# +recaptcha_private_key: {{ matrix_synapse_recaptcha_private_key|to_json }} + +# Uncomment to enable ReCaptcha checks when registering, preventing signup +# unless a captcha is answered. Requires a valid ReCaptcha +# public/private key. Defaults to 'false'. +# +enable_registration_captcha: {{ matrix_synapse_enable_registration_captcha|to_json }} + +# The API endpoint to use for verifying m.login.recaptcha responses. +# Defaults to "https://www.recaptcha.net/recaptcha/api/siteverify". +# +#recaptcha_siteverify_api: "https://my.recaptcha.site" + + +## TURN ## + +# The public URIs of the TURN server to give to clients +# +turn_uris: {{ matrix_synapse_turn_uris|to_json }} + +# The shared secret used to compute passwords for the TURN server +# +turn_shared_secret: {{ matrix_synapse_turn_shared_secret|string|to_json }} + +# The Username and password if the TURN server needs them and +# does not use a token +# +#turn_username: "TURNSERVER_USERNAME" +#turn_password: "TURNSERVER_PASSWORD" + +# How long generated TURN credentials last +# +#turn_user_lifetime: 1h + +# Whether guests should be allowed to use the TURN server. +# This defaults to True, otherwise VoIP will be unreliable for guests. +# However, it does introduce a slight security risk as it allows users to +# connect to arbitrary endpoints without having first signed up for a +# valid account (e.g. by passing a CAPTCHA). +# +turn_allow_guests: {{ matrix_synapse_turn_allow_guests|to_json }} + + +## Registration ## +# +# Registration can be rate-limited using the parameters in the "Ratelimiting" +# section of this file. + +# Enable registration for new users. +# +enable_registration: {{ matrix_synapse_enable_registration|to_json }} + +# Time that a user's session remains valid for, after they log in. +# +# Note that this is not currently compatible with guest logins. +# +# Note also that this is calculated at login time: changes are not applied +# retrospectively to users who have already logged in. +# +# By default, this is infinite. +# +#session_lifetime: 24h + +# The user must provide all of the below types of 3PID when registering. +# +#registrations_require_3pid: +# - email +# - msisdn +{% if matrix_synapse_registrations_require_3pid|length > 0 %} +registrations_require_3pid: {{ matrix_synapse_registrations_require_3pid|to_json }} +{% endif %} + +# Explicitly disable asking for MSISDNs from the registration +# flow (overrides registrations_require_3pid if MSISDNs are set as required) +# +#disable_msisdn_registration: true + +# Mandate that users are only allowed to associate certain formats of +# 3PIDs with accounts on this server. +# +#allowed_local_3pids: +# - medium: email +# pattern: '^[^@]+@matrix\.org$' +# - medium: email +# pattern: '^[^@]+@vector\.im$' +# - medium: msisdn +# pattern: '\+44' +{% if matrix_synapse_allowed_local_3pids|length > 0 %} +allowed_local_3pids: {{ matrix_synapse_allowed_local_3pids|to_json }} +{% endif %} + +# Enable 3PIDs lookup requests to identity servers from this server. +# +#enable_3pid_lookup: true + +# If set, allows registration of standard or admin accounts by anyone who +# has the shared secret, even if registration is otherwise disabled. +# +registration_shared_secret: {{ matrix_synapse_registration_shared_secret|string|to_json }} + +# Set the number of bcrypt rounds used to generate password hash. +# Larger numbers increase the work factor needed to generate the hash. +# The default number is 12 (which equates to 2^12 rounds). +# N.B. that increasing this will exponentially increase the time required +# to register or login - e.g. 24 => 2^24 rounds which will take >20 mins. +# +#bcrypt_rounds: 12 + +# Allows users to register as guests without a password/email/etc, and +# participate in rooms hosted on this server which have been made +# accessible to anonymous users. +# +allow_guest_access: {{ matrix_synapse_allow_guest_access|to_json }} + +# The identity server which we suggest that clients should use when users log +# in on this server. +# +# (By default, no suggestion is made, so it is left up to the client. +# This setting is ignored unless public_baseurl is also set.) +# +#default_identity_server: https://matrix.org + +# Handle threepid (email/phone etc) registration and password resets through a set of +# *trusted* identity servers. Note that this allows the configured identity server to +# reset passwords for accounts! +# +# Be aware that if `email` is not set, and SMTP options have not been +# configured in the email config block, registration and user password resets via +# email will be globally disabled. +# +# Additionally, if `msisdn` is not set, registration and password resets via msisdn +# will be disabled regardless, and users will not be able to associate an msisdn +# identifier to their account. This is due to Synapse currently not supporting +# any method of sending SMS messages on its own. +# +# To enable using an identity server for operations regarding a particular third-party +# identifier type, set the value to the URL of that identity server as shown in the +# examples below. +# +# Servers handling the these requests must answer the `/requestToken` endpoints defined +# by the Matrix Identity Service API specification: +# https://matrix.org/docs/spec/identity_service/latest +# +# If a delegate is specified, the config option public_baseurl must also be filled out. +# +account_threepid_delegates: + email: {{ matrix_synapse_account_threepid_delegates_email|to_json }} + msisdn: {{ matrix_synapse_account_threepid_delegates_msisdn|to_json }} + +# Whether users are allowed to change their displayname after it has +# been initially set. Useful when provisioning users based on the +# contents of a third-party directory. +# +# Does not apply to server administrators. Defaults to 'true' +# +#enable_set_displayname: false + +# Whether users are allowed to change their avatar after it has been +# initially set. Useful when provisioning users based on the contents +# of a third-party directory. +# +# Does not apply to server administrators. Defaults to 'true' +# +#enable_set_avatar_url: false + +# Whether users can change the 3PIDs associated with their accounts +# (email address and msisdn). +# +# Defaults to 'true' +# +#enable_3pid_changes: false + +# Users who register on this homeserver will automatically be joined +# to these rooms. +# +# By default, any room aliases included in this list will be created +# as a publicly joinable room when the first user registers for the +# homeserver. This behaviour can be customised with the settings below. +# If the room already exists, make certain it is a publicly joinable +# room. The join rule of the room must be set to 'public'. +# +#auto_join_rooms: +# - "#example:example.com" +{% if matrix_synapse_auto_join_rooms|length > 0 %} +auto_join_rooms: +{{ matrix_synapse_auto_join_rooms|to_nice_yaml }} +{% endif %} + +# Where auto_join_rooms are specified, setting this flag ensures that the +# the rooms exist by creating them when the first user on the +# homeserver registers. +# +# By default the auto-created rooms are publicly joinable from any federated +# server. Use the autocreate_auto_join_rooms_federated and +# autocreate_auto_join_room_preset settings below to customise this behaviour. +# +# Setting to false means that if the rooms are not manually created, +# users cannot be auto-joined since they do not exist. +# +# Defaults to true. Uncomment the following line to disable automatically +# creating auto-join rooms. +# +autocreate_auto_join_rooms: {{ matrix_synapse_autocreate_auto_join_rooms|to_json }} + +# Whether the auto_join_rooms that are auto-created are available via +# federation. Only has an effect if autocreate_auto_join_rooms is true. +# +# Note that whether a room is federated cannot be modified after +# creation. +# +# Defaults to true: the room will be joinable from other servers. +# Uncomment the following to prevent users from other homeservers from +# joining these rooms. +# +#autocreate_auto_join_rooms_federated: false + +# The room preset to use when auto-creating one of auto_join_rooms. Only has an +# effect if autocreate_auto_join_rooms is true. +# +# This can be one of "public_chat", "private_chat", or "trusted_private_chat". +# If a value of "private_chat" or "trusted_private_chat" is used then +# auto_join_mxid_localpart must also be configured. +# +# Defaults to "public_chat", meaning that the room is joinable by anyone, including +# federated servers if autocreate_auto_join_rooms_federated is true (the default). +# Uncomment the following to require an invitation to join these rooms. +# +#autocreate_auto_join_room_preset: private_chat + +# The local part of the user id which is used to create auto_join_rooms if +# autocreate_auto_join_rooms is true. If this is not provided then the +# initial user account that registers will be used to create the rooms. +# +# The user id is also used to invite new users to any auto-join rooms which +# are set to invite-only. +# +# It *must* be configured if autocreate_auto_join_room_preset is set to +# "private_chat" or "trusted_private_chat". +# +# Note that this must be specified in order for new users to be correctly +# invited to any auto-join rooms which have been set to invite-only (either +# at the time of creation or subsequently). +# +# Note that, if the room already exists, this user must be joined and +# have the appropriate permissions to invite new members. +# +#auto_join_mxid_localpart: system + +# When auto_join_rooms is specified, setting this flag to false prevents +# guest accounts from being automatically joined to the rooms. +# +# Defaults to true. +# +#auto_join_rooms_for_guests: false + + +## Metrics ### + +# Enable collection and rendering of performance metrics +# +enable_metrics: {{ matrix_synapse_metrics_enabled|to_json }} + +# Enable sentry integration +# NOTE: While attempts are made to ensure that the logs don't contain +# any sensitive information, this cannot be guaranteed. By enabling +# this option the sentry server may therefore receive sensitive +# information, and it in turn may then diseminate sensitive information +# through insecure notification channels if so configured. +# +{% if matrix_synapse_sentry_dsn != "" %} +sentry: + dsn: {{ matrix_synapse_sentry_dsn|to_json }} +{% endif %} + +# Flags to enable Prometheus metrics which are not suitable to be +# enabled by default, either for performance reasons or limited use. +# +metrics_flags: + # Publish synapse_federation_known_servers, a gauge of the number of + # servers this homeserver knows about, including itself. May cause + # performance problems on large homeservers. + # + #known_servers: true + +# Whether or not to report anonymized homeserver usage statistics. +# +report_stats: {{ matrix_synapse_report_stats|to_json }} + +# The endpoint to report the anonymized homeserver usage statistics to. +# Defaults to https://matrix.org/report-usage-stats/push +# +#report_stats_endpoint: https://example.com/report-usage-stats/push + + +## API Configuration ## + +# Controls for the state that is shared with users who receive an invite +# to a room +# +room_prejoin_state: + # By default, the following state event types are shared with users who + # receive invites to the room: + # + # - m.room.join_rules + # - m.room.canonical_alias + # - m.room.avatar + # - m.room.encryption + # - m.room.name + # - m.room.create + # + # Uncomment the following to disable these defaults (so that only the event + # types listed in 'additional_event_types' are shared). Defaults to 'false'. + # + #disable_default_event_types: true + + # Additional state event types to share with users when they are invited + # to a room. + # + # By default, this list is empty (so only the default event types are shared). + # + #additional_event_types: + # - org.example.custom.event.type + + +# A list of application service config files to use +# +app_service_config_files: {{ matrix_synapse_app_service_config_files|to_json }} + +# Uncomment to enable tracking of application service IP addresses. Implicitly +# enables MAU tracking for application service users. +# +#track_appservice_user_ips: True + + +# a secret which is used to sign access tokens. If none is specified, +# the registration_shared_secret is used, if one is given; otherwise, +# a secret key is derived from the signing key. +# +macaroon_secret_key: {{ matrix_synapse_macaroon_secret_key|string|to_json }} + +# a secret which is used to calculate HMACs for form values, to stop +# falsification of values. Must be specified for the User Consent +# forms to work. +# +form_secret: {{ matrix_synapse_form_secret|string|to_json }} + +## Signing Keys ## + +# Path to the signing key to sign messages with +# +signing_key_path: "/data/{{ matrix_server_fqn_matrix }}.signing.key" + +# The keys that the server used to sign messages with but won't use +# to sign new messages. +# +old_signing_keys: + # For each key, `key` should be the base64-encoded public key, and + # `expired_ts`should be the time (in milliseconds since the unix epoch) that + # it was last used. + # + # It is possible to build an entry from an old signing.key file using the + # `export_signing_key` script which is provided with synapse. + # + # For example: + # + #"ed25519:id": { key: "base64string", expired_ts: 123456789123 } + +# How long key response published by this server is valid for. +# Used to set the valid_until_ts in /key/v2 APIs. +# Determines how quickly servers will query to check which keys +# are still valid. +# +#key_refresh_interval: 1d + +# The trusted servers to download signing keys from. +# +# When we need to fetch a signing key, each server is tried in parallel. +# +# Normally, the connection to the key server is validated via TLS certificates. +# Additional security can be provided by configuring a `verify key`, which +# will make synapse check that the response is signed by that key. +# +# This setting supercedes an older setting named `perspectives`. The old format +# is still supported for backwards-compatibility, but it is deprecated. +# +# 'trusted_key_servers' defaults to matrix.org, but using it will generate a +# warning on start-up. To suppress this warning, set +# 'suppress_key_server_warning' to true. +# +# Options for each entry in the list include: +# +# server_name: the name of the server. required. +# +# verify_keys: an optional map from key id to base64-encoded public key. +# If specified, we will check that the response is signed by at least +# one of the given keys. +# +# accept_keys_insecurely: a boolean. Normally, if `verify_keys` is unset, +# and federation_verify_certificates is not `true`, synapse will refuse +# to start, because this would allow anyone who can spoof DNS responses +# to masquerade as the trusted key server. If you know what you are doing +# and are sure that your network environment provides a secure connection +# to the key server, you can set this to `true` to override this +# behaviour. +# +# An example configuration might look like: +# +#trusted_key_servers: +# - server_name: "my_trusted_server.example.com" +# verify_keys: +# "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr" +# - server_name: "my_other_trusted_server.example.com" +# +trusted_key_servers: {{ matrix_synapse_trusted_key_servers|to_json }} + + +# Uncomment the following to disable the warning that is emitted when the +# trusted_key_servers include 'matrix.org'. See above. +# +#suppress_key_server_warning: true + +# The signing keys to use when acting as a trusted key server. If not specified +# defaults to the server signing key. +# +# Can contain multiple keys, one per line. +# +#key_server_signing_keys_path: "key_server_signing_keys.key" + + +## Single sign-on integration ## + +# The following settings can be used to make Synapse use a single sign-on +# provider for authentication, instead of its internal password database. +# +# You will probably also want to set the following options to `false` to +# disable the regular login/registration flows: +# * enable_registration +# * password_config.enabled +# +# You will also want to investigate the settings under the "sso" configuration +# section below. + +# Enable SAML2 for registration and login. Uses pysaml2. +# +# At least one of `sp_config` or `config_path` must be set in this section to +# enable SAML login. +# +# Once SAML support is enabled, a metadata file will be exposed at +# https://:/_synapse/client/saml2/metadata.xml, which you may be able to +# use to configure your SAML IdP with. Alternatively, you can manually configure +# the IdP to use an ACS location of +# https://:/_synapse/client/saml2/authn_response. +# +saml2_config: + # `sp_config` is the configuration for the pysaml2 Service Provider. + # See pysaml2 docs for format of config. + # + # Default values will be used for the 'entityid' and 'service' settings, + # so it is not normally necessary to specify them unless you need to + # override them. + # + sp_config: + # Point this to the IdP's metadata. You must provide either a local + # file via the `local` attribute or (preferably) a URL via the + # `remote` attribute. + # + #metadata: + # local: ["saml2/idp.xml"] + # remote: + # - url: https://our_idp/metadata.xml + + # Allowed clock difference in seconds between the homeserver and IdP. + # + # Uncomment the below to increase the accepted time difference from 0 to 3 seconds. + # + #accepted_time_diff: 3 + + # By default, the user has to go to our login page first. If you'd like + # to allow IdP-initiated login, set 'allow_unsolicited: true' in a + # 'service.sp' section: + # + #service: + # sp: + # allow_unsolicited: true + + # The examples below are just used to generate our metadata xml, and you + # may well not need them, depending on your setup. Alternatively you + # may need a whole lot more detail - see the pysaml2 docs! + + #description: ["My awesome SP", "en"] + #name: ["Test SP", "en"] + + #ui_info: + # display_name: + # - lang: en + # text: "Display Name is the descriptive name of your service." + # description: + # - lang: en + # text: "Description should be a short paragraph explaining the purpose of the service." + # information_url: + # - lang: en + # text: "https://example.com/terms-of-service" + # privacy_statement_url: + # - lang: en + # text: "https://example.com/privacy-policy" + # keywords: + # - lang: en + # text: ["Matrix", "Element"] + # logo: + # - lang: en + # text: "https://example.com/logo.svg" + # width: "200" + # height: "80" + + #organization: + # name: Example com + # display_name: + # - ["Example co", "en"] + # url: "http://example.com" + + #contact_person: + # - given_name: Bob + # sur_name: "the Sysadmin" + # email_address": ["admin@example.com"] + # contact_type": technical + + # Instead of putting the config inline as above, you can specify a + # separate pysaml2 configuration file: + # + #config_path: "/data/sp_conf.py" + + # The lifetime of a SAML session. This defines how long a user has to + # complete the authentication process, if allow_unsolicited is unset. + # The default is 15 minutes. + # + #saml_session_lifetime: 5m + + # An external module can be provided here as a custom solution to + # mapping attributes returned from a saml provider onto a matrix user. + # + user_mapping_provider: + # The custom module's class. Uncomment to use a custom module. + # + #module: mapping_provider.SamlMappingProvider + + # Custom configuration values for the module. Below options are + # intended for the built-in provider, they should be changed if + # using a custom module. This section will be passed as a Python + # dictionary to the module's `parse_config` method. + # + config: + # The SAML attribute (after mapping via the attribute maps) to use + # to derive the Matrix ID from. 'uid' by default. + # + # Note: This used to be configured by the + # saml2_config.mxid_source_attribute option. If that is still + # defined, its value will be used instead. + # + #mxid_source_attribute: displayName + + # The mapping system to use for mapping the saml attribute onto a + # matrix ID. + # + # Options include: + # * 'hexencode' (which maps unpermitted characters to '=xx') + # * 'dotreplace' (which replaces unpermitted characters with + # '.'). + # The default is 'hexencode'. + # + # Note: This used to be configured by the + # saml2_config.mxid_mapping option. If that is still defined, its + # value will be used instead. + # + #mxid_mapping: dotreplace + + # In previous versions of synapse, the mapping from SAML attribute to + # MXID was always calculated dynamically rather than stored in a + # table. For backwards- compatibility, we will look for user_ids + # matching such a pattern before creating a new account. + # + # This setting controls the SAML attribute which will be used for this + # backwards-compatibility lookup. Typically it should be 'uid', but if + # the attribute maps are changed, it may be necessary to change it. + # + # The default is 'uid'. + # + #grandfathered_mxid_source_attribute: upn + + # It is possible to configure Synapse to only allow logins if SAML attributes + # match particular values. The requirements can be listed under + # `attribute_requirements` as shown below. All of the listed attributes must + # match for the login to be permitted. + # + #attribute_requirements: + # - attribute: userGroup + # value: "staff" + # - attribute: department + # value: "sales" + + # If the metadata XML contains multiple IdP entities then the `idp_entityid` + # option must be set to the entity to redirect users to. + # + # Most deployments only have a single IdP entity and so should omit this + # option. + # + #idp_entityid: 'https://our_idp/entityid' + + +# List of OpenID Connect (OIDC) / OAuth 2.0 identity providers, for registration +# and login. +# +# Options for each entry include: +# +# idp_id: a unique identifier for this identity provider. Used internally +# by Synapse; should be a single word such as 'github'. +# +# Note that, if this is changed, users authenticating via that provider +# will no longer be recognised as the same user! +# +# (Use "oidc" here if you are migrating from an old "oidc_config" +# configuration.) +# +# idp_name: A user-facing name for this identity provider, which is used to +# offer the user a choice of login mechanisms. +# +# idp_icon: An optional icon for this identity provider, which is presented +# by clients and Synapse's own IdP picker page. If given, must be an +# MXC URI of the format mxc:///. (An easy way to +# obtain such an MXC URI is to upload an image to an (unencrypted) room +# and then copy the "url" from the source of the event.) +# +# idp_brand: An optional brand for this identity provider, allowing clients +# to style the login flow according to the identity provider in question. +# See the spec for possible options here. +# +# discover: set to 'false' to disable the use of the OIDC discovery mechanism +# to discover endpoints. Defaults to true. +# +# issuer: Required. The OIDC issuer. Used to validate tokens and (if discovery +# is enabled) to discover the provider's endpoints. +# +# client_id: Required. oauth2 client id to use. +# +# client_secret: oauth2 client secret to use. May be omitted if +# client_secret_jwt_key is given, or if client_auth_method is 'none'. +# +# client_secret_jwt_key: Alternative to client_secret: details of a key used +# to create a JSON Web Token to be used as an OAuth2 client secret. If +# given, must be a dictionary with the following properties: +# +# key: a pem-encoded signing key. Must be a suitable key for the +# algorithm specified. Required unless 'key_file' is given. +# +# key_file: the path to file containing a pem-encoded signing key file. +# Required unless 'key' is given. +# +# jwt_header: a dictionary giving properties to include in the JWT +# header. Must include the key 'alg', giving the algorithm used to +# sign the JWT, such as "ES256", using the JWA identifiers in +# RFC7518. +# +# jwt_payload: an optional dictionary giving properties to include in +# the JWT payload. Normally this should include an 'iss' key. +# +# client_auth_method: auth method to use when exchanging the token. Valid +# values are 'client_secret_basic' (default), 'client_secret_post' and +# 'none'. +# +# scopes: list of scopes to request. This should normally include the "openid" +# scope. Defaults to ["openid"]. +# +# authorization_endpoint: the oauth2 authorization endpoint. Required if +# provider discovery is disabled. +# +# token_endpoint: the oauth2 token endpoint. Required if provider discovery is +# disabled. +# +# userinfo_endpoint: the OIDC userinfo endpoint. Required if discovery is +# disabled and the 'openid' scope is not requested. +# +# jwks_uri: URI where to fetch the JWKS. Required if discovery is disabled and +# the 'openid' scope is used. +# +# skip_verification: set to 'true' to skip metadata verification. Use this if +# you are connecting to a provider that is not OpenID Connect compliant. +# Defaults to false. Avoid this in production. +# +# user_profile_method: Whether to fetch the user profile from the userinfo +# endpoint. Valid values are: 'auto' or 'userinfo_endpoint'. +# +# Defaults to 'auto', which fetches the userinfo endpoint if 'openid' is +# included in 'scopes'. Set to 'userinfo_endpoint' to always fetch the +# userinfo endpoint. +# +# allow_existing_users: set to 'true' to allow a user logging in via OIDC to +# match a pre-existing account instead of failing. This could be used if +# switching from password logins to OIDC. Defaults to false. +# +# user_mapping_provider: Configuration for how attributes returned from a OIDC +# provider are mapped onto a matrix user. This setting has the following +# sub-properties: +# +# module: The class name of a custom mapping module. Default is +# 'synapse.handlers.oidc.JinjaOidcMappingProvider'. +# See https://github.com/matrix-org/synapse/blob/master/docs/sso_mapping_providers.md#openid-mapping-providers +# for information on implementing a custom mapping provider. +# +# config: Configuration for the mapping provider module. This section will +# be passed as a Python dictionary to the user mapping provider +# module's `parse_config` method. +# +# For the default provider, the following settings are available: +# +# subject_claim: name of the claim containing a unique identifier +# for the user. Defaults to 'sub', which OpenID Connect +# compliant providers should provide. +# +# localpart_template: Jinja2 template for the localpart of the MXID. +# If this is not set, the user will be prompted to choose their +# own username (see 'sso_auth_account_details.html' in the 'sso' +# section of this file). +# +# display_name_template: Jinja2 template for the display name to set +# on first login. If unset, no displayname will be set. +# +# email_template: Jinja2 template for the email address of the user. +# If unset, no email address will be added to the account. +# +# extra_attributes: a map of Jinja2 templates for extra attributes +# to send back to the client during login. +# Note that these are non-standard and clients will ignore them +# without modifications. +# +# When rendering, the Jinja2 templates are given a 'user' variable, +# which is set to the claims returned by the UserInfo Endpoint and/or +# in the ID Token. +# +# It is possible to configure Synapse to only allow logins if certain attributes +# match particular values in the OIDC userinfo. The requirements can be listed under +# `attribute_requirements` as shown below. All of the listed attributes must +# match for the login to be permitted. Additional attributes can be added to +# userinfo by expanding the `scopes` section of the OIDC config to retrieve +# additional information from the OIDC provider. +# +# If the OIDC claim is a list, then the attribute must match any value in the list. +# Otherwise, it must exactly match the value of the claim. Using the example +# below, the `family_name` claim MUST be "Stephensson", but the `groups` +# claim MUST contain "admin". +# +# attribute_requirements: +# - attribute: family_name +# value: "Stephensson" +# - attribute: groups +# value: "admin" +# +# See https://github.com/matrix-org/synapse/blob/master/docs/openid.md +# for information on how to configure these options. +# +# For backwards compatibility, it is also possible to configure a single OIDC +# provider via an 'oidc_config' setting. This is now deprecated and admins are +# advised to migrate to the 'oidc_providers' format. (When doing that migration, +# use 'oidc' for the idp_id to ensure that existing users continue to be +# recognised.) +# +oidc_providers: + # Generic example + # + #- idp_id: my_idp + # idp_name: "My OpenID provider" + # idp_icon: "mxc://example.com/mediaid" + # discover: false + # issuer: "https://accounts.example.com/" + # client_id: "provided-by-your-issuer" + # client_secret: "provided-by-your-issuer" + # client_auth_method: client_secret_post + # scopes: ["openid", "profile"] + # authorization_endpoint: "https://accounts.example.com/oauth2/auth" + # token_endpoint: "https://accounts.example.com/oauth2/token" + # userinfo_endpoint: "https://accounts.example.com/userinfo" + # jwks_uri: "https://accounts.example.com/.well-known/jwks.json" + # user_mapping_provider: + # config: + # subject_claim: "id" + # localpart_template: "{% raw %}{{ user.login }}{% endraw %}" + # display_name_template: "{% raw %}{{ user.name }}{% endraw %}" + # email_template: "{% raw %}{{ user.email }}{% endraw %}" + # attribute_requirements: + # - attribute: userGroup + # value: "synapseUsers" + + +# Enable Central Authentication Service (CAS) for registration and login. +# +cas_config: + # Uncomment the following to enable authorization against a CAS server. + # Defaults to false. + # + #enabled: true + + # The URL of the CAS authorization endpoint. + # + #server_url: "https://cas-server.com" + + # The attribute of the CAS response to use as the display name. + # + # If unset, no displayname will be set. + # + #displayname_attribute: name + + # It is possible to configure Synapse to only allow logins if CAS attributes + # match particular values. All of the keys in the mapping below must exist + # and the values must match the given value. Alternately if the given value + # is None then any value is allowed (the attribute just must exist). + # All of the listed attributes must match for the login to be permitted. + # + #required_attributes: + # userGroup: "staff" + # department: None + + +# Additional settings to use with single-sign on systems such as OpenID Connect, +# SAML2 and CAS. +# +sso: + # A list of client URLs which are whitelisted so that the user does not + # have to confirm giving access to their account to the URL. Any client + # whose URL starts with an entry in the following list will not be subject + # to an additional confirmation step after the SSO login is completed. + # + # WARNING: An entry such as "https://my.client" is insecure, because it + # will also match "https://my.client.evil.site", exposing your users to + # phishing attacks from evil.site. To avoid this, include a slash after the + # hostname: "https://my.client/". + # + # If public_baseurl is set, then the login fallback page (used by clients + # that don't natively support the required login flows) is whitelisted in + # addition to any URLs in this list. + # + # By default, this list is empty. + # + #client_whitelist: + # - https://riot.im/develop + # - https://my.custom.client/ + + # Uncomment to keep a user's profile fields in sync with information from + # the identity provider. Currently only syncing the displayname is + # supported. Fields are checked on every SSO login, and are updated + # if necessary. + # + # Note that enabling this option will override user profile information, + # regardless of whether users have opted-out of syncing that + # information when first signing in. Defaults to false. + # + #update_profile_information: true + + # Directory in which Synapse will try to find the template files below. + # If not set, or the files named below are not found within the template + # directory, default templates from within the Synapse package will be used. + # + # Synapse will look for the following templates in this directory: + # + # * HTML page to prompt the user to choose an Identity Provider during + # login: 'sso_login_idp_picker.html'. + # + # This is only used if multiple SSO Identity Providers are configured. + # + # When rendering, this template is given the following variables: + # * redirect_url: the URL that the user will be redirected to after + # login. + # + # * server_name: the homeserver's name. + # + # * providers: a list of available Identity Providers. Each element is + # an object with the following attributes: + # + # * idp_id: unique identifier for the IdP + # * idp_name: user-facing name for the IdP + # * idp_icon: if specified in the IdP config, an MXC URI for an icon + # for the IdP + # * idp_brand: if specified in the IdP config, a textual identifier + # for the brand of the IdP + # + # The rendered HTML page should contain a form which submits its results + # back as a GET request, with the following query parameters: + # + # * redirectUrl: the client redirect URI (ie, the `redirect_url` passed + # to the template) + # + # * idp: the 'idp_id' of the chosen IDP. + # + # * HTML page to prompt new users to enter a userid and confirm other + # details: 'sso_auth_account_details.html'. This is only shown if the + # SSO implementation (with any user_mapping_provider) does not return + # a localpart. + # + # When rendering, this template is given the following variables: + # + # * server_name: the homeserver's name. + # + # * idp: details of the SSO Identity Provider that the user logged in + # with: an object with the following attributes: + # + # * idp_id: unique identifier for the IdP + # * idp_name: user-facing name for the IdP + # * idp_icon: if specified in the IdP config, an MXC URI for an icon + # for the IdP + # * idp_brand: if specified in the IdP config, a textual identifier + # for the brand of the IdP + # + # * user_attributes: an object containing details about the user that + # we received from the IdP. May have the following attributes: + # + # * display_name: the user's display_name + # * emails: a list of email addresses + # + # The template should render a form which submits the following fields: + # + # * username: the localpart of the user's chosen user id + # + # * HTML page allowing the user to consent to the server's terms and + # conditions. This is only shown for new users, and only if + # `user_consent.require_at_registration` is set. + # + # When rendering, this template is given the following variables: + # + # * server_name: the homeserver's name. + # + # * user_id: the user's matrix proposed ID. + # + # * user_profile.display_name: the user's proposed display name, if any. + # + # * consent_version: the version of the terms that the user will be + # shown + # + # * terms_url: a link to the page showing the terms. + # + # The template should render a form which submits the following fields: + # + # * accepted_version: the version of the terms accepted by the user + # (ie, 'consent_version' from the input variables). + # + # * HTML page for a confirmation step before redirecting back to the client + # with the login token: 'sso_redirect_confirm.html'. + # + # When rendering, this template is given the following variables: + # + # * redirect_url: the URL the user is about to be redirected to. + # + # * display_url: the same as `redirect_url`, but with the query + # parameters stripped. The intention is to have a + # human-readable URL to show to users, not to use it as + # the final address to redirect to. + # + # * server_name: the homeserver's name. + # + # * new_user: a boolean indicating whether this is the user's first time + # logging in. + # + # * user_id: the user's matrix ID. + # + # * user_profile.avatar_url: an MXC URI for the user's avatar, if any. + # None if the user has not set an avatar. + # + # * user_profile.display_name: the user's display name. None if the user + # has not set a display name. + # + # * HTML page which notifies the user that they are authenticating to confirm + # an operation on their account during the user interactive authentication + # process: 'sso_auth_confirm.html'. + # + # When rendering, this template is given the following variables: + # * redirect_url: the URL the user is about to be redirected to. + # + # * description: the operation which the user is being asked to confirm + # + # * idp: details of the Identity Provider that we will use to confirm + # the user's identity: an object with the following attributes: + # + # * idp_id: unique identifier for the IdP + # * idp_name: user-facing name for the IdP + # * idp_icon: if specified in the IdP config, an MXC URI for an icon + # for the IdP + # * idp_brand: if specified in the IdP config, a textual identifier + # for the brand of the IdP + # + # * HTML page shown after a successful user interactive authentication session: + # 'sso_auth_success.html'. + # + # Note that this page must include the JavaScript which notifies of a successful authentication + # (see https://matrix.org/docs/spec/client_server/r0.6.0#fallback). + # + # This template has no additional variables. + # + # * HTML page shown after a user-interactive authentication session which + # does not map correctly onto the expected user: 'sso_auth_bad_user.html'. + # + # When rendering, this template is given the following variables: + # * server_name: the homeserver's name. + # * user_id_to_verify: the MXID of the user that we are trying to + # validate. + # + # * HTML page shown during single sign-on if a deactivated user (according to Synapse's database) + # attempts to login: 'sso_account_deactivated.html'. + # + # This template has no additional variables. + # + # * HTML page to display to users if something goes wrong during the + # OpenID Connect authentication process: 'sso_error.html'. + # + # When rendering, this template is given two variables: + # * error: the technical name of the error + # * error_description: a human-readable message for the error + # + # You can see the default templates at: + # https://github.com/matrix-org/synapse/tree/master/synapse/res/templates + # + #template_dir: "res/templates" + + +# JSON web token integration. The following settings can be used to make +# Synapse JSON web tokens for authentication, instead of its internal +# password database. +# +# Each JSON Web Token needs to contain a "sub" (subject) claim, which is +# used as the localpart of the mxid. +# +# Additionally, the expiration time ("exp"), not before time ("nbf"), +# and issued at ("iat") claims are validated if present. +# +# Note that this is a non-standard login type and client support is +# expected to be non-existent. +# +# See https://github.com/matrix-org/synapse/blob/master/docs/jwt.md. +# +#jwt_config: + # Uncomment the following to enable authorization using JSON web + # tokens. Defaults to false. + # + #enabled: true + + # This is either the private shared secret or the public key used to + # decode the contents of the JSON web token. + # + # Required if 'enabled' is true. + # + #secret: "provided-by-your-issuer" + + # The algorithm used to sign the JSON web token. + # + # Supported algorithms are listed at + # https://pyjwt.readthedocs.io/en/latest/algorithms.html + # + # Required if 'enabled' is true. + # + #algorithm: "provided-by-your-issuer" + + # The issuer to validate the "iss" claim against. + # + # Optional, if provided the "iss" claim will be required and + # validated for all JSON web tokens. + # + #issuer: "provided-by-your-issuer" + + # A list of audiences to validate the "aud" claim against. + # + # Optional, if provided the "aud" claim will be required and + # validated for all JSON web tokens. + # + # Note that if the "aud" claim is included in a JSON web token then + # validation will fail without configuring audiences. + # + #audiences: + # - "provided-by-your-issuer" + + +password_config: + # Uncomment to disable password login + # + #enabled: false + + # Uncomment to disable authentication against the local password + # database. This is ignored if `enabled` is false, and is only useful + # if you have other password_providers. + # + localdb_enabled: {{ matrix_synapse_password_config_localdb_enabled|to_json }} + + # Uncomment and change to a secret random string for extra security. + # DO NOT CHANGE THIS AFTER INITIAL SETUP! + # + pepper: {{ matrix_synapse_password_config_pepper|string|to_json }} + + # Define and enforce a password policy. Each parameter is optional. + # This is an implementation of MSC2000. + # + policy: + # Whether to enforce the password policy. + # Defaults to 'false'. + # + #enabled: true + + # Minimum accepted length for a password. + # Defaults to 0. + # + #minimum_length: 15 + + # Whether a password must contain at least one digit. + # Defaults to 'false'. + # + #require_digit: true + + # Whether a password must contain at least one symbol. + # A symbol is any character that's not a number or a letter. + # Defaults to 'false'. + # + #require_symbol: true + + # Whether a password must contain at least one lowercase letter. + # Defaults to 'false'. + # + #require_lowercase: true + + # Whether a password must contain at least one lowercase letter. + # Defaults to 'false'. + # + #require_uppercase: true + +ui_auth: + # The amount of time to allow a user-interactive authentication session + # to be active. + # + # This defaults to 0, meaning the user is queried for their credentials + # before every action, but this can be overridden to allow a single + # validation to be re-used. This weakens the protections afforded by + # the user-interactive authentication process, by allowing for multiple + # (and potentially different) operations to use the same validation session. + # + # This is ignored for potentially "dangerous" operations (including + # deactivating an account, modifying an account password, and + # adding a 3PID). + # + # Uncomment below to allow for credential validation to last for 15 + # seconds. + # + #session_timeout: "15s" + + +{% if matrix_synapse_email_enabled %} +# Configuration for sending emails from Synapse. +# +email: + # The hostname of the outgoing SMTP server to use. Defaults to 'localhost'. + # + #smtp_host: mail.server + smtp_host: {{ matrix_synapse_email_smtp_host|string|to_json }} + + # The port on the mail server for outgoing SMTP. Defaults to 25. + # + #smtp_port: 587 + smtp_port: {{ matrix_synapse_email_smtp_port|to_json }} + + # Username/password for authentication to the SMTP server. By default, no + # authentication is attempted. + # + #smtp_user: "exampleusername" + #smtp_pass: "examplepassword" + + # Uncomment the following to require TLS transport security for SMTP. + # By default, Synapse will connect over plain text, and will then switch to + # TLS via STARTTLS *if the SMTP server supports it*. If this option is set, + # Synapse will refuse to connect unless the server supports STARTTLS. + # + #require_transport_security: true + require_transport_security: {{ matrix_synapse_email_smtp_require_transport_security|to_json }} + + # Enable sending emails for messages that the user has missed + # + #enable_notifs: false + enable_notifs: true + + # notif_from defines the "From" address to use when sending emails. + # It must be set if email sending is enabled. + # + # The placeholder '%(app)s' will be replaced by the application name, + # which is normally 'app_name' (below), but may be overridden by the + # Matrix client application. + # + # Note that the placeholder must be written '%(app)s', including the + # trailing 's'. + # + #notif_from: "Your Friendly %(app)s homeserver " + notif_from: {{ matrix_synapse_email_notif_from|string|to_json }} + + # app_name defines the default value for '%(app)s' in notif_from and email + # subjects. It defaults to 'Matrix'. + # + #app_name: my_branded_matrix_server + app_name: Matrix + + # Uncomment the following to disable automatic subscription to email + # notifications for new users. Enabled by default. + # + #notif_for_new_users: false + notif_for_new_users: True + + # Custom URL for client links within the email notifications. By default + # links will be based on "https://matrix.to". + # + # (This setting used to be called riot_base_url; the old name is still + # supported for backwards-compatibility but is now deprecated.) + # + #client_base_url: "http://localhost/riot" + client_base_url: {{ matrix_synapse_email_client_base_url|string|to_json }} + + # Configure the time that a validation email will expire after sending. + # Defaults to 1h. + # + #validation_token_lifetime: 15m + + # Directory in which Synapse will try to find the template files below. + # If not set, or the files named below are not found within the template + # directory, default templates from within the Synapse package will be used. + # + # Synapse will look for the following templates in this directory: + # + # * The contents of email notifications of missed events: 'notif_mail.html' and + # 'notif_mail.txt'. + # + # * The contents of account expiry notice emails: 'notice_expiry.html' and + # 'notice_expiry.txt'. + # + # * The contents of password reset emails sent by the homeserver: + # 'password_reset.html' and 'password_reset.txt' + # + # * An HTML page that a user will see when they follow the link in the password + # reset email. The user will be asked to confirm the action before their + # password is reset: 'password_reset_confirmation.html' + # + # * HTML pages for success and failure that a user will see when they confirm + # the password reset flow using the page above: 'password_reset_success.html' + # and 'password_reset_failure.html' + # + # * The contents of address verification emails sent during registration: + # 'registration.html' and 'registration.txt' + # + # * HTML pages for success and failure that a user will see when they follow + # the link in an address verification email sent during registration: + # 'registration_success.html' and 'registration_failure.html' + # + # * The contents of address verification emails sent when an address is added + # to a Matrix account: 'add_threepid.html' and 'add_threepid.txt' + # + # * HTML pages for success and failure that a user will see when they follow + # the link in an address verification email sent when an address is added + # to a Matrix account: 'add_threepid_success.html' and + # 'add_threepid_failure.html' + # + # You can see the default templates at: + # https://github.com/matrix-org/synapse/tree/master/synapse/res/templates + # + #template_dir: "res/templates" + + # Subjects to use when sending emails from Synapse. + # + # The placeholder '%(app)s' will be replaced with the value of the 'app_name' + # setting above, or by a value dictated by the Matrix client application. + # + # If a subject isn't overridden in this configuration file, the value used as + # its example will be used. + # + #subjects: + + # Subjects for notification emails. + # + # On top of the '%(app)s' placeholder, these can use the following + # placeholders: + # + # * '%(person)s', which will be replaced by the display name of the user(s) + # that sent the message(s), e.g. "Alice and Bob". + # * '%(room)s', which will be replaced by the name of the room the + # message(s) have been sent to, e.g. "My super room". + # + # See the example provided for each setting to see which placeholder can be + # used and how to use them. + # + # Subject to use to notify about one message from one or more user(s) in a + # room which has a name. + #message_from_person_in_room: "[%(app)s] You have a message on %(app)s from %(person)s in the %(room)s room..." + # + # Subject to use to notify about one message from one or more user(s) in a + # room which doesn't have a name. + #message_from_person: "[%(app)s] You have a message on %(app)s from %(person)s..." + # + # Subject to use to notify about multiple messages from one or more users in + # a room which doesn't have a name. + #messages_from_person: "[%(app)s] You have messages on %(app)s from %(person)s..." + # + # Subject to use to notify about multiple messages in a room which has a + # name. + #messages_in_room: "[%(app)s] You have messages on %(app)s in the %(room)s room..." + # + # Subject to use to notify about multiple messages in multiple rooms. + #messages_in_room_and_others: "[%(app)s] You have messages on %(app)s in the %(room)s room and others..." + # + # Subject to use to notify about multiple messages from multiple persons in + # multiple rooms. This is similar to the setting above except it's used when + # the room in which the notification was triggered has no name. + #messages_from_person_and_others: "[%(app)s] You have messages on %(app)s from %(person)s and others..." + # + # Subject to use to notify about an invite to a room which has a name. + #invite_from_person_to_room: "[%(app)s] %(person)s has invited you to join the %(room)s room on %(app)s..." + # + # Subject to use to notify about an invite to a room which doesn't have a + # name. + #invite_from_person: "[%(app)s] %(person)s has invited you to chat on %(app)s..." + + # Subject for emails related to account administration. + # + # On top of the '%(app)s' placeholder, these one can use the + # '%(server_name)s' placeholder, which will be replaced by the value of the + # 'server_name' setting in your Synapse configuration. + # + # Subject to use when sending a password reset email. + #password_reset: "[%(server_name)s] Password reset" + # + # Subject to use when sending a verification email to assert an address's + # ownership. + #email_validation: "[%(server_name)s] Validate your email" +{% endif %} + +# Password providers allow homeserver administrators to integrate +# their Synapse installation with existing authentication methods +# ex. LDAP, external tokens, etc. +# +# For more information and known implementations, please see +# https://github.com/matrix-org/synapse/blob/master/docs/password_auth_providers.md +# +# Note: instances wishing to use SAML or CAS authentication should +# instead use the `saml2_config` or `cas_config` options, +# respectively. +# +# password_providers: +# # Example config for an LDAP auth provider +# - module: "ldap_auth_provider.LdapAuthProvider" +# config: +# enabled: true +# uri: "ldap://ldap.example.com:389" +# start_tls: true +# base: "ou=users,dc=example,dc=com" +# attributes: +# uid: "cn" +# mail: "email" +# name: "givenName" +# #bind_dn: +# #bind_password: +# #filter: "(objectClass=posixAccount)" +{% if matrix_synapse_password_providers_enabled %} +password_providers: +{% if matrix_synapse_ext_password_provider_shared_secret_auth_enabled %} + - module: "shared_secret_authenticator.SharedSecretAuthenticator" + config: + sharedSecret: {{ matrix_synapse_ext_password_provider_shared_secret_auth_shared_secret|string|to_json }} +{% endif %} +{% if matrix_synapse_ext_password_provider_rest_auth_enabled %} + - module: "rest_auth_provider.RestAuthProvider" + config: + endpoint: {{ matrix_synapse_ext_password_provider_rest_auth_endpoint|string|to_json }} + policy: + registration: + username: + enforceLowercase: {{ matrix_synapse_ext_password_provider_rest_auth_registration_enforce_lowercase }} + profile: + name: {{ matrix_synapse_ext_password_provider_rest_auth_registration_profile_name_autofill }} + login: + profile: + name: {{ matrix_synapse_ext_password_provider_rest_auth_login_profile_name_autofill }} +{% endif %} +{% if matrix_synapse_ext_password_provider_ldap_enabled %} + - module: "ldap_auth_provider.LdapAuthProvider" + config: + enabled: true + uri: {{ matrix_synapse_ext_password_provider_ldap_uri|string|to_json }} + start_tls: {{ matrix_synapse_ext_password_provider_ldap_start_tls|to_json }} + base: {{ matrix_synapse_ext_password_provider_ldap_base|string|to_json }} + active_directory: {{ matrix_synapse_ext_password_provider_ldap_active_directory|to_json }} + default_domain: {{ matrix_synapse_ext_password_provider_ldap_default_domain|string|to_json }} + attributes: + uid: {{ matrix_synapse_ext_password_provider_ldap_attributes_uid|string|to_json }} + mail: {{ matrix_synapse_ext_password_provider_ldap_attributes_mail|string|to_json }} + name: {{ matrix_synapse_ext_password_provider_ldap_attributes_name|string|to_json }} + bind_dn: {{ matrix_synapse_ext_password_provider_ldap_bind_dn|string|to_json }} + bind_password: {{ matrix_synapse_ext_password_provider_ldap_bind_password|string|to_json }} + filter: {{ matrix_synapse_ext_password_provider_ldap_filter|string|to_json }} +{% endif %} +{% endif %} + + +## Push ## + +push: + # Clients requesting push notifications can either have the body of + # the message sent in the notification poke along with other details + # like the sender, or just the event ID and room ID (`event_id_only`). + # If clients choose the former, this option controls whether the + # notification request includes the content of the event (other details + # like the sender are still included). For `event_id_only` push, it + # has no effect. + # + # For modern android devices the notification content will still appear + # because it is loaded by the app. iPhone, however will send a + # notification saying only that a message arrived and who it came from. + # + # The default value is "true" to include message details. Uncomment to only + # include the event ID and room ID in push notification payloads. + # + include_content: {{ matrix_synapse_push_include_content|to_json }} + + # When a push notification is received, an unread count is also sent. + # This number can either be calculated as the number of unread messages + # for the user, or the number of *rooms* the user has unread messages in. + # + # The default value is "true", meaning push clients will see the number of + # rooms with unread messages in them. Uncomment to instead send the number + # of unread messages. + # + #group_unread_count_by_room: false + + +# Spam checkers are third-party modules that can block specific actions +# of local users, such as creating rooms and registering undesirable +# usernames, as well as remote users by redacting incoming events. +# +# spam_checker: + #- module: "my_custom_project.SuperSpamChecker" + # config: + # example_option: 'things' + #- module: "some_other_project.BadEventStopper" + # config: + # example_stop_events_from: ['@bad:example.com'] +spam_checker: {{ matrix_synapse_spam_checker|to_json }} + + +## Rooms ## + +# Controls whether locally-created rooms should be end-to-end encrypted by +# default. +# +# Possible options are "all", "invite", and "off". They are defined as: +# +# * "all": any locally-created room +# * "invite": any room created with the "private_chat" or "trusted_private_chat" +# room creation presets +# * "off": this option will take no effect +# +# The default value is "off". +# +# Note that this option will only affect rooms created after it is set. It +# will also not affect rooms created by other servers. +# +#encryption_enabled_by_default_for_room_type: invite + + +# Uncomment to allow non-server-admin users to create groups on this server +# +enable_group_creation: {{ matrix_synapse_enable_group_creation|to_json }} + +# If enabled, non server admins can only create groups with local parts +# starting with this prefix +# +#group_creation_prefix: "unofficial_" + + + +# User Directory configuration +# +user_directory: + # Defines whether users can search the user directory. If false then + # empty responses are returned to all queries. Defaults to true. + # + # Uncomment to disable the user directory. + # + #enabled: false + + # Defines whether to search all users visible to your HS when searching + # the user directory, rather than limiting to users visible in public + # rooms. Defaults to false. + # + # If you set it true, you'll have to rebuild the user_directory search + # indexes, see: + # https://github.com/matrix-org/synapse/blob/master/docs/user_directory.md + # + # Uncomment to return search results containing all known users, even if that + # user does not share a room with the requester. + # + #search_all_users: true + + # Defines whether to prefer local users in search query results. + # If True, local users are more likely to appear above remote users + # when searching the user directory. Defaults to false. + # + # Uncomment to prefer local over remote users in user directory search + # results. + # + #prefer_local_users: true + + +# User Consent configuration +# +# for detailed instructions, see +# https://github.com/matrix-org/synapse/blob/master/docs/consent_tracking.md +# +# Parts of this section are required if enabling the 'consent' resource under +# 'listeners', in particular 'template_dir' and 'version'. +# +# 'template_dir' gives the location of the templates for the HTML forms. +# This directory should contain one subdirectory per language (eg, 'en', 'fr'), +# and each language directory should contain the policy document (named as +# '.html') and a success page (success.html). +# +# 'version' specifies the 'current' version of the policy document. It defines +# the version to be served by the consent resource if there is no 'v' +# parameter. +# +# 'server_notice_content', if enabled, will send a user a "Server Notice" +# asking them to consent to the privacy policy. The 'server_notices' section +# must also be configured for this to work. Notices will *not* be sent to +# guest users unless 'send_server_notice_to_guests' is set to true. +# +# 'block_events_error', if set, will block any attempts to send events +# until the user consents to the privacy policy. The value of the setting is +# used as the text of the error. +# +# 'require_at_registration', if enabled, will add a step to the registration +# process, similar to how captcha works. Users will be required to accept the +# policy before their account is created. +# +# 'policy_name' is the display name of the policy users will see when registering +# for an account. Has no effect unless `require_at_registration` is enabled. +# Defaults to "Privacy Policy". +# +#user_consent: +# template_dir: res/templates/privacy +# version: 1.0 +# server_notice_content: +# msgtype: m.text +# body: >- +# To continue using this homeserver you must review and agree to the +# terms and conditions at %(consent_uri)s +# send_server_notice_to_guests: True +# block_events_error: >- +# To continue using this homeserver you must review and agree to the +# terms and conditions at %(consent_uri)s +# require_at_registration: False +# policy_name: Privacy Policy +# + + + +# Settings for local room and user statistics collection. See +# docs/room_and_user_statistics.md. +# +stats: + # Uncomment the following to disable room and user statistics. Note that doing + # so may cause certain features (such as the room directory) not to work + # correctly. + # + #enabled: false + + +# Server Notices room configuration +# +# Uncomment this section to enable a room which can be used to send notices +# from the server to users. It is a special room which cannot be left; notices +# come from a special "notices" user id. +# +# If you uncomment this section, you *must* define the system_mxid_localpart +# setting, which defines the id of the user which will be used to send the +# notices. +# +# It's also possible to override the room name, the display name of the +# "notices" user, and the avatar for the user. +# +#server_notices: +# system_mxid_localpart: notices +# system_mxid_display_name: "Server Notices" +# system_mxid_avatar_url: "mxc://server.com/oumMVlgDnLYFaPVkExemNVVZ" +# room_name: "Server Notices" + + + +# Uncomment to disable searching the public room list. When disabled +# blocks searching local and remote room lists for local and remote +# users by always returning an empty list for all queries. +# +#enable_room_list_search: false + +enable_room_list_search: {{ matrix_synapse_enable_room_list_search|to_json }} + +# The `alias_creation` option controls who's allowed to create aliases +# on this server. +# +# The format of this option is a list of rules that contain globs that +# match against user_id, room_id and the new alias (fully qualified with +# server name). The action in the first rule that matches is taken, +# which can currently either be "allow" or "deny". +# +# Missing user_id/room_id/alias fields default to "*". +# +# If no rules match the request is denied. An empty list means no one +# can create aliases. +# +# Options for the rules include: +# +# user_id: Matches against the creator of the alias +# alias: Matches against the alias being created +# room_id: Matches against the room ID the alias is being pointed at +# action: Whether to "allow" or "deny" the request if the rule matches +# +# The default is: +# +#alias_creation_rules: +# - user_id: "*" +# alias: "*" +# room_id: "*" +# action: allow + +alias_creation_rules: {{ matrix_synapse_alias_creation_rules|to_json }} + +# The `room_list_publication_rules` option controls who can publish and +# which rooms can be published in the public room list. +# +# The format of this option is the same as that for +# `alias_creation_rules`. +# +# If the room has one or more aliases associated with it, only one of +# the aliases needs to match the alias rule. If there are no aliases +# then only rules with `alias: *` match. +# +# If no rules match the request is denied. An empty list means no one +# can publish rooms. +# +# Options for the rules include: +# +# user_id: Matches against the creator of the alias +# room_id: Matches against the room ID being published +# alias: Matches against any current local or canonical aliases +# associated with the room +# action: Whether to "allow" or "deny" the request if the rule matches +# +# The default is: +# +#room_list_publication_rules: +# - user_id: "*" +# alias: "*" +# room_id: "*" +# action: allow + +room_list_publication_rules: {{ matrix_synapse_room_list_publication_rules|to_json }} + + +## Opentracing ## + +# These settings enable opentracing, which implements distributed tracing. +# This allows you to observe the causal chains of events across servers +# including requests, key lookups etc., across any server running +# synapse or any other other services which supports opentracing +# (specifically those implemented with Jaeger). +# +opentracing: + # tracing is disabled by default. Uncomment the following line to enable it. + # + #enabled: true + + # The list of homeservers we wish to send and receive span contexts and span baggage. + # See docs/opentracing.rst. + # + # This is a list of regexes which are matched against the server_name of the + # homeserver. + # + # By default, it is empty, so no servers are matched. + # + #homeserver_whitelist: + # - ".*" + + # A list of the matrix IDs of users whose requests will always be traced, + # even if the tracing system would otherwise drop the traces due to + # probabilistic sampling. + # + # By default, the list is empty. + # + #force_tracing_for_users: + # - "@user1:server_name" + # - "@user2:server_name" + + # Jaeger can be configured to sample traces at different rates. + # All configuration options provided by Jaeger can be set here. + # Jaeger's configuration is mostly related to trace sampling which + # is documented here: + # https://www.jaegertracing.io/docs/latest/sampling/. + # + #jaeger_config: + # sampler: + # type: const + # param: 1 + # logging: + # false + + +## Workers ## + +# Disables sending of outbound federation transactions on the main process. +# Uncomment if using a federation sender worker. +# +#send_federation: false + +# It is possible to run multiple federation sender workers, in which case the +# work is balanced across them. +# +# This configuration must be shared between all federation sender workers, and if +# changed all federation sender workers must be stopped at the same time and then +# started, to ensure that all instances are running with the same config (otherwise +# events may be dropped). +# +#federation_sender_instances: +# - federation_sender1 + +# When using workers this should be a map from `worker_name` to the +# HTTP replication listener of the worker, if configured. +# +#instance_map: +# worker1: +# host: localhost +# port: 8034 + +# Experimental: When using workers you can define which workers should +# handle event persistence and typing notifications. Any worker +# specified here must also be in the `instance_map`. +# +#stream_writers: +# events: worker1 +# typing: worker1 + +# The worker that is used to run background tasks (e.g. cleaning up expired +# data). If not provided this defaults to the main process. +# +#run_background_tasks_on: worker1 + +# A shared secret used by the replication APIs to authenticate HTTP requests +# from workers. +# +# By default this is unused and traffic is not authenticated. +# +#worker_replication_secret: "" + + +# Configuration for Redis when using workers. This *must* be enabled when +# using workers (unless using old style direct TCP configuration). +# +redis: + # Uncomment the below to enable Redis support. + # + enabled: {{ matrix_synapse_redis_enabled }} + + # Optional host and port to use to connect to redis. Defaults to + # localhost and 6379 + # + host: {{ matrix_synapse_redis_host }} + port: {{ matrix_synapse_redis_port }} + + # Optional password if configured on the Redis instance + # + password: {{ matrix_synapse_redis_password }} + + +# vim:ft=yaml diff --git a/roles/matrix-synapse/templates/synapse/synapse.log.config.j2 b/roles/matrix-synapse/templates/synapse/synapse.log.config.j2 new file mode 100644 index 00000000..09f07a2e --- /dev/null +++ b/roles/matrix-synapse/templates/synapse/synapse.log.config.j2 @@ -0,0 +1,36 @@ +#jinja2: lstrip_blocks: "True" + +version: 1 + +formatters: + precise: + format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' + +filters: + context: + (): synapse.util.logcontext.LoggingContextFilter + request: "" + +handlers: + console: + class: logging.StreamHandler + formatter: precise + filters: [context] + +loggers: + synapse: + level: {{ matrix_synapse_log_level }} + + synapse.storage.SQL: + # beware: increasing this to DEBUG will make synapse log sensitive + # information such as access tokens. + level: {{ matrix_synapse_storage_sql_log_level }} + +{% for logger in matrix_synapse_additional_loggers %} + {{ logger.name }}: + level: {{ logger.level }} +{% endfor %} + +root: + level: {{ matrix_synapse_root_log_level }} + handlers: [console] diff --git a/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse-worker.service.j2 b/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse-worker.service.j2 new file mode 100644 index 00000000..6c90c9a3 --- /dev/null +++ b/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse-worker.service.j2 @@ -0,0 +1,64 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Synapse worker ({{ matrix_synapse_worker_container_name }}) +AssertPathExists={{ matrix_synapse_config_dir_path }}/{{ matrix_synapse_worker_config_file_name }} +After=matrix-synapse.service + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" + +ExecStartPre=-{{ matrix_host_command_docker }} kill {{ matrix_synapse_worker_container_name }} +ExecStartPre=-{{ matrix_host_command_docker }} rm {{ matrix_synapse_worker_container_name }} + +# Intentional delay, so that the homeserver can manage to start. +ExecStartPre={{ matrix_host_command_sleep }} 5 + +ExecStart={{ matrix_host_command_docker }} run --rm --name {{ matrix_synapse_worker_container_name }} \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + -e UID={{ matrix_user_uid }} \ + -e GID={{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --tmpfs=/tmp:rw,noexec,nosuid,size={{ matrix_synapse_tmp_directory_size_mb }}m \ + --network={{ matrix_docker_network }} \ + {% if matrix_synapse_worker_details.port != 0 %} + --health-cmd 'curl -fSs http://localhost:{{ matrix_synapse_worker_details.port }}/health || exit 1' \ + {% else %} + --no-healthcheck \ + {% endif %} + {% if matrix_synapse_workers_enabled and matrix_synapse_workers_container_host_bind_address %} + {% if matrix_synapse_worker_details.port != 0 %} + -p {{ '' if matrix_synapse_workers_container_host_bind_address == '*' else (matrix_synapse_workers_container_host_bind_address + ':') }}{{ matrix_synapse_worker_details.port }}:{{ matrix_synapse_worker_details.port }} \ + {% endif %} + {% if matrix_synapse_worker_details.metrics_port != 0 %} + -p {{ '' if matrix_synapse_workers_container_host_bind_address == '*' else (matrix_synapse_workers_container_host_bind_address + ':') }}{{ matrix_synapse_worker_details.metrics_port }}:{{ matrix_synapse_worker_details.metrics_port }} \ + {% endif %} + {% endif %} + --mount type=bind,src={{ matrix_synapse_config_dir_path }},dst=/data,ro \ + --mount type=bind,src={{ matrix_synapse_storage_path }},dst=/matrix-media-store-parent,bind-propagation=slave \ + {% for volume in matrix_synapse_container_additional_volumes %} + -v {{ volume.src }}:{{ volume.dst }}:{{ volume.options }} \ + {% endfor %} + {% for arg in matrix_synapse_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_synapse_docker_image }} \ + run -m synapse.app.{{ matrix_synapse_worker_details.type }} -c /data/homeserver.yaml -c /data/{{ matrix_synapse_worker_config_file_name }} + + +ExecStop=-{{ matrix_host_command_docker }} kill {{ matrix_synapse_worker_container_name }} +ExecStop=-{{ matrix_host_command_docker }} rm {{ matrix_synapse_worker_container_name }} + +ExecReload={{ matrix_host_command_docker }} exec {{ matrix_synapse_worker_container_name }} /bin/sh -c 'kill -HUP 1' +Restart=always +RestartSec=30 +SyslogIdentifier={{ matrix_synapse_worker_container_name }} + +# Intentionally not making this WantedBy=matrix-synapse.service, +# as matrix.synapse.service already has `Wants=` lines. +# Also, WantedBy will trigger the creation of some `matrix-synapse.service.wants/` directory, +# which we'd have to clean, etc. Better not. +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse.service.j2 b/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse.service.j2 new file mode 100644 index 00000000..2fbaac7b --- /dev/null +++ b/roles/matrix-synapse/templates/synapse/systemd/matrix-synapse.service.j2 @@ -0,0 +1,76 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Synapse server +{% for service in matrix_synapse_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} + +{% endfor %} +{% for service in matrix_synapse_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} + +{% if matrix_synapse_workers_enabled %} +{% for matrix_synapse_worker_details in matrix_synapse_workers_enabled_list %} +Wants=matrix-synapse-worker-{{ matrix_synapse_worker_details.type }}-{{ matrix_synapse_worker_details.port }}.service +{% endfor %} +{% endif %} + +DefaultDependencies=no + +[Service] +Type=simple +Environment="HOME={{ matrix_systemd_unit_home_path }}" +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse 2>/dev/null' +ExecStartPre=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse 2>/dev/null' +{% if matrix_s3_media_store_enabled %} +# Allow for some time before starting, so that media store can mount. +# Mounting can happen later too, but if we start writing, +# we'd write files to the local filesystem and fusermount will complain. +ExecStartPre={{ matrix_host_command_sleep }} 3 +{% endif %} + +ExecStart={{ matrix_host_command_docker }} run --rm --name matrix-synapse \ + --log-driver=none \ + --user={{ matrix_user_uid }}:{{ matrix_user_gid }} \ + --env=UID={{ matrix_user_uid }} \ + --env=GID={{ matrix_user_gid }} \ + --cap-drop=ALL \ + --read-only \ + --tmpfs=/tmp:rw,noexec,nosuid,size={{ matrix_synapse_tmp_directory_size_mb }}m \ + --network={{ matrix_docker_network }} \ + {% if matrix_synapse_container_client_api_host_bind_port %} + -p {{ matrix_synapse_container_client_api_host_bind_port }}:8008 \ + {% endif %} + {% if matrix_synapse_federation_enabled and matrix_synapse_tls_federation_listener_enabled and matrix_synapse_container_federation_api_tls_host_bind_port %} + -p {{ matrix_synapse_container_federation_api_tls_host_bind_port }}:8448 \ + {% endif %} + {% if matrix_synapse_federation_enabled and matrix_synapse_container_federation_api_plain_host_bind_port %} + -p {{ matrix_synapse_container_federation_api_plain_host_bind_port }}:8048 \ + {% endif %} + {% if matrix_synapse_metrics_enabled and matrix_synapse_container_metrics_api_host_bind_port %} + -p {{ matrix_synapse_container_metrics_api_host_bind_port }}:{{ matrix_synapse_metrics_port }} \ + {% endif %} + {% if matrix_synapse_manhole_enabled and matrix_synapse_container_manhole_api_host_bind_port %} + -p {{ matrix_synapse_container_manhole_api_host_bind_port }}:9000 \ + {% endif %} + --mount type=bind,src={{ matrix_synapse_config_dir_path }},dst=/data,ro \ + --mount type=bind,src={{ matrix_synapse_storage_path }},dst=/matrix-media-store-parent,bind-propagation=slave \ + {% for volume in matrix_synapse_container_additional_volumes %} + -v {{ volume.src }}:{{ volume.dst }}:{{ volume.options }} \ + {% endfor %} + {% for arg in matrix_synapse_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_synapse_docker_image }} \ + run -m synapse.app.homeserver -c /data/homeserver.yaml + +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} kill matrix-synapse 2>/dev/null' +ExecStop=-{{ matrix_host_command_sh }} -c '{{ matrix_host_command_docker }} rm matrix-synapse 2>/dev/null' +ExecReload={{ matrix_host_command_docker }} exec matrix-synapse /bin/sh -c 'kill -HUP 1' +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-synapse + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-synapse/templates/synapse/usr-local-bin/matrix-synapse-register-user.j2 b/roles/matrix-synapse/templates/synapse/usr-local-bin/matrix-synapse-register-user.j2 new file mode 100644 index 00000000..456c0667 --- /dev/null +++ b/roles/matrix-synapse/templates/synapse/usr-local-bin/matrix-synapse-register-user.j2 @@ -0,0 +1,17 @@ +#jinja2: lstrip_blocks: "True" +#!/bin/bash + +if [ $# -ne 3 ]; then + echo "Usage: "$0" " + exit 1 +fi + +user=$1 +password=$2 +admin=$3 + +if [ "$admin" -eq "1" ]; then + docker exec matrix-synapse register_new_matrix_user -u "$user" -p "$password" -c /data/homeserver.yaml --admin http://localhost:8008 +else + docker exec matrix-synapse register_new_matrix_user -u "$user" -p "$password" -c /data/homeserver.yaml --no-admin http://localhost:8008 +fi diff --git a/roles/matrix-synapse/templates/synapse/worker.yaml.j2 b/roles/matrix-synapse/templates/synapse/worker.yaml.j2 new file mode 100644 index 00000000..36ae5a7e --- /dev/null +++ b/roles/matrix-synapse/templates/synapse/worker.yaml.j2 @@ -0,0 +1,45 @@ +#jinja2: lstrip_blocks: "True" +worker_app: synapse.app.{{ matrix_synapse_worker_details.type }} +worker_name: {{ matrix_synapse_worker_details.type ~ ':' ~ matrix_synapse_worker_details.port }} + +{% if matrix_synapse_replication_listener_enabled %} +worker_replication_host: matrix-synapse +worker_replication_http_port: {{ matrix_synapse_replication_http_port }} +{% endif %} + +{% set has_listeners = (matrix_synapse_worker_details.type not in [ 'appservice', 'federation_sender', 'pusher' ] or matrix_synapse_metrics_enabled) %} + +{% set http_resources = [] %} + +{% if matrix_synapse_worker_details.type in ['generic_worker', 'frontend_proxy', 'user_dir'] %} + {% set http_resources = http_resources + ['client'] %} +{% endif %} +{% if matrix_synapse_worker_details.type in ['generic_worker'] %} + {% set http_resources = http_resources+ ['federation'] %} +{% endif %} +{% if matrix_synapse_worker_details.type in ['media_repository'] %} + {% set http_resources = http_resources + ['media'] %} +{% endif %} + +{% if http_resources|length > 0 or matrix_synapse_metrics_enabled %} +worker_listeners: +{% if http_resources|length > 0 %} + - type: http + bind_addresses: ['::'] + port: {{ matrix_synapse_worker_details.port }} + resources: + - names: {{ http_resources|to_json }} +{% endif %} +{% if matrix_synapse_metrics_enabled %} + - type: metrics + bind_addresses: ['0.0.0.0'] + port: {{ matrix_synapse_worker_details.metrics_port }} +{% endif %} +{% endif %} + +{% if matrix_synapse_worker_details.type == 'frontend_proxy' %} +worker_main_http_uri: http://matrix-synapse:8008 +{% endif %} + +worker_daemonize: false +worker_log_config: /data/{{ matrix_server_fqn_matrix }}.log.config diff --git a/roles/matrix-synapse/vars/main.yml b/roles/matrix-synapse/vars/main.yml new file mode 100644 index 00000000..5839aa81 --- /dev/null +++ b/roles/matrix-synapse/vars/main.yml @@ -0,0 +1,34 @@ +--- + +matrix_synapse_client_api_url_endpoint_public: "https://{{ matrix_server_fqn_matrix }}/_matrix/client/versions" +matrix_synapse_federation_api_url_endpoint_public: "https://{{ matrix_server_fqn_matrix }}:{{ matrix_federation_public_port }}/_matrix/federation/v1/version" + +# Tells whether this role had executed or not. Toggled to `true` during runtime. +matrix_synapse_role_executed: false + +matrix_synapse_media_store_directory_name: "{{ matrix_synapse_media_store_path|basename }}" + +# A Synapse generic worker can handle both federation and client-server API endpoints. +# We wish to split these, as we normally serve federation separately and don't want them mixed up. +# +# This is some ugly Ansible/Jinja2 hack (seen here: https://stackoverflow.com/a/47831492), +# which takes a list of various strings and removes the ones NOT containing `/_matrix/client` anywhere in them. +# +# We intentionally don't do a diff between everything possible (`matrix_synapse_workers_generic_worker_endpoints`) and `matrix_synapse_workers_generic_worker_federation_endpoints`, +# because `matrix_synapse_workers_generic_worker_endpoints` also contains things like `/_synapse/client/`, etc. +# While /_synapse/client/ endpoints are somewhat client-server API-related, they're: +# - neither part of the client-server API spec (and are thus, different) +# - nor always OK to forward to a worker (we're supposed to obey `matrix_nginx_proxy_proxy_matrix_client_api_forwarded_location_synapse_client_api_enabled`) +# +# It's also not too many of these APIs (only `^/_synapse/client/password_reset/email/submit_token$` at the time of this writing / 2021-01-24), +# so it's not that important whether we forward them or not. +# +# Basically, we aim to cover most things. Skipping `/_synapse/client` or a few other minor things doesn't matter too much. +matrix_synapse_workers_generic_worker_client_server_endpoints: "{{ matrix_synapse_workers_generic_worker_endpoints|default([]) | map('regex_search', '.*/_matrix/client.*')| list | difference([none]) }}" + +# A Synapse generic worker can handle both federation and client-server API endpoints. +# We wish to split these, as we normally serve federation separately and don't want them mixed up. +# +# This is some ugly Ansible/Jinja2 hack (seen here: https://stackoverflow.com/a/47831492), +# which takes a list of various strings and removes the ones NOT containing `/_matrix/federation` or `/_matrix/key` anywhere in them. +matrix_synapse_workers_generic_worker_federation_endpoints: "{{ matrix_synapse_workers_generic_worker_endpoints|default([]) | map('regex_search', '.*(/_matrix/federation|/_matrix/key).*')| list | difference([none]) }}" diff --git a/roles/matrix-synapse/vars/workers.yml b/roles/matrix-synapse/vars/workers.yml new file mode 100644 index 00000000..1f817c8e --- /dev/null +++ b/roles/matrix-synapse/vars/workers.yml @@ -0,0 +1,322 @@ +--- + +matrix_synapse_workers_generic_worker_endpoints: + # This worker can handle API requests matching the following regular + # expressions: + + # Sync requests + - ^/_matrix/client/(v2_alpha|r0)/sync$ + - ^/_matrix/client/(api/v1|v2_alpha|r0)/events$ + - ^/_matrix/client/(api/v1|r0)/initialSync$ + - ^/_matrix/client/(api/v1|r0)/rooms/[^/]+/initialSync$ + + # Federation requests + - ^/_matrix/federation/v1/event/ + - ^/_matrix/federation/v1/state/ + - ^/_matrix/federation/v1/state_ids/ + - ^/_matrix/federation/v1/backfill/ + - ^/_matrix/federation/v1/get_missing_events/ + - ^/_matrix/federation/v1/publicRooms + - ^/_matrix/federation/v1/query/ + - ^/_matrix/federation/v1/make_join/ + - ^/_matrix/federation/v1/make_leave/ + - ^/_matrix/federation/v1/send_join/ + - ^/_matrix/federation/v2/send_join/ + - ^/_matrix/federation/v1/send_leave/ + - ^/_matrix/federation/v2/send_leave/ + - ^/_matrix/federation/v1/invite/ + - ^/_matrix/federation/v2/invite/ + - ^/_matrix/federation/v1/query_auth/ + - ^/_matrix/federation/v1/event_auth/ + - ^/_matrix/federation/v1/exchange_third_party_invite/ + - ^/_matrix/federation/v1/user/devices/ + - ^/_matrix/federation/v1/get_groups_publicised$ + - ^/_matrix/key/v2/query + + # Inbound federation transaction request + - ^/_matrix/federation/v1/send/ + + # Client API requests + - ^/_matrix/client/(api/v1|r0|unstable)/publicRooms$ + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/joined_members$ + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/context/.*$ + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/members$ + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state$ + - ^/_matrix/client/(api/v1|r0|unstable)/account/3pid$ + - ^/_matrix/client/(api/v1|r0|unstable)/devices$ + - ^/_matrix/client/(api/v1|r0|unstable)/keys/query$ + - ^/_matrix/client/(api/v1|r0|unstable)/keys/changes$ + - ^/_matrix/client/versions$ + - ^/_matrix/client/(api/v1|r0|unstable)/voip/turnServer$ + - ^/_matrix/client/(api/v1|r0|unstable)/joined_groups$ + - ^/_matrix/client/(api/v1|r0|unstable)/publicised_groups$ + - ^/_matrix/client/(api/v1|r0|unstable)/publicised_groups/ + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/event/ + - ^/_matrix/client/(api/v1|r0|unstable)/joined_rooms$ + - ^/_matrix/client/(api/v1|r0|unstable)/search$ + + # Registration/login requests + - ^/_matrix/client/(api/v1|r0|unstable)/login$ + - ^/_matrix/client/(r0|unstable)/register$ + + # Event sending requests + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/redact + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/send + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state/ + - ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/(join|invite|leave|ban|unban|kick)$ + - ^/_matrix/client/(api/v1|r0|unstable)/join/ + - ^/_matrix/client/(api/v1|r0|unstable)/profile/ + + + # Additionally, the following REST endpoints can be handled for GET requests: + + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/federation/v1/groups/ + + # Pagination requests can also be handled, but all requests for a given + # room must be routed to the same instance. Additionally, care must be taken to + # ensure that the purge history admin API is not used while pagination requests + # for the room are in flight: + + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/messages$ + + # Additionally, the following endpoints should be included if Synapse is configured + # to use SSO (you only need to include the ones for whichever SSO provider you're + # using): + + # for all SSO providers + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(api/v1|r0|unstable)/login/sso/redirect + # ^/_synapse/client/pick_idp$ + # ^/_synapse/client/pick_username + # ^/_synapse/client/new_user_consent$ + # ^/_synapse/client/sso_register$ + + # OpenID Connect requests. + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_synapse/client/oidc/callback$ + + # SAML requests. + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_synapse/client/saml2/authn_response$ + + # CAS requests. + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(api/v1|r0|unstable)/login/cas/ticket$ + + # Ensure that all SSO logins go to a single process. + # For multiple workers not handling the SSO endpoints properly, see + # [#7530](https://github.com/matrix-org/synapse/issues/7530) and + # [#9427](https://github.com/matrix-org/synapse/issues/9427). + + # Note that a HTTP listener with `client` and `federation` resources must be + # configured in the `worker_listeners` option in the worker config. + + # #### Load balancing + + # It is possible to run multiple instances of this worker app, with incoming requests + # being load-balanced between them by the reverse-proxy. However, different endpoints + # have different characteristics and so admins + # may wish to run multiple groups of workers handling different endpoints so that + # load balancing can be done in different ways. + + # For `/sync` and `/initialSync` requests it will be more efficient if all + # requests from a particular user are routed to a single instance. Extracting a + # user ID from the access token or `Authorization` header is currently left as an + # exercise for the reader. Admins may additionally wish to separate out `/sync` + # requests that have a `since` query parameter from those that don't (and + # `/initialSync`), as requests that don't are known as "initial sync" that happens + # when a user logs in on a new device and can be *very* resource intensive, so + # isolating these requests will stop them from interfering with other users ongoing + # syncs. + + # Federation and client requests can be balanced via simple round robin. + + # The inbound federation transaction request `^/_matrix/federation/v1/send/` + # should be balanced by source IP so that transactions from the same remote server + # go to the same process. + + # Registration/login requests can be handled separately purely to help ensure that + # unexpected load doesn't affect new logins and sign ups. + + # Finally, event sending requests can be balanced by the room ID in the URI (or + # the full URI, or even just round robin), the room ID is the path component after + # `/rooms/`. If there is a large bridge connected that is sending or may send lots + # of events, then a dedicated set of workers can be provisioned to limit the + # effects of bursts of events from that bridge on events sent by normal users. + + # #### Stream writers + + # Additionally, there is *experimental* support for moving writing of specific + # streams (such as events) off of the main process to a particular worker. (This + # is only supported with Redis-based replication.) + + # Currently supported streams are `events` and `typing`. + + # To enable this, the worker must have a HTTP replication listener configured, + # have a `worker_name` and be listed in the `instance_map` config. For example to + # move event persistence off to a dedicated worker, the shared configuration would + # include: + + # ```yaml + # instance_map: + # event_persister1: + # host: localhost + # port: 8034 + + # stream_writers: + # events: event_persister1 + # ``` + + # The `events` stream also experimentally supports having multiple writers, where + # work is sharded between them by room ID. Note that you *must* restart all worker + # instances when adding or removing event persisters. An example `stream_writers` + # configuration with multiple writers: + + # ```yaml + # stream_writers: + # events: + # - event_persister1 + # - event_persister2 + # ``` + + # #### Background tasks + + # There is also *experimental* support for moving background tasks to a separate + # worker. Background tasks are run periodically or started via replication. Exactly + # which tasks are configured to run depends on your Synapse configuration (e.g. if + # stats is enabled). + + # To enable this, the worker must have a `worker_name` and can be configured to run + # background tasks. For example, to move background tasks to a dedicated worker, + # the shared configuration would include: + + # ```yaml + # run_background_tasks_on: background_worker + # ``` + + # You might also wish to investigate the `update_user_directory` and + # `media_instance_running_background_jobs` settings. + +# pusher worker (no API endpoints) [ + # Handles sending push notifications to sygnal and email. Doesn't handle any + # REST endpoints itself, but you should set `start_pushers: False` in the + # shared configuration file to stop the main synapse sending push notifications. + + # To run multiple instances at once the `pusher_instances` option should list all + # pusher instances by their worker name, e.g.: + + # ```yaml + # pusher_instances: + # - pusher_worker1 + # - pusher_worker2 + # ``` + +# ] + +# appservice worker (no API endpoints) [ + # Handles sending output traffic to Application Services. Doesn't handle any + # REST endpoints itself, but you should set `notify_appservices: False` in the + # shared configuration file to stop the main synapse sending appservice notifications. + + # Note this worker cannot be load-balanced: only one instance should be active. + +# ] + +# federation_sender worker (no API endpoints) [ + # Handles sending federation traffic to other servers. Doesn't handle any + # REST endpoints itself, but you should set `send_federation: False` in the + # shared configuration file to stop the main synapse sending this traffic. + + # If running multiple federation senders then you must list each + # instance in the `federation_sender_instances` option by their `worker_name`. + # All instances must be stopped and started when adding or removing instances. + # For example: + + # ```yaml + # federation_sender_instances: + # - federation_sender1 + # - federation_sender2 + # ``` +# ] + +matrix_synapse_workers_media_repository_endpoints: + # Handles the media repository. It can handle all endpoints starting with: + + - ^/_matrix/media/ + + # ... and the following regular expressions matching media-specific administration APIs: + + - ^/_synapse/admin/v1/purge_media_cache$ + - ^/_synapse/admin/v1/room/.*/media.*$ + - ^/_synapse/admin/v1/user/.*/media.*$ + - ^/_synapse/admin/v1/media/.*$ + - ^/_synapse/admin/v1/quarantine_media/.*$ + + # You should also set `enable_media_repo: False` in the shared configuration + # file to stop the main synapse running background jobs related to managing the + # media repository. + + # In the `media_repository` worker configuration file, configure the http listener to + # expose the `media` resource. For example: + + # ```yaml + # worker_listeners: + # - type: http + # port: 8085 + # resources: + # - names: + # - media + # ``` + + # Note that if running multiple media repositories they must be on the same server + # and you must configure a single instance to run the background tasks, e.g.: + + # ```yaml + # media_instance_running_background_jobs: "media-repository-1" + # ``` + + # Note that if a reverse proxy is used , then `/_matrix/media/` must be routed for both inbound client and federation requests (if they are handled separately). + +matrix_synapse_workers_user_dir_endpoints: + # Handles searches in the user directory. It can handle REST endpoints matching + # the following regular expressions: + + - ^/_matrix/client/(api/v1|r0|unstable)/user_directory/search$ + + # When using this worker you must also set `update_user_directory: False` in the + # shared configuration file to stop the main synapse running background + # jobs related to updating the user directory. + +matrix_synapse_workers_frontend_proxy_endpoints: + # Proxies some frequently-requested client endpoints to add caching and remove + # load from the main synapse. It can handle REST endpoints matching the following + # regular expressions: + + - ^/_matrix/client/(api/v1|r0|unstable)/keys/upload + + # If `use_presence` is False in the homeserver config, it can also handle REST + # endpoints matching the following regular expressions: + + # FIXME: ADDITIONAL CONDITIONS REQUIRED: to be enabled manually + # ^/_matrix/client/(api/v1|r0|unstable)/presence/[^/]+/status + + # This "stub" presence handler will pass through `GET` request but make the + # `PUT` effectively a no-op. + + # It will proxy any requests it cannot handle to the main synapse instance. It + # must therefore be configured with the location of the main instance, via + # the `worker_main_http_uri` setting in the `frontend_proxy` worker configuration + # file. For example: + + # worker_main_http_uri: http://127.0.0.1:8008 + +matrix_synapse_workers_avail_list: + - appservice + - federation_sender + - frontend_proxy + - generic_worker + - media_repository + - pusher + - user_dir diff --git a/setup.yml b/setup.yml new file mode 100755 index 00000000..142364c4 --- /dev/null +++ b/setup.yml @@ -0,0 +1,58 @@ +--- +- name: "Set up a Matrix server" + hosts: "{{ target if target is defined else 'matrix_servers' }}" + become: true + + vars_files: + - roles/matrix-synapse/vars/workers.yml + + roles: + - matrix-awx + - matrix-base + - matrix-dynamic-dns + - matrix-mailer + - matrix-postgres + - matrix-redis + - matrix-corporal + - matrix-bridge-appservice-discord + - matrix-bridge-appservice-slack + - matrix-bridge-appservice-webhooks + - matrix-bridge-appservice-irc + - matrix-bridge-mautrix-facebook + - matrix-bridge-mautrix-hangouts + - matrix-bridge-mautrix-instagram + - matrix-bridge-mautrix-signal + - matrix-bridge-mautrix-telegram + - matrix-bridge-mautrix-whatsapp + - matrix-bridge-mx-puppet-discord + - matrix-bridge-mx-puppet-groupme + - matrix-bridge-mx-puppet-steam + - matrix-bridge-mx-puppet-skype + - matrix-bridge-mx-puppet-slack + - matrix-bridge-mx-puppet-twitter + - matrix-bridge-mx-puppet-instagram + - matrix-bridge-sms + - matrix-bridge-heisenbridge + - matrix-bot-matrix-reminder-bot + - matrix-bot-go-neb + - matrix-bot-mjolnir + - matrix-synapse + - matrix-synapse-admin + - matrix-prometheus-node-exporter + - matrix-prometheus + - matrix-grafana + - matrix-registration + - matrix-client-element + - matrix-client-hydrogen + - matrix-jitsi + - matrix-ma1sd + - matrix-dimension + - matrix-etherpad + - matrix-email2matrix + - matrix-sygnal + - matrix-nginx-proxy + - matrix-coturn + - matrix-aux + - matrix-postgres-backup + - matrix-prometheus-postgres-exporter + - matrix-common-after \ No newline at end of file