1 Punkte von GN⁺ 2025-01-06 | 1 Kommentare | Auf WhatsApp teilen
  • NAT Traversal ist die grundlegende Technik, mit der Geräte hinter NAT und Firewalls UDP-Pakete direkt austauschen können, sodass Tailscale WireGuard-Tunnel ohne zentralen Hub verbinden kann
  • Die zentrale Voraussetzung ist, dass das Protokoll UDP-basiert ist und Pakete für die NAT-Erkennung sowie die eigentlichen Kommunikationspakete über denselben Netzwerk-Socket gesendet und empfangen werden können
  • Stateful Firewalls erlauben nur Antworten, die zu zuvor ausgehenden UDP-Paketen passen; wenn Peers also gegenseitig ihre ip:port kennen und nahezu gleichzeitig Pakete senden, lässt sich der Firewall-State öffnen
  • Da NAT Quell-IP und Port verändert, sind ergänzende Techniken wie STUN, Port-Mapping, NAT64-Behandlung, Port-Suche auf Basis des Geburtstagsparadoxons und Relays nötig
  • ICE testet mögliche Kandidatenpfade gleichzeitig und wählt den besten Pfad aus; Tailscale stellt zunächst sofort eine Verbindung über ein DERP-Relay her und wechselt transparent, sobald ein besserer direkter Pfad gefunden wird

Grundvoraussetzungen für NAT Traversal

  • Ziel ist es, zwischen zwei Geräten einen bidirektionalen UDP-Paketfluss aufzubauen, auf dem dann Protokolle wie WireGuard, QUIC oder WebRTC laufen können
  • Für eine eigene Implementierung sind zwei Bedingungen wichtig
    • Das Protokoll muss UDP-basiert sein
      • Mit TCP ist es ebenfalls möglich, aber die Komplexität ist höher, und je nach Implementierungsansatz können Kernel-Änderungen erforderlich sein
      • Wenn eine streamorientierte Verbindung nötig ist, kann man QUIC in Betracht ziehen, das über UDP läuft
    • Das Programm muss den Netzwerk-Socket, über den Pakete gesendet und empfangen werden, direkt kontrollieren
      • Da NAT Traversal zusätzliche Pakete außerhalb des eigentlichen Protokolls senden muss, lässt es sich nur schwer einfach an bestehende Netzwerkbibliotheken anflanschen
      • Eine Struktur ist hilfreich, in der NAT-Traversal-Logik und das eigentliche Protokoll denselben Socket teilen und parallel laufen
  • Wenn direkter Socket-Zugriff schwierig ist, kann ein lokaler Proxy eingesetzt werden
    • Das ursprüngliche Protokoll kommuniziert mit dem Proxy
    • Der Proxy übernimmt NAT Traversal und das Relay von Paketen zum Peer

Stateful Firewalls durchqueren

  • Stateful Firewalls merken sich früher gesehene Pakete und entscheiden damit, ob neue Pakete zugelassen werden
    • Beispiele sind Windows Defender Firewall, Ubuntu ufw, BSD pf, pf unter macOS und AWS Security Groups
    • Eine gängige Konfiguration erlaubt alle ausgehenden Verbindungen und blockiert alle eingehenden Verbindungen
  • Bei UDP sind die Regeln einfach
    • Wenn die Firewall ein ausgehendes UDP-Paket von 2.2.2.2:1234 nach 5.5.5.5:5678 gesehen hat, erlaubt sie umgekehrt ein eingehendes Paket von 5.5.5.5:5678 nach 2.2.2.2:1234
    • Einige locker konfigurierte Firewalls erlauben möglicherweise eingehenden Traffic von überall zu einem lokalen Port, über den einmal kommuniziert wurde, aber das wird immer seltener
  • In einer Server-Client-Struktur ist das Problem gering, weil das Gerät hinter der Firewall die Verbindung zuerst starten kann
    • Bei VPNs entsteht daraus eine Hub-and-Spoke-Struktur, bei der sich Spokes hinter Firewalls mit einem Hub ohne Firewall verbinden
  • Wenn zwei Clients direkt miteinander kommunizieren sollen, kann die Situation entstehen, dass beide Firewalls einander blockieren
    • Beide Seiten müssen zuerst ausgehend senden, um eine Antwort empfangen zu können, aber die Gegenseite befindet sich in derselben Lage
    • Ports manuell durch Nutzer öffnen zu lassen, ist unbequem und skaliert in Mesh-Netzwerken wie Tailscale schlecht
    • Es gibt zudem viele Firewalls, die Nutzer nicht kontrollieren können, etwa Router in Flughäfen oder Cafés
  • Der Kern der Lösung ist, dass UDP-Firewall-Regeln keine echte Antwortbeziehung prüfen, sondern nur die Kombination aus IP und Port betrachten
    • Wenn beide Peers die ip:port der Gegenseite im Voraus kennen und gleichzeitig UDP-Pakete senden, können die ersten Pakete zwar blockiert werden, doch der Firewall-State wird geöffnet
    • Danach sehen die vom Gegenüber gesendeten Pakete wie Antworten aus und passieren die Firewall
  • Diese Methode erfordert einen Seitenkanal
    • Beide Endpunkte müssen nahezu gleichzeitig versuchen zu kommunizieren
    • Eine Verzögerung von einigen Sekunden ist tolerierbar; es genügt ein Kommunikationspfad, der nur einige Tausend Byte übertragen kann
    • WebRTC verlangt einen Signaling-Kanal, und Tailscale nutzt den Coordination Server und DERP-Server als Seitenkanal
  • Firewall-State ist nicht dauerhaft
    • Ein häufiger Wert für UDP-Session-Timeouts sind 30 Sekunden
    • Um die Verbindung aufrechtzuerhalten, muss man regelmäßig Pakete senden oder die Verbindung bei Bedarf out-of-band neu starten
  • Selbst wenn mehrere Schichten stateful Firewalls vorhanden sind, kann die gleichzeitige Sendemethode sie durchqueren, sofern ausgehender Traffic erlaubt ist

