Refactor TURN/STUN handling:

- Split internal/external Coturn for BBB and Nextcloud
- Added dedicated relay port ranges per app
- Updated env and compose overrides for coturn
- Ensure coturn role is loaded conditionally
- Standardize credential/env passing for coturn
@See https://chatgpt.com/share/68d6f376-4878-800f-b4f7-62822caa49ea
This commit is contained in:
2025-09-26 22:11:55 +02:00
parent e3c0880e98
commit 73a38e0b2b
17 changed files with 97 additions and 27 deletions

View File

@@ -92,14 +92,17 @@ ports:
web-app-bigbluebutton: 3478 # Not sure if it's right placed here or if it should be moved to localhost section web-app-bigbluebutton: 3478 # Not sure if it's right placed here or if it should be moved to localhost section
# Occupied by BBB: 3479 # Occupied by BBB: 3479
web-app-nextcloud: 3480 web-app-nextcloud: 3480
web-app-coturn: 3481 web-svc-coturn: 3481
turn: turn:
web-app-bigbluebutton: 5349 # Not sure if it's right placed here or if it should be moved to localhost section web-app-bigbluebutton: 5349 # Not sure if it's right placed here or if it should be moved to localhost section
web-app-nextcloud: 5350 # Not used yet web-app-nextcloud: 5350 # Not used yet
web-app-coturn: 5351 web-svc-coturn: 5351
federation: federation:
web-app-matrix_synapse: 8448 web-app-matrix_synapse: 8448
relay_port_ranges: relay_port_ranges:
web-app-coturn_start: 49152 web-app-bigbluebutton_start: 40000
web-app-coturn_end: 65535 web-app-bigbluebutton_end: 59999
web-app-nextcloud_start: 60000
web-app-nextcloud_end: 79999
web-svc-coturn_start: 80000
web-svc-coturn_end: 99999

View File

@@ -32,4 +32,4 @@ docker:
greenlight: greenlight:
enabled: true enabled: true
coturn: coturn:
enabled: true internal: "{{ not 'web-svc-coturn' in group_names | lower }}"

View File

@@ -68,3 +68,13 @@
include_tasks: "02_administrator.yml" include_tasks: "02_administrator.yml"
- name: "Load Coturn Role for '{{ application_id }}'"
include_role:
name: web-svc-coturn
vars:
flush_handlers: true
when:
- run_once_web_svc_coturn is not defined
- not BBB_INTERNAL_COTURN_ENABLED

View File

@@ -5,3 +5,25 @@ services:
MS_ENABLE_IPV6: "false" MS_ENABLE_IPV6: "false"
MS_WEBRTC_LISTEN_IPS: >- MS_WEBRTC_LISTEN_IPS: >-
[{"ip":"0.0.0.0","announcedIp":"${EXTERNAL_IPv4}"}] [{"ip":"0.0.0.0","announcedIp":"${EXTERNAL_IPv4}"}]
coturn:
ports:
- "{{ BBB_TURN_PORT }}:{{ BBB_TURN_PORT }}/udp"
- "{{ BBB_TURN_PORT }}:{{ BBB_TURN_PORT }}/tcp"
- "{{ BBB_STUN_PORT }}:{{ BBB_STUN_PORT }}/udp"
- "{{ BBB_STUN_PORT }}:{{ BBB_STUN_PORT }}/tcp"
- "{{ BBB_RELAY_PORT_RANGE }}/udp"
command: >-
--use-auth-secret
--static-auth-secret=${TURN_SECRET}
--lt-cred-mech
--realm=${DOMAIN}
--fingerprint
--no-multicast-peers
--no-cli
--no-tcp-relay
--min-port={{ BBB_RELAY_PORT_START }}
--max-port={{ BBB_RELAY_PORT_END }}
--external-ip=${EXTERNAL_IPv4}
{% if BBB_IP6_ENABLED %}--external-ip=${EXTERNAL_IPv6}{% endif %}
--cert=${COTURN_TLS_CERT_PATH}
--pkey=${COTURN_TLS_KEY_PATH}

View File

@@ -1,7 +1,7 @@
# Coturn # Coturn
ENABLE_COTURN={{ BBB_COTURN_ENABLED }} ENABLE_COTURN={{ BBB_INTERNAL_COTURN_ENABLED }}
# Credentials ## Credentials
COTURN_TLS_CERT_PATH={{ BBB_COTURN_TLS_CERT_PATH }} COTURN_TLS_CERT_PATH={{ BBB_COTURN_TLS_CERT_PATH }}
COTURN_TLS_KEY_PATH={{ BBB_COTURN_TLS_KEY_PATH }} COTURN_TLS_KEY_PATH={{ BBB_COTURN_TLS_KEY_PATH }}

