Die STUN-Abfrage in turn-einrichten.mjs hatte noch nie einen antwortenden Server gesehen. Eine Abfrage, die immer "keine Antwort" sagt, laesst uns an der Firewall suchen, waehrend dort nichts falsch ist. Jetzt gemessen: mit laufendem Server erkennt sie ihn, ohne meldet sie ihn als nicht erreichbar. Co-Authored-By: Claude Opus 5 <[email protected]>
214 lines
8.9 KiB
Bash
214 lines
8.9 KiB
Bash
#!/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" <<CONF
|
|
listening-ip=127.0.0.1
|
|
relay-ip=127.0.0.1
|
|
listening-port=34780
|
|
no-tls
|
|
no-dtls
|
|
min-port=49400
|
|
max-port=49420
|
|
use-auth-secret
|
|
static-auth-secret=$GEHEIM
|
|
realm=dogfather-universe.com
|
|
no-multicast-peers
|
|
no-tcp-relay
|
|
no-cli
|
|
user-quota=12
|
|
total-quota=100
|
|
CONF
|
|
if [ "$1" = "mit-sperre" ]; then
|
|
cat >> "$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
|
|
echo "--- 5. Sieht das Einrichtungswerkzeug einen laufenden Server? ---"
|
|
echo " (eine Abfrage, die IMMER \"keine Antwort\" sagt, laesst uns an"
|
|
echo " der Firewall suchen, waehrend dort nichts falsch ist)"
|
|
WZ="$(dirname "$0")/turn-einrichten.mjs"
|
|
if [ ! -f "$WZ" ]; then
|
|
echo " -- turn-einrichten.mjs nicht gefunden -- nicht nachgesehen"
|
|
else
|
|
"$NODE" -e "
|
|
const {DatabaseSync} = require('node:sqlite');
|
|
const d = new DatabaseSync(process.argv[1]);
|
|
d.exec('CREATE TABLE einstellungen (schluessel TEXT PRIMARY KEY, wert TEXT, geaendert TEXT, von INTEGER)');
|
|
d.close();" "$P/w.db"
|
|
printf 'ein-geheimnis-das-lang-genug-ist-fuer-diesen-probelauf
|
|
' > "$P/geheim"
|
|
|
|
WORKSPACE_DB="$P/w.db" TURN_GEHEIMNIS_DATEI="$P/geheim" "$NODE" "$WZ" eintragen turn:127.0.0.1:34780 >/dev/null 2>&1
|
|
MIT=$?
|
|
anhalten
|
|
WORKSPACE_DB="$P/w.db" TURN_GEHEIMNIS_DATEI="$P/geheim" "$NODE" "$WZ" pruefen >/dev/null 2>&1
|
|
OHNE=$?
|
|
|
|
[ "$MIT" -eq 0 ] && ok "es erkennt den laufenden Server" || fehl "es erkennt den laufenden Server NICHT (Rueckgabewert $MIT)"
|
|
[ "$OHNE" -ne 0 ] && ok "und meldet den abgeschalteten als nicht erreichbar -- beide Faelle" || fehl "es meldet auch ohne Server Erfolg -- die Abfrage misst nichts"
|
|
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
|