TCP-Forwarding über das Iroh-Netz (QUIC), adressiert per Public Key statt IP (Umsetzung von Option B aus recherche-vpn-vs-p2p-netzwerk.md). - server: nimmt Iroh-Verbindungen an, leitet QUIC-Streams an lokalen Port weiter - client: lauscht lokal auf TCP, öffnet pro Verbindung einen QUIC-Stream zum Peer - keygen: erzeugt Key-Datei und gibt EndpointId aus (Vorab-Verteilung) - Allowlist (--allow) erlaubter Client-EndpointIds - QUIC-Multiplexing, Reconnect mit Backoff, n0-Default-Relays + DNS-Discovery Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
Recherche: VPN vs. P2P-Netzwerk
Teil 1 – Vergleichende Übersicht
Klassifikation nach Betriebsmodell
| Begriff | Bedeutung |
|---|---|
| Netzwerk-Tunnel / Daemon | Läuft als System-Dienst, erzeugt ein virtuelles Netzwerkinterface (tun/tap). Routing erfolgt OS-seitig. |
| Overlay-Netzwerk / Mesh | Spannt ein virtuelles Netz über bestehende Infrastruktur auf. Jeder Knoten kennt (ggf. über Koordination) alle anderen. |
| App-Konnektivität / P2P-SDK | Keine Netzwerkschnittstelle im OS. Konnektivität wird direkt in die Applikation eingebettet. Verbindung über kryptographische Schlüssel, nicht IPs. |
Protokoll-Vergleich
| Protokoll | OSI-Schicht | Betriebsmodell | Koordination | Verschlüsselung | Besonderheiten |
|---|---|---|---|---|---|
| IPSec | L3–L4 (Netzwerk/Transport) | Netzwerk-Tunnel / Kernel | Manuell (IKEv2/IKEv1) oder PKI | AES-GCM, ChaCha20 (via Suite B) | Standard in Enterprise-Firewalls; komplex in Konfiguration; NAT-Probleme (NAT-T nötig); zwei Modi: Transport (L4-Header bleibt) und Tunnel (ganzes IP-Paket) |
| OpenVPN | L4-Tunnel → präsentiert L2/L3 | Netzwerk-Tunnel / Daemon | CA-basiert (TLS-Zertifikate) | TLS (OpenSSL/mbedTLS) | TUN=L3, TAP=L2; läuft über UDP oder TCP; sehr verbreitet, aber langsam (Userspace); gut durch Firewalls tunnelbar (TCP:443) |
| WireGuard | L3 (Netzwerk) | Overlay-Netzwerk / Kernel-Modul | Manuell (Pre-shared Keys + Public Keys) | ChaCha20-Poly1305, Curve25519 | Minimaler Kernel-Code (~4000 Zeilen); kein Handshake-Overhead im Betrieb; keine eingebaute Key-Discovery; bildet Basis für Tailscale/Netbird |
| Yggdrasil | L3-Overlay (IPv6) | Overlay-Netzwerk / Mesh-Daemon | Selbstorganisierend (distributed routing) | X25519 + Ed25519 | Adresse = Hash des Public Key; kein zentraler Koordinator; schlechte Performance bei sehr großem Mesh; experimentell |
| Tailscale | L3-Overlay | Overlay-Netzwerk / Managed Mesh | Zentraler Koordinations-Server (control plane) | WireGuard darunter | „Zero-config VPN"; DERP-Relay-Netz von Tailscale für NAT-Traversal; SaaS-Modell (oder Headscale self-hosted); gut für Team-Zugriff auf interne Dienste |
| Iroh | L4–L7 (QUIC/Application) | App-Konnektivität / P2P-SDK | Relay-Server (optional, betreibbar) | TLS 1.3 über QUIC | Kein Netzwerkinterface; Bibliothek (Rust, Python, Node.js, Swift, Kotlin); Adressierung über Public Keys; 95 % direkte Verbindungen; QUIC Multipath; kein Flottenmanagement eingebaut |
L4 vs. L7 – was bedeutet das hier konkret?
L4 (Transport):
- IPSec Tunnel-Modus, OpenVPN, WireGuard und Tailscale operieren auf dieser Ebene oder darunter
- Das OS erhält ein virtuelles Interface (
wg0,tun0) und routet IP-Traffic darüber - Alle Anwendungen profitieren automatisch – keine Code-Änderung nötig
L7 (Applikation):
- Iroh arbeitet ausschließlich auf Applikationsebene
- Die Anwendung selbst öffnet eine Iroh-Verbindung (QUIC-Session) zu einem Remote-Node via dessen Public Key
- Kein System-Routing, kein Interface – die App muss Iroh aktiv nutzen
- Vergleichbar mit WebRTC: im Browser transparent, aber nur für eingebettete Anwendungen sinnvoll
Wann welches Modell?
| Szenario | Empfehlung |
|---|---|
| Site-to-Site zwischen Firewalls | IPSec (native OPNsense-Support) |
| Remote-Access für Mitarbeiter | Tailscale oder WireGuard |
| Self-hosted Mesh ohne SaaS | WireGuard + Netbird/Headscale |
| P2P in eigener Anwendung gebaut | Iroh |
| Gerät soll von überall erreichbar sein, ohne feste IP | Iroh oder Tailscale |
| Experimentelles dezentrales Netz | Yggdrasil |
Teil 2 – Iroh-Integration in bestehendes Setup
Ausgangslage
Internet
│
[OPNsense] ──── Firewall / Router
│
[Nginx] ──── Reverse Proxy (terminiert TLS, routet vhosts)
│
[Serving Host] ──── Anwendungsserver (bisher feste IP / internes Netz)
Das Problem: Der Serving Host muss derzeit über ein geroutetes Netz (VPN oder direkt) erreichbar sein. Ziel ist es, ihn standortunabhängig zu betreiben – ohne VPN-Konfiguration auf Netzwerkebene.
Iroh-Konzept: Dial by Key, not by IP
Jeder Iroh-Node hat ein Ed25519-Schlüsselpaar. Der Public Key ist die Adresse. Solange ein Node seinen Relay kennt (oder direkten QUIC-Hole-Punch schafft), ist er erreichbar – unabhängig von Netzwerkposition, NAT oder dynamischer IP.
Verfügbare Iroh-Tools (ohne eigenen Code)
| Tool | Funktion | Einsatzbarkeit |
|---|---|---|
| dumbpipe | Bidirektionale Byte-Pipe zwischen zwei Iroh-Nodes (wie nc über Iroh) |
Ja – als TCP-Tunnel mit socat kombinierbar |
| sendme | Einmalige Dateiübertragung | Nein – kein dauerhafter Dienst |
| iroh-relay | Eigener Relay-Server (DERP-ähnlich) | Ja – für selbst-gehostetes Setup |
dumbpipe ist die einzige Komponente, die einen dauerhaften Kanal aufbauen kann – aber sie überträgt einen Byte-Stream, kein TCP-Multiplexing. Für einen HTTP-Upstream im Nginx braucht man ein TCP-Forward-Setup.
Architektur-Optionen
Option A: dumbpipe + socat als TCP-Proxy-Kette
[Client]
↓ HTTPS
[Nginx] → upstream: localhost:8080
↓
socat LISTEN:8080 → dumbpipe connect <serving-host-key>
↓ [Iroh-Netz]
dumbpipe listen → socat → localhost:3000 (App)
[Serving Host]
Vorgehen:
# Serving Host
dumbpipe listen | socat - TCP:localhost:3000
# → gibt Node-Key aus, z.B. "ki2..."
# Nginx-Host
socat TCP-LISTEN:8080,fork EXEC:"dumbpipe connect ki2..."
# Nginx: proxy_pass http://127.0.0.1:8080;
Problem: dumbpipe ist eine Single-Connection-Pipe, kein TCP-Listener. Für parallele HTTP-Verbindungen müsste man pro Request eine neue Pipe öffnen oder ein Connection-Multiplexer vorschalten. Für produktiven Einsatz ungeeignet ohne eigenen Code.
Option B: Eigenes Iroh-TCP-Forward-Tool (empfohlen)
Ein kleines Tool in Rust oder Python (ca. 100–200 Zeilen), das:
- Serving Host: Iroh-Endpunkt öffnet, akzeptiert Verbindungen, leitet an lokalen Port weiter
- Nginx-Host: Lauscht auf lokalem TCP-Port, öffnet pro Verbindung eine neue Iroh-QUIC-Stream
Iroh unterstützt nativ multiple Streams pro Connection (QUIC), daher skaliert das ohne Tricks.
[Nginx]
proxy_pass http://127.0.0.1:9080
↓
[iroh-forward-client] lauscht :9080, öffnet QUIC-Stream zu Serving Host
↓ QUIC / Iroh
[iroh-forward-server] auf Serving Host, leitet weiter an :3000
↓
[App]
Aufwand: ~1–2 Tage Entwicklung in Rust mit der iroh-Crate. Python-Bindings sind verfügbar, aber QUIC-Performance ist in Rust besser.
Option C: OPNsense bleibt unberührt, Iroh nur Nginx ↔ Serving Host
Dies ist das sauberste Setup: OPNsense braucht nichts von Iroh zu wissen. Der Iroh-Tunnel existiert nur auf Applikationsebene zwischen Nginx-Host und Serving Host.
Internet → OPNsense → Nginx (Port 443) → iroh-forward-client → [Iroh] → Serving Host
OPNsense sieht nur: Nginx-Host macht ausgehende QUIC-Verbindungen (UDP) zu Iroh-Relays. Kein eingehender Traffic auf dem Serving Host nötig.
Credential-Verteilung
| Komponente | Credential | Wo gespeichert | Übergabe |
|---|---|---|---|
| Iroh Node Key (Serving Host) | Ed25519-Keypair | /etc/iroh/node.key (Serving Host) |
Public Key wird einmalig aus dem Log extrahiert |
| Iroh Node Key (Nginx-Host) | Ed25519-Keypair | /etc/iroh/client.key (Nginx-Host) |
Automatisch generiert beim ersten Start |
| Serving Host Public Key | Public Key (String) | Nginx-Host Config / .env |
Out-of-band: z.B. über Ansible/Vault, Gitea-Secret, o.ä. |
| Relay-URL | URL | beide Hosts | Im iroh-config oder Env-Variable |
| App-Credentials | DB-Passwörter etc. | Serving Host lokal | Unverändert – Iroh ändert nichts daran |
Wichtig: Der Public Key des Serving Host muss sicher und authentisch übertragen werden – er ist der einzige Vertrauensanker. Wird er kompromittiert (Man-in-the-Middle beim Austausch), kann ein Angreifer eine eigene Verbindung vortäuschen.
Vor- und Nachteile
Vorteile
- Standortunabhängigkeit: Serving Host kann in Hetzner, Zuhause, AWS oder hinter CGNAT laufen – kein eingehender Port nötig
- Kein VPN-Daemon: Keine WireGuard-Konfiguration, keine IKEv2-PKI – Aufwand sinkt deutlich
- OPNsense bleibt unberührt: Firewall-Regeln, Routing-Tabellen – alles unverändert
- Ende-zu-Ende-verschlüsselt: TLS 1.3 über QUIC; Relay sieht nur verschlüsselte Bytes
- Selbst-hostbarer Relay: Kein SaaS-Abhängigkeit wie bei Tailscale;
iroh-relayist Open Source - Direkte Verbindungen wo möglich: 95 % der Verbindungen gehen ohne Relay-Hop durch (Iroh-Angabe)
Nachteile
- L7-only: Nginx muss explizit konfiguriert werden; kein transparentes OS-Routing
- Kein Flottenmanagement: Key-Rotation, Revocation, Inventar – alles selbst gebaut
- dumbpipe reicht nicht für HTTP-Upstream: Eigene Entwicklung nötig (Option B)
- QUIC-Firewall-Probleme: Manche Netze blockieren UDP; WebSocket-Fallback existiert, aber nicht alle Clients unterstützen ihn
- Relay-Abhängigkeit bei symmetrischem NAT: Eigener Relay nötig für vollständige Unabhängigkeit
- Fehlerdiagnose schwieriger: Kein
tcpdumpauf dem Iroh-Interface; Debugging läuft über Iroh-eigene Logs - Nginx kennt keine echte Client-IP: Bei Relay-Routing kommt Traffic von der Relay-IP;
X-Forwarded-Formuss manuell gesetzt werden
Empfehlung
Für ein produktives Setup ist Option B (eigenes Iroh-Forward-Tool) der richtige Weg. Der Aufwand ist überschaubar und man bekommt ein sauberes TCP-Forwarding mit QUIC-Multiplexing, ohne auf dumbpipe-Hacks angewiesen zu sein.
Für einen ersten Proof of Concept reicht Option A mit dumbpipe + socat aus, um die Prinzipien zu validieren, bevor eigener Code entsteht.
OPNsense und Nginx müssen in keinem der Szenarien strukturell verändert werden – Iroh ist eine reine Ergänzung auf Applikationsebene.