Wie NAT das Problem erschwert

  • NAT (Network Address Translator) verhält sich wie eine stateful Firewall und verändert zusätzlich IP-Adresse oder Port der Pakete
  • Für NAT Traversal ist vor allem Source NAT (SNAT) problematisch
    • SNAT ermöglicht es mehreren Geräten, eine kleinere Zahl von IP-Adressen zu teilen, meist eine einzige öffentliche IPv4-Adresse
    • DNAT gibt es ebenfalls, es hat aber wenig mit dem hier behandelten NAT-Traversal-Problem zu tun
  • Wenn ein Laptop beispielsweise von 192.168.0.20:1234 ein UDP-Paket an den Internetserver 7.7.7.7:5678 sendet, wählt der Heimrouter einen freien Port der öffentlichen IP, etwa 2.2.2.2:4242
    • Der Router erstellt ein NAT Mapping, das 192.168.0.20:1234 und 2.2.2.2:4242 einander zuordnet
    • Nachfolgende ausgehende Pakete werden so umgeschrieben, als kämen sie von 2.2.2.2:4242
    • Eingehende Antworten werden wieder auf 192.168.0.20:1234 umgeschrieben
  • In Unternehmensnetzwerken gilt dasselbe Prinzip
    • Der Unterschied ist, dass die NAT-Schicht wegen Hochverfügbarkeit oder Kapazität aus mehreren Geräten bestehen und mehrere öffentliche IPs haben kann

STUN und das Entdecken von NAT-Mappings

  • Ein Peer hinter NAT kann seine öffentliche ip:port, wie sie die Gegenseite sieht, nicht kennen, und NAT-Mappings entstehen normalerweise erst, wenn Traffic ins Internet gesendet wird
  • STUN ist ein Protokoll, mit dem ein Client hinter NAT herausfindet, wie er aus dem Internet gesehen wird
    • Der Client fragt den STUN-Server: „Wie sieht mein Endpoint für dich aus?“
    • Der STUN-Server antwortet mit der öffentlichen ip:port, von der das UDP-Paket kam
  • Wenn die von STUN gemeldete öffentliche ip:port mit dem Peer geteilt wird, lässt sich die beim Durchqueren der Firewall verwendete gleichzeitige Sendemethode anwenden
  • Genau deshalb müssen NAT-Traversal-Logik und das eigentliche Kommunikationsprotokoll denselben Socket verwenden
    • Für jeden Socket entsteht auf dem NAT-Gerät ein anderes Mapping
    • Führt man STUN über einen anderen Socket aus als den, der für die eigentliche Kommunikation verwendet wird, erhält man eine nutzlose ip:port
  • STUN allein kann nicht alle NATs bewältigen
    • Bei den meisten Heimroutern kann es funktionieren
    • Bei manchen Unternehmens-NAT-Gateways kann es scheitern
    • Die Annahme, dass 2.2.2.2:4242, wie von STUN gesehen, im gesamten Internet dieselbe Bedeutung hat, trifft nicht immer zu

Einfache und schwierige NATs

  • NAT-Geräte können je nach Ziel unterschiedliche Mappings anlegen oder unabhängig vom Ziel dasselbe Mapping beibehalten
  • RFC 4787 nennt die einfache Form, bei der das Mapping unabhängig vom Ziel erhalten bleibt, Endpoint-Independent Mapping (EIM)
  • Die schwierige Form, bei der sich das Mapping je nach Ziel ändert, heißt Endpoint-Dependent Mapping (EDM)
    • Sie kann sich nur nach der Ziel-IP unterscheiden oder nach Ziel-IP und Port zusammen
    • Aus Sicht von NAT Traversal sind beide Varianten ungünstig
  • Ältere Begriffe wie Full Cone, Restricted Cone, Port-Restricted Cone und Symmetric NAT vermischen NAT-Mapping-Verhalten und Firewall-Verhalten
    • In praktischen Implementierungen ist die Unterscheidung „Symmetric gegenüber dem Rest“ oder EIM gegenüber EDM wichtiger
  • Die gleichzeitige Sendemethode kann verschiedene Arten von Firewalls durchqueren
    • In realen Umgebungen sind Firewalls, die von IP und Port abhängen, bei Weitem am häufigsten
    • Befindet sich jedoch irgendwo auf dem Pfad auch nur ein Hard NAT, reichen STUN und gleichzeitiges Senden allein nicht aus

Relay bei fehlgeschlagener Direktverbindung

  • Eine Direktverbindung kann trotz aller Techniken fehlschlagen
    • Wenn NAT schwierig ist oder ein Netzwerk wie das Guest-Wi-Fi der UC Berkeley ausgehendes UDP außer DNS blockiert, lässt sich das nicht mit NAT-Techniken lösen
  • In diesem Fall können beide Seiten Pakete über ein Relay austauschen, das für beide erreichbar ist
    • Das ist nicht so gut wie eine Direktverbindung, aber wenn das Relay nahe genug am Pfad liegt und genügend Bandbreite hat, muss die Verbindungsqualität nicht stark leiden
    • Selbst wenn die Latenz steigt oder die Bandbreite sinkt, ist das besser als gar keine Verbindung
  • Das klassische Relay-Protokoll ist TURN
    • Der Client authentifiziert sich beim TURN-Server
    • Der TURN-Server weist ein ip:port für das Relay zu
    • Der Peer kommuniziert über dieses ip:port
  • Tailscale entwickelte statt TURN DERP (Detoured Encrypted Routing Protocol)
    • DERP läuft über HTTP
    • Das ist in Netzwerken mit strikten Outbound-Regeln nützlich
    • Es leitet verschlüsselte Payloads anhand des öffentlichen Schlüssels des Ziels weiter
  • DERP übernimmt zwei Rollen
    • Daten-Relay, wenn NAT Traversal fehlschlägt
    • Side Channel zur Unterstützung von NAT Traversal
  • Wenn STUN, gleichzeitiges Senden und Relay implementiert sind, wird geschätzt, dass in über 90 % der Fälle eine Direktverbindung möglich ist und ein Relay stets irgendeine Form von Konnektivität gewährleisten kann

