Retire the shared coturn service — every WebRTC/SIP service now runs its own

Shared coturn (services/coturn.sh, ensure_coturn_user in lib/common.sh) is no
longer an installable or usable option anywhere in this repo. It's moved to
attic/coturn.sh (with tools/coturn-test-check.sh alongside it), which is
outside setup.sh's services/*.sh glob, so it never registers, never appears
in the menu, and `sudo ./setup.sh coturn` now fails with "unknown service".

Asterisk and Mattermost each already had an opt-out to run their own
dedicated coturn instead of the shared one; that opt-out is now the only
behavior — the shared-coturn preference, the opt-out prompt, and every
ensure_coturn_user() call site are gone. find_free_coturn_range()
(lib/common.sh) is what makes unconditional dedicated coturn safe: it scans
every coturn-owning service's own .env on the box for already-claimed relay
ranges and picks one that can't collide, so Asterisk + any number of
Mattermost instances can each run their own coturn on one box without the
relay-port collisions this repo's coturn history warns about.

Existing installs still pointed at a shared coturn container are left
running as-is on `update` (no silent migration attempt against a service
that no longer exists to heal against) — a full/fresh reinstall is the
migration path, which generates a new dedicated coturn with fresh
credentials and says so.

Also updates CLAUDE.md's coturn guidance for future service authors,
attic/README.md with the retirement rationale, and stale
services/coturn.sh path references in services/asterisk.sh,
tools/pstn-test-check.sh, README.md, and docs/vps-sizing-recommendations.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crt4ymNEHEbWqscB1qvZgC
This commit is contained in:
Claude
2026-08-13 02:12:32 +00:00
parent d835e0d734
commit ca239a3886
10 changed files with 242 additions and 367 deletions
+62 -53
View File
@@ -674,7 +674,7 @@ interpolates `${WEB_PORT}` directly. A quoted `<< 'MD'` heredoc doesn't,
and converting it means escaping *every* backtick used for inline-code
formatting (`` \`...\` ``) — miss one and bash tries to execute it as a
command substitution the next time the heredoc is read, the same class of
bug the coturn.sh backtick incident was (see `services/coturn.sh`'s
bug the coturn.sh backtick incident was (see `attic/coturn.sh`'s
`write_readme` call). For a README with only one or two backticks,
escaping them is fine. For one with many (`services/iopaint.sh`'s model
reference table), it's safer to leave the heredoc quoted and patch the
@@ -778,63 +778,72 @@ so that hostname resolves; `configure_caddy_for_service`'s bare-port upstream
case already does this for you — don't hand-roll `localhost:PORT` in a
Caddy site block.
## Shared coturn (TURN/STUN) relay
## coturn (TURN/STUN) relay — dedicated per service, not shared
Any service that needs a TURN server for WebRTC/SIP NAT traversal shares
**one** coturn instance (`services/coturn.sh`) instead of running its own.
This exists because it didn't always: `asterisk` and `mattermost` used to
each embed a dedicated coturn container (`network_mode: host`, each with its
own relay port range) — confirmed live, their default ranges overlapped by
~100 UDP ports, so running both on one box meant a coin-flip over which
service's active call lost its media relay. One shared instance with one
port range removes the collision instead of just moving it around.
Any service that needs a TURN server for WebRTC/SIP NAT traversal runs its
**own dedicated** coturn container. There is no shared coturn service to
install or point at — `services/coturn.sh` was tried and retired; it's
parked at `attic/coturn.sh` (outside `services/*.sh`'s glob, so it never
registers or appears in the menu — see `attic/README.md`). Sharing one
instance saved a container per consumer (~40MB) but was a single point of
failure every consumer depended on, and needing a dedicated per-consumer
long-term-credential user added real setup complexity for a small RAM win.
Don't reintroduce it — give every new WebRTC/SIP-capable service its own
coturn, following the pattern below.
**Use `ensure_coturn_user` (`lib/common.sh`), not your own coturn container:**
**The collision this pattern has to avoid:** `asterisk` and `mattermost`
each embed a dedicated coturn container (`network_mode: host`, each with
its own relay port range) — confirmed live, two independent coturns'
default ranges used to overlap by ~100 UDP ports, so running both on one
box meant a coin-flip over which service's active call lost its media
relay. Static default ranges alone don't solve this; something has to pick
non-overlapping ranges per box.
**Use `find_free_coturn_range` (`lib/common.sh`) to size the range, not a
hardcoded default:**
```bash
ensure_coturn_user "my-service"
if [ -n "$COTURN_HOST" ]; then
# Out-params (not `local` — read them after the call returns, same
# convention as configure_caddy_for_service's CADDY_SERVICE_*):
# COTURN_HOST COTURN_PORT COTURN_USERNAME COTURN_PASSWORD
else
# coturn unavailable (not installed and services/coturn.sh isn't loaded
# to chain-install it — e.g. this file run fully standalone) — degrade
# gracefully. Don't block the rest of your install on this.
fi
local MY_COTURN_MIN_PORT=49152 MY_COTURN_MAX_PORT=49252
find_free_coturn_range MY_COTURN_MIN_PORT MY_COTURN_MAX_PORT 100 49152
[[ "$MY_COTURN_MIN_PORT" != 49152 ]] && \
log_info "Dedicated coturn relay range shifted to ${MY_COTURN_MIN_PORT}-${MY_COTURN_MAX_PORT} to stay clear of another coturn already on this box."
```
`ensure_coturn_user` chain-installs `services/coturn.sh` the first time
*any* service needs one (guarded with `declare -F install_coturn`, same
pattern as the asterisk → security-dashboard chaining below), then
registers a dedicated long-term-credential username/password for your
consumer name. The credential is cached in
`~/docker/coturn/users/<consumer>.env`, so calling this again on a rerun
reuses the same credential instead of minting a new one and silently
orphaning whatever client already has the old one configured.
Unlike a single fixed host port (`find_free_port`'s job — coturn's relay
range isn't a statically bound listening socket you can detect with a live
`ss`/socket scan), `find_free_coturn_range` scans every `$DOCKER_DIR/*/.env`
for a `TURN_MAX_PORT=` line and starts the new range 50 ports past the
highest one found — so it works across every coturn-owning service on the
box (Asterisk, each Mattermost instance, yours), regardless of install
order. Persist the chosen range as `TURN_MIN_PORT=`/`TURN_MAX_PORT=` in your
own `.env` so later installs' scans see it, and on an `update` rerun read
those same keys back from the existing `.env` instead of re-scanning — a
live coturn container must never silently move to a different port range
(breaks in-flight/repeat sessions on whatever client already has the old
range's ports allowed through its own firewall/NAT). See
`services/asterisk.sh`'s and `services/mattermost.sh`'s `EMBEDDED_COTURN_MIN_PORT`/
`MM_COTURN_MIN_PORT` handling for the reference pattern, including the
`MODE != "update"` gate that scans only on a fresh install.
**Why long-term credentials (`--lt-cred-mech`), not the REST-API/HMAC mode
(`--use-auth-secret`) some WebRTC apps default to:** coturn does not support
running both auth mechanisms on one instance at once — enabling
`--use-auth-secret` silently overrides `--lt-cred-mech` server-wide, which
would break every static-credential consumer. `--lt-cred-mech` supports any
number of named users out of the box, which is the actual shape a
shared-multi-consumer coturn needs. If the service you're adding only
exposes an HMAC-secret TURN setting in its own UI (no plain
username/password option), check its docs for an alternative field first —
Mattermost's Calls plugin looked HMAC-only at a glance but also accepts a
fixed username/credential pair via its "ICE Servers Configurations" JSON
field (see `services/mattermost.sh` for the exact format). Don't fall back
to a second coturn instance just because the first field you found expects
a shared secret.
**Auth mode — long-term credentials (`--lt-cred-mech`) or HMAC
(`--use-auth-secret`), your choice per instance:** coturn doesn't support
running both on one instance at once, but since each service now owns its
instance outright, this is a free per-service choice — no shared-instance
constraint forcing one mode across every consumer. `services/asterisk.sh`
uses `--lt-cred-mech` (fixed username/password, simplest to bake into a SIP
device's config); `services/mattermost.sh` uses `--use-auth-secret` (HMAC),
matching the Calls plugin's own "TURN Static Auth Secret" field. Check the
consuming app's own TURN settings UI for which fields it actually exposes
before picking.
**Migrating an existing service from its own embedded coturn:** don't do it
silently. An `update` rerun must keep whatever coturn shape a service
already has — detect the existing embedded container (e.g. `grep -q '^
coturn:' docker-compose.yml` before regenerating it) and preserve it
exactly, the same non-destructive rule as every other `update` path in this
file. Only switch to the shared coturn on an explicit `fresh` reinstall, and
warn before doing it — the TURN username/password changes, and any
already-configured client (a SIP phone, a browser session) keeps the old
credentials until it's reconfigured. See `services/asterisk.sh`'s
`USE_EMBEDDED_COTURN` handling for the reference pattern.
**Legacy installs still on the old shared coturn:** an `update` rerun on an
install that predates this repo's dedicated-coturn-only model (no `coturn:`
block in its `docker-compose.yml`) must not try to silently migrate or
"heal" it — there's no shared coturn service left in this repo to heal it
against. Leave it running exactly as-is (an `update` never touches `.env`
anyway) and point at a full/fresh reinstall as the migration path, which
generates a new dedicated coturn container with fresh credentials. See the
`_HAD_EMBEDDED_COTURN` handling in `services/asterisk.sh` and
`services/mattermost.sh` for the reference pattern — detect via `grep -q
'^ coturn:' docker-compose.yml` before regenerating it, same as any other
non-destructive `update` path in this file.
+1 -2
View File
@@ -185,7 +185,7 @@ a ready-to-copy Caddy config snippet to `~/docker/caddy-snippets/`.
| Group | Services |
|-------|---------|
| `base` | `net-tools`, `ncdu`, `git`, `curl`, `wget`, `htop`, `tree`, `zip`/`unzip`, `ca-certificates`, `gnupg`, `jq`, `rsync`; `glow` (terminal markdown reader, Charm apt repo); Docker CE + Compose plugin; `openssh-server` with GitHub/Launchpad SSH key import, optional password-auth lockdown, and SSH Host aliases; optional NetBird overlay network |
| `homelab` | `caddy`, `crowdsec`, `authelia`, `coturn` (shared TURN/STUN relay — Asterisk, Mattermost Calls, and future WebRTC-capable services all register a dedicated credential against one instance instead of each running its own), `homeassistant`, `asterisk`, `pstn-trunk`, `sms-inbound`, `security-dashboard`, `sunshine`, `vpn-data-mount` (mount existing SMB shares from a NetBird-connected home box — SSH trust bootstrap, then read-only discovery of shares already configured there; never writes to the home box's Samba config; repeatable, pick from any number of a home box's shares in one pass; optional per-share [gocryptfs decrypt layer](#client-side-encryption-for-vpn-data-mount) so the VPS only ever handles ciphertext) |
| `homelab` | `caddy`, `crowdsec`, `authelia`, `homeassistant`, `asterisk` (own dedicated coturn for TURN/STUN — see `mattermost` below for the other coturn-owning service), `pstn-trunk`, `sms-inbound`, `security-dashboard`, `sunshine`, `vpn-data-mount` (mount existing SMB shares from a NetBird-connected home box — SSH trust bootstrap, then read-only discovery of shares already configured there; never writes to the home box's Samba config; repeatable, pick from any number of a home box's shares in one pass; optional per-share [gocryptfs decrypt layer](#client-side-encryption-for-vpn-data-mount) so the VPS only ever handles ciphertext) |
| `utilities` | `actualbudget`, `ai-gpu`, `ai-stack`, `archivebox`, `beszel` (lightweight server + Docker monitoring — CPU/RAM/disk/network, auto-discovers running containers via the Docker socket; complements Gatus rather than replacing it — Gatus is a black-box HTTP check, Beszel is white-box host/process monitoring), `beszel-agent` (agent-only Beszel install for a remote/homelab box reporting to a hub elsewhere — connects outbound over HTTPS, no VPN/port-forwarding/FQDN needed on that box), `changedetection`, `ddclient`, `filebrowser`, `fmd`, `gatus`, `homebox`, `iopaint`, `joplin`, `koha`, `magicmirror`, `mail-archiver`, `mattermost`, `mealie`, `meshcentral`, `n8n`, `nextcloud`, `ntfy`, `onlyoffice`, `paintplus`, `portainer`, `rustdesk`, `stirling-pdf`, `syncthing`, `traccar`, `unifi`, `uptimekuma`, `vaultwarden`, `watchyourlan`, `watchtower`, `wg-easy`, `wordpress` (multi-site, dedicated MariaDB per site — blogs, business sites, e-commerce via WooCommerce) |
| `media` | `arm`, `audiobookshelf`, `calibre-web`, `emby`, `immich`, `jellyfin`, `lyrion` |
| `cameras` | `frigate`, `frigate-audio`, `frigate-notify`, `sky-cam` |
@@ -207,7 +207,6 @@ homelab
caddy
crowdsec
authelia
coturn
homeassistant
asterisk
pstn-trunk
+30
View File
@@ -34,3 +34,33 @@ rather than `fresh` at the reinstall prompt, and having a snapshot.
Two copies of the same logic is the exact problem the merge existed to fix,
and this one will drift the moment `services/asterisk.sh` gets a fix that
isn't backported here — which it deliberately won't be.
## `coturn.sh` / `coturn-test-check.sh`
The shared-coturn service (`services/coturn.sh`, moved here unchanged from
`services/`) and its standalone health-check tool (`tools/coturn-test-check.sh`,
moved from `tools/`). This model — every WebRTC/SIP-capable service
(`asterisk`, `mattermost`) sharing one coturn instance via `ensure_coturn_user`
— is no longer offered anywhere in this repo. Every service now runs its own
dedicated coturn instead, with `lib/common.sh`'s `find_free_coturn_range()`
avoiding the relay-port collisions a shared instance used to prevent by
scanning every coturn-owning service's own `.env` on the box. See
`CLAUDE.md`'s "coturn (TURN/STUN) relay" section for the current pattern.
Sharing one instance only ever saved ~40MB RAM per additional consumer
beyond the first — real, but small — against being a single point of
failure every consumer depended on. Parked here, not deleted, since the
code is still correct and someone could resurrect it if a future need for
it shows up. Both files still run standalone if invoked directly:
```bash
sudo bash attic/coturn.sh
sudo bash attic/coturn-test-check.sh
```
Nothing in this repo calls `ensure_coturn_user()` anymore (the function
itself was removed from `lib/common.sh`), so resurrecting this only makes
sense if you're deliberately reintroducing the shared-coturn pattern
yourself — a new consumer service would need its own call to whatever
takes `ensure_coturn_user`'s place, since that helper no longer exists to
call.
@@ -1,4 +1,10 @@
#!/usr/bin/env bash
# attic/coturn-test-check.sh — RETIRED along with attic/coturn.sh (formerly
# services/coturn.sh). This tool only makes sense against a shared coturn
# instance, which this repo no longer offers — see attic/coturn.sh's header
# for what replaced it (each service gets its own dedicated coturn now).
# Kept for reference alongside it, not actively maintained.
#
# tools/coturn-test-check.sh — Health-check for the shared coturn (TURN/STUN)
# instance services/coturn.sh sets up, and every consumer registered against
# it (Asterisk, one or more Mattermost instances, anything else added via
+25 -5
View File
@@ -1,10 +1,30 @@
#!/bin/bash
# services/coturn.sh — Shared TURN/STUN relay (coturn) for WebRTC-capable services.
# Part of the modular post-install system (sourced by setup.sh).
# attic/coturn.sh — RETIRED. Formerly services/coturn.sh.
#
# Can also be run standalone on any machine:
# sudo bash coturn.sh
# (Docker must already be installed when run standalone)
# The shared-coturn model this file implements is no longer offered by this
# repo at all: services/asterisk.sh and services/mattermost.sh each now run
# their own dedicated coturn unconditionally, with lib/common.sh's
# find_free_coturn_range() making that safe (it scans every coturn-owning
# service's own .env on the box for already-claimed relay ranges and picks
# a block that can't collide with any of them, dedicated or shared). Sharing
# one instance only ever saved ~40MB RAM per additional consumer beyond the
# first — real, but small — and it was a single point of failure every
# consumer depended on. Parked here, not deleted, since the code is still
# correct and someone could resurrect it if a future need for it shows up;
# living in attic/ (outside services/*.sh's glob) means it never
# self-registers, never appears in the menu, and `sudo ./setup.sh coturn`
# now correctly fails with "unknown service" instead of silently offering
# a coturn shape nothing else in this repo will register a user against.
#
# ── Everything below this point is the file exactly as it ran before
# retirement, kept for reference/rollback, not actively maintained. ────────
#
# Can still be run standalone on any machine, same as before:
# sudo bash attic/coturn.sh
# (Docker must already be installed when run standalone) — but nothing in
# this repo will call ensure_coturn_user() to register with it anymore, so
# doing this only makes sense if you're deliberately reintroducing the
# shared-coturn pattern yourself.
#
# One coturn instance, shared by every service that needs TURN (Asterisk,
# Mattermost, and anything added later) instead of each service running its
+11 -5
View File
@@ -31,10 +31,16 @@ Rules of thumb:
`ensure_swapfile()` unconditionally, which offers a 2GB swapfile any time
RAM is ≤4096MB and none exists yet (`services/asterisk.sh` also calls it
directly for the standalone-run case, so it's covered either way).
- Sharing one `coturn` instance (`services/coturn.sh`) instead of letting
each WebRTC-capable service (Asterisk, Mattermost) embed its own saves a
container per consumer and — more importantly — avoids relay-port
collisions between them.
- **Historical note, no longer applicable:** this doc's Tier 2/3 plans below
were sized around one shared `coturn` instance instead of each WebRTC-
capable service (Asterisk, Mattermost) embedding its own — it saved a
container per consumer and avoided relay-port collisions between them.
That shared-coturn service has since been retired from this repo (see
`attic/coturn.sh`); every service now runs its own dedicated coturn, and
`lib/common.sh`'s `find_free_coturn_range()` avoids the same relay-port
collisions by scanning each coturn-owning service's `.env` instead. Budget
a coturn container per WebRTC-capable service/instance, not one shared
~40MB line, when re-planning a box from scratch.
## Tier 1 — ~1 vCPU / 1GB RAM / 25GB SSD
@@ -43,7 +49,7 @@ Example: DigitalOcean Basic, $6/mo.
This is tight enough that Docker's own daemon overhead is already a
meaningful fraction of the box. **Pick one purpose, not a stack:**
- **Option A — Asterisk only.** Asterisk + the shared coturn service fits
- **Option A — Asterisk only.** Asterisk + its own dedicated coturn fits
comfortably per this repo's own droplet-sizing notes (`services/asterisk.sh`
README section) — a swapfile is added automatically (RAM ≤4GB, see above),
and this plan is "fine for a couple of extensions and light personal use."
-116
View File
@@ -1038,119 +1038,3 @@ CADDY_BLOCK
echo ""
}
# ── Shared coturn (TURN/STUN) wiring ──────────────────────────────────────────
# Usage: ensure_coturn_user "<consumer-name>"
#
# Installs the shared coturn service (services/coturn.sh) if this is the
# first service on the box that needs TURN, then registers (or reuses) a
# dedicated long-term-credential user for the caller — one coturn instance,
# one relay port range, shared by every consumer instead of each service
# running its own and fighting over host ports (see services/coturn.sh's
# header for why that used to be a real, confirmed-live problem).
#
# Out-params (not `local` — read them after the call returns), same
# convention as configure_caddy_for_service's CADDY_SERVICE_* above:
# COTURN_HOST host/IP TURN clients should connect to
# COTURN_PORT coturn's listening port
# COTURN_USERNAME this consumer's long-term-credential username
# COTURN_PASSWORD this consumer's long-term-credential password
# COTURN_HOST is left empty if coturn couldn't be installed or reached —
# callers should treat that as "no TURN available" and degrade gracefully,
# same as checking CADDY_SERVICE_CONFIGURED after configure_caddy_for_service.
#
# Credentials are cached per-consumer in coturn's own users/<name>.env so a
# service re-running its own installer reuses the same one instead of
# minting a new credential and orphaning the old one (which would silently
# break already-configured clients still holding it).
ensure_coturn_user() {
local _consumer="$1"
COTURN_HOST="" COTURN_PORT="" COTURN_USERNAME="" COTURN_PASSWORD=""
if [ ! -d "$DOCKER_DIR/coturn" ]; then
if declare -F install_coturn >/dev/null 2>&1; then
log_info "No shared coturn (TURN/STUN) server yet — setting one up for $_consumer..."
# install_coturn cd's into $DOCKER_DIR/coturn and never cd's back —
# the caller (e.g. asterisk.sh, already cd'd into its own install
# directory) would otherwise return here with the wrong cwd and go
# on to write ITS docker-compose.yml/.env into coturn's directory
# instead of its own. Confirmed live: this clobbered coturn's
# compose file and left the consumer's own directory without one,
# so its later `docker compose up --build` failed with "Dockerfile:
# no such file or directory" (no Dockerfile in coturn's directory).
local _caller_pwd
_caller_pwd="$(pwd)"
install_coturn
local _coturn_rc=$?
cd "$_caller_pwd" || true
[ "$_coturn_rc" -ne 0 ] && { log_warning "coturn setup failed — $_consumer will run without TURN."; return 1; }
else
log_warning "services/coturn.sh not loaded — $_consumer will run without TURN."
log_warning "Run: sudo ./setup.sh coturn"
return 1
fi
fi
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] Would register coturn user '$_consumer'"
return 0
fi
local _env="$DOCKER_DIR/coturn/.env"
[ -f "$_env" ] || { log_warning "coturn installed but $_env missing — cannot register '$_consumer'."; return 1; }
local _realm _host _port
_realm="$(grep '^COTURN_REALM=' "$_env" | cut -d= -f2-)"
_host="$(grep '^COTURN_HOST=' "$_env" | cut -d= -f2-)"
_port="$(grep '^COTURN_PORT=' "$_env" | cut -d= -f2-)"; _port="${_port:-3478}"
local _userdir="$DOCKER_DIR/coturn/users"
local _userfile="$_userdir/${_consumer}.env"
mkdir -p "$_userdir"
if [ -f "$_userfile" ]; then
local _u _p
_u="$(grep '^COTURN_USER=' "$_userfile" | cut -d= -f2-)"
_p="$(grep '^COTURN_PASS=' "$_userfile" | cut -d= -f2-)"
COTURN_USERNAME="$_u" COTURN_PASSWORD="$_p"
# The cache file surviving doesn't mean the username still exists in
# coturn's own live database — confirmed live: a coturn
# container/volume recreated without preserving ./db wipes the
# database while this file (a separate directory) survives
# untouched, silently orphaning every consumer's credentials until
# something re-registers them. Without this check, re-running the
# consumer's installer (fresh or update) never re-registers anything
# since it only ever hits the else branch below on a MISSING cache
# file — a stale-but-present one looked identical to a healthy one.
# A real "user[realm]" line never contains a space; turnadmin -l's
# own startup log lines do (confirmed live, at least one coturn
# build writes them to stdout, not stderr), so filtering on that
# keeps this robust across builds without needing to match a
# specific log format.
local _db_users
_db_users="$(docker exec coturn turnadmin -l -b /var/lib/coturn/turndb 2>/dev/null | grep -v ' ' | sed -E 's/\[.*//' | awk 'NF')"
if ! grep -qx "$_u" <<< "$_db_users"; then
log_warning "coturn user '$_u' ($_consumer) has cached credentials but isn't in coturn's live database — re-registering with the same password."
if docker exec coturn turnadmin -a -u "$_u" -p "$_p" -r "$_realm" -b /var/lib/coturn/turndb >/dev/null 2>&1; then
log_success "Re-registered coturn user '$_u' for $_consumer"
else
log_warning "Could not re-register coturn user '$_u' for $_consumer — is the coturn container running?"
fi
fi
else
COTURN_USERNAME="$_consumer"
COTURN_PASSWORD="$(generate_password 24)"
if docker exec coturn turnadmin -a -u "$COTURN_USERNAME" -p "$COTURN_PASSWORD" \
-r "$_realm" -b /var/lib/coturn/turndb >/dev/null 2>&1; then
{ echo "COTURN_USER=$COTURN_USERNAME"; echo "COTURN_PASS=$COTURN_PASSWORD"; } > "$_userfile"
chmod 600 "$_userfile"
log_success "Registered coturn user '$COTURN_USERNAME' for $_consumer"
else
log_warning "Could not register a coturn user for $_consumer — is the coturn container running?"
COTURN_USERNAME="" COTURN_PASSWORD=""
return 1
fi
fi
COTURN_HOST="$_host"
COTURN_PORT="$_port"
}
+51 -111
View File
@@ -280,7 +280,7 @@ CBLOCK
fi
# ─────────────────────────────────────────────────────────────────────────────
register_service asterisk homelab "Easy Asterisk PBX (intercom/VoIP; auto-tunes for a DigitalOcean droplet); TURN via the shared coturn service" 5061
register_service asterisk homelab "Easy Asterisk PBX (intercom/VoIP; auto-tunes for a DigitalOcean droplet); own dedicated coturn for TURN" 5061
# ── Install layout: directory + container names ────────────────────────────
# Sets ASTERISK_DIR / ASTERISK_CONTAINER / ASTERISK_COTURN / ASTERISK_PROJECT.
@@ -1024,14 +1024,14 @@ _asterisk_offer_dashboard_and_trunk() {
# and container names are therefore substituted afterwards, same placeholder
# trick the Caddy volume line already uses below.
#
# USE_EMBEDDED_COTURN controls whether this install runs its own dedicated
# coturn container (legacy shape) or relies on the shared coturn service
# (services/coturn.sh) instead. This is NOT a free choice at every call site
# — an install that already has its own embedded coturn must keep getting
# one on every "update" regeneration of this file, or the next `docker
# compose up` silently drops the container its own .env TURN_PASSWORD still
# points at, breaking every already-configured phone with no warning. See
# the two call sites below for how each decides.
# Every install now runs its own dedicated coturn — there's no shared coturn
# service left in this repo to opt into (see attic/coturn.sh for why it was
# retired). USE_EMBEDDED_COTURN still exists as a parameter purely for
# backward compatibility with pre-retirement installs that were pointed at
# the old shared coturn service instead: an "update" on one of those must
# keep NOT writing a coturn: block (there's no .env TURN_PASSWORD for it to
# use), so it stays exactly as it was rather than silently gaining or losing
# a container. See the two call sites below for how each decides.
_asterisk_write_compose() {
local PROJECT="$1" CONTAINER="$2" COTURN_CONTAINER="$3" USE_EMBEDDED_COTURN="${4:-true}"
local COTURN_MIN_PORT_VAL="${5:-49152}" COTURN_MAX_PORT_VAL="${6:-49252}"
@@ -1106,9 +1106,9 @@ EOF
#
# This is entirely about Asterisk's OWN SIP transport-tls cert (port
# 5061) -- it has nothing to do with coturn's separate, unrelated TURNS
# (TLS-wrapped TURN) capability, which the shared coturn service indeed
# doesn't support (see services/coturn.sh's README). A previous version
# of this check gated the mount on USE_EMBEDDED_COTURN == true, conflating
# (TLS-wrapped TURN) capability, which the (since-retired) shared coturn
# service indeed didn't support (see attic/coturn.sh's README). A
# previous version of this check gated the mount on USE_EMBEDDED_COTURN == true, conflating
# the two. Confirmed live: on a shared-coturn install with a real Caddy
# cert already sitting on disk for DOMAIN_NAME, Asterisk silently kept
# generating (and re-generating) a self-signed cert forever, because
@@ -1380,8 +1380,8 @@ _asterisk_configure_do_cloud_firewall() {
# silently dropped everything else before it ever reached the box. UFW being
# wide open proves nothing about a layer in front of it that UFW can't see.
_asterisk_remind_non_do_firewall() {
local WEB_ADMIN_PORT_VAL="$1" WEB_ADMIN_PUBLIC_ACCESS_NEEDED="$2" USE_EMBEDDED_COTURN_VAL="${3:-true}"
local COTURN_MIN_PORT_VAL="${4:-49152}" COTURN_MAX_PORT_VAL="${5:-49252}"
local WEB_ADMIN_PORT_VAL="$1" WEB_ADMIN_PUBLIC_ACCESS_NEEDED="$2"
local COTURN_MIN_PORT_VAL="${3:-49152}" COTURN_MAX_PORT_VAL="${4:-49252}"
echo ""
log_warning "This box is reachable via FQDN but wasn't set up as a DigitalOcean droplet,"
log_warning "so no automatic network-edge firewall was configured (that step only exists"
@@ -1397,15 +1397,8 @@ _asterisk_remind_non_do_firewall() {
[[ "$WEB_ADMIN_PUBLIC_ACCESS_NEEDED" == true ]] && echo " TCP ${WEB_ADMIN_PORT_VAL} (web admin)"
echo " TCP 8088, 8089 (Asterisk HTTP/HTTPS)"
echo " UDP 10000-20000 (RTP media)"
if [[ "$USE_EMBEDDED_COTURN_VAL" == true ]]; then
echo " UDP/TCP 3478 (TURN/STUN)"
echo " UDP ${COTURN_MIN_PORT_VAL}-${COTURN_MAX_PORT_VAL} (TURN relay)"
else
echo " UDP/TCP 3478 too, if the shared coturn instance (services/coturn.sh) lives"
echo " on this same box — its exact TURN relay range is in its own README"
echo " (~/docker/coturn/README.md), not repeated here since it's independently"
echo " configurable and this install doesn't own it."
fi
}
# ── Shared: README ─────────────────────────────────────────────────────────
@@ -1461,9 +1454,7 @@ connecting a phone. The Security Dashboard's Extensions tab
| TURN username | ${TURN_USERNAME_VAL} |
| TURN password | see \`.env\` → \`TURN_PASSWORD\` |
$( [[ "$USE_EMBEDDED_COTURN" == true ]] \
&& echo "This install runs its own dedicated coturn container (the \`coturn:\` service in docker-compose.yml)." \
|| echo "TURN is served by the box's shared coturn service, not a container in this compose file — see \`~/docker/coturn/README.md\`. Every service on the box that needs TURN (Mattermost Calls, etc.) shares this same relay, each with its own dedicated username." )
This install runs its own dedicated coturn container (the \`coturn:\` service in docker-compose.yml).
Recommended softphones: Linphone, Zoiper, Bria, Grandstream Wave, and
[Sipnetic](https://www.sipnetic.com/) on Android (free, TLS/SRTP +
@@ -1682,11 +1673,10 @@ install_asterisk() {
echo "[DRY-RUN] - offer local OR remote Authelia to protect the web admin"
echo "[DRY-RUN] - offer to create a DigitalOcean Cloud Firewall via doctl"
echo "[DRY-RUN] Would scan for a free web admin port starting at 8081 (avoids e.g. CrowdSec's 8080)"
echo "[DRY-RUN] Would register a TURN user with the shared coturn service (chain-installing it"
echo "[DRY-RUN] if this is the first service on the box that needs one), falling back to"
echo "[DRY-RUN] Asterisk's own dedicated coturn if the shared service is unavailable"
echo "[DRY-RUN] Would run its own dedicated coturn container for TURN, with a relay port"
echo "[DRY-RUN] range picked to avoid colliding with any other coturn already on the box"
echo "[DRY-RUN] Would open UFW ports: 5060, 5061, <web admin port>, 8088, 8089, 10000-20000,"
echo "[DRY-RUN] plus 3478 + 49152-49252 only if falling back to a dedicated coturn"
echo "[DRY-RUN] plus 3478 + the dedicated coturn's relay port range"
echo "[DRY-RUN] Would offer 'update in place' instead of a fresh install if $EA_DIR already exists"
echo "[DRY-RUN] Would patch vendor device-creation code + extensions.conf generator to route"
echo "[DRY-RUN] internal SIP MESSAGE through a dedicated [sip-messaging] dialplan context,"
@@ -1754,19 +1744,16 @@ install_asterisk() {
log_warning "docker compose up failed — check: docker compose -f $EA_DIR/docker-compose.yml logs"
fi
# Self-heal a stale/orphaned shared-coturn registration on
# every update, not just a full reinstall — the check inside
# ensure_coturn_user() is what actually re-registers a
# missing user, this just needs to reach it. Gated on NOT
# having an embedded coturn: an install with its own
# dedicated coturn deliberately never touches the shared one
# on update (see the warning above and CLAUDE.md's coturn
# migration guidance) — calling this unconditionally would
# silently chain-install services/coturn.sh for a box that
# was never using it, the exact "don't migrate silently on
# update" mistake that guidance warns against.
# A pre-existing install with no embedded coturn block predates
# this repo's dedicated-coturn-only model — it's still pointed
# at a shared coturn container this repo no longer installs or
# manages (attic/coturn.sh). Leave it running as-is; update
# never touches .env or firewall rules anyway. Point at a
# fresh reinstall as the migration path instead of silently
# trying to heal a registration against a service that no
# longer exists here.
if [[ "$_HAD_EMBEDDED_COTURN" != true ]]; then
ensure_coturn_user "asterisk"
log_info "This install still points at a shared coturn service, which this repo no longer installs or manages. It will keep working as long as that coturn container keeps running. Run a full reinstall (not update) to migrate to a dedicated coturn."
fi
_asterisk_run_presence_step "$EA_DIR" "$CONTAINER"
@@ -1794,8 +1781,9 @@ install_asterisk() {
echo ""
log_warning "Full reinstall stops the existing containers and re-runs every"
log_warning "prompt below from scratch (domain, networking, firewall, Caddy/"
log_warning "Authelia). The TURN credential registered with the shared coturn"
log_warning "service is reused as-is — no need to touch coturn for this."
log_warning "Authelia), including generating a fresh dedicated coturn container"
log_warning "with new TURN credentials — any already-configured phone's TURN"
log_warning "settings will need to be updated afterward (re-scan its QR code)."
local _WIPE_PBX_DATA=""
prompt_yn " Also delete stored PBX data (extensions, voicemail, recordings, spool)? (y/n):" "n" _WIPE_PBX_DATA
@@ -1899,48 +1887,15 @@ install_asterisk() {
fi
# ── Secrets / TURN ───────────────────────────────────────────────────────
# Prefer the shared coturn service (services/coturn.sh) — one TURN server
# for every service on the box instead of Asterisk running its own and
# fighting other consumers (Mattermost, etc.) over relay ports. Falls
# back to Asterisk's own dedicated coturn if the shared service isn't
# available (e.g. this file run standalone with no sibling services/*.sh
# sourced) or registration fails for any reason — Asterisk should never
# end up with no TURN at all just because the shared path had a problem.
# Asterisk always runs its own dedicated coturn — there is no shared
# coturn service in this repo anymore (see attic/coturn.sh for why it
# was retired). find_free_coturn_range (below) is what makes running a
# dedicated coturn per service safe: it checks every coturn-owning
# service's .env on the box and picks a relay range that can't collide
# with any of them.
local USE_EMBEDDED_COTURN=true
local TURN_USERNAME TURN_PASSWORD TURN_PORT_VAL TURN_SERVER_VAL
local FORCE_EMBEDDED_COTURN=""
# Only reachable here via an explicit "fresh" choice above — "update"
# is handled separately and always preserves whatever coturn shape
# already exists, never silently switches it.
if [[ -f "$EA_DIR/docker-compose.yml" ]] && grep -q '^ coturn:' "$EA_DIR/docker-compose.yml" 2>/dev/null; then
echo ""
log_warning "This box's existing Asterisk install has its own dedicated coturn."
log_warning "Continuing may switch it to the new shared coturn service — any"
log_warning "phone/softphone configured with the OLD TURN username/password will"
log_warning "need updating once this completes."
fi
# Opt-out of the shared coturn preference below, for the rare case where
# you specifically want Asterisk isolated on its own TURN relay again
# (e.g. reproducing an older install's exact shape to rule out anything
# coturn-sharing-specific during troubleshooting). Only offered when a
# shared instance actually exists — no meaningful choice otherwise.
if [[ -d "$DOCKER_DIR/coturn" ]]; then
local _USE_SHARED_COTURN=""
prompt_yn "Use the shared coturn service for TURN? (n = run Asterisk's own dedicated coturn instead) (y/n):" "y" _USE_SHARED_COTURN
[[ "$_USE_SHARED_COTURN" =~ ^[Nn]$ ]] && FORCE_EMBEDDED_COTURN=true
fi
[[ "$FORCE_EMBEDDED_COTURN" != true ]] && ensure_coturn_user "asterisk"
if [[ "$FORCE_EMBEDDED_COTURN" != true && -n "${COTURN_HOST:-}" ]]; then
USE_EMBEDDED_COTURN=false
TURN_USERNAME="$COTURN_USERNAME"
TURN_PASSWORD="$COTURN_PASSWORD"
TURN_PORT_VAL="$COTURN_PORT"
TURN_SERVER_VAL="${COTURN_HOST}:${COTURN_PORT}"
log_success "Using the shared coturn service — TURN username '$COTURN_USERNAME'."
else
TURN_USERNAME="easyasterisk"
TURN_PASSWORD="$(generate_password 24)"
TURN_PORT_VAL="3478"
@@ -1953,29 +1908,20 @@ install_asterisk() {
elif [[ -n "$DOMAIN_NAME" ]]; then
TURN_SERVER_VAL="${DOMAIN_NAME}:3478"
fi
if [[ "$FORCE_EMBEDDED_COTURN" == true ]]; then
log_info "Running Asterisk's own dedicated coturn, as requested."
else
log_info "Shared coturn unavailable — Asterisk will run its own dedicated coturn."
fi
fi
# A dedicated embedded coturn running ALONGSIDE any other coturn on the
# same box (the shared instance, Asterisk's own on a prior install,
# any Mattermost instance's own) is exactly the pre-merge collision bug
# this repo's coturn history warns about if two of them claim overlapping
# relay ports — confirmed live, two independent coturns' default ranges
# used to overlap by ~100 UDP ports. find_free_coturn_range (lib/common.sh)
# checks every coturn-owning service's .env on the box, not just the
# shared instance's, and picks a range starting safely past whatever's
# already claimed. No other coturn on the box at all leaves it at the
# historical 49152-49252 default — nothing to collide with yet.
# A dedicated coturn here running alongside Asterisk's own on a prior
# install, or any Mattermost instance's own, is exactly the pre-merge
# collision bug this repo's coturn history warns about if two of them
# claim overlapping relay ports — confirmed live, two independent
# coturns' default ranges used to overlap by ~100 UDP ports.
# find_free_coturn_range (lib/common.sh) checks every coturn-owning
# service's .env on the box and picks a range starting safely past
# whatever's already claimed. No other coturn on the box at all leaves
# it at the historical 49152-49252 default — nothing to collide with yet.
local EMBEDDED_COTURN_MIN_PORT=49152 EMBEDDED_COTURN_MAX_PORT=49252
if [[ "$USE_EMBEDDED_COTURN" == true ]]; then
find_free_coturn_range EMBEDDED_COTURN_MIN_PORT EMBEDDED_COTURN_MAX_PORT 100 49152
[[ "$EMBEDDED_COTURN_MIN_PORT" != 49152 ]] && \
log_info "Dedicated coturn relay range shifted to ${EMBEDDED_COTURN_MIN_PORT}-${EMBEDDED_COTURN_MAX_PORT} to stay clear of another coturn already on this box."
fi
_asterisk_write_compose "$ASTERISK_PROJECT" "$CONTAINER" "$ASTERISK_COTURN" "$USE_EMBEDDED_COTURN" \
"$EMBEDDED_COTURN_MIN_PORT" "$EMBEDDED_COTURN_MAX_PORT"
@@ -2017,18 +1963,16 @@ install_asterisk() {
DOMAIN_NAME=${DOMAIN_NAME}
# ── TURN/STUN ─────────────────────────────────────────────────
# $( [[ "$USE_EMBEDDED_COTURN" == true ]] && echo "This install runs its own dedicated coturn (see the coturn: service in docker-compose.yml)." || echo "Using the shared coturn service — see ~/docker/coturn/README.md." )
# This install runs its own dedicated coturn (see the coturn: service in docker-compose.yml).
TURN_USERNAME=${TURN_USERNAME}
TURN_PASSWORD=${TURN_PASSWORD}
TURN_PORT=${TURN_PORT_VAL}
# Empty when there's no publicly resolvable address (LAN-only, no FQDN).
TURN_SERVER=${TURN_SERVER_VAL}
# This install's OWN coturn relay range -- only set when USE_EMBEDDED_COTURN
# is true above. Left blank when using the shared coturn service, so other
# services' find_free_coturn_range (lib/common.sh) scan correctly skips this
# file instead of treating a range this install doesn't actually own as claimed.
TURN_MIN_PORT=$( [[ "$USE_EMBEDDED_COTURN" == true ]] && echo "$EMBEDDED_COTURN_MIN_PORT" )
TURN_MAX_PORT=$( [[ "$USE_EMBEDDED_COTURN" == true ]] && echo "$EMBEDDED_COTURN_MAX_PORT" )
# This install's own coturn relay range — other services' find_free_coturn_range
# (lib/common.sh) scans this file to avoid claiming an overlapping range.
TURN_MIN_PORT=${EMBEDDED_COTURN_MIN_PORT}
TURN_MAX_PORT=${EMBEDDED_COTURN_MAX_PORT}
# ── RTP port range ────────────────────────────────────────────
RTP_START=10000
@@ -2092,13 +2036,9 @@ ENV
ufw allow 8088/tcp
ufw allow 8089/tcp
ufw allow 10000:20000/udp
if [[ "$USE_EMBEDDED_COTURN" == true ]]; then
ufw allow 3478/udp
ufw allow 3478/tcp
ufw allow "${EMBEDDED_COTURN_MIN_PORT}:${EMBEDDED_COTURN_MAX_PORT}/udp"
fi
# Shared coturn opens its own ports once, at its own install time
# (services/coturn.sh) — nothing to open here when using it.
ensure_ufw_enabled
log_success "UFW rules added."
fi
@@ -2108,7 +2048,7 @@ ENV
_asterisk_configure_do_cloud_firewall "$DROPLET_ID" "$WEB_ADMIN_PORT_VAL" "$WEB_ADMIN_PUBLIC_ACCESS_NEEDED" \
"$EMBEDDED_COTURN_MIN_PORT" "$EMBEDDED_COTURN_MAX_PORT"
elif [[ -n "$DOMAIN_NAME" ]]; then
_asterisk_remind_non_do_firewall "$WEB_ADMIN_PORT_VAL" "$WEB_ADMIN_PUBLIC_ACCESS_NEEDED" "$USE_EMBEDDED_COTURN" \
_asterisk_remind_non_do_firewall "$WEB_ADMIN_PORT_VAL" "$WEB_ADMIN_PUBLIC_ACCESS_NEEDED" \
"$EMBEDDED_COTURN_MIN_PORT" "$EMBEDDED_COTURN_MAX_PORT"
fi
+27 -48
View File
@@ -238,7 +238,7 @@ CBLOCK
fi
# ─────────────────────────────────────────────────────────────────────────────
register_service mattermost utilities "Team messaging with voice/video calls (Mattermost; TURN via the shared coturn service); supports multiple isolated instances" 8065
register_service mattermost utilities "Team messaging with voice/video calls (Mattermost; own dedicated coturn for TURN); supports multiple isolated instances" 8065
install_mattermost() {
require_docker || return 1
@@ -251,7 +251,6 @@ install_mattermost() {
local INSTANCE_SUFFIX="" PROJECT="mattermost"
local MM_CONTAINER="mattermost" DB_CONTAINER="mattermost-db"
local WEB_PORT="8065" CALLS_UDP_PORT="8443"
local COTURN_CONSUMER="mattermost"
if [ -d "$DIR" ]; then
echo ""
@@ -280,7 +279,6 @@ install_mattermost() {
PROJECT="mattermost-$_suffix"
MM_CONTAINER="mattermost-$_suffix"
DB_CONTAINER="mattermost-$_suffix-db"
COTURN_CONSUMER="mattermost-$_suffix"
log_info "New instance: $DIR"
fi
fi
@@ -305,8 +303,8 @@ install_mattermost() {
echo "[DRY-RUN] Would create $DIR with docker-compose.yml"
echo "[DRY-RUN] Would write .env with DB and Mattermost secrets"
echo "[DRY-RUN] Would create data/ logs/ config/ plugins/ db/ subdirectories"
echo "[DRY-RUN] Would register a TURN user with the shared coturn service for '$COTURN_CONSUMER'"
echo "[DRY-RUN] (falling back to a dedicated coturn if the shared service is unavailable)"
echo "[DRY-RUN] Would run this instance's own dedicated coturn container for TURN, with a relay"
echo "[DRY-RUN] port range picked to avoid colliding with any other coturn already on the box"
echo "[DRY-RUN] Would open UFW ports ${WEB_PORT}/tcp, ${CALLS_UDP_PORT}/udp"
return 0
fi
@@ -323,16 +321,11 @@ install_mattermost() {
return 0
;;
fresh)
if [ "$_HAD_EMBEDDED_COTURN" = true ]; then
echo ""
log_warning "This install has its own dedicated coturn. Continuing may switch it to"
log_warning "the shared coturn service — the Calls plugin's TURN config in System"
log_warning "Console will need updating to the new credentials afterward (see below)."
fi
echo ""
log_warning "Full reinstall stops the existing containers and re-runs every prompt"
log_warning "below from scratch. The TURN credential registered with the shared"
log_warning "coturn service is reused as-is — no need to touch coturn for this."
log_warning "below from scratch, including generating a fresh dedicated coturn"
log_warning "container with new TURN credentials — the Calls plugin's TURN config in"
log_warning "System Console will need updating afterward (see below)."
local _WIPE_MM_DATA=""
prompt_yn " Also delete stored data (Postgres database, uploaded files, config, plugins)? (y/n):" "n" _WIPE_MM_DATA
@@ -408,47 +401,31 @@ networks:
"
fi
# ── TURN: shared coturn preferred, dedicated coturn as fallback ─────────
# See services/coturn.sh's header for why one shared TURN server beats
# every service (Asterisk, each Mattermost instance, ...) running its
# own and fighting over host relay ports.
# ── TURN: always this instance's own dedicated coturn ───────────────────
# There is no shared coturn service in this repo anymore (see
# attic/coturn.sh for why it was retired) — every instance runs its own.
# find_free_coturn_range (below) is what makes that safe: it checks
# every coturn-owning service's .env on the box and picks a relay range
# that can't collide with any of them.
local USE_EMBEDDED_COTURN=true
local TURN_HOST_VAL="" TURN_PORT_VAL="" TURN_USERNAME_VAL="" TURN_PASSWORD_VAL=""
local FORCE_EMBEDDED_COTURN=""
# Opt-out of the shared coturn preference, same as services/asterisk.sh —
# only offered on a genuinely fresh install (never re-asked on update,
# matching every other coturn-shape decision in this file) and only when
# a shared instance actually exists to opt out of.
if [ "$MODE" = "fresh" ] && [ -d "$DOCKER_DIR/coturn" ]; then
local _USE_SHARED_COTURN=""
prompt_yn "Use the shared coturn service for TURN? (n = run this instance's own dedicated coturn instead) (y/n):" "y" _USE_SHARED_COTURN
[[ "$_USE_SHARED_COTURN" =~ ^[Nn]$ ]] && FORCE_EMBEDDED_COTURN=true
fi
if [ "$MODE" = "update" ] && [ "$_HAD_EMBEDDED_COTURN" = true ]; then
USE_EMBEDDED_COTURN=true # preserve exactly — never switch on update
elif [ "$FORCE_EMBEDDED_COTURN" = true ]; then
USE_EMBEDDED_COTURN=true
log_info "Running this instance's own dedicated coturn, as requested."
else
ensure_coturn_user "$COTURN_CONSUMER"
if [ -n "${COTURN_HOST:-}" ]; then
# A pre-existing instance with no embedded coturn block predates this
# repo's dedicated-coturn-only model — it's still pointed at a shared
# coturn container this repo no longer installs or manages. Leave it
# running as-is (update never touches .env anyway) rather than trying
# to heal a registration against a service that no longer exists here.
if [ "$MODE" = "update" ] && [ "$_HAD_EMBEDDED_COTURN" != true ]; then
USE_EMBEDDED_COTURN=false
TURN_HOST_VAL="$COTURN_HOST"; TURN_PORT_VAL="$COTURN_PORT"
TURN_USERNAME_VAL="$COTURN_USERNAME"; TURN_PASSWORD_VAL="$COTURN_PASSWORD"
log_success "Using the shared coturn service — TURN username '$COTURN_USERNAME'."
else
log_info "Shared coturn unavailable — this instance will run its own dedicated coturn."
fi
log_info "This instance still points at a shared coturn service, which this repo no longer installs or manages. It will keep working as long as that coturn container keeps running. Run a full reinstall (not update) to migrate to a dedicated coturn."
fi
[ -n "$MM_SECRET" ] || MM_SECRET=$(generate_password 48)
# A dedicated coturn here running alongside the shared instance, Asterisk's
# own, or a sibling Mattermost instance's own is the same pre-merge relay-
# port collision this repo's coturn history warns about (confirmed live:
# two independent coturns' default ranges used to overlap by ~100 UDP
# ports). find_free_coturn_range (lib/common.sh) checks every coturn-
# A dedicated coturn here running alongside Asterisk's own, a sibling
# Mattermost instance's own, or a legacy shared instance still running is
# the same pre-merge relay-port collision this repo's coturn history
# warns about (confirmed live: two independent coturns' default ranges
# used to overlap by ~100 UDP ports). find_free_coturn_range (lib/common.sh) checks every coturn-
# owning service's .env on the box and picks a range starting safely past
# whatever's already claimed; the historical 49153-49352 default only
# survives when nothing else on the box claims a range at all.
@@ -593,7 +570,9 @@ EOF
ufw allow 3479/udp; ufw allow 3479/tcp
ufw allow "${MM_COTURN_MIN_PORT}:${MM_COTURN_MAX_PORT}/udp" comment "Mattermost coturn relay"
fi
# Shared coturn opens its own ports once, at its own install time.
# A legacy instance still on a shared coturn (USE_EMBEDDED_COTURN=false
# above) has nothing to open here — that coturn's ports were opened
# once, at its own install time, whenever that was.
fi
echo ""
+9 -7
View File
@@ -172,8 +172,9 @@ fi
# Anveo-style ICE-enabled endpoints. Asterisk caches its OWN TURN_* values
# in its .env at the point it was configured — testing with those (not
# re-deriving fresh credentials) proves what Asterisk is actually set up
# to use, not just that the shared coturn instance works in general (that
# broader, multi-consumer check is tools/coturn-test-check.sh's job). ─────────
# to use. Every current install runs its own dedicated coturn; a box that
# still points at a legacy shared coturn instance predates that (see
# attic/coturn.sh) and can be spot-checked with attic/coturn-test-check.sh. ─────────
section "coturn (TURN relay for Asterisk)"
ASTERISK_ENV="$EA_DIR/.env"
@@ -197,11 +198,11 @@ else
if grep -q '^ coturn:' "$EA_DIR/docker-compose.yml" 2>/dev/null; then
COTURN_CONTAINER="easy-asterisk-coturn"
[[ "$CONTAINER" == *-do ]] && COTURN_CONTAINER="easy-asterisk-do-coturn"
ok "Using an embedded, per-Asterisk coturn ($COTURN_CONTAINER) — not the shared"
ok "instance, so tools/coturn-test-check.sh won't see this one; tested separately below."
ok "Using an embedded, per-Asterisk coturn ($COTURN_CONTAINER); tested separately below."
else
COTURN_CONTAINER="coturn"
ok "Using the shared coturn instance (also covered by tools/coturn-test-check.sh)"
ok "Using a legacy shared coturn instance (this repo no longer installs this shape —"
ok "see attic/coturn.sh; also covered by attic/coturn-test-check.sh)"
fi
if ! docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$COTURN_CONTAINER"; then
@@ -210,7 +211,8 @@ else
warn "turnutils_uclient not found in $COTURN_CONTAINER — skipping live allocation test"
else
# Plain UDP only — no -t/-T (TCP/TLS) flags. coturn is started with
# --no-tls --no-dtls (services/coturn.sh), so requesting an
# --no-tls --no-dtls (services/asterisk.sh's embedded coturn, and
# attic/coturn.sh's legacy shared one — same flags either way), so requesting an
# encrypted/TCP transport here just fails the allocation outright
# against a server that never offered one, misreporting a config
# problem that doesn't exist. Confirmed live: this was the actual
@@ -220,7 +222,7 @@ else
# turnutils_uclient also refuses to run at all without either -e
# <peer> or -y ("Either -e peer_address or -y must be specified",
# confirmed live). -e needs an actual reachable, non-loopback peer
# to relay through — services/coturn.sh never sets
# to relay through — this repo's coturn containers never set
# --allow-loopback-peers, so -e 127.0.0.1 gets rejected with
# "channel bind: error 403 (Forbidden IP)" (also confirmed live,
# against a real local coturn instance built to test this exact