Fix coturn TLS listeners and pin relay to public IP.

Two problems found once coturn was running:

TLS listeners never started. The live/ certificate files are relative
symlinks into ../../archive/, so mounting live/ alone left them dangling
and coturn silently fell back to no TLS, disabling turns: on 5349.
Prosody's identical mount works only because its entrypoint copies the
files; coturn reads them in place. Mount both trees at their real paths
and reference the cert through live/.

Relay used every interface. Without explicit addresses coturn discovered
all of them and offered relay candidates on the Docker bridges and
loopback -- unreachable for remote peers, and needless exposure of the
internal networks. Pin listening-ip and relay-ip to the public address.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mguschin
2026-08-18 22:20:37 +03:00
parent 3fc1ac3d12
commit c11f05481c
3 changed files with 19 additions and 6 deletions

View File

@@ -112,9 +112,11 @@ the current cert's SANs. To use `turn.guschin.info` instead, reissue the
certificate with that name added and update both `TURN_HOST` and the certificate with that name added and update both `TURN_HOST` and the
`external-ip`/`cert` settings accordingly. `external-ip`/`cert` settings accordingly.
`external-ip` in `turnserver.conf` is the server's public address. **Update it `turnserver.conf` pins the server's public address in three places:
if the server IP changes**, otherwise coturn advertises relay candidates that `listening-ip`, `relay-ip`, and `external-ip`. **Update all three if the
peers cannot reach. server IP changes**, otherwise coturn advertises relay candidates that peers
cannot reach. Pinning them also stops coturn from auto-discovering the Docker
bridge interfaces and relaying on unreachable private addresses.
### Verifying ### Verifying

View File

@@ -10,6 +10,13 @@
listening-port=3478 listening-port=3478
tls-listening-port=5349 tls-listening-port=5349
# Pin both the listening and relay addresses to the public interface.
# Without these, coturn auto-discovers every interface and hands out relay
# candidates on the Docker bridges (172.x, 192.168.x) and loopback, which
# remote peers cannot reach and which needlessly expose internal networks.
listening-ip=176.124.216.197
relay-ip=176.124.216.197
# Public address advertised in relay candidates. Without this, coturn hands # Public address advertised in relay candidates. Without this, coturn hands
# out its own view of the interface address, which breaks behind any NAT. # out its own view of the interface address, which breaks behind any NAT.
external-ip=176.124.216.197 external-ip=176.124.216.197
@@ -27,8 +34,8 @@ realm=guschin.info
# TLS for turns:. Uses the existing multi-SAN guschin.info certificate, which # TLS for turns:. Uses the existing multi-SAN guschin.info certificate, which
# is why the advertised TURN host is guschin.info and not turn.guschin.info # is why the advertised TURN host is guschin.info and not turn.guschin.info
# (the latter is not in the cert's SANs, so TLS validation would fail). # (the latter is not in the cert's SANs, so TLS validation would fail).
cert=/etc/coturn/certs/fullchain.pem cert=/etc/letsencrypt/live/guschin.info/fullchain.pem
pkey=/etc/coturn/certs/privkey.pem pkey=/etc/letsencrypt/live/guschin.info/privkey.pem
# Harden: this is a relay for our own users, not an open proxy. # Harden: this is a relay for our own users, not an open proxy.
# Deny relaying to private ranges so TURN can't be used to reach internal # Deny relaying to private ranges so TURN can't be used to reach internal

View File

@@ -69,7 +69,11 @@ services:
network_mode: host network_mode: host
volumes: volumes:
- ./coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro - ./coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro
- /etc/letsencrypt/live/guschin.info:/etc/coturn/certs:ro # The live/ files are relative symlinks into ../../archive/, so both
# trees must be mounted at their real paths for the links to resolve.
# coturn reads the certificate directly (unlike Prosody, whose
# entrypoint copies it), so a broken symlink silently disables TLS.
- /etc/letsencrypt/live/guschin.info:/etc/letsencrypt/live/guschin.info:ro
- /etc/letsencrypt/archive/guschin.info:/etc/letsencrypt/archive/guschin.info:ro - /etc/letsencrypt/archive/guschin.info:/etc/letsencrypt/archive/guschin.info:ro
command: ["-c", "/etc/coturn/turnserver.conf"] command: ["-c", "/etc/coturn/turnserver.conf"]
restart: unless-stopped restart: unless-stopped