Zusätzliche Techniken für Hard NAT

  • Bei Hard NAT weiß der Peer auf der einfacheren Seite nicht, welchen Port das NAT auf der schwierigen Seite geöffnet hat
    • Per STUN kann man im Allgemeinen davon ausgehen, dass die IP stimmt
    • Unbekannt ist der Port, und es gibt 65.535 mögliche Werte
  • Würde man einfach alle Ports durchprobieren, dauerte das im Worst Case bei 100 Paketen pro Sekunde etwa 10 Minuten und sähe wie ein Portscan aus
  • Mit dem Geburtstagsparadoxon lassen sich die Suchkosten reduzieren
    • Die Hard-NAT-Seite öffnet mit 256 Sockets 256 Ports, und die Easy-NAT-Seite sucht zufällige Zielports ab
    • Unter der Annahme, dass 256 Ports geöffnet sind, ist die Erfolgswahrscheinlichkeit wie folgt
      • 174 zufällige Versuche: 50 %
      • 256 zufällige Versuche: 64 %
      • 1024 zufällige Versuche: 98 %
      • 2048 zufällige Versuche: 99,9 %
    • Bei 100 Ports pro Sekunde kommt die Hälfte innerhalb von 2 Sekunden durch; nach etwa 20 Sekunden hat man weniger als 4 % des gesamten Raums durchsucht und ist trotzdem fast immer erfolgreich
  • Wenn beide Seiten Hard NAT haben, wird es deutlich schwieriger
    • Jetzt muss das Paar {source port, destination port} passen
    • Unter denselben Bedingungen liegt die Erfolgswahrscheinlichkeit nach 20 Sekunden bei 0,01 %
    • Für eine Erfolgswahrscheinlichkeit von 99,9 % müssen beide Seiten jeweils 170.000 Probes senden; bei 100 Paketen pro Sekunde dauert das 28 Minuten
  • Diese Methode kann die Konnektivität in Home-Office-, Home-Cloud- sowie einigen Office-Cloud- oder Cloud-Cloud-Szenarien verbessern
    • Heimrouter tendieren zu einfachem NAT, während Hard NAT eher bei Office-Routern oder Cloud-NAT-Gateways vorkommt

Port-Mapping-Protokolle

  • Es gibt Protokolle, die NAT direkt darum bitten: „Leite diesen WAN-Port an dieses LAN-ip:port weiter“
  • Die drei wichtigsten sind
    • UPnP IGD: ein Protokoll aus den späten 1990er-Jahren, das Technologien wie XML, SOAP und multicast HTTP über UDP verwendet und bei Implementierung und Sicherheit anspruchsvoll ist
    • NAT-PMP: das von Apple entwickelte NAT Port Mapping Protocol, das nur Port Forwarding durchführt und einfach gehalten ist
    • PCP: die Weiterentwicklung von NAT-PMP v2 zum Port Control Protocol
  • Wenn man am lokalen Default Gateway UPnP IGD, NAT-PMP und PCP ausprobiert und eine Antwort erhält, kann man ein öffentliches Port-Mapping anfordern
    • Bei Erfolg erfährt man nicht nur wie bei STUN das öffentliche ip:port, sondern kann das NAT auch dazu bringen, sich für diesen Port großzügiger zu verhalten
    • Jedes Paket, das am gemappten Port ankommt, wird an das interne Gerät weitergeleitet, egal woher es stammt
  • Auf diese Protokolle kann man sich nicht verlassen
    • Sie können auf dem Gerät nicht implementiert sein
    • Sie können standardmäßig deaktiviert sein
    • Sie können aus Policy-Gründen deaktiviert sein
  • Wegen früherer UPnP-Schwachstellen werden sie mitunter aus Policy-Gründen abgeschaltet
    • Manche Geräte deaktivieren über eine einzige „UPnP“-Checkbox UPnP, NAT-PMP und PCP gemeinsam
  • Wenn sie nutzbar sind, verschwindet ein NAT auf dem Datenpfad praktisch, was die Verbindung erleichtert

