From cce8147059b1653281813aa674e9a34a0d4cf6fb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 15:04:51 +0000 Subject: [PATCH] Live-verify a newly assigned Mattermost coturn slot isn't already bound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Requested check: the slot-allocation scheme added in the previous commit only checked against OTHER mattermost*/.env files on the box, not against what's actually listening. A slot whose numbers happen to be free by that bookkeeping could still be squatted by something this script doesn't track (a manually-run process, an unrelated service) — this box already learned that lesson once, from Asterisk and Mattermost's embedded coturn ranges overlapping without either side knowing. Only a NEWLY assigned slot gets the live check — an already-cached slot (read back from this instance's own .env) is trusted as-is, since a live conflict on an already-configured, already-running instance's own port is a real problem to report, not something to silently route around by moving that instance's TURN port out from under it. Can't scan the full 200-port relay range port-by-port (large ranges use the offset scheme instead of scanning per CLAUDE.md's port-collision section) — checks the control port plus both relay-range boundaries as the practical middle ground. Verified against a mock: a candidate slot whose control port is already bound gets skipped in favor of the next free one. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn --- services/mattermost.sh | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/services/mattermost.sh b/services/mattermost.sh index 828a427..acfacfd 100644 --- a/services/mattermost.sh +++ b/services/mattermost.sh @@ -440,12 +440,33 @@ networks: local EMBEDDED_COTURN_SLOT="" [ -f "$DIR/.env" ] && EMBEDDED_COTURN_SLOT="$(grep '^EMBEDDED_COTURN_SLOT=' "$DIR/.env" 2>/dev/null | cut -d= -f2-)" if [ -z "$EMBEDDED_COTURN_SLOT" ]; then - local _used_slots + # Assigning a NEW slot — also live-verify the candidate control port + # and relay-range boundaries aren't already bound by something this + # box's own .env files don't know about (a manually-run process, an + # unrelated service). An already-cached slot (the branch above) is + # trusted as-is and never re-verified — that's what "stable across + # re-runs" means; a live process squatting on an already-assigned + # slot's port is a conflict to report, not silently route around by + # moving an already-configured instance. Can't scan the full + # 200-port relay range port-by-port (CLAUDE.md's + # port-collision-avoidance section covers why large ranges use an + # offset instead of scanning) — checking the control port plus the + # relay range's own two boundary ports is the practical middle + # ground between "no live check at all" and a full range scan. + local _used_slots _cand _p _min _max _used_slots="$(grep -h '^EMBEDDED_COTURN_SLOT=' "$DOCKER_DIR"/mattermost*/.env 2>/dev/null | cut -d= -f2-)" - EMBEDDED_COTURN_SLOT=0 - while echo "$_used_slots" | grep -qx "$EMBEDDED_COTURN_SLOT"; do - EMBEDDED_COTURN_SLOT=$((EMBEDDED_COTURN_SLOT + 1)) + _cand=0 + while true; do + _p=$((3479 + _cand)); _min=$((49253 + _cand * 200)); _max=$((_min + 199)) + if echo "$_used_slots" | grep -qx "$_cand" \ + || port_in_use "$_p" || port_in_use "$_p" udp \ + || port_in_use "$_min" udp || port_in_use "$_max" udp; then + _cand=$((_cand + 1)) + continue + fi + break done + EMBEDDED_COTURN_SLOT="$_cand" fi local _MM_COTURN_PORT=$((3479 + EMBEDDED_COTURN_SLOT)) local _MM_COTURN_MIN=$((49253 + EMBEDDED_COTURN_SLOT * 200))