niom-turn

Minimal TURN server scaffold for the niom project (MVP).

Goals

  • Provide a TURN server with long-term authentication and TLS (turns) for WebRTC clients.
  • Start with a minimal, well-tested parsing/utility layer and an in-memory credential store interface that can be replaced later.

Current status

  • UDP listener on 0.0.0.0:3478 (STUN/TURN) with Allocate, CreatePermission, ChannelBind, and Send flows forwarding traffic via relay sockets.
  • Long-term authentication with REALM/NONCE challenges and MESSAGE-INTEGRITY validation driven by AuthManager.
  • STUN message parser + builder in src/stun.rs.
  • Optional TLS listener (0.0.0.0:5349) mirrors the UDP path for turns: clients.

Design

  • Modules
    • stun.rs STUN/TURN message parsing, MESSAGE-INTEGRITY helpers, and response builders.
    • auth.rs AuthManager orchestrates nonce minting, realm checking, and key derivation using the pluggable CredentialStore (default: InMemoryStore).
    • alloc.rs Relay allocation management with permission and channel tracking.
    • main.rs / tls.rs Runtime wiring for UDP and TLS listeners using the shared authentication + allocation stack.

Authentication & credential store

  • CredentialStore is an async trait with get_password(username) -> Option<String> used by AuthManager.
  • AuthManager derives the RFC long-term key (MD5(username:realm:password)) and validates MESSAGE-INTEGRITY while issuing signed, timestamped nonces.
  • The default InMemoryStore is provided for tests and local dev. Swap in a production store by implementing the trait and passing it to AuthManager.

How to build

cd niom-turn
cargo build

How to test (quick local smoke)

  • Start the server in one terminal (it listens on UDP/3478):
cd niom-turn
cargo run
  • From another machine or container, use a STUN client or sipsak/custom script to send a minimal STUN Binding request and observe the 401 reply.

Next steps

  • Implement full STUN attribute parsing (MESSAGE-INTEGRITY, FINGERPRINT).
  • Implement long-term auth validation using MESSAGE-INTEGRITY.
  • Implement Allocate + relayed sockets and permission handling.
  • Add TLS listener (port 5349) using tokio-rustls and support turns:.