Double NAT und CGNAT

  • Bei Double NAT, also zwei NAT-Schichten vor einem Gerät, ist das Verhalten der äußeren NAT-Schicht direkt vor dem Internet am wichtigsten
    • Wie bei mehreren Schichten zustandsbehafteter Firewalls sind zusätzliche NAT-Schichten meist unsichtbar
    • Die bestehenden Techniken können unabhängig von der Anzahl der NAT-Schichten funktionieren
  • Was Double NAT deutlich beeinträchtigt, sind Port-Mapping-Protokolle
    • Port Mapping wirkt auf die NAT-Schicht, die dem Client am nächsten ist
    • Der entfernte Peer muss jedoch die äußerste NAT-Schicht passieren
    • Das resultierende ip:port ist eine Adresse des Zwischennetzes, die der entfernte Peer nicht erreichen kann
  • Double NAT ist für die meisten normalen Anwendungen, die kein explizites NAT Traversal durchführen, unsichtbar
    • Es kann jedoch den Multiplayer vieler Spiele verschlechtern und IPv6 entfernen, wodurch eine Verbindungsoption ohne NAT wegfällt
  • CGNAT (Carrier-Grade NAT) ist eine Struktur, bei der der ISP ein weiteres SNAT anwendet, um den Mangel an IPv4-Adressen zu bewältigen
    • Der Heimrouter führt SNAT für Geräte auf eine Zwischen-IP durch
    • Eine zweite NAT-Schicht im ISP-Netzwerk mappt diese Zwischen-IPs auf eine kleinere Zahl öffentlicher IPs
  • Bei CGNAT kann der Nutzer das NAT des ISP nicht neu konfigurieren
    • Früher konnten fortgeschrittene Nutzer Probleme durch Port Forwarding am Heimrouter umgehen, doch bei CGNAT ist dieser Weg versperrt
  • Da CGNAT im Grunde ebenfalls Double NAT ist, funktionieren die meisten bestehenden Techniken weiterhin
    • Port-Mapping-Protokolle sind die Ausnahme und haben hier Grenzen

Hairpinning-Probleme

  • Zwei Peers, die hinter demselben CGNAT, aber hinter unterschiedlichen Heim-NATs sitzen, stoßen auf ein besonderes Problem
    • Der STUN-Server teilt das öffentliche ip:port mit, wie es von außerhalb des Internets gesehen wird
    • Was die beiden Peers tatsächlich benötigen, ist jedoch das ip:port, das im Zwischennetz innerhalb des CGNAT funktioniert
  • Wenn auch nur eines der Heim-NATs Port-Mapping-Protokolle unterstützt, kann die Verbindung einfacher werden
    • Wegen Double NAT ist es in diesem Fall sogar hilfreich, dass das Port-Mapping-Protokoll das ip:port des Zwischennetzes liefert
  • Wenn Port Mapping nicht verfügbar ist, ist Hairpinning erforderlich
    • Beispielsweise sendet Peer A ein Paket an 2.2.2.2:5678 von Peer B, das er per STUN erhalten hat
    • Das CGNAT muss dieses Paket nicht ins äußere Internet schicken, sondern intern zur NAT-Zuordnung von Peer B zurückleiten
  • Viele NATs unterstützen Hairpinning nicht
    • Manche Geräte nehmen an, dass Pakete aus dem internen Netzwerk an Nicht-Internal-IPs immer ins Internet gehen
    • Diese Annahme kann im Routing-Silizium verankert sein und sich ohne neue Hardware möglicherweise nicht beheben lassen
  • Wenn CGNAT im Spiel ist, wird Hairpinning für die Konnektivität wichtig
    • Wenn sowohl Hairpinning als auch Port Mapping fehlschlagen, muss ein Relay verwendet werden

IPv6 und NAT64

  • In einer Welt nur mit IPv6 wäre das NAT-Problem deutlich einfacher
    • Jedes Gerät könnte eine ohne NAT erreichbare Adresse haben
    • Zustandsbehaftete Firewalls blieben jedoch weiterhin bestehen, daher wären Firewall-Traversal und Side-Channels weiterhin nötig
    • Auch Fallback-Relays mit Protokollen wie HTTP bleiben für Netzwerke nützlich, die ausgehendes UDP blockieren
  • IPv6 allein reicht noch nicht aus
    • Die Welt ist größtenteils IPv4 und nur zu etwa 33 % IPv6
    • Die IPv6-Verbreitung ist nicht gleichmäßig; je nach Peer-Kombination kann sie 100 % IPv6 oder 0 % IPv6 betragen
    • Wenn das Ziel eine Verbindung unter allen Umständen ist, muss IPv4+NAT weiterhin behandelt werden
  • Die Koexistenz von IPv6 und IPv4 schafft mit NAT64 einen zusätzlichen Fall
    • NAT44 übersetzt IPv4 in ein anderes IPv4
    • NAT64 übersetzt internes IPv6 in externes IPv4
    • Zusammen mit DNS64 wirkt es für Endgeräte wie ein IPv6-only-Netzwerk, stellt aber Zugriff auf das IPv4-Internet bereit
  • Anwendungen, die nur DNS-Namen verwenden, müssen NAT64 kaum beachten
    • NAT Traversal arbeitet jedoch direkt mit konkreten IPs und Ports und braucht daher eine gesonderte Behandlung
  • Wenn ein Gerät CLAT (Customer-side translator) unterstützt, behandelt das Betriebssystem NAT64 im Hintergrund und lässt es so aussehen, als gäbe es eine direkte IPv4-Verbindung
    • CLAT ist auf Mobilgeräten verbreitet
    • Auf Desktops, Notebooks und Servern ist es selten
  • Ohne CLAT muss NAT64+DNS64 direkt erkannt werden
    • Dazu sendet man eine DNS-Anfrage an ipv4only.arpa.
    • Dieser Name wird nur auf bekannte feste IPv4-Adressen aufgelöst
    • Kommt eine IPv6-Adresse zurück, wurde sie von DNS64 übersetzt, sodass sich das NAT64-Präfix ermitteln lässt
  • Um anschließend mit einer IPv4-Adresse zu kommunizieren, sendet man IPv6-Pakete an {NAT64 prefix + IPv4 address}
    • Führt man STUN über NAT64 aus und findet so das öffentliche ip:port, landet man wieder beim normalen NAT-Traversal-Problem

