Add multi-port forwarding and functional self-hosted relay

- Multi-Port: server akzeptiert mehrere `--to NAME=ADDR`, client mehrere
  `--listen NAME=ADDR`. Pro Bi-Stream wird ein Service-Name-Vorspann gesendet,
  den der Server einem Ziel zuordnet; alle Forwards teilen eine QUIC-Connection.
  Bloßes ADDR ohne NAME ⇒ Service `default` (Backward-Compat).
- `--relay <URL>`: ersetzt den n0-Relay durch einen self-hosted iroh-relay,
  n0-DNS-Discovery bleibt erhalten. Neuer gemeinsamer Endpoint-Builder in net.rs.
- ALPN auf iroh-forward/1 erhöht (Wire-Protokoll um Service-Header erweitert).
- Stream-Protokoll (write_service/read_service) in proxy.rs.
- README: Abschnitte zu Multi-Port und Self-hosted Relay, Optionen aktualisiert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sko
2026-06-21 14:32:43 +00:00
co-authored by Claude Opus 4.8
parent ec7c96b092
commit 241e05c578
6 changed files with 242 additions and 79 deletions
+45 -10
View File
@@ -67,10 +67,11 @@ location / {
| Flag | Default | Beschreibung |
| ---- | ------- | ------------ |
| `--to` (server) | `127.0.0.1:3000` | lokales Forward-Ziel |
| `--to` (server) | `127.0.0.1:3000` | Forward-Ziel als `NAME=ADDR`, mehrfach angebbar. Bloßes `ADDR` ⇒ Name `default` |
| `--allow` (server) | — | erlaubte Client-EndpointId; mehrfach angebbar. Ohne Angabe: alle erlaubt |
| `--listen` (client) | `127.0.0.1:9080` | lokaler TCP-Listen-Port |
| `--listen` (client) | `127.0.0.1:9080` | Listener als `NAME=ADDR`, mehrfach angebbar. `NAME` muss einem `--to`-Namen des Servers entsprechen |
| `--peer` (client) | — | EndpointId des Serving Host (Pflicht) |
| `--relay` (beide) | — | self-hosted Relay-URL (z.B. `https://relay.example.com`). Ohne Angabe: n0-Default-Relays |
| `--key-file` | `./node.key` / `./client.key` | persistierter SecretKey (Datei wird mit `0600` angelegt) |
Zusätzlicher Subcommand `keygen --key-file <PATH>`: erzeugt (falls nötig) eine Key-Datei und gibt
@@ -139,20 +140,54 @@ iroh-forward server --to 127.0.0.1:3000 --key-file node.key \
Besonders wichtig, sobald sensible Dienste (SSH, DB) statt nur eines HTTP-Upstreams getunnelt
werden.
## Sicherheit & Grenzen (v1)
## Multi-Port: mehrere Dienste über einen Tunnel
Server und Client können mehrere benannte Forwards bedienen. Jeder Client-Listener sendet seinen
**Service-Namen** als Stream-Vorspann; der Server ordnet ihn dem passenden `--to`-Ziel zu. Alle
Forwards laufen über **eine** geteilte QUIC-Connection (Multiplexing).
```bash
# Serving Host — z.B. Web-App + SSH:
iroh-forward server --key-file node.key \
--to web=127.0.0.1:3000 \
--to ssh=127.0.0.1:22 \
--allow <client-id>
# Nginx-Host — lokale Ports, je einem Service zugeordnet:
iroh-forward client --peer <server-id> --key-file client.key \
--listen web=127.0.0.1:9080 \
--listen ssh=127.0.0.1:2222
```
Dann `nginx → 127.0.0.1:9080` (web) und `ssh -p 2222 user@127.0.0.1` (ssh). Ein Listener mit
einem Service-Namen, den der Server nicht kennt, wird serverseitig verworfen.
Es funktioniert jedes TCP-Protokoll (SSH, MySQL, PostgreSQL, Redis, …). Der einfache Fall ohne
`NAME=` nutzt automatisch den Service `default` auf beiden Seiten.
## Self-hosted Relay
Standardmäßig werden die öffentlichen n0-Relays genutzt. Mit `--relay <URL>` (auf beiden Seiten)
wird stattdessen ein eigener [`iroh-relay`](https://www.iroh.computer/docs) verwendet; die
EndpointId-Discovery läuft weiterhin über n0-DNS.
```bash
iroh-forward server --relay https://relay.example.com ...
iroh-forward client --relay https://relay.example.com ...
```
Der Relay sieht nur verschlüsselte Bytes. Praktisch wird er meist nur fürs initiale Rendezvous und
bei symmetrischem NAT gebraucht — die meisten Verbindungen laufen nach dem Hole-Punching direkt P2P.
## Sicherheit & Grenzen
- **Vertrauensanker:** siehe [Schlüsselmodell](#schlüsselmodell-endpointid-vs-secretkey). Ohne
`--allow` ist die EndpointId faktisch ein Zugangs-Token → geheim halten; Key-Dateien `0600`.
- **Relay:** es werden die öffentlichen n0-Relays genutzt (`presets::N0`). Der Relay sieht nur
verschlüsselte Bytes. Das `--relay`-Flag ist als Platzhalter vorhanden, aber noch nicht
ausgewertet.
- **Nur TCP:** reine UDP-Protokolle werden nicht getunnelt. Eine Instanz forwardet genau einen
Port auf genau ein Ziel.
- **Nur TCP:** reine UDP-Protokolle werden nicht getunnelt.
- **Client-IP:** das Ziel sieht als Quelle immer den `server`-Prozess (`127.0.0.1`), nicht die
echte Client-IP — `X-Forwarded-For` ggf. davor an der Edge setzen.
### Geplante Erweiterungen
- Self-hosted `iroh-relay` statt n0-Relays (`--relay` funktionsfähig machen).
- Mehrere Port-Mappings pro Prozess.
- systemd-Units für beide Rollen.
- Allowlist/Service-Zuordnung pro Client (welcher Client welche Services nutzen darf).