diff --git a/matrix.yml b/matrix.yml new file mode 100644 index 0000000..d662d21 --- /dev/null +++ b/matrix.yml @@ -0,0 +1,27 @@ +--- + +# Infrastructure +# Ansible instructions to deploy the infrastructure +# Copyright (C) 2020 Saibotk +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +- hosts: matrix + roles: + - docker + - docker_compose + - docker_cleanup + - traefik + - matrix + environment: + PYTHONPATH: /opt/python2/ansible-dependencies/lib/python2.7/site-packages diff --git a/roles/matrix/README.md b/roles/matrix/README.md new file mode 100644 index 0000000..6719222 --- /dev/null +++ b/roles/matrix/README.md @@ -0,0 +1,36 @@ +Matrix +========= + +This will setup a Matrix (Synapse) instance using their official docker container and traefik as a reverse proxy. +Additionally this will setup an Element Web instance and the required delegation if needed. + +Requirements +------------ + +You will need to have docker, docker-compose and traefik installed or declared as dependencies with their respective roles. + +**This role assumes that you have setup traefik with an endpoint called `websecure`.** + +Role Variables +-------------- + +**Please look at the [defaults/main.yml](defaults/main.yml) for all available variables and their description.** + +**Note: Lines that are commented out via `#` are usually still valid/used variables, but they are not defined by default, so they might enable a feature, when uncommenting/defining them!** + +### Global variables, that are used: + +- `proxy_network`: Defined by the local traefik installation, this is the shared proxy network used by traefik to reach the containers. (optional) +- `proxy_hiddenservice`: Defined by the local traefik installation, this is used to generate the alt-svc header for the alternative Tor domain. (optional) + +Dependencies +------------ + +- docker +- docker-compose +- traefik + +License +------- + +GPL-3.0-only diff --git a/roles/matrix/defaults/main.yml b/roles/matrix/defaults/main.yml new file mode 100644 index 0000000..2923f47 --- /dev/null +++ b/roles/matrix/defaults/main.yml @@ -0,0 +1,81 @@ +--- +# Default variables for the matrix role + +# Infrastructure +# Ansible instructions to deploy the infrastructure +# Copyright (C) 2019-2020 Christoph (Sheogorath) Kern +# Copyright (C) 2020 Saibotk +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# The install location +matrix_install_location: /srv/matrix + +# The container data volume mount locations +matrix_data_location: "{{ matrix_install_location}}/data" +matrix_database_location: "{{ matrix_install_location}}/database" +matrix_elementweb_location: "{{ matrix_install_location}}/elementweb" + +# The certresolver that is used by traefik for this domain +matrix_traefik_certresolver: letsencrypt_http + +# The domain that traefik will server synapse under +matrix_synapse_servername: matrix.example.com +matrix_synapse_domain: "{{ matrix_synapse_servername }}" + +# Additional synapse ENV options (keys will automatically be prefixed with SYNAPSE_) see https://github.com/matrix-org/synapse/tree/develop/docker#generating-a-configuration-file +matrix_synapse_options: [] + +# The database password to use +matrix_database_password: "{{ lookup('passwordstore', matrix_synapse_domain + '/db create=true length=42') }}" + +# Container versions +matrix_synapse_version: "1.20.1" +matrix_database_version: "13.0" +matrix_delegate_nginx_version: "1.19" +matrix_elementweb_version: "1.7.7" + +# Container tag definitions +matrix_synapse_image_version: "v{{ matrix_synapse_version }}" +matrix_database_image_version: "{{ matrix_database_version }}-alpine" +matrix_delegate_nginx_image_version: "{{ matrix_delegate_nginx_version }}-alpine" +matrix_elementweb_image_version: "v{{ matrix_elementweb_version }}" + +# The domain that traefik will server element-web under +matrix_elementweb_domain: element.example.com + +# The homeserver URL and display name +matrix_elementweb_base_url: "https://{{ matrix_synapse_domain }}" +matrix_elementweb_servername: "{{ matrix_synapse_servername }}" + +# Should the registration button on welcome page be shown? +matrix_elementweb_registration_enabled: false + +# Controls whether Element shows the presence feature for all (empty list) or specific servers (key = value list with the key being the server url) +matrix_elementweb_enable_presence_by_hs_url: [] + +# Should users only be allowed to use this instance with the given matrix server? +matrix_elementweb_disable_custom_urls: true + +# Should Element-Web disable guests? (without sign-in) +matrix_elementweb_disable_guests: true + +# Integration Server URLs to use (see https://github.com/vector-im/element-web/blob/develop/docs/config.md) +matrix_elementweb_integrations_ui_url: "https://scalar.vector.im/" +matrix_elementweb_integrations_rest_url: "https://scalar.vector.im/api" +matrix_elementweb_integrations_widgets_urls: ["https://scalar.vector.im/api"] +matrix_elementweb_integrations_jitsi_widget_url: "https://scalar.vector.im/api/widgets/jitsi.html" + +# Element Web public room directory server(s) +matrix_elementweb_roomdir_servers: ['matrix.org'] diff --git a/roles/matrix/handlers/main.yml b/roles/matrix/handlers/main.yml new file mode 100644 index 0000000..d0278b2 --- /dev/null +++ b/roles/matrix/handlers/main.yml @@ -0,0 +1,31 @@ +--- +# Handlers file for the matrix role + +# Infrastructure +# Ansible instructions to deploy the infrastructure +# Copyright (C) 2019-2020 Alexander (w4tsn) Wellbrock +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +- name: restart matrix delegate nginx + docker_compose: + services: + - delegate + state: present + project_src: "{{ matrix_install_location }}" + restarted: yes + tags: + - docker + - matrix + become: true diff --git a/roles/matrix/tasks/main.yml b/roles/matrix/tasks/main.yml new file mode 100644 index 0000000..9eeff18 --- /dev/null +++ b/roles/matrix/tasks/main.yml @@ -0,0 +1,140 @@ +--- +# Tasks file for the matrix role + +# Infrastructure +# Ansible instructions to deploy the infrastructure +# Copyright (C) 2019-2020 Christoph (Sheogorath) Kern +# Copyright (C) 2019-2020 Alexander (w4tsn) Wellbrock +# Copyright (C) 2020 Saibotk +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +- name: Update default SELinux contexts + sefcontext: + target: '{{ item }}(/.*)?' + setype: "container_file_t" + state: present + with_items: + - "{{ matrix_database_location }}" + - "{{ matrix_data_location }}" + - "{{ matrix_elementweb_location }}" + become: true + +- name: Create install directory + file: + path: "{{ item }}" + state: directory + mode: '0700' + owner: 'root' + group: 'root' + with_items: + - "{{ matrix_install_location }}" + become: true + +- name: Create data directory + file: # noqa 208 # Container manages permissions on its own + path: "{{ item }}" + state: directory + setype: "container_file_t" + with_items: + - "{{ matrix_database_location }}" + - "{{ matrix_data_location }}" + - "{{ matrix_elementweb_location }}" + become: true + +- name: Deploy docker-compose.yml + template: + src: "docker-compose.yml" + dest: "{{ matrix_install_location }}/docker-compose.yml" + mode: '0600' + owner: 'root' + group: 'root' + validate: python2 -m compose -f %s config -q + tags: + - matrix + become: true + +- name: Deploy delegation config files + template: + src: "{{ item }}" + dest: "{{ matrix_install_location }}/{{ item }}" + setype: "container_file_t" + mode: 0644 + owner: 'root' + group: 'root' + with_items: + - "server-delegation.json" + - "client-delegation.json" + tags: + - docker + - matrix + become: true + +- name: Deploy nginx delegate config + template: + src: "delegate-nginx.conf" + dest: "{{ matrix_install_location }}/nginx.conf" + setype: "container_file_t" + mode: 0600 + owner: 'root' + group: 'root' + tags: + - docker + - matrix + notify: restart matrix delegate nginx + become: true + +- name: Deploy Element-Web config files + template: + src: "elementweb-config.json" + dest: "{{ matrix_elementweb_location }}/config.json" + setype: "container_file_t" + mode: '0644' + owner: '991' + group: '991' + tags: + - docker + - matrix + become: true + +- name: Check if homeserver.yaml (config) exists + stat: path="{{ matrix_data_location }}/homeserver.yaml" + register: matrix_synapse_homeserver_yaml + tags: + - docker + - matrix + become: true + +- name: Generate synapse config + command: 'python2 -m compose run -e "SYNAPSE_SERVER_NAME={{ matrix_synapse_servername }}" synapse generate' + args: + chdir: "{{ matrix_install_location }}" + creates: "{{ matrix_data_location }}/homeserver.yaml" + tags: + - docker + - matrix + become: true + when: + - not matrix_synapse_homeserver_yaml.stat.exists + +- name: Compose matrix + docker_compose: + state: present + project_src: "{{ matrix_install_location }}" + pull: yes + remove_orphans: yes + tags: + - docker + - matrix + become: true diff --git a/roles/matrix/templates/client-delegation.json b/roles/matrix/templates/client-delegation.json new file mode 100644 index 0000000..b88c12f --- /dev/null +++ b/roles/matrix/templates/client-delegation.json @@ -0,0 +1,5 @@ +{ + "m.homeserver": { + "base_url": "https://{{ matrix_synapse_domain }}" + } + } diff --git a/roles/matrix/templates/delegate-nginx.conf b/roles/matrix/templates/delegate-nginx.conf new file mode 100644 index 0000000..bdcc2dd --- /dev/null +++ b/roles/matrix/templates/delegate-nginx.conf @@ -0,0 +1,74 @@ +{{ ansible_managed | comment }} + +# From https://git.shivering-isles.com/w4tsn/infrastructure/-/blob/2d8d03a025b833e11533fa8610cc77cff7a59bb9/roles/matrix/templates/delegate-nginx.conf.j2 + +# Infrastructure +# Ansible instructions to deploy the infrastructure +# Copyright (C) 2019-2020 Alexander (w4tsn) Wellbrock +# Copyright (C) 2020 Saibotk +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +worker_processes 1; + +error_log stderr; + +pid /tmp/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log off; + + # Turn off the bloody buffering to temp files + proxy_buffering off; + + sendfile on; + keepalive_timeout 120; + + gzip on; + gzip_types text/plain application/json; + + server_names_hash_bucket_size 128; + + # These two should be the same or nginx will start writing + # large request bodies to temp files + client_body_buffer_size 10m; + client_max_body_size 10m; + + server { + listen 80; + server_name localhost; + server_tokens off; + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + + location /.well-known/matrix { + root /usr/share/nginx/html; + {# + 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 *; + } + } +} diff --git a/roles/matrix/templates/docker-compose.yml b/roles/matrix/templates/docker-compose.yml new file mode 100644 index 0000000..2a82bc8 --- /dev/null +++ b/roles/matrix/templates/docker-compose.yml @@ -0,0 +1,173 @@ +--- +# Infrastructure +# Ansible instructions to deploy the infrastructure +# Copyright (C) 2019-2020 Christoph (Sheogorath) Kern +# Copyright (C) 2019-2020 Alexander (w4tsn) Wellbrock +# Copyright (C) 2020 Saibotk +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +version: '2' +services: + synapse: + image: docker.io/matrixdotorg/synapse:{{ matrix_synapse_image_version }} + cpu_shares: 256 + mem_limit: 1280mb + memswap_limit: 1536mb + read_only: true + restart: always + security_opt: + - no-new-privileges + tmpfs: + - "/tmp:size=64M" + - "/compiled:size=128K" + environment: +{% for key, value in matrix_synapse_options.items() %} + - "SYNAPSE_{{ key }}={{ value }}" +{% endfor %} + + - "POSTGRES_HOST=database" + - "POSTGRES_PASSWORD={{ matrix_database_password }}" + volumes: + - "{{ matrix_data_location }}:/data" + depends_on: + - database + labels: + - "traefik.http.routers.matrix.rule=Host(`{{ matrix_synapse_servername }}`) && PathPrefix(`/_matrix/federation`) || Host(`{{ matrix_synapse_domain }}`) && PathPrefix(`/`)" + - "traefik.http.routers.matrix.entrypoints=websecure" + - "traefik.http.routers.matrix.tls.certresolver={{ matrix_traefik_certresolver }}" + - "traefik.http.routers.matrix.middlewares=matrix,compress" + - "traefik.http.routers.matrix.service=matrix" + - "traefik.http.services.matrix.loadbalancer.server.port=8008" + - "traefik.http.middlewares.matrix.headers.sslredirect=true" + - "traefik.http.middlewares.matrix.headers.stsSeconds=63072000" + - "traefik.http.middlewares.matrix.headers.referrerPolicy=no-referrer" + + - "traefik.enable=true" +{% if proxy_network is defined %} + - "traefik.docker.network={{ proxy_network }}" +{% endif %} +{% if proxy_hiddenservice is defined and proxy_hiddenservice.content is defined %} + - "traefik.http.middlewares.matrix.headers.customresponseheaders.alt-svc=h2={{ proxy_hiddenservice['content'] | b64decode | trim }}:443; ma=2592000" +{% endif %} + + networks: + database: +{% if proxy_network is defined %} + {{ proxy_network }}: +{% endif %} + + database: + image: docker.io/postgres:{{ matrix_database_image_version }} + mem_limit: 512mb + memswap_limit: 768mb + read_only: true + security_opt: + - no-new-privileges + tmpfs: + - /run/postgresql:size=512K + - /tmp:size=128K + stop_grace_period: 2m + stop_signal: SIGINT + environment: + - "POSTGRES_USER=synapse" + - "POSTGRES_PASSWORD={{ matrix_database_password }}" + - "POSTGRES_INITDB_ARGS=--lc-collate C --lc-ctype C --encoding UTF8" + volumes: + - "{{ matrix_database_location }}:/var/lib/postgresql/data" + networks: + database: + restart: always + + delegate: + image: docker.io/nginx:{{ matrix_delegate_nginx_image_version }} + mem_limit: 256mb + memswap_limit: 512mb + read_only: true + tmpfs: + - "/var/cache/nginx:size=10M" + - "/run/nginx:size=512K" + - "/tmp:size=128K" + security_opt: + - no-new-privileges + volumes: + - "{{ matrix_install_location }}/server-delegation.json:/usr/share/nginx/html/.well-known/matrix/server:ro" + - "{{ matrix_install_location }}/client-delegation.json:/usr/share/nginx/html/.well-known/matrix/client:ro" + - "{{ matrix_install_location }}/nginx.conf:/etc/nginx/nginx.conf:ro" + labels: + - "traefik.enable=true" + + - "traefik.http.routers.matrix-delegate.rule=Host(`{{ matrix_synapse_servername }}`) && PathPrefix(`/.well-known/matrix`)" + - "traefik.http.routers.matrix-delegate.entrypoints=websecure" + - "traefik.http.routers.matrix-delegate.tls.certresolver={{ matrix_traefik_certresolver }}" + - "traefik.http.routers.matrix-delegate.middlewares=matrix-delegate,compress" + - "traefik.http.middlewares.matrix-delegate.headers.sslredirect=true" + - "traefik.http.middlewares.matrix-delegate.headers.stsSeconds=63072000" + - "traefik.http.middlewares.matrix-delegate.headers.referrerPolicy=no-referrer" + +{% if proxy_network is defined %} + - "traefik.docker.network={{ proxy_network }}" +{% endif %} +{% if proxy_hiddenservice['content'] is defined %} + - "traefik.http.middlewares.matrix-delegate.headers.customresponseheaders.alt-svc:h2={{ proxy_hiddenservice['content'] | b64decode | trim }}:443; ma=2592000" +{% endif %} + restart: always +{% if proxy_network is defined %} + networks: + {{ proxy_network }}: +{% endif %} + + elementweb: + image: docker.io/vectorim/riot-web:{{ matrix_elementweb_image_version }} + mem_limit: 512mb + memswap_limit: 768mb + read_only: true + security_opt: + - no-new-privileges + tmpfs: + - "/var/cache/nginx:size=10M" + - "/run:size=512K" + - "/tmp:rw,noexec,nosuid,size=10M" + labels: + - "traefik.enable=true" + + - "traefik.http.routers.matrix-elementweb.rule=Host(`{{ matrix_elementweb_domain }}`) && PathPrefix(`/`)" + - "traefik.http.routers.matrix-elementweb.entrypoints=websecure" + - "traefik.http.routers.matrix-elementweb.tls.certresolver={{ matrix_traefik_certresolver }}" + - "traefik.http.routers.matrix-elementweb.middlewares=matrix-elementweb,compress" + - "traefik.http.middlewares.matrix-elementweb.headers.sslredirect=true" + - "traefik.http.middlewares.matrix-elementweb.headers.stsSeconds=63072000" + - "traefik.http.middlewares.matrix-elementweb.headers.referrerPolicy=no-referrer" + +{% if proxy_network is defined %} + - "traefik.docker.network={{ proxy_network }}" +{% endif %} +{% if proxy_hiddenservice['content'] is defined %} + - "traefik.http.middlewares.matrix-elementweb.headers.customresponseheaders.alt-svc:h2={{ proxy_hiddenservice['content'] | b64decode | trim }}:443; ma=2592000" +{% endif %} + volumes: + - "{{ matrix_elementweb_location }}/config.json:/app/config.json:ro" +{% if proxy_network is defined %} + networks: + {{ proxy_network }}: +{% endif %} + restart: always + +networks: + database: + internal: true +{% if proxy_network is defined %} + {{ proxy_network }}: + external: true +{% endif %} diff --git a/roles/matrix/templates/elementweb-config.json b/roles/matrix/templates/elementweb-config.json new file mode 100644 index 0000000..c7f136d --- /dev/null +++ b/roles/matrix/templates/elementweb-config.json @@ -0,0 +1,22 @@ +{ + "default_server_config": { + "m.homeserver": { + "base_url": {{ matrix_elementweb_base_url | string | to_json }}, + "server_name": {{ matrix_elementweb_servername | string | to_json }} + } + }, + "disable_custom_urls": {{ matrix_elementweb_disable_custom_urls | to_json }}, + "disable_guests": {{ matrix_elementweb_disable_guests | to_json }}, + "integrations_ui_url": {{ matrix_elementweb_integrations_ui_url | string | to_json }}, + "integrations_rest_url": {{ matrix_elementweb_integrations_rest_url | string | to_json }}, + "integrations_widgets_urls": {{ matrix_elementweb_integrations_widgets_urls | to_json }}, + "integrations_jitsi_widget_url": {{ matrix_elementweb_integrations_jitsi_widget_url | string | to_json }}, + "bug_report_endpoint_url": "https://element.io/bugreports/submit", + "enableLabs": true, +{% if matrix_elementweb_enable_presence_by_hs_url %} + "enable_presence_by_hs_url": {{ matrix_elementweb_enable_presence_by_hs_url | to_json }}, +{% endif %} + "roomDirectory": { + "servers": {{ matrix_elementweb_roomdir_servers | to_json }} + } +} diff --git a/roles/matrix/templates/server-delegation.json b/roles/matrix/templates/server-delegation.json new file mode 100644 index 0000000..3163298 --- /dev/null +++ b/roles/matrix/templates/server-delegation.json @@ -0,0 +1,3 @@ +{ + "m.server": "{{ matrix_synapse_domain }}:443" +}