From ac26cc1cb0d6423b751b74ddb06a8c793f63dd56 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Wed, 19 Feb 2025 11:48:12 +0200 Subject: [PATCH] Allow STUN/TURN exposure over TCP/UDP to be controlled separately & disable STUN over UDP by default --- CHANGELOG.md | 22 ++++++++++++++++ docs/prerequisites.md | 7 +++--- group_vars/matrix_servers | 4 +-- roles/custom/matrix-coturn/defaults/main.yml | 25 ++++++++++++++++--- .../matrix-coturn/tasks/validate_config.yml | 2 ++ .../systemd/matrix-coturn.service.j2 | 16 +++++++----- 6 files changed, 60 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85bdaed2e..c27faf943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +# 2025-02-19 + +## The playbook now defaults to exposing the Coturn STUN port (3478) only over TCP + +We've previously done some work to **decrease the severity** of DDoS amplification attacks done through the [Coturn](./docs/configuring-playbook-turn.md)'s STUN port (2.8x -> 1.6x) as reported in [coturn: Lower DDoS amplification/reflection factor from 2.8 to 1.6 #2592](https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/2592). + +To **completely eliminate the problem** of DDoS amplification attacks done through the [Coturn](./docs/configuring-playbook-turn.md) STUN port even further (read more about this in [this article](https://stormwall.network/resources/blog/protect-against-ddos-based-on-stun-exploit)), the playbook now **disables exposure of the Coturn STUN port (`3478`) over UDP**. This is a bit heavy-handed, but is probably the only way to completely eliminate the problem. + +The playbook now **only exposes the Coturn STUN port (`3478`) over TCP by default**. + +💡 Users may wish to further remove the (now unnnecessary) firewall rule allowing access to `3478/udp`. + +If you'd like the Coturn STUN port to be exposed over UDP like before, you can revert to the previous behavior by using the following configuration in your `vars.yml` file: + +```yaml +matrix_coturn_container_stun_plain_host_bind_port_udp: "3478" +``` + +> [!WARNING] +> People running Coturn directly on the `host` network (using `matrix_coturn_container_network: host`) will still have the STUN port exposed over UDP, as port exposure is done directly via Coturn and not via Docker. In such cases, the playbook cannot prevent `3478/udp` port exposure and you'd need to do it in another way (separate firewall rule, etc). + + # 2025-02-17 ## FluffyChat Web suport diff --git a/docs/prerequisites.md b/docs/prerequisites.md index 3bd3da975..94445befe 100644 --- a/docs/prerequisites.md +++ b/docs/prerequisites.md @@ -48,10 +48,9 @@ We will be using `example.com` as the domain in the following instruction. Pleas - `80/tcp`: HTTP webserver - `443/tcp` and `443/udp`: 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) + - `3478/tcp`: STUN/TURN over TCP (used by [coturn](./docs/configuring-playbook-turn.md)) + - `5349/tcp`: TURN over TCP (used by [coturn](./docs/configuring-playbook-turn.md)) + - `5349/udp`: TURN over UDP (used by [coturn](./docs/configuring-playbook-turn.md)) - `8448/tcp` and `8448/udp`: Matrix Federation API HTTPS webserver. Some components like [Matrix User Verification Service](configuring-playbook-user-verification-service.md#open-matrix-federation-port) require this port to be opened **even with federation disabled**. - 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. diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index ac2f386dd..46e08f6a8 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -3615,8 +3615,8 @@ jitsi_web_framing_enabled: true jitsi_turn_credentials: "{{ matrix_coturn_turn_static_auth_secret if (matrix_coturn_enabled and matrix_coturn_authentication_method == 'auth-secret') else '' }}" jitsi_turn_host: "{{ ('turn.' + matrix_server_fqn_matrix) if matrix_coturn_enabled else '' }}" jitsi_turns_host: "{{ ('turn.' + matrix_server_fqn_matrix) if matrix_coturn_enabled else '' }}" -jitsi_turn_port: "{{ matrix_coturn_container_stun_plain_host_bind_port.split(':')[-1] if matrix_coturn_enabled else '' }}" -jitsi_turns_port: "{{ matrix_coturn_container_stun_tls_host_bind_port.split(':')[-1] if matrix_coturn_enabled else '' }}" +jitsi_turn_port: "{{ matrix_coturn_container_stun_plain_host_bind_port_tcp.split(':')[-1] if matrix_coturn_enabled else '' }}" +jitsi_turns_port: "{{ matrix_coturn_container_stun_tls_host_bind_port_tcp.split(':')[-1] if matrix_coturn_enabled else '' }}" # If the self-hosted Etherpad instance is available, it will also show up in Jitsi conferences, # unless explicitly disabled by setting `jitsi_etherpad_enabled` to false. diff --git a/roles/custom/matrix-coturn/defaults/main.yml b/roles/custom/matrix-coturn/defaults/main.yml index 60641c196..78e8e35f9 100644 --- a/roles/custom/matrix-coturn/defaults/main.yml +++ b/roles/custom/matrix-coturn/defaults/main.yml @@ -51,15 +51,32 @@ 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). +# Controls whether the coturn container exposes its plain STUN port (tcp/3478 in the container) over TCP. # # 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' if matrix_coturn_container_network != 'host' else '' }}" +matrix_coturn_container_stun_plain_host_bind_port_tcp: "{{ '3478' if matrix_coturn_container_network != 'host' else '' }}" -# Controls whether the coturn container exposes its TLS STUN port (tcp/5349 and udp/5349 in the container). +# Controls whether the coturn container exposes its plain STUN port (udp/3478 in the container) over UDP. +# +# Takes an ":" or "" value (e.g. "127.0.0.1:3478"), or empty string to not expose. +# +# This is not done by default to decrease the risk of DDoS amplification attacks. +# See: https://stormwall.network/resources/blog/protect-against-ddos-based-on-stun-exploit +matrix_coturn_container_stun_plain_host_bind_port_udp: "" + +# Controls whether the coturn container exposes its TLS STUN port (tcp/5349 in the container) over TCP. # # 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' if matrix_coturn_container_network != 'host' else '' }}" +matrix_coturn_container_stun_tls_host_bind_port_tcp: "{{ '5349' if matrix_coturn_container_network != 'host' else '' }}" + +# Controls whether the coturn container exposes its TLS STUN port (udp/5349 in the container) over UDP. +# +# Takes an ":" or "" value (e.g. "127.0.0.1:5349"), or empty string to not expose. +# +# This is enabled by default, unlike `matrix_coturn_container_stun_plain_host_bind_port_udp`, +# because the risk of DDoS amplification attacks is lower for TLS +# due to the handshake requiring two-way authentication and being generally more expensive. +matrix_coturn_container_stun_tls_host_bind_port_udp: "{{ '5349' if matrix_coturn_container_network != 'host' else '' }}" # Controls whether the coturn container exposes its TURN UDP port range and which interface to do it on. # diff --git a/roles/custom/matrix-coturn/tasks/validate_config.yml b/roles/custom/matrix-coturn/tasks/validate_config.yml index 7fd26e881..a90adb812 100644 --- a/roles/custom/matrix-coturn/tasks/validate_config.yml +++ b/roles/custom/matrix-coturn/tasks/validate_config.yml @@ -8,6 +8,8 @@ when: "item.old in vars" with_items: - {'old': 'matrix_coturn_docker_network', 'new': 'matrix_coturn_container_network'} + - {'old': 'matrix_coturn_container_stun_plain_host_bind_port', 'new': 'superseded by matrix_coturn_container_stun_plain_host_bind_port_tcp and matrix_coturn_container_stun_plain_host_bind_port_udp'} + - {'old': 'matrix_coturn_container_stun_tls_host_bind_port', 'new': 'superseded by matrix_coturn_container_stun_tls_host_bind_port_tcp and matrix_coturn_container_stun_tls_host_bind_port_udp'} - name: Fail if matrix_coturn_authentication_method is invalid ansible.builtin.fail: diff --git a/roles/custom/matrix-coturn/templates/systemd/matrix-coturn.service.j2 b/roles/custom/matrix-coturn/templates/systemd/matrix-coturn.service.j2 index 072975b2d..6d3ffe310 100644 --- a/roles/custom/matrix-coturn/templates/systemd/matrix-coturn.service.j2 +++ b/roles/custom/matrix-coturn/templates/systemd/matrix-coturn.service.j2 @@ -24,13 +24,17 @@ ExecStartPre={{ devture_systemd_docker_base_host_command_docker }} create \ --read-only \ --tmpfs=/var/tmp:rw,noexec,nosuid,size=100m \ --network={{ matrix_coturn_container_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 \ + {% if matrix_coturn_container_stun_plain_host_bind_port_tcp != '' %} + -p {{ matrix_coturn_container_stun_plain_host_bind_port_tcp }}:3478 \ {% 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 \ + {% if matrix_coturn_container_stun_plain_host_bind_port_udp != '' %} + -p {{ matrix_coturn_container_stun_plain_host_bind_port_udp }}:3478/udp \ + {% endif %} + {% if matrix_coturn_container_stun_tls_host_bind_port_tcp != '' %} + -p {{ matrix_coturn_container_stun_tls_host_bind_port_tcp }}:5349 \ + {% endif %} + {% if matrix_coturn_container_stun_tls_host_bind_port_udp != '' %} + -p {{ matrix_coturn_container_stun_tls_host_bind_port_udp }}:5349/udp \ {% endif %} {% if matrix_coturn_container_turn_range_listen_interface is not in [none, '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 \