Security / Deployment

  • For production, run behind properly provisioned TLS certs (Let's Encrypt or mounted certs) and secure credential storage.
  • Ensure UDP and TCP/TLS ports (3478/5349) are reachable from the internet when used as a public TURN server.

Auth caveat

  • The current implementation intentionally keeps things simple: credentials live in-memory, A1 keys are derived via MD5 for RFC compatibility, and nonces are signed with HMAC-SHA1. Replace these pieces (Argon2-backed store, modern KDFs, nonce rotation) before production rollout. See src/auth.rs for the pluggable surface.

Milestone 1 — Protocol Backlog

This milestone focuses on turning the current MVP into a feature-complete TURN core that can be used reliably by niom-webrtc.

Prioritised Backlog (live order)

  1. TURN Data Plane EnablementCreatePermission, ChannelBind, Send/Data indications, and peer forwarding so allocations actually relay packets between clients and peers.
  2. Authentication Hardening — nonce lifecycle, realm configuration, Argon2-backed credential storage, and detailed error handling for 401/438 responses.
  3. Allocation Lifecycle & Quotas — timers, refresh requests, cleanup of expired allocations, and resource limits per user/IP.
  4. Protocol Compliance Extras — FINGERPRINT support, XOR-MAPPED-ADDRESS, IPv6 evaluation, checksum validation, and fuzz/interop testing.
  5. Observability & Limits — structured tracing, metrics, rate limiting, and CI coverage (incl. the bundled smoke_client).

Artifacts that track this milestone live in two places:

  1. This README section is kept up to date while the milestone is in progress.
  2. Inline module docs (//!) inside src/ record the current responsibilities and open backlog items for each subsystem as we iterate.

Task in progress

  • TURN data plane enablement:
    • CreatePermission handling and permission tracking
    • ChannelBind setup and Send forwarding to peers
    • ChannelData framing and Data Indication responses from relay to client

License: MIT

Smoke-Test (End-to-End)

Diese Anleitung beschreibt, wie du lokal den laufenden TURN/STUN-Server prüfst und welche Ergebnisse zu erwarten sind.

  1. Server starten

Starte den Server im Projektverzeichnis; die Ausgabe wird normal in stdout geschrieben. In meinen Tests habe ich den Server im Hintergrund gestartet und die Logs in /tmp/niom-turn-server.log umgeleitet:

cd niom-turn
# Im Vordergrund (für Entwicklung)
cargo run --bin niom-turn

# Oder im Hintergrund mit Log-Redirect
cargo run --bin niom-turn &>/tmp/niom-turn-server.log &
  1. Smoke-Client ausführen

Das Repo enthält ein kleines Test-Binary smoke_client, das eine STUN Binding-Request mit USERNAME und MESSAGE-INTEGRITY an 127.0.0.1:3478 sendet.

# Build (falls noch nicht gebaut)
cargo build --bin smoke_client

# Ausführen
./target/debug/smoke_client
  1. Erwartetes Ergebnis

Der Smoke-Client gibt die erhaltenen Bytes aus. Bei einer erfolgreichen MESSAGE-INTEGRITY-Prüfung sendet der Server eine STUN Success Response (Message Type 0x0101). Beispielsweise habe ich folgende Rückgabe gesehen:

got 20 bytes from 127.0.0.1:3478
[01, 01, 00, 00, 21, 12, a4, 42, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07, 07]

Erklärung:

  • 01 01 → STUN Success Response (0x0101)
  • 21 12 a4 42 → Magic Cookie (0x2112A442)
  • die folgenden 12 Bytes sind die Transaction ID (in diesem Test 07 wiederholt)

Das bedeutet: Der Server hat die MESSAGE-INTEGRITY des Requests akzeptiert und eine 200-Antwort gesendet.

Wenn stattdessen eine 401-Antwort (Challenge) ausgegeben wird, sieht man im Hex typischerweise einen Fehler-Response-Typ und REALM/NONCE-Attribute im Payload; das zeigt, dass die Authentifizierung nicht erfolgt ist und der Client die Challenge verarbeiten muss.

Hinweis

  • Die derzeitige Auth-Implementierung ist minimal und für Tests gedacht. Für Produktion bitte die README-Abschnitte "Auth caveat" beachten: sichere Credentials, TLS, und ggf. ephemeral credentials verwenden.

Configuration (appsettings.json)

Das Projekt kann eine JSON-Konfigdatei appsettings.json im Arbeitsverzeichnis lesen. Eine Beispiel-Datei appsettings.example.json liegt im Repository und zeigt die erwartete Struktur:

{
  "server": {
    "bind": "0.0.0.0:3478",
    "tls_cert": null,
    "tls_key": null
  },
  "credentials": [
    {
      "username": "testuser",
      "password": "secretpassword"
    }
  ],
  "auth": {
    "realm": "niom-turn.local",
    "nonce_secret": null,
    "nonce_ttl_seconds": 300
  }
}

Wenn appsettings.json vorhanden ist, verwendet der Server die server.bind Adresse, befüllt den Credential-Store aus dem credentials-Array und übernimmt zusätzlich Realm/Nonce-Einstellungen aus auth. Falls die Datei fehlt, verwendet der Server die internen Defaults (Bind 0.0.0.0:3478, Demo-Cred testuser, Realm niom-turn.local).

Deployment & TLS / Long-term Auth roadmap

Siehe DEPLOYMENT_TLS_AUTH.md für eine kurze Roadmap und empfohlene Schritte zur Integration von TLS (turns: / TCP+TLS Port 5349) und der Implementierung von RFC-konformer Long-Term Authentication.

Description
TURN Server für nioM WebRTC Kommunikation
Readme 360 KiB
Languages
Rust 100%