Kandidatenpfade mit ICE zusammenführen

  • Vorab exakt zu klassifizieren, welche aller Techniken verwendet werden soll, skaliert schlecht
    • Denn Netzwerkingenieure und Implementierer von NAT-Geräten erzeugen sehr unterschiedliche Verhaltensweisen
  • Der Kern von ICE (Interactive Connectivity Establishment) ist ein Algorithmus, der alles Mögliche gleichzeitig ausprobiert und unter den funktionierenden Optionen den besten Pfad auswählt
  • Beim Verbindungsaufbau wird eine Liste von Kandidaten-Endpunkten für den lokalen Socket gesammelt
    • IPv6-ip:ports
    • IPv4-LAN-ip:ports
    • per STUN gefundene IPv4-WAN-ip:ports
    • über einen NAT64-Translator gefundene IPv4-WAN-ip:ports
    • per Port-Mapping-Protokoll zugewiesenes IPv4-WAN-ip:port
    • vom Betreiber bereitgestellte Endpunkte, etwa statisch konfigurierte Portweiterleitungen
  • Danach werden die Kandidatenlisten über einen Side-Channel ausgetauscht und Probe-Pakete an alle vom Gegenüber gelieferten Endpunkte gesendet
    • Probe-Pakete dienen als Pakete zum Öffnen von Firewalls und NATs
    • Gleichzeitig dienen sie als Zustandsprüfung in Form von ping/pong
  • Nach einer gewissen Zeit wird aus den bestätigten funktionierenden Kandidatenpfaden heuristisch der beste Pfad ausgewählt
    • ICE verwendet üblicherweise vorab vergebene Bewertungen wie LAN > WAN > WAN+NAT
    • Tailscale nutzt seit v0.100.0 statt einer hartcodierten Präferenzreihenfolge die Round-Trip-Latenz
  • Tailscale trennt Verbindungen nicht strikt in Probe-Phase und Kommunikationsphase
    • Jede Verbindung startet mit bereits ausgewähltem DERP
    • Nutzer können die Verbindung über den Fallback-Pfad sofort verwenden
    • Die Pfadsuche läuft parallel, und wenn nach einigen Sekunden ein besserer Pfad gefunden wird, erfolgt transparent ein Upgrade

Pfade im Betrieb erhalten und Sicherheit

  • Auf asymmetrische Pfade muss geachtet werden
    • ICE versucht, beide Peers denselben Netzwerkpfad wählen zu lassen, damit der bidirektionale Paketfluss erhalten bleibt
    • Auch wenn man kein Verfahren auf demselben Niveau implementiert, muss auf allen genutzten Pfaden bidirektionaler Traffic vorhanden sein
    • Schon regelmäßige ping/pong-Probes können das aufrechterhalten
  • Der aktuell ausgewählte Pfad kann ausfallen
    • Ein Beispiel ist, dass durch NAT-Wartung der Zustand verloren geht
    • Man kann alle möglichen Pfade weiter prüfen, um warme Fallbacks vorzuhalten
    • Da Downgrades jedoch selten sind, kann es effizienter sein, auf ein Relay als letzte Zuflucht zurückzufallen und danach die Pfadsuche neu zu starten
  • Wichtig ist die Annahme, dass das übergeordnete Protokoll eigene Sicherheit bereitstellt
    • QUIC verwendet TLS-Zertifikate
    • WireGuard verwendet eigene öffentliche Schlüssel
  • Bei dynamischem Pfadwechsel verliert IP-basierte Sicherheit ihre Bedeutung
    • Mindestens End-to-End-Authentifizierung ist erforderlich
  • Wenn die obere Schicht End-to-End-Sicherheit hat, kann ein Angreifer selbst bei spoofbaren ping/pong-Probes im schlimmsten Fall nur versuchen, Traffic über sich selbst umzuleiten
    • Dennoch ist es besser, auch Pakete zur Pfadsuche zu authentifizieren und zu verschlüsseln

Bausteine für robustes NAT Traversal

  • Für robustes NAT Traversal werden folgende Elemente benötigt
    • Ein Protokoll, das auf UDP-Basis erweitert werden kann
    • Ein Socket, auf den das Programm direkt zugreifen kann
    • Ein Side-Channel für die Kommunikation mit Peers
    • Einige STUN-Server
    • Ein optionales, aber dringend empfohlenes Fallback-Relay-Netzwerk
  • Die Durchführungsschritte sind wie folgt
    • Alle ip:ports des Sockets auf direkt verbundenen Interfaces auflisten
    • STUN-Server abfragen, um WAN-ip:ports und den Schwierigkeitsgrad des NAT zu ermitteln
    • Über Port-Mapping-Protokolle zusätzliche WAN-ip:ports finden
    • Falls NAT64 vorhanden ist, es erkennen und auch über diesen Pfad das WAN-ip:port finden
    • Über den Side-Channel alle ip:ports und Verschlüsselungsschlüssel mit dem Peer austauschen
    • Für einen schnellen Verbindungsaufbau kann zuerst über das Fallback-Relay kommuniziert werden
    • Alle ip:ports des Gegenübers proben und bei Bedarf eine auf dem Geburtstagsparadoxon basierende Suche durchführen, um hard NAT zu durchqueren
    • Wenn ein besserer Verbindungspfad als der aktuelle gefunden wird, transparent upgraden
    • Wenn der aktive Pfad ausfällt, bei Bedarf downgraden, um die Konnektivität aufrechtzuerhalten
    • Die gesamte Kommunikation muss Ende-zu-Ende verschlüsselt und authentifiziert sein

1 Kommentare

 
GN⁺ 2025-01-06
Meinungen auf Hacker News
  • Ein hervorragender Beitrag. Es gibt oft die implizite Faustregel, TCP-basiertes Hole Punching nicht zu machen, weil es schwieriger sei als UDP. Tatsächlich scheint die zusätzliche Komplexität im Vergleich zum ohnehin schon komplexen UDP-Ablauf aber nicht groß zu sein.
    Der Artikel räumt ebenfalls ein, dass TCP-NAT-Traversal möglich ist, aber zusätzliche Komplexität mit sich bringt und, wenn man tief einsteigt, sogar Kernel-Änderungen erfordern könnte. Meiner Ansicht nach müsste man jedoch nur den Teil, der die Verbindung mit rohen UDP-Paketen startet, durch TCP-SYN-Pakete und Unterstützung für simultaneous open ersetzen.
    Gerade wenn man bedenkt, dass es Netzwerke wie das Gast-Wi‑Fi der UC Berkeley gibt, die sämtlichen ausgehenden UDP-Traffic außer DNS blockieren, ist es schade, TCP Hole Punching nur mit der Begründung „schwieriger als UDP“ abzutun. Ich halte es für nahezu ähnlich realisierbar, mit begrenzter zusätzlicher Komplexität.
    https://ttcplinux.sourceforge.net/documents/one/tcpstate/tcp...

    • Da es zustandsbehaftete Firewalls gibt und die meisten NAT-Filter nicht EIF, sondern EDF sind, braucht man auch bei UDP simultaneous open, also gleichzeitiges Senden.
      Daher ist die zusätzliche Komplexität für simultaneous open mit TCP ziemlich gering. Die eigentliche Schwierigkeit besteht darin, das öffentliche Mapping zu übermitteln und das „gleichzeitige“ Punching/Öffnen zu koordinieren; das ist in der Regel auch bei UDP nötig.
      Ein zusätzlicher Komplexitätspunkt bei TCP ist, dass man statt gefälschter TCP-SYN-Pakete echte connect()-Aufrufe machen muss. Manche Firewalls prüfen nämlich Sequenznummern.
    • Wirklich guter Punkt. Ich habe TCP Hole Punching selbst implementiert und inzwischen eine ziemlich brauchbare Umsetzung. Der große Vorteil von TCP ist, dass man nach dem Öffnen des Lochs nicht wieder eine arme Variante von TCP auf UDP aufsetzen muss.
      Allerdings kann TCP Hole Punching deutlich stärker wie ein SYN flood aussehen als UDP-Pakete, sodass die Erfolgsquote in manchen Netzwerken niedriger sein kann. In der Praxis habe ich bisher aber noch nicht viel Filtering gesehen.
      TCP Hole Punching ist ziemlich interessant. Ich habe es so implementiert, dass über mehrere NTP-Messungen berechnet wird, wie weit die Systemuhr von NTP abweicht, also ein „Uhren-Offset“, und der Initiator dann einen zukünftigen Treffzeitpunkt auf NTP-Basis festlegt. Das ist genauer, als man erwarten würde, und TCP Hole Punching funktioniert sogar zwischen Sockets auf derselben Schnittstelle.
      Der Grund, warum ich diesen seltsamen lokalen Punching-Modus unterstützt habe, war: Wenn Punching innerhalb eines Hosts mit dieser Effizienz klappt, ist die Chance hoch, dass es auch im LAN und im Internet schnell genug ist. Der Code ist in Python, und der erste Versuch war ziemlich ernüchternd. TCP Hole Punching ist timing-empfindlich, und mit althergebrachter direkter Socket-Verwaltung in Python, Threading und einer wackligen Event Loop auf Basis von C-Socket-Erfahrung kam es zu Fehlschlägen.
      Damit der Code funktionierte, musste ich die Priorität des Python-Prozesses erhöhen, damit andere Prozesse zwischen den Punching-Versuchen keine Verzögerungen verursachen konnten. Bei einer ineffizienten Implementierung ist es so zeitkritisch. Die aktuelle Implementierung nutzt einen Prozess-Pool, bei dem jeder Prozess seine eigene Event Loop hat, erstellt eine zeitlich verteilte Aufgabenliste und lässt jede Aufgabe denselben Socket wiederverwenden, um Verbindungen zu öffnen. Nach Tests auf den wichtigsten Betriebssystemen kam ich zu dem Schluss, dass dieser Ansatz in Python am besten ist.
      Ich stimme zu, dass TCP- und UDP-Hole-Punching ähnlich schwierig sind. Bei beiden ist der schwierigste Teil die Phase der NAT-Vorhersage. Code zum Umgehen symmetrischer NATs habe ich noch nicht eingesetzt, aber ich sehe allmählich Wege, ihn zu integrieren oder als neues Plugin umzusetzen.
    • Mir ist noch ein weiterer Nachteil eingefallen, durch den TCP-Punching gegenüber UDP im Nachteil ist: Bei TCP muss der Router den Verbindungszustand speichern.
      Die Zustandstabellen von Routern sind sehr klein, und manche Punching-Verfahren sind ziemlich aggressiv. Wenn man etwa, wie bei Algorithmen zum Umgehen symmetrischer NATs, Hunderte TCP-Verbindungen öffnet, könnte man den Router in einen Denial-of-Service-Zustand versetzen.
      UDP könnte dank Optimierungen beim State Management weniger wahrscheinlich dazu führen, dass der gesamte Router wegen des Punchings ausfällt. Das ist allerdings Spekulation.
  • Die Wirkung ist auf interessante Weise gut, aber wenn jemand vorschlägt, so etwas in ein produktives Unternehmensnetz zu bringen, wird mir irgendwie unwohl.
    Es wirkt riskant, weil es traditionelle NATs und Firewalls umgeht und stattdessen nur auf eine Software-ACL setzt. Wenn es zum Beispiel in einer verwaisten VM in einer AWS-Testumgebung Tailscale gibt und ein Angreifer dort Zugriff bekommt, scheint ein Pfad zu entstehen, bei dem bis zum Laptop im internen Unternehmensnetz nur noch der Tailscale-ACL-Code im Userspace nach dem Kernel über Zulassen oder Blockieren entscheidet.
    Ich weiß nicht, ob man überhaupt merken würde, wenn jemand Unbefugtes bis zu diesem Punkt vorgedrungen ist.

    • Genau deshalb wiederholen viele ständig: NAT ist kein Sicherheitsmechanismus.
      NAT und die meisten damit verbundenen zustandsbehafteten Filter lassen sich sehr leicht durchdringen. Ich habe so etwas in echten produktiven Unternehmensumgebungen als verkäufliches Produkt implementiert; das ist keine Magie, sondern Praktikern gut bekannte Technik.
      Wenn man echtes Paketfiltering, also eine Firewall, will, muss man unabhängig von NAT eine Firewall-Instanz mit passenden Regeln platzieren. Allerdings hilft auch das vor allem dabei, die Menge des Traffics zu reduzieren; der tatsächliche Sicherheitsgewinn durch die Firewall selbst ist heute gering. Die meisten Angriffe kommen nämlich über höhere Schichten wie HTTP/HTTPS oder POP/IMAP herein.
    • Fairerweise muss man sagen: Der Grund, warum alle NAT fälschlich für einen Sicherheitsmechanismus halten, ist, dass NAT traditionell zusammen mit einer zustandsbehafteten Firewall ausgerollt wurde.
      In Wirklichkeit erledigt die zustandsbehaftete Firewall den Großteil der Arbeit, während NAT den Ruhm einstreicht. Tailscale schafft die Firewall nicht ab, sondern bietet eine korrekte, ACL-basierte und deutlich umfassendere Konfiguration.
      Allerdings räume ich ein, dass Tailscales ACL-Werkzeuge noch viel Raum für Verbesserungen haben.
    • Networking ist seit Langem eine Art Sondermülldeponie für Sicherheit und Fehlkonfigurationen. Dazu kommt inzwischen auch noch das moderne hostbasierte Networking-Modell für Container.
      Dadurch hat sich auch der Windows-Netzwerkstack deutlich verändert und ist komplexer geworden. Seit WireGuard in Linux gelandet ist, hat praktisch jeder irgendwo ein VPN, das sich mit einem VPS verbindet. Weil man nicht weiß, was man nicht weiß, ist die tatsächliche Lage wahrscheinlich schlimmer, als man denkt.
    • Das hier ist für NAT-Traversal, also für Geräte, die gebaut wurden, um die Knappheit von IPv4-Adressen zu umgehen.
      Eine Firewall ist ein anderes Konzept. Wenn man Konnektivität und Sicherheit dennoch zusammen betrachtet, ist es traurig und beunruhigend, dass Internetsicherheit sich immer darauf gestützt hat, Pakete anhand des Zielports zu blockieren.
      Man tut das Einfache statt des Richtigen, und trotzdem heißt es dann „professionelle Lösung“.
    • VoIP funktioniert seit jeher so, und es gibt viel standardisierte öffentliche Infrastruktur, um das zu erleichtern, etwa ICE, TURN.
      Trotzdem muss intern zunächst irgendetwas nach außen sprechen, daher sollte eine echte Firewall sowohl ausgehende als auch eingehende Verbindungen per Allowlist verwalten.
      Anders gesagt: Wenn man sich auf Perimetersicherheit verlässt, ist es nur eine Frage der Zeit, bis jemand herausfindet, was in der eigenen Organisation die „Warnweste“ ist.
  • Für Geräte, die bereits auf Anwendungsebene verschlüsseln, wäre eine Tailscale-ähnliche Alternative ohne Verbindungsverschlüsselung schön. So wie fast das gesamte Internet funktioniert, muss man nicht immer bis in die unteren Schichten hinein verschlüsseln.
    Bei stromsparenden Geräten, etwa IoT-Geräten, die einen Tailscale-ähnlichen Tunnel betreiben, sind die Rechenkosten besonders hoch.
    GRE-Tunnel gibt es und sie werden tatsächlich häufig genutzt, aber UDP Hole Punching wird damit nicht abgedeckt, daher braucht man eine Hub-and-Spoke-Struktur. Mit GRE, also ip fou, kann man kein Mesh zwischen Peers bauen.
    Ich frage mich, ob es eine Bibliothek gibt, die nach einem kryptografischen Handshake zur Identitätsprüfung UDP Hole Punching und unverschlüsselte GRE-Tunnel bereitstellt.

    • Der etablierte Standard in diesem Bereich ist ICE (Interactive Connectivity Establishment), worauf WebRTC aufbaut. Es gibt gute Bibliotheken, die es oder Teile davon implementieren.
      Wenn man etwas für allgemeinere Konnektivität sucht, könnte libp2p dem nahekommen, was gewünscht ist.
      https://datatracker.ietf.org/doc/html/rfc8445
      https://github.com/pion/webrtc
      https://github.com/algesten/str0m
      https://libp2p.io
    • Nicht UDP, aber ich habe hier TCP Hole Punching und andere wichtige NAT-Traversal-Methoden implementiert: https://github.com/robertsdotpm/p2pd
      Es ist in Python geschrieben. Allerdings setzt es, wie der meiste Netzwerkcode, nicht die Nutzung des Standard-Interfaces voraus. Ich wollte es ermöglichen, Dienste auf beliebigen gewünschten Interfaces laufen zu lassen, um vielfältigere und nützlichere Dinge bauen zu können.
      Es basiert größtenteils auf Modulen der Standardbibliothek. C-Erweiterungen mag ich nicht, weil sie plattformübergreifende Pakete häufig kaputtmachen.
    • Da in VoIP TURN, STUN und ICE für Hole Punching genutzt werden, kann man Bibliotheken aus diesem Umfeld wiederverwenden.
    • Man könnte auch versuchen, Teredo wiederzubeleben.
  • Wenn ich lese, dass Peers die vom Gegenüber verwendete ip:port im Voraus kennen müssen und dass dafür ein Koordinationsserver gebaut wurde, denke ich, dass SIP seinem Namen besser hätte gerecht werden sollen.
    SIP steht für Session Initiation Protocol, also ein Name, der nahelegt, dass damit auch beliebige Sessions wie etwa VPNs gestartet werden können sollten. In der Praxis ist es aber ein so kompliziertes Durcheinander, dass es den Aufwand kaum noch wert ist. Ursprünglich war es wohl als Kommunikations-Nebenkanal gedacht, um P2P-RTP-Streams aufzubauen.

    • SIP macht so viele Dinge, dass es fast beängstigend ist, alles gleichzeitig im Kopf zu behalten.
      Es ist wie HTTP, aber zustandsbehaftet, bidirektional, föderiert und funktioniert auch über UDP.
      Wenn man sieht, wie viel baresip nur für SIP implementiert, einschließlich TLS over UDP, ist das enorm. Und es ist nicht einmal aufgebläht; diese Funktionen werden tatsächlich gebraucht.
  • Das ist ein Beitrag aus dem Jahr 2020. Frühere Diskussionen dazu waren:
    2022: https://news.ycombinator.com/item?id=30707711
    2020: https://news.ycombinator.com/item?id=24241105

  • Genau diesen Artikel habe ich Leuten geschickt, wenn ich NAT-Traversal erklären wollte.
    Wenn wir P2P-Apps bauen, werden wir uns womöglich weiter auf diese Methode stützen müssen. IPv6 hat nicht genug Momentum bekommen, und NAT sowie SNI-Routing lösen für die meisten Menschen die meisten Probleme.
    Aus Sicht der ISP gibt es auch wenig Anreiz, an dieser Situation etwas zu ändern.

  • Meiner Ansicht nach gehört er zu den ausführlichsten Artikeln im ganzen Internet über NAT-Traversal. Allerdings fehlen Informationen zum Delta-Verhalten.
    Das ist nichts Kompliziertes: Es bedeutet, dass manche NATs bei der Zuweisung aufeinanderfolgender externer Ports ein beobachtbares Muster haben. Das häufigste Muster ist, den Quell-Port beizubehalten; es kann aber auch Muster geben, bei denen gegenüber dem vorherigen Mapping jeweils um 1 erhöht wird.
    Theoretisch ist das ein sehr guter Artikel, aber ich frage mich, in welchem Maß Software Engineers ihn praktisch nutzen können. Er erklärt vieles, ist aber möglicherweise nicht detailliert genug, um daraus einen Algorithmus zu schreiben. Ich weiß zum Beispiel nicht, ob man allein anhand dieses Artikels einen Algorithmus zum Testen des NAT-Typs schreiben oder den eigenen Hole-Punching-Code anpassen könnte.
    Ich habe persönlich auch Papers gesehen, bei denen eine einfache Tabelle nützlicher war als ein so langer Artikel. Trotzdem kann er ein guter Ausgangspunkt sein.
    Der letzte Abschnitt des Artikels ist besonders wichtig. Es besteht die Möglichkeit, symmetrische NATs, wie sie in mobilen Systemen verwendet werden, zu umgehen. Aktuelle Forschung zu NAT-Traversal nutzt ähnliche Techniken und behauptet Erfolgsraten von nahezu 100 %.

  • Ein interessanter Artikel, der Erinnerungen an früher weckt. 2010 habe ich ein oblivious P2P-Mesh-Netzwerk gebaut, das solche Methoden nutzte.
    Damals kümmerten sich die Leute nicht so sehr um Sicherheit, wie wir dachten, und auch heute tun sie es immer noch nicht ausreichend. Es gibt mehr Geräte, und ihr Wert ist gestiegen, aber sie sind weiterhin ziemlich unsicher.
    Hardware-Root-of-Trust, eine sichere Vertrauenskette für Authentifizierung/Autorisierung und wirklich sichere Endpunkte mit minimalen temporären Rechten sind nach wie vor schwierig; in Heimnetzen, Unternehmensnetzen und großen Produktionsrechenzentrumsnetzen geht das Theater um Netzwerk-Perimetersicherheit weiter.
    Der einzige Grund, warum diese Dinge nicht als die wichtigste Grundursache von Sicherheitsverletzungen erscheinen, ist, dass es immer noch jede Menge einfachere Angriffswege gibt.

  • Etwas am Thema vorbei, aber vor ein paar Wochen habe ich mich ein wenig eingelesen, obwohl ich von diesem Bereich überhaupt keine Ahnung hatte.
    Mein Eindruck war, dass IPv6 all das überflüssig macht und NAT-Traversal nicht mehr nötig wäre. Daher frage ich mich, warum IPv6 nicht breiter genutzt wird und wie man im Heimnetzwerk und mit Tailscale VPN damit anfangen sollte.

    • Ich weiß nicht, wie stark es als einer der Gründe für die geringere Beliebtheit von IPv6 ins Gewicht fällt, aber dass es für Menschen schwer zu benutzen ist, war schon immer eine Herausforderung.
      Es fehlen auch geschäftliche Anreize.
  • Schon die Tatsache, dass statt IPv6 so etwas entstanden ist, zeigt sehr gut die Macht von hinreichend brauchbaren Hacks.