diff --git a/README.md b/README.md index 9a2726871..301e7f05f 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ Using this playbook, you can get the following services configured on your serve - (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 diff --git a/docs/configuring-playbook-bot-mjolnir.md b/docs/configuring-playbook-bot-mjolnir.md new file mode 100644 index 000000000..d58e23120 --- /dev/null +++ b/docs/configuring-playbook-bot-mjolnir.md @@ -0,0 +1,70 @@ +# Setting up Mjolnir (optional) + +The playbook can install and configure [Mjolnir](https://github.com/matrix-org/mjolnir) for you. + +Mjolnir is a moderation tool for Matrix. + +See the project's [documentation](https://github.com/matrix-org/mjolnir) 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.mjolnir 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.mjolnir" }, + "password": "PASSWORD_FOR_THE_BOT", + "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). + + +## Make sure account is free from rate limiting + +TODO + +``` +insert into ratelimit_override values ("@bot.mjolnir:DOMAIN", 0, 0); +``` + +## Adjusting the playbook configuration + +Add the following configuration to your `inventory/host_vars/matrix.DOMAIN/vars.yml` file (adapt to your needs): + +```yaml +TODO +``` + + +## Installing + +After configuring the playbook, run the [installation](installing.md) command again: + +``` +ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start +``` + + +## Usage + +TODO + +You can also refer to the upstream [documentation](https://github.com/matrix-org/mjolnir). diff --git a/docs/container-images.md b/docs/container-images.md index ee9cbd3ca..7cc7c2f68 100644 --- a/docs/container-images.md +++ b/docs/container-images.md @@ -90,6 +90,8 @@ These services are not part of our default installation, but can be enabled by [ - [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 diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index a6c433cb1..630890305 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -768,6 +768,29 @@ matrix_bot_go_neb_container_http_host_bind_port: "{{ '' if matrix_nginx_proxy_en ###################################################################### +###################################################################### +# +# matrix-bot-mjolnir +# +###################################################################### + +# We don't enable bots by default. +matrix_bot_mjolnir_enabled: false + +matrix_bot_mjolnir_systemd_required_services_list: | + {{ + ['docker.service'] + + + (['matrix-synapse.service'] if matrix_synapse_enabled else []) + }} + +###################################################################### +# +# /matrix-bot-mjolnir +# +###################################################################### + + ###################################################################### # # matrix-corporal diff --git a/roles/matrix-bot-mjolnir/defaults/main.yml b/roles/matrix-bot-mjolnir/defaults/main.yml new file mode 100644 index 000000000..e7b54364e --- /dev/null +++ b/roles/matrix-bot-mjolnir/defaults/main.yml @@ -0,0 +1,52 @@ +# A moderation tool for Matrix +# See: https://github.com/matrix-org/mjolnir + +matrix_bot_mjolnir_enabled: true +matrix_bot_mjolnir_version: "v0.1.17" +matrix_bot_mjolnir_docker_image: "docker.io/matrixdotorg/mjolnir:{{ matrix_bot_mjolnir_version }}" +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" + +# 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 000000000..d54684d20 --- /dev/null +++ b/roles/matrix-bot-mjolnir/tasks/init.yml @@ -0,0 +1,3 @@ +- 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 000000000..eada8de57 --- /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 000000000..4c18bf6af --- /dev/null +++ b/roles/matrix-bot-mjolnir/tasks/setup_install.yml @@ -0,0 +1,50 @@ +--- + +- 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 } + when: "item.when|bool" + +- name: Ensure mjolnir 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 }}" + +- 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 000000000..7127543e5 --- /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 000000000..cb961baf3 --- /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 000000000..e5eb3aeae --- /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 000000000..c2294daa3 --- /dev/null +++ b/roles/matrix-bot-mjolnir/templates/systemd/matrix-bot-mjolnir.service.j2 @@ -0,0 +1,39 @@ +#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' + +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/setup.yml b/setup.yml index c4a203ccc..683f19558 100755 --- a/setup.yml +++ b/setup.yml @@ -34,6 +34,7 @@ - matrix-bridge-sms - matrix-bot-matrix-reminder-bot - matrix-bot-go-neb + - matrix-bot-mjolnir - matrix-synapse - matrix-synapse-admin - matrix-prometheus-node-exporter