View File

@@ -34,9 +34,12 @@ BBB_COTURN_TLS_CERT_PATH: "{{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_fold
BBB_COTURN_TLS_KEY_PATH: "{{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_folder, 'privkey.pem'] | path_join }}" BBB_COTURN_TLS_KEY_PATH: "{{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_folder, 'privkey.pem'] | path_join }}"
## Turn ## Turn
BBB_TURN_DOMAIN: "{{ networks.internet.ip4 if BBB_COTURN_ENABLED else domains | get_domain('web-svc-coturn') }}" BBB_TURN_DOMAIN: "{{ networks.internet.ip4 if BBB_INTERNAL_COTURN_ENABLED else domains | get_domain('web-svc-coturn') }}"
BBB_TURN_PORT: "{{ ports.public.turn[application_id] if BBB_COTURN_ENABLED else ports.public.turn['web-svc-coturn'] }}" BBB_TURN_PORT: "{{ ports.public.turn[application_id] if BBB_INTERNAL_COTURN_ENABLED else ports.public.turn['web-svc-coturn'] }}"
BBB_STUN_PORT: "{{ ports.public.turn[application_id] if BBB_COTURN_ENABLED else ports.public.stun['web-svc-coturn'] }}" BBB_STUN_PORT: "{{ ports.public.turn[application_id] if BBB_INTERNAL_COTURN_ENABLED else ports.public.stun['web-svc-coturn'] }}"
BBB_RELAY_PORT_START: "{{ ports.public.relay_port_ranges[application_id ~ '_start'] }}"
BBB_RELAY_PORT_END: "{{ ports.public.relay_port_ranges[application_id ~ '_end'] }}"
BBB_RELAY_PORT_RANGE: "{{ BBB_RELAY_PORT_START }}-{{ BBB_RELAY_PORT_END }}"
## Switchs ## Switchs
@@ -45,7 +48,7 @@ BBB_IP6_ENABLED: "{{ applications | get_app_conf(applicatio
### Container ### Container
BBB_GREENLIGHT_ENABLED: "{{ applications | get_app_conf(application_id, 'docker.services.greenlight.enabled') }}" BBB_GREENLIGHT_ENABLED: "{{ applications | get_app_conf(application_id, 'docker.services.greenlight.enabled') }}"
BBB_COTURN_ENABLED: "{{ applications | get_app_conf(application_id, 'docker.services.coturn.enabled') }}" BBB_INTERNAL_COTURN_ENABLED: "{{ applications | get_app_conf(application_id, 'docker.services.coturn.internal') }}"
### SSO ### SSO
BBB_LDAP_ENABLED: "{{ applications | get_app_conf(application_id, 'features.ldap') }}" BBB_LDAP_ENABLED: "{{ applications | get_app_conf(application_id, 'features.ldap') }}"

View File

@@ -51,6 +51,7 @@ docker:
version: "latest" version: "latest"
backup: backup:
no_stop_required: false no_stop_required: false
internal: "{{ not 'web-svc-coturn' in group_names | lower }}"
whiteboard: whiteboard:
name: "nextcloud-whiteboard" name: "nextcloud-whiteboard"
image: "ghcr.io/nextcloud-releases/whiteboard" image: "ghcr.io/nextcloud-releases/whiteboard"

View File

@@ -0,0 +1,7 @@
- name: "Load Coturn Role for '{{ application_id }}'"
include_role:
name: web-svc-coturn
vars:
flush_handlers: true
when:
- run_once_web_svc_coturn is not defined

View File

@@ -34,7 +34,7 @@
{% include 'roles/docker-container/templates/networks.yml.j2' %} {% include 'roles/docker-container/templates/networks.yml.j2' %}
ipv4_address: 192.168.102.69 ipv4_address: 192.168.102.69
{% if NEXTCLOUD_TALK_ENABLED %} {% if NEXTCLOUD_TALK_INTERNAL_ENABLED %}
talk: talk:
{% set container_port = NEXTCLOUD_TALK_PORT_INTERNAL %} {% set container_port = NEXTCLOUD_TALK_PORT_INTERNAL %}
{% include 'roles/docker-container/templates/base.yml.j2' %} {% include 'roles/docker-container/templates/base.yml.j2' %}
@@ -43,8 +43,9 @@
container_name: {{ NEXTCLOUD_TALK_CONTAINER }} container_name: {{ NEXTCLOUD_TALK_CONTAINER }}
init: true init: true
ports: ports:
- {{ networks.internet.ip4 }}:{{ NEXTCLOUD_TALK_STUN_PORT }}:{{ NEXTCLOUD_TALK_INT_TURN_PORT }}/tcp #TURN TCP - {{ networks.internet.ip4 }}:{{ NEXTCLOUD_TALK_STUN_PORT }}:{{ NEXTCLOUD_TALK_INT_TURN_PORT }}/tcp
- {{ networks.internet.ip4 }}:{{ NEXTCLOUD_TALK_STUN_PORT }}:{{ NEXTCLOUD_TALK_INT_TURN_PORT }}/udp #TURN UDP - {{ networks.internet.ip4 }}:{{ NEXTCLOUD_TALK_STUN_PORT }}:{{ NEXTCLOUD_TALK_INT_TURN_PORT }}/udp
- {{ NEXTCLOUD_TALK_RELAY_PORT_RANGE }}:{{ NEXTCLOUD_TALK_RELAY_PORT_RANGE }}/udp
expose: expose:
- "{{ container_port }}" - "{{ container_port }}"
networks: networks:

View File

@@ -49,6 +49,10 @@ SIGNALING_SECRET={{ applications | get_app_conf(application_id, 'credentials.tal
INTERNAL_SECRET={{ applications | get_app_conf(application_id, 'credentials.talk_internal_secret') }} INTERNAL_SECRET={{ applications | get_app_conf(application_id, 'credentials.talk_internal_secret') }}
TZ={{ HOST_TIMEZONE }} TZ={{ HOST_TIMEZONE }}
TALK_PORT={{ NEXTCLOUD_TALK_INT_TURN_PORT }} TALK_PORT={{ NEXTCLOUD_TALK_INT_TURN_PORT }}
TURN_MIN_PORT={{ NEXTCLOUD_TALK_RELAY_PORT_START }}
TURN_MAX_PORT={{ NEXTCLOUD_TALK_RELAY_PORT_END }}
COTURN_MIN_PORT={{ NEXTCLOUD_TALK_RELAY_PORT_START }}
COTURN_MAX_PORT={{ NEXTCLOUD_TALK_RELAY_PORT_END }}
{% endif %} {% endif %}
{% if NEXTCLOUD_WHITEBOARD_ENABLED %} {% if NEXTCLOUD_WHITEBOARD_ENABLED %}

View File

@@ -58,16 +58,23 @@ NEXTCLOUD_PROXY_VERSION: "{{ applications | get_app_conf(application_
NEXTCLOUD_CRON_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.cron.name') }}" NEXTCLOUD_CRON_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.cron.name') }}"
### Talk ### Talk
#### Service
NEXTCLOUD_TALK_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.talk.name') }}" NEXTCLOUD_TALK_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.talk.name') }}"
NEXTCLOUD_TALK_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.talk.image') }}" NEXTCLOUD_TALK_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.talk.image') }}"
NEXTCLOUD_TALK_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.talk.version') }}" NEXTCLOUD_TALK_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.talk.version') }}"
NEXTCLOUD_TALK_ENABLED: "{{ applications | get_app_conf(application_id, 'plugins.spreed.enabled') }}" NEXTCLOUD_TALK_ENABLED: "{{ applications | get_app_conf(application_id, 'docker.services.talk.internal') }}"
NEXTCLOUD_TALK_STUN_PORT: "{{ ports.public.stun[application_id] }}" NEXTCLOUD_TALK_INTERNAL_ENABLED: "{{ applications | get_app_conf(application_id, 'plugins.spreed.enabled') }}"
NEXTCLOUD_TALK_DOMAIN: "{{ NEXTCLOUD_DOMAIN }}"
NEXTCLOUD_TALK_LOCATION: "/standalone-signaling/" NEXTCLOUD_TALK_LOCATION: "/standalone-signaling/"
NEXTCLOUD_TALK_URL: "{{ [ NEXTCLOUD_URL, NEXTCLOUD_TALK_LOCATION ] | url_join }}"
NEXTCLOUD_TALK_PORT_INTERNAL: "8081" NEXTCLOUD_TALK_PORT_INTERNAL: "8081"
NEXTCLOUD_TALK_INT_TURN_PORT: "3478" NEXTCLOUD_TALK_INT_TURN_PORT: "3478"
NEXTCLOUD_TALK_RELAY_PORT_START: "{{ ports.public.relay_port_ranges[application_id ~ '_start'] }}"
NEXTCLOUD_TALK_RELAY_PORT_END: "{{ ports.public.relay_port_ranges[application_id ~ '_end' ] }}"
NEXTCLOUD_TALK_RELAY_PORT_RANGE: "{{ NEXTCLOUD_TALK_RELAY_PORT_START }}-{{ NEXTCLOUD_TALK_RELAY_PORT_END }}"
# Connection
NEXTCLOUD_TALK_STUN_PORT: "{{ ports.public.stun[application_id] }}"
NEXTCLOUD_TALK_DOMAIN: "{{ NEXTCLOUD_DOMAIN }}"
NEXTCLOUD_TALK_URL: "{{ [ NEXTCLOUD_URL, NEXTCLOUD_TALK_LOCATION ] | url_join }}"
### Whiteboard ### Whiteboard
NEXTCLOUD_WHITEBOARD_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.whiteboard.name') }}" NEXTCLOUD_WHITEBOARD_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.whiteboard.name') }}"

