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` 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 ```bash cd niom-turn cargo build ``` How to test (quick local smoke) - Start the server in one terminal (it listens on UDP/3478): ```bash 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 Enablement** — `CreatePermission`, `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: - [x] `CreatePermission` handling and permission tracking - [x] `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: ```bash 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 & ``` 2) 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. ```bash # Build (falls noch nicht gebaut) cargo build --bin smoke_client # Ausführen ./target/debug/smoke_client ``` 3) 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: ```json { "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.