#!/bin/bash # ===================================================================== # BEWEIST, DASS COTURN UNSERE ZUGANGSDATEN AKZEPTIERT -- 18.09.2026 # # Aufrufen auf dem Server: bash tools/turn-probelauf.sh # # --------------------------------------------------------------------- # WARUM ES DIESES SKRIPT GIBT # # `pruef-anruf.mjs` prueft die Rechenvorschrift gegen einen festen # Vergleichswert. Das schuetzt davor, dass sie sich unbemerkt aendert -- # es beweist aber NICHT, dass coturn das Ergebnis akzeptiert. Wir # koennten beide Seiten konsequent falsch rechnen, und jede Pruefung # waere gruen, waehrend kein einziger Anruf ueber die Vermittlung # zustande kaeme. Das faellt im Betrieb nicht auf: Es scheitern nur die # 15-25 %, die den Server ueberhaupt brauchen. # # Hier laeuft deshalb ein echter coturn und ein echter Client. # # --------------------------------------------------------------------- # DASS ES NICHTS ANFASST, IST ABSICHT # # Eigenes Heimatverzeichnis, eigener Port, nur 127.0.0.1, kein root, # kein Systemdienst. Der laufende coturn auf 3478 bleibt unberuehrt, # und von aussen ist der Probe-Server nicht erreichbar. # ===================================================================== set -u P="${TMPDIR:-/tmp}/turn-probe-$$" FEHLER=0 ok() { echo " ok $1"; } fehl() { echo " FEHL $1"; FEHLER=$((FEHLER+1)); } aufraeumen() { pkill -f "$P/turn.conf" 2>/dev/null; kill "${GEGENPART:-0}" 2>/dev/null; sleep 1; rm -rf "$P"; } trap aufraeumen EXIT command -v turnserver >/dev/null || { echo "KANN NICHT NACHSEHEN: coturn ist nicht installiert."; exit 3; } command -v turnutils_uclient >/dev/null || { echo "KANN NICHT NACHSEHEN: turnutils_uclient fehlt."; exit 3; } NODE=$(command -v node || echo /usr/local/bin/node) [ -x "$NODE" ] || { echo "KANN NICHT NACHSEHEN: node nicht gefunden."; exit 3; } mkdir -p "$P" # ZIEL FUER DEN SPERRTEST -- und warum ausgerechnet dieses. # Der erste Anlauf nahm 127.0.0.1. Die Gegenprobe (Abschnitt 4) zeigte: # Auch OHNE Sperrliste kam 403 -- coturn verbietet Loopback-Ziele von # sich aus. Der Abschnitt haette also NICHT unsere Sperrliste gemessen, # sondern eine eingebaute Regel, und waere gruen geblieben, wenn jemand # die Liste entfernt. Genau dafuer ist die Gegenprobe da. # Die Docker-Bruecke ist ein echtes oertliches Ziel auf diesem Server # und KEIN Loopback -- ohne unsere Liste waere sie erreichbar. ZIEL="${TURN_PROBE_ZIEL:-172.17.0.1}" # WARUM HIER KEIN `-y` STEHT (und im ersten Anlauf stand): # `-y` heisst "client-to-client" -- der Client verbindet dann zwei # EIGENE Kanaele miteinander und ignoriert `-e` vollstaendig. Das Ziel # war damit immer die Relay-Adresse 127.0.0.1, also Loopback, und das # sperrt coturn von sich aus. Die Gegenprobe blieb deshalb zweimal rot, # ohne dass an der Sperrliste etwas falsch war. Erst die Hilfeseite des # Werkzeugs hat es gezeigt -- nicht das dritte Herumprobieren. # Jetzt laeuft ein echter Gegenpart auf der Docker-Bruecke. gegenpart_starten() { turnutils_peer -L "$ZIEL" -p 3480 >/dev/null 2>&1 & GEGENPART=$! sleep 1 } gegenpart_beenden() { kill "$GEGENPART" 2>/dev/null; } GEHEIM=$(head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n') # Die Konfiguration entspricht deploy/turnserver.conf; abweichend nur # Adresse, Ports und dass das Protokoll in den Probeordner geht. schreibe_konf() { # $1 = "mit-sperre" oder "ohne-sperre" cat > "$P/turn.conf" <> "$P/turn.conf" <<'CONF' denied-peer-ip=127.0.0.0-127.255.255.255 denied-peer-ip=10.0.0.0-10.255.255.255 denied-peer-ip=172.16.0.0-172.31.255.255 denied-peer-ip=192.168.0.0-192.168.255.255 CONF fi } starten() { turnserver -c "$P/turn.conf" --no-stdout-log --log-file="$P/turn.log" >/dev/null 2>&1 & for _ in 1 2 3 4 5 6 7 8 9 10; do ss -lnu 2>/dev/null | grep -q 34780 && return 0 sleep 0.5 done return 1 } anhalten() { pkill -f "$P/turn.conf" 2>/dev/null; sleep 1; } # Dieselbe Rechnung wie server/workspace-turn.js -- bewusst nachgebaut # statt importiert: Wenn beide Seiten aus derselben Zeile kaemen, waere # ein Fehler in dieser Zeile hier unsichtbar. zugang() { "$NODE" -e " const {createHmac} = require('node:crypto'); const u = (Math.floor(Date.now()/1000) + 12*3600) + ':w7'; console.log(u + '\t' + createHmac('sha1', process.argv[1]).update(u).digest('base64')); " "$GEHEIM" } echo "=== Probelauf gegen einen echten coturn ===" echo # -------------------------------------------------------------------- echo "--- 1. Die Konfiguration aus deploy/turnserver.conf ---" schreibe_konf mit-sperre gegenpart_starten if starten; then ok "coturn startet und lauscht auf 127.0.0.1:34780" else fehl "coturn startet nicht -- $(tail -3 "$P/turn.log" 2>/dev/null | tr '\n' ' ')"; exit 1; fi A=$(turnutils_stunclient -p 34780 127.0.0.1 2>&1) echo "$A" | grep -q "reflexive addr" \ && ok "er beantwortet eine STUN-Anfrage ($(echo "$A" | grep -m1 -o '127[0-9.:]*'))" \ || fehl "keine STUN-Antwort: $(echo "$A" | head -2 | tr '\n' ' ')" # -------------------------------------------------------------------- echo echo "--- 2. Die Kernfrage: nimmt coturn unsere gerechneten Daten an? ---" L=$(zugang); U=$(echo "$L" | cut -f1); W=$(echo "$L" | cut -f2) RICHTIG=$(turnutils_uclient -u "$U" -w "$W" -p 34780 -n 2 -c -e "$ZIEL" -r 3480 127.0.0.1 2>&1) FALSCH=$(turnutils_uclient -u "$U" -w "nicht-das-richtige-passwort" -p 34780 -n 1 -c -e "$ZIEL" -r 3480 127.0.0.1 2>&1) # Der Unterschied ist eindeutig und genau der richtige: # Mit gueltigen Daten kommt der Client UEBER die Allocation hinaus und # scheitert erst am gesperrten Ziel. Mit ungueltigen scheitert schon # die Allocation selbst. echo "$RICHTIG" | grep -q "Cannot complete Allocation" \ && fehl "unsere Zugangsdaten werden ABGEWIESEN -- kein Anruf ginge ueber die Vermittlung" \ || ok "unsere Zugangsdaten werden angenommen (die Zuteilung gelingt)" echo "$FALSCH" | grep -q "Cannot complete Allocation" \ && ok "ein falsches Passwort wird abgewiesen -- die Pruefung prueft wirklich" \ || fehl "ein FALSCHES Passwort kam durch -- der Server steht jedem offen" # -------------------------------------------------------------------- echo echo "--- 3. Die Sperrliste: kommt jemand an die oertlichen Dienste? ---" echo "$RICHTIG" | grep -qi "403" \ && ok "ein Ziel im oertlichen Netz ($ZIEL) wird verweigert (403)" \ || fehl "$ZIEL war als Ziel ERLAUBT -- ein Weg zu jedem oertlichen Dienst" # -------------------------------------------------------------------- echo echo "--- 4. Gegenprobe: ohne Sperrliste MUSS derselbe Versuch durchgehen ---" echo " (sonst haette Abschnitt 3 nur gemessen, dass irgendetwas scheitert)" anhalten schreibe_konf ohne-sperre if starten; then L=$(zugang); U=$(echo "$L" | cut -f1); W=$(echo "$L" | cut -f2) OHNE=$(turnutils_uclient -u "$U" -w "$W" -p 34780 -n 4 -c -e "$ZIEL" -r 3480 127.0.0.1 2>&1) echo "$OHNE" | grep -qi "403" \ && fehl "auch ohne Sperrliste 403 -- Abschnitt 3 beweist dann nichts" \ || ok "ohne Sperrliste laeuft derselbe Versuch durch -- die Sperre war die Ursache" echo "$OHNE" | grep -qiE "total transmit|tot_send_msgs|Total transmit time" \ && ok "und es fliessen wirklich Daten ueber die Weiterleitung" \ || echo " -- Datenfluss nicht ablesbar (nicht schlimm, der Rest steht)" else fehl "zweiter Start misslungen" fi echo if [ "$FEHLER" -eq 0 ]; then echo "ALLES IN ORDNUNG -- coturn und der Workspace rechnen dasselbe." exit 0 fi echo "NICHT IN ORDNUNG -- $FEHLER Punkt(e)." exit 1