View File

@@ -1,3 +1,4 @@
username: coturnconsumer
server: server:
domains: domains:
canonical: canonical:

View File

@@ -0,0 +1,3 @@
- name: "Load 'sys-stk-semi-stateless' for '{{ application_id }}'"
include_role:
name: sys-stk-semi-stateless

View File

@@ -1,4 +1,6 @@
--- ---
- name: "Load 'sys-stk-semi-stateless' for '{{ application_id }}'" - block:
include_role: - name: "Load core functions for '{{ application_id }}'"
name: sys-stk-semi-stateless include_tasks: 01_core.yml
- include_tasks: utils/run_once.yml
when: run_once_web_svc_coturn is not defined

View File

@@ -11,17 +11,19 @@
- "{{ COTURN_STUN_PORT }}:{{ COTURN_STUN_PORT }}/tcp" - "{{ COTURN_STUN_PORT }}:{{ COTURN_STUN_PORT }}/tcp"
- "{{ COTURN_STUN_PORT }}:{{ COTURN_STUN_PORT }}/udp" - "{{ COTURN_STUN_PORT }}:{{ COTURN_STUN_PORT }}/udp"
- "{{ COTURN_RELAY_PORT_RANGE }}/udp" - "{{ COTURN_RELAY_PORT_RANGE }}/udp"
- "{{ COTURN_TLS_CERT_PATH }}:{{ COTURN_TLS_CERT_PATH }}:ro"
- "{{ COTURN_TLS_KEY_PATH }}:{{ COTURN_TLS_KEY_PATH }}:ro"
command: > command: >
--use-auth-secret --use-auth-secret
--static-auth-secret={{ COTURN_STATIC_AUTH_SECRET }} --static-auth-secret=${ COTURN_STATIC_AUTH_SECRET }
--lt-cred-mech --lt-cred-mech
--user={{ COTURN_USER_NAME }}:{{ COTURN_USER_PASSWORD }} --user=${ COTURN_USER_NAME }:${ COTURN_USER_PASSWORD }
--log-file=stdout --log-file=stdout
--external-ip={{ networks.internet.ip4 }} --external-ip={{ networks.internet.ip4 }}
{% if networks.internet.ip6|default('') %} {% if networks.internet.ip6|default('') %}
--external-ip={{ networks.internet.ip6 }} --external-ip={{ networks.internet.ip6 }}
{% endif %} {% endif %}
--realm={{ COTURN_REALM }} --realm=${ COTURN_REALM }
--fingerprint --fingerprint
--total-quota=100 --total-quota=100
--stale-nonce --stale-nonce

View File

@@ -0,0 +1,4 @@
COTURN_STATIC_AUTH_SECRET={{ COTURN_STATIC_AUTH_SECRET }}
COTURN_USER_NAME={{ COTURN_USER_NAME }}
COTURN_USER_PASSWORD={{ COTURN_USER_PASSWORD }}
COTURN_REALM={{ COTURN_REALM }}

View File

@@ -19,7 +19,7 @@ COTURN_RELAY_PORT_END: "{{ ports.public.relay_port_ranges[application_id ~
COTURN_RELAY_PORT_RANGE: "{{ COTURN_RELAY_PORT_START }}-{{ COTURN_RELAY_PORT_END }}" COTURN_RELAY_PORT_RANGE: "{{ COTURN_RELAY_PORT_START }}-{{ COTURN_RELAY_PORT_END }}"
## Credentials ## Credentials
COTURN_USER_NAME: "{{ applications | get_app_conf(application_id, 'credentials.user_name') }}" COTURN_USER_NAME: "{{ applications | get_app_conf(application_id, 'username') }}"
COTURN_USER_PASSWORD: "{{ applications | get_app_conf(application_id, 'credentials.user_password') }}" COTURN_USER_PASSWORD: "{{ applications | get_app_conf(application_id, 'credentials.user_password') }}"
COTURN_STATIC_AUTH_SECRET: "{{ applications | get_app_conf(application_id, 'credentials.auth_secret') }}" COTURN_STATIC_AUTH_SECRET: "{{ applications | get_app_conf(application_id, 'credentials.auth_secret') }}"