From 4d46b625ffb8e6bbb08160187e40c9daf1df1578 Mon Sep 17 00:00:00 2001 From: Chasethechicken Date: Thu, 8 Aug 2024 05:41:45 +0200 Subject: [PATCH] Draupnir proxy (#3313) * Allow redircting abuse-reports to draupnir * Document redirecting abuse-reports to draupnir via traefik * Apply suggestions from code review Co-authored-by: Slavi Pantaleev * Rename variable --------- Co-authored-by: Slavi Pantaleev --- docs/configuring-playbook-bot-draupnir.md | 5 +- group_vars/matrix_servers | 13 ++++- .../matrix-bot-draupnir/defaults/main.yml | 31 +++++++++++ .../tasks/setup_install.yml | 11 ++++ .../matrix-bot-draupnir/templates/labels.j2 | 53 +++++++++++++++++++ .../templates/production.yaml.j2 | 49 ++++++++--------- .../systemd/matrix-bot-draupnir.service.j2 | 6 ++- 7 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 roles/custom/matrix-bot-draupnir/templates/labels.j2 diff --git a/docs/configuring-playbook-bot-draupnir.md b/docs/configuring-playbook-bot-draupnir.md index 27b2a06a6..da3e170c5 100644 --- a/docs/configuring-playbook-bot-draupnir.md +++ b/docs/configuring-playbook-bot-draupnir.md @@ -146,7 +146,10 @@ matrix_bot_draupnir_configuration_extension_yaml: | Draupnir supports two methods to receive reports in the management room. The first method intercepts the report API endpoint of the client-server API, which requires integration with the reverse proxy in front of the homeserver. -While this playbook uses reverse proxies, it does not yet implement this. +If you are using traefik, this playbook can set this up for you: +```yaml +matrix_bot_draupnir_abuse_reporting_enabled: true +``` The other method polls an synapse admin API endpoint and is hence only available when using synapse and when the Draupnir user is an admin user (see step 1). To enable it, set `pollReports: true` in Draupnir's config: diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index 46b9ef5b1..84f9cda1d 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -2791,6 +2791,8 @@ matrix_bot_mjolnir_raw_homeserver_url: "{{ matrix_addons_homeserver_client_api_u # We don't enable bots by default. matrix_bot_draupnir_enabled: false +matrix_bot_draupnir_container_http_host_bind_port: "{{ (matrix_playbook_service_host_bind_interface_prefix ~ '8769') if matrix_playbook_service_host_bind_interface_prefix else '' }}" + matrix_bot_draupnir_systemd_required_services_list_auto: | {{ matrix_addons_homeserver_systemd_services_list @@ -2805,7 +2807,11 @@ matrix_bot_draupnir_container_network: "{{ matrix_addons_container_network }}" matrix_bot_draupnir_container_additional_networks_auto: |- {{ ( - ([] if matrix_addons_homeserver_container_network == '' else [matrix_addons_homeserver_container_network]) + ([] if matrix_addons_homeserver_container_network == '' else + [matrix_addons_homeserver_container_network]) + + + ([matrix_playbook_reverse_proxyable_services_additional_network] if + (matrix_playbook_reverse_proxyable_services_additional_network and matrix_bot_draupnir_container_labels_traefik_enabled) else []) + ([matrix_pantalaimon_container_network] if (matrix_bot_draupnir_pantalaimon_use and matrix_pantalaimon_container_network != matrix_bot_draupnir_container_network) else []) ) | unique @@ -2814,6 +2820,11 @@ matrix_bot_draupnir_container_additional_networks_auto: |- matrix_bot_draupnir_homeserver_url: "{{ 'http://matrix-pantalaimon:8009' if matrix_bot_draupnir_pantalaimon_use else matrix_addons_homeserver_client_api_url }}" matrix_bot_draupnir_raw_homeserver_url: "{{ matrix_addons_homeserver_client_api_url }}" +matrix_bot_draupnir_container_labels_traefik_enabled: "{{ matrix_bot_draupnir_web_enabled and matrix_playbook_reverse_proxy_type in ['playbook-managed-traefik', 'other-traefik-container'] }}" +matrix_bot_draupnir_container_labels_traefik_docker_network: "{{ matrix_playbook_reverse_proxyable_services_additional_network }}" +matrix_bot_draupnir_container_labels_traefik_entrypoints: "{{ devture_traefik_entrypoint_primary }}" +matrix_bot_draupnir_container_labels_traefik_tls_certResolver: "{{ devture_traefik_certResolver_primary }}" + ###################################################################### # # /matrix-bot-draupnir diff --git a/roles/custom/matrix-bot-draupnir/defaults/main.yml b/roles/custom/matrix-bot-draupnir/defaults/main.yml index 53dd53e43..d70867627 100644 --- a/roles/custom/matrix-bot-draupnir/defaults/main.yml +++ b/roles/custom/matrix-bot-draupnir/defaults/main.yml @@ -19,12 +19,21 @@ matrix_bot_draupnir_config_path: "{{ matrix_bot_draupnir_base_path }}/config" matrix_bot_draupnir_data_path: "{{ matrix_bot_draupnir_base_path }}/data" matrix_bot_draupnir_docker_src_files_path: "{{ matrix_bot_draupnir_base_path }}/docker-src" +matrix_bot_draupnir_abuse_reporting_enabled: false +matrix_bot_draupnir_web_enabled: "{{ matrix_bot_draupnir_abuse_reporting_enabled }}" +matrix_bot_draupnir_display_reports: "{{ matrix_bot_draupnir_abuse_reporting_enabled }}" + matrix_bot_draupnir_container_network: "" matrix_bot_draupnir_container_additional_networks: "{{ matrix_bot_draupnir_container_additional_networks_auto + matrix_bot_draupnir_container_additional_networks_custom }}" matrix_bot_draupnir_container_additional_networks_auto: [] matrix_bot_draupnir_container_additional_networks_custom: [] +# Controls whether the matrix-bot-draupnir 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_bot_draupnir_container_http_host_bind_port: '' + # A list of extra arguments to pass to the container matrix_bot_draupnir_container_extra_arguments: [] @@ -96,3 +105,25 @@ matrix_bot_draupnir_configuration_extension: "{{ matrix_bot_draupnir_configurati # 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_draupnir_configuration_yaml`. matrix_bot_draupnir_configuration: "{{ matrix_bot_draupnir_configuration_yaml | from_yaml | combine(matrix_bot_draupnir_configuration_extension, recursive=True) }}" + +# Controls whether labels will be added that redirect the /_matrix/client/../rooms/../report paths to draupnir +# Following these recommendations (https://github.com/element-hq/synapse/blob/master/docs/reverse_proxy.md), by default, we don't. +# Regardless of whether this is enabled, it may or may not take effect due to the value of other variables. +# See `matrix_synapse_container_labels_traefik_enabled` or `matrix_synapse_container_labels_matrix_related_labels_enabled` +matrix_bot_draupnir_container_labels_traefik_enabled: false +matrix_bot_draupnir_container_labels_traefik_docker_network: "{{ matrix_draupnir_bot_container_network }}" +matrix_bot_draupnir_container_labels_traefik_hostname: "{{ matrix_synapse_container_labels_traefik_hostname }}" +matrix_bot_draupnir_container_labels_traefik_path_regexp: "^/_matrix/client/(r0|v3)/rooms/([^/]*)/report/" +matrix_bot_draupnir_container_labels_traefik_rule: "Host(`{{ matrix_bot_draupnir_container_labels_traefik_hostname }}`) && PathRegexp(`{{ matrix_bot_draupnir_container_labels_traefik_path_regexp }}`)" +matrix_bot_draupnir_container_labels_traefik_priority: 0 +matrix_bot_draupnir_container_labels_traefik_entrypoints: "{{ matrix_synapse_container_labels_traefik_entrypoints }}" +matrix_bot_draupnir_container_labels_traefik_tls: "{{ matrix_bot_draupnir_container_labels_traefik_entrypoints != 'web' }}" +matrix_bot_draupnir_container_labels_traefik_tls_certResolver: "{{ matrix_synapse_container_labels_traefik_tls_certResolver }}" # noqa var-naming +# matrix_bot_draupnir_container_labels_traefik_labels_additional_labels contains a multiline string with additional labels to add to the container label file. +# See `../templates/labels.j2` for details. +# +# Example: +# matrix_bot_draupnir_container_labels_traefik_labels_additional_labels: | +# my.label=1 +# another.label="here" +matrix_bot_draupnir_container_labels_traefik_labels_additional_labels: '' diff --git a/roles/custom/matrix-bot-draupnir/tasks/setup_install.yml b/roles/custom/matrix-bot-draupnir/tasks/setup_install.yml index 91830ac98..a549ad513 100644 --- a/roles/custom/matrix-bot-draupnir/tasks/setup_install.yml +++ b/roles/custom/matrix-bot-draupnir/tasks/setup_install.yml @@ -17,6 +17,17 @@ - {path: "{{ matrix_bot_draupnir_docker_src_files_path }}", when: "{{ matrix_bot_draupnir_container_image_self_build }}"} when: "item.when | bool" +- name: Ensure matrix-bot-draupnir support files installed + ansible.builtin.template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: "{{ matrix_user_username }}" + group: "{{ matrix_user_groupname }}" + mode: 0644 + with_items: + - src: "{{ role_path }}/templates/labels.j2" + dest: "{{ matrix_bot_draupnir_base_path }}/labels" + - name: Ensure draupnir Docker image is pulled community.docker.docker_image: name: "{{ matrix_bot_draupnir_docker_image }}" diff --git a/roles/custom/matrix-bot-draupnir/templates/labels.j2 b/roles/custom/matrix-bot-draupnir/templates/labels.j2 new file mode 100644 index 000000000..f24a77ddf --- /dev/null +++ b/roles/custom/matrix-bot-draupnir/templates/labels.j2 @@ -0,0 +1,53 @@ +{% if matrix_bot_draupnir_container_labels_traefik_enabled %} +traefik.enable=true + +{% if matrix_bot_draupnir_container_labels_traefik_docker_network %} +traefik.docker.network={{ matrix_bot_draupnir_container_labels_traefik_docker_network }} +{% endif %} + +traefik.http.services.matrix-bot-draupnir.loadbalancer.server.port=8080 + +############################################################ +# # +# Abuse Reports (/_matrix/client/../rooms/../report) # +# # +############################################################ + +{% set middlewares = [] %} + +traefik.http.middlewares.matrix-bot-draupnir-redirect.replacepathregex.regex=^/_matrix/client/(r0|v3)/rooms/([^/]*)/report/(.*)$ +traefik.http.middlewares.matrix-bot-draupnir-redirect.replacepathregex.replacement=/api/1/report/$2/$3 + +{% set middlewares = middlewares + ['matrix-bot-draupnir-redirect'] %} + +traefik.http.middlewares.matrix-bot-draupnir-cors.headers.accesscontrolalloworiginlist=* +traefik.http.middlewares.matrix-bot-draupnir-cors.headers.accesscontrolallowheaders=Content-Type,Authorization +traefik.http.middlewares.matrix-bot-draupnir-cors.headers.accesscontrolallowmethods=POST,OPTIONS + +{% set middlewares = middlewares + ['matrix-bot-draupnir-cors'] %} + +traefik.http.routers.matrix-bot-draupnir.rule={{ matrix_bot_draupnir_container_labels_traefik_rule }} + +{% if matrix_bot_draupnir_container_labels_traefik_priority | int > 0 %} +traefik.http.routers.matrix-bot-draupnir.priority={{ matrix_bot_draupnir_container_labels_traefik_priority }} +{% endif %} +{% if middlewares | length > 0 %} +traefik.http.routers.matrix-bot-draupnir.middlewares={{ middlewares | join(',') }} +{% endif %} + +traefik.http.routers.matrix-bot-draupnir.service=matrix-bot-draupnir +traefik.http.routers.matrix-bot-draupnir.entrypoints={{ matrix_bot_draupnir_container_labels_traefik_entrypoints }} +traefik.http.routers.matrix-bot-draupnir.tls={{ matrix_bot_draupnir_container_labels_traefik_tls | to_json }} + +{% if matrix_bot_draupnir_container_labels_traefik_tls %} +traefik.http.routers.matrix-bot-draupnir.tls.certResolver={{ matrix_bot_draupnir_container_labels_traefik_tls_certResolver }} +{% endif %} + +############################################################ +# # +# /Abuse Reports (/_matrix/client/../rooms/../report) # +# # +############################################################ +{% endif %} + +{{ matrix_bot_draupnir_container_labels_traefik_labels_additional_labels }} diff --git a/roles/custom/matrix-bot-draupnir/templates/production.yaml.j2 b/roles/custom/matrix-bot-draupnir/templates/production.yaml.j2 index b4d3a0bcc..f472447b2 100644 --- a/roles/custom/matrix-bot-draupnir/templates/production.yaml.j2 +++ b/roles/custom/matrix-bot-draupnir/templates/production.yaml.j2 @@ -227,32 +227,29 @@ health: # Defaults to 418. unhealthyStatus: 418 +{% if matrix_bot_draupnir_web_enabled %} # Options for exposing web APIs. -#web: -# # Whether to enable web APIs. -# enabled: false -# -# # The port to expose the webserver on. Defaults to 8080. -# port: 8080 -# -# # The address to listen for requests on. Defaults to only the current -# # computer. -# address: localhost -# -# # Alternative setting to open to the entire web. Be careful, -# # as this will increase your security perimeter: -# # -# # address: "0.0.0.0" -# -# # A web API designed to intercept Matrix API -# # POST /_matrix/client/r0/rooms/{roomId}/report/{eventId} -# # and display readable abuse reports in the moderation room. -# # -# # If you wish to take advantage of this feature, you will need -# # to configure a reverse proxy, see e.g. test/nginx.conf -# abuseReporting: -# # Whether to enable this feature. -# enabled: false +web: + # Whether to enable web APIs. + enabled: true + + # The port to expose the webserver on. Defaults to 8080. + port: 8080 + + # The address to listen for requests on. Defaults to only the current + # computer. + address: 0.0.0.0 + + # A web API designed to intercept Matrix API + # POST /_matrix/client/r0/rooms/{roomId}/report/{eventId} + # and display readable abuse reports in the moderation room. + # + # If you wish to take advantage of this feature, you will need + # to configure a reverse proxy, see e.g. test/nginx.conf + abuseReporting: + # Whether to enable this feature. + enabled: {{ matrix_bot_draupnir_abuse_reporting_enabled | to_json }} +{% endif %} # Whether or not to actively poll synapse for abuse reports, to be used # instead of intercepting client calls to synapse's abuse endpoint, when that @@ -261,4 +258,4 @@ pollReports: false # Whether or not new reports, received either by webapi or polling, # should be printed to our managementRoom. -displayReports: false +displayReports: {{ matrix_bot_draupnir_display_reports | to_json }} diff --git a/roles/custom/matrix-bot-draupnir/templates/systemd/matrix-bot-draupnir.service.j2 b/roles/custom/matrix-bot-draupnir/templates/systemd/matrix-bot-draupnir.service.j2 index 7a88d2d3f..d83f088f2 100644 --- a/roles/custom/matrix-bot-draupnir/templates/systemd/matrix-bot-draupnir.service.j2 +++ b/roles/custom/matrix-bot-draupnir/templates/systemd/matrix-bot-draupnir.service.j2 @@ -24,13 +24,17 @@ ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} create \ --cap-drop=ALL \ --read-only \ --network={{ matrix_bot_draupnir_container_network }} \ + {% if matrix_bot_draupnir_container_http_host_bind_port %} + -p {{ matrix_bot_draupnir_container_http_host_bind_port }}:8080 \ + {% endif %} + --label-file={{ matrix_bot_draupnir_base_path }}/labels \ --mount type=bind,src={{ matrix_bot_draupnir_config_path }},dst=/data/config,ro \ --mount type=bind,src={{ matrix_bot_draupnir_data_path }},dst=/data \ {% for arg in matrix_bot_draupnir_container_extra_arguments %} {{ arg }} \ {% endfor %} {{ matrix_bot_draupnir_docker_image }} \ - bot --draupnir-config /data/config/production.yaml + bot --draupnir-config /data/config/production.yaml {% for network in matrix_bot_draupnir_container_additional_networks %} ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} network connect {{ network }} matrix-bot-draupnir