From b22030b12d81c7428e5049d77cefbc2a42fc7e56 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 10 Feb 2020 12:55:48 +0000 Subject: [PATCH] First draft of a maubot role --- roles/matrix-maubot/defaults/main.yml | 22 ++++ roles/matrix-maubot/tasks/main.yml | 122 ++++++++++++++++++ .../templates/matrix-maubot.service.j2 | 38 ++++++ .../templates/maubot_config.yaml.j2 | 99 ++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 roles/matrix-maubot/defaults/main.yml create mode 100644 roles/matrix-maubot/tasks/main.yml create mode 100644 roles/matrix-maubot/templates/matrix-maubot.service.j2 create mode 100644 roles/matrix-maubot/templates/maubot_config.yaml.j2 diff --git a/roles/matrix-maubot/defaults/main.yml b/roles/matrix-maubot/defaults/main.yml new file mode 100644 index 000000000..d3082d546 --- /dev/null +++ b/roles/matrix-maubot/defaults/main.yml @@ -0,0 +1,22 @@ +matrix_maubot_enabled: true +matrix_maubot_host_data_dir: "{{ matrix_base_data_path }}/maubot/data" +matrix_maubot_host_config_dir: "{{ matrix_base_data_path }}/maubot/config" +matrix_maubot_container_data_dir: "/data" +matrix_maubot_container_config_dir: "/root/.config/" +matrix_maubot_config_filename: "config.yaml" +matrix_maubot_config_path: "{{ matrix_maubot_host_data_dir + '/' + matrix_maubot_config_filename }}" +matrix_maubot_systemd_required_services_list: ['docker.service'] +matrix_maubot_systemd_wanted_services_list: [] +matrix_maubot_container_extra_arguments: [] + +matrix_maubot_database_url: "sqlite:///{{ matrix_maubot_container_data_dir }}/maubot.db" +matrix_maubot_management_port: 29316 +matrix_maubot_management_hostname: "0.0.0.0" + +matrix_maubot_shared_secret: false +matrix_maubot_registration_shared_secret: +matrix_maubot_cs_url: http://matrix-synapse:8008 +matrix_maubot_admins: false + +matrix_maubot_docker_image: dock.mau.dev/maubot/maubot:latest +matrix_maubot_docker_image_force_pull: "{{ matrix_dimension_docker_image.endswith(':latest') }}" diff --git a/roles/matrix-maubot/tasks/main.yml b/roles/matrix-maubot/tasks/main.yml new file mode 100644 index 000000000..209e2ad37 --- /dev/null +++ b/roles/matrix-maubot/tasks/main.yml @@ -0,0 +1,122 @@ +--- +- name: Create Config Dir + file: + path: "{{ matrix_maubot_host_data_dir }}" + state: directory + mode: 0755 + owner: "{{ matrix_user_username }}" + when: matrix_maubot_enabled|bool + +- name: Create User Config Dir + file: + path: "{{ matrix_maubot_host_config_dir }}" + state: directory + mode: 0755 + owner: "{{ matrix_user_username }}" + when: matrix_maubot_enabled|bool and matrix_maubot_host_config_dir|bool + +- name: Create Config File + template: + src: maubot_config.yaml.j2 + dest: "{{ matrix_maubot_config_path }}" + owner: "{{ matrix_user_username }}" + mode: "u=rwx" + when: matrix_maubot_enabled|bool + +- name: Ensure Maubot image is pulled + docker_image: + name: "{{ matrix_maubot_docker_image }}" + source: "{{ 'pull' if ansible_version.major > 2 or ansible_version.minor > 7 else omit }}" + force_source: "{{ matrix_maubot_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_maubot_docker_image_force_pull }}" + when: matrix_maubot_enabled|bool + +- name: Ensure matrix-maubot.service installed + template: + src: "{{ role_path }}/templates/matrix-maubot.service.j2" + dest: "/etc/systemd/system/matrix-maubot.service" + mode: 0644 + register: matrix_maubot_systemd_service_result + when: matrix_maubot_enabled|bool + +- set_fact: + matrix_systemd_services_list: "{{ matrix_systemd_services_list + ['matrix-maubot'] }}" + +- name: Fail if matrix-nginx-proxy role already executed + fail: + msg: >- + Trying to append Maubot'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-maubot role. + when: matrix_nginx_proxy_role_executed|default(False)|bool and matrix_maubot_enabled|bool + +- name: Generate Maubot proxying configuration for matrix-nginx-proxy + set_fact: + matrix_maubot_matrix_nginx_proxy_configuration: | + location /_matrix/maubot { + {% 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-maubot:{{ matrix_maubot_management_port }}"; + proxy_pass http://$backend; + {% else %} + {# Generic configuration for use outside of our container setup #} + proxy_pass http://127.0.0.1:{{ matrix_maubot_management_port }}; + {% endif %} + } + when: matrix_maubot_enabled|bool + +- name: Register Maubot's 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_maubot_matrix_nginx_proxy_configuration] + }} + when: matrix_maubot_enabled|bool + +- name: Warn about reverse-proxying if matrix-nginx-proxy not used + debug: + msg: >- + NOTE: You've enabled Maubot but are not using the matrix-nginx-proxy + reverse proxy. + Please make sure that you're proxying the `/_matrix/maubot` + URL endpoint to the matrix-maubot container. + when: "matrix_maubot_enabled|bool and matrix_nginx_proxy_enabled is not defined" + +# +# Tasks related to getting rid of the maubot (if it was previously enabled) +# + +- name: Check existence of matrix-maubot service + stat: + path: "/etc/systemd/system/matrix-maubot.service" + register: matrix_maubot_service_stat + when: "not matrix_maubot_enabled|bool" + +- name: Ensure matrix-maubot is stopped + service: + name: matrix-maubot + state: stopped + daemon_reload: yes + register: stopping_result + when: "not matrix_maubot_enabled|bool and matrix_maubot_service_stat.stat.exists" + +- name: Ensure matrix-maubot.service doesn't exist + file: + path: "/etc/systemd/system/matrix-maubot.service" + state: absent + when: "not matrix_maubot_enabled|bool and matrix_maubot_service_stat.stat.exists" + +- name: Ensure systemd reloaded after matrix-maubot.service removal + service: + daemon_reload: yes + when: "not matrix_maubot_enabled|bool and matrix_maubot_service_stat.stat.exists" + +- name: Ensure maubot Docker image doesn't exist + docker_image: + name: "{{ matrix_maubot_docker_image }}" + state: absent + when: "not matrix_maubot_enabled|bool" diff --git a/roles/matrix-maubot/templates/matrix-maubot.service.j2 b/roles/matrix-maubot/templates/matrix-maubot.service.j2 new file mode 100644 index 000000000..cd2728212 --- /dev/null +++ b/roles/matrix-maubot/templates/matrix-maubot.service.j2 @@ -0,0 +1,38 @@ +#jinja2: lstrip_blocks: "True" +[Unit] +Description=Matrix Maubot +{% for service in matrix_maubot_systemd_required_services_list %} +Requires={{ service }} +After={{ service }} +{% endfor %} +{% for service in matrix_maubot_systemd_wanted_services_list %} +Wants={{ service }} +{% endfor %} + +[Service] +Type=simple +ExecStartPre=-/usr/bin/docker kill matrix-maubot +ExecStartPre=-/usr/bin/docker rm matrix-maubot + +# 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-maubot \ + --log-driver=none \ + -e UID={{ matrix_user_uid }} \ + -e GID={{ matrix_user_gid }} \ + --network={{ matrix_docker_network }} \ + -v {{ matrix_maubot_host_data_dir }}:/data:z \ + {% for arg in matrix_maubot_container_extra_arguments %} + {{ arg }} \ + {% endfor %} + {{ matrix_maubot_docker_image }} + +ExecStop=-/usr/bin/docker kill matrix-maubot +ExecStop=-/usr/bin/docker rm matrix-maubot +Restart=always +RestartSec=30 +SyslogIdentifier=matrix-maubot + +[Install] +WantedBy=multi-user.target diff --git a/roles/matrix-maubot/templates/maubot_config.yaml.j2 b/roles/matrix-maubot/templates/maubot_config.yaml.j2 new file mode 100644 index 000000000..1bd28c596 --- /dev/null +++ b/roles/matrix-maubot/templates/maubot_config.yaml.j2 @@ -0,0 +1,99 @@ +# 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_maubot_database_url }} + +plugin_directories: + # The directory where uploaded new plugins should be stored. + upload: {{ matrix_maubot_container_data_dir }}/plugins + # The directories from which plugins should be loaded. + # Duplicate plugin IDs will be moved to the trash. + load: + - {{ matrix_maubot_container_data_dir }}/plugins + # The directory where old plugin versions and conflicting plugins should be moved. + # Set to "delete" to delete files immediately. + trash: {{ matrix_maubot_container_data_dir }}/trash + # The directory where plugin databases should be stored. + db: {{ matrix_maubot_container_data_dir }}/plugins + +server: + # The IP and port to listen to. + hostname: {{ matrix_maubot_management_hostname }} + port: {{ matrix_maubot_management_port }} + # The base management API path. + base_path: /_matrix/maubot/v1 + # The base path for the UI. + ui_base_path: /_matrix/maubot + # Override path from where to load UI resources. + # Set to false to using pkg_resources to find the path. + override_resource_path: /opt/maubot/frontend + # The base appservice API path. Use / for legacy appservice API and /_matrix/app/v1 for v1. + appservice_base_path: /_matrix/app/v1 + # The shared secret to sign API access tokens. + # Set to "generate" to generate and save a new token at startup. + unshared_secret: generate + +{% if maubot_shared_secret %} +# Shared registration secrets to allow registering new users from the management UI +registration_secrets: + {{ matrix_domain }}: + # Client-server API URL + url: {{ matrix_maubot_cs_url }} + # registration_shared_secret from synapse config + secret: {{ matrix_maubot_registration_shared_secret }} +{% endif %} + +# List of administrator users. Plaintext passwords will be bcrypted on startup. Set empty password +# to prevent normal login. Root is a special user that can't have a password and will always exist. +admins: + root: "" +{% if matrix_maubot_admins %} +{% for user, password in matrix_maubot_admins %} + {{ user }}: '{{ password }}' +{% endfor %} +{% endif %} + +# API feature switches. +api_features: + login: true + plugin: true + plugin_upload: true + instance: true + instance_database: true + client: true + client_proxy: true + client_auth: true + dev_open: true + log: true + +# 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: + file: + class: logging.handlers.RotatingFileHandler + formatter: precise + filename: /var/log/maubot.log + maxBytes: 10485760 + backupCount: 10 + console: + class: logging.StreamHandler + formatter: precise + loggers: + maubot: + level: DEBUG + mautrix: + level: DEBUG + aiohttp: + level: INFO + root: + level: DEBUG + handlers: [file, console]