1 points par GN⁺ 2025-01-06 | 1 commentaires | Partager sur WhatsApp
  • Le NAT Traversal est la technologie de base qui permet à Tailscale d’établir des tunnels WireGuard sans hub central, en faisant en sorte que des appareils derrière des NAT et des pare-feu échangent directement des paquets UDP
  • Le prérequis essentiel est que le protocole soit basé sur UDP, et que les paquets destinés à la découverte NAT comme ceux de la communication réelle puissent être envoyés et reçus sur le même socket réseau
  • Un pare-feu avec état n’autorise que les réponses correspondant à des paquets UDP sortants envoyés auparavant ; si les pairs connaissent mutuellement leurs ip:port et envoient des paquets presque simultanément, ils peuvent ouvrir l’état du pare-feu
  • Comme le NAT modifie l’IP et le port source, des techniques complémentaires sont nécessaires : STUN, mapping de ports, gestion de NAT64, recherche de ports fondée sur le paradoxe des anniversaires, relais, etc.
  • ICE teste en parallèle les chemins candidats possibles et choisit le meilleur ; Tailscale se connecte immédiatement via un relais DERP, puis bascule de façon transparente si un meilleur chemin direct est trouvé

Conditions de base du NAT Traversal

  • L’objectif est de créer un flux bidirectionnel de paquets UDP entre deux appareils, sur lequel peuvent fonctionner des protocoles comme WireGuard, QUIC ou WebRTC
  • Pour l’implémenter directement, deux conditions sont importantes
    • Le protocole doit être basé sur UDP
      • C’est aussi possible avec TCP, mais la complexité est plus élevée et, selon l’implémentation, des modifications du noyau peuvent être nécessaires
      • Si une connexion orientée flux est nécessaire, on peut envisager QUIC, qui fonctionne au-dessus d’UDP
    • Le programme doit contrôler directement le socket réseau qui envoie et reçoit les paquets
      • Le NAT Traversal devant envoyer des paquets supplémentaires en dehors du protocole principal, il est difficile de l’ajouter simplement à une bibliothèque réseau existante
      • Une architecture où la logique de NAT Traversal et le protocole principal partagent le même socket et fonctionnent en parallèle est utile
  • Si l’accès direct au socket est difficile, on peut placer un proxy local
    • Le protocole d’origine communique avec le proxy
    • Le proxy se charge du NAT Traversal et du relais des paquets vers le pair

Traverser un pare-feu avec état

  • Un pare-feu avec état mémorise les paquets qu’il a vus auparavant pour décider s’il autorise de nouveaux paquets
    • On en trouve sous des formes comme Windows Defender Firewall, ufw sur Ubuntu, pf sur BSD, pf sur macOS ou AWS Security Groups
    • Une configuration courante consiste à autoriser toutes les connexions outbound et à bloquer toutes les connexions inbound
  • Avec UDP, la règle est simple
    • Si le pare-feu a vu un paquet UDP sortant de 2.2.2.2:1234 vers 5.5.5.5:5678, il autorise en sens inverse les paquets entrants de 5.5.5.5:5678 vers 2.2.2.2:1234
    • Certains pare-feu plus permissifs peuvent autoriser le trafic entrant depuis n’importe où vers un port local qui a déjà communiqué une fois, mais cela devient de plus en plus rare
  • Dans une architecture serveur-client, le problème est limité, car l’appareil derrière le pare-feu peut initier la connexion
    • Dans un VPN, cela donne une architecture hub-and-spoke où un hub sans pare-feu se connecte à des spokes derrière des pare-feu
  • Pour que deux clients communiquent directement, les pare-feu des deux côtés peuvent se bloquer mutuellement
    • Les deux doivent d’abord envoyer un paquet pour pouvoir recevoir une réponse, mais l’autre côté est dans la même situation
    • Demander à l’utilisateur de configurer manuellement l’ouverture de ports est peu pratique et peu scalable dans un réseau maillé comme Tailscale
    • Il existe aussi de nombreux pare-feu que l’utilisateur ne peut pas contrôler, comme les routeurs d’aéroport ou de café
  • La clé de la solution est que les règles de pare-feu UDP ne vérifient pas la réalité de la relation de réponse : elles ne regardent que la combinaison IP et port
    • Si les deux pairs connaissent à l’avance le ip:port de l’autre et envoient simultanément des paquets UDP, les premiers paquets peuvent être bloqués, mais l’état du pare-feu s’ouvre
    • Les paquets envoyés ensuite par l’autre pair ressemblent à des réponses et passent
  • Cette méthode nécessite un canal latéral
    • Les deux extrémités doivent tenter de communiquer presque simultanément
    • Un chemin de communication avec quelques secondes de latence et capable de transporter seulement quelques milliers d’octets suffit
    • WebRTC exige un canal de signalisation, et Tailscale utilise son coordination server et ses serveurs DERP comme canaux latéraux
  • L’état du pare-feu n’est pas permanent
    • Une valeur courante de timeout pour une session UDP est de 30 secondes
    • Pour maintenir la connexion, il faut envoyer périodiquement des paquets, ou relancer la connexion out-of-band lorsque c’est nécessaire
  • Même avec plusieurs couches de pare-feu avec état, si l’outbound est autorisé, la méthode d’envoi simultané peut les traverser

Comment le NAT complique le problème

  • Un NAT (Network Address Translator) agit comme un pare-feu avec état tout en modifiant l’adresse IP ou le port des paquets
  • Dans le NAT Traversal, le problème vient surtout du Source NAT (SNAT)
    • Le SNAT permet à plusieurs appareils de partager un nombre plus faible d’adresses IP, souvent une seule adresse IPv4 publique
    • Le DNAT existe aussi, mais il a peu de rapport avec le problème de NAT Traversal traité ici
  • Par exemple, si un ordinateur portable envoie un paquet UDP de 192.168.0.20:1234 vers un serveur Internet 7.7.7.7:5678, le routeur domestique choisit un port libre sur son IP publique, par exemple 2.2.2.2:4242
    • Le routeur crée un mapping NAT indiquant que 192.168.0.20:1234 et 2.2.2.2:4242 correspondent
    • Les paquets sortants suivants sont modifiés pour sembler provenir de 2.2.2.2:4242
    • Les réponses entrantes sont à nouveau modifiées vers 192.168.0.20:1234
  • Le même principe s’applique dans les réseaux d’entreprise
    • La différence est que la couche NAT peut être constituée de plusieurs équipements pour des raisons de haute disponibilité ou de capacité, et qu’elle peut disposer de plusieurs IP publiques

STUN et découverte des mappings NAT

  • Un pair derrière un NAT ne peut pas connaître son ip:port public vu par l’autre côté, et un mapping NAT n’est généralement créé que lorsqu’un trafic sort vers Internet
  • STUN est un protocole qui permet à un client derrière un NAT de savoir comment il apparaît depuis Internet
    • Le client demande au serveur STUN : « à quoi ressemble mon endpoint pour toi ? »
    • Le serveur STUN répond avec le ip:port public d’où provient le paquet UDP
  • En partageant avec le pair le ip:port public indiqué par STUN, on peut appliquer la technique d’envoi simultané utilisée pour traverser les pare-feu
  • C’est aussi pour cette raison que la logique de NAT Traversal et le protocole de communication réel doivent utiliser le même socket
    • Chaque socket obtient un mapping différent sur l’équipement NAT
    • Si STUN est exécuté sur un socket différent de celui utilisé pour la communication réelle, on obtient un ip:port inutile
  • STUN seul ne permet pas de résoudre tous les cas de NAT
    • Il peut fonctionner avec la plupart des routeurs domestiques
    • Il peut échouer avec certaines passerelles NAT d’entreprise
    • L’hypothèse selon laquelle le 2.2.2.2:4242 visible via STUN a la même signification sur tout Internet n’est pas toujours vraie

NAT faciles et NAT difficiles

  • Un équipement NAT peut créer des mappings différents selon la destination, ou conserver le même mapping indépendamment de la destination
  • La RFC 4787 appelle Endpoint-Independent Mapping (EIM) la forme simple où le mapping est conservé indépendamment de la destination
  • La forme plus difficile, où le mapping varie selon la destination, est appelée Endpoint-Dependent Mapping (EDM)
    • Elle peut varier uniquement selon l’IP de destination, ou selon l’IP et le port de destination ensemble
    • Du point de vue du NAT Traversal, les deux cas sont défavorables
  • Les anciens termes Full Cone, Restricted Cone, Port-Restricted Cone et Symmetric NAT mélangent le comportement de mapping du NAT et le comportement du pare-feu
    • Dans une implémentation pratique, la distinction « Symmetric contre le reste » ou EIM contre EDM est plus importante
  • La technique d’envoi simultané peut traverser plusieurs types de pare-feu
    • Dans les environnements réels, les pare-feu dépendant de l’IP et du port sont de loin les plus nombreux
    • Mais s’il y a ne serait-ce qu’un hard NAT quelque part sur le chemin, STUN et l’envoi simultané seuls posent problème

Relais en cas d’échec de la connexion directe

  • Une connexion directe peut échouer même en utilisant toutes les techniques
    • Quand le NAT est difficile, ou sur des réseaux qui bloquent l’UDP sortant à l’exception du DNS, comme le Wi-Fi invité de l’UC Berkeley, les techniques de NAT ne peuvent pas résoudre le problème
  • Dans ce cas, les deux côtés peuvent échanger des paquets via un relais accessible par les deux
    • Ce n’est pas aussi bon qu’une connexion directe, mais si le relais est suffisamment proche du chemin et dispose d’une bande passante suffisante, la dégradation de la qualité de connexion peut rester limitée
    • Même si la latence augmente ou que la bande passante diminue, c’est mieux que de ne pas avoir de connexion du tout
  • Le protocole de relais traditionnel est TURN
    • Le client s’authentifie auprès du serveur TURN
    • Le serveur TURN alloue un ip:port pour le relais
    • Le pair communique avec cet ip:port
  • Tailscale a créé DERP (Detoured Encrypted Routing Protocol) au lieu d’utiliser TURN
    • DERP fonctionne au-dessus de HTTP
    • Il est utile sur les réseaux ayant des règles sortantes strictes
    • Il relaie des charges utiles chiffrées en se basant sur la clé publique de la destination
  • DERP joue deux rôles
    • Relais de données lorsque le NAT Traversal échoue
    • Canal latéral aidant au NAT Traversal
  • Avec STUN, l’envoi simultané et le relais implémentés, on estime que plus de 90 % des connexions peuvent être directes, et que le relais peut toujours garantir une forme de connectivité

Techniques supplémentaires pour les NAT hard

  • Avec un NAT hard, le pair du côté facile ne sait pas quel port le NAT du côté difficile a ouvert
    • Avec STUN, on peut généralement supposer que l’IP est correcte
    • Ce qui est inconnu, c’est le port, avec 65 535 valeurs possibles
  • Balayer simplement tous les ports prendrait environ 10 minutes dans le pire des cas à 100 paquets/seconde, et ressemblerait à un scan de ports
  • Le paradoxe des anniversaires permet de réduire le coût de recherche
    • Le côté en NAT hard ouvre 256 ports avec 256 sockets, et le côté en NAT facile explore des ports de destination aléatoires
    • En supposant que 256 ports sont ouverts, la probabilité de succès est la suivante
      • 174 essais aléatoires : 50 %
      • 256 essais aléatoires : 64 %
      • 1024 essais aléatoires : 98 %
      • 2048 essais aléatoires : 99,9 %
    • À 100 ports/seconde, la moitié passe en moins de 2 secondes, et vers 20 secondes on réussit presque toujours en n’ayant exploré que moins de 4 % de l’espace total
  • Si les deux côtés sont derrière un NAT hard, c’est beaucoup plus difficile
    • Il faut maintenant que la paire {source port, destination port} corresponde
    • Dans les mêmes conditions, la probabilité de succès après 20 secondes est de 0,01 %
    • Pour atteindre 99,9 % de réussite, les deux côtés doivent chacun envoyer 170 000 probes, ce qui prend 28 minutes à 100 paquets/seconde
  • Cette méthode peut améliorer la connectivité dans des scénarios domicile-bureau, domicile-cloud, et certains scénarios bureau-cloud ou cloud-cloud
    • Les routeurs domestiques ont tendance à être des NAT faciles, tandis que les NAT hard sont souvent des routeurs d’entreprise ou des passerelles NAT cloud

Protocoles de mappage de ports

  • Il existe des protocoles permettant de demander directement au NAT : « transfère ce port WAN vers cet ip:port LAN »
  • Les trois principaux sont les suivants
    • UPnP IGD : protocole apparu à la fin des années 1990, utilisant des technologies comme XML, SOAP et du HTTP multicast sur UDP, avec une implémentation et une sécurité délicates
    • NAT-PMP : NAT Port Mapping Protocol créé par Apple, simple et limité au port forwarding
    • PCP : évolution de NAT-PMP v2 devenue Port Control Protocol
  • En essayant UPnP IGD, NAT-PMP et PCP sur la passerelle locale par défaut, on peut demander un mappage de port public s’il y a une réponse
    • En cas de succès, non seulement on peut connaître l’ip:port public comme avec STUN, mais on peut aussi amener le NAT à se comporter de façon plus permissive pour ce port
    • Tout paquet arrivant sur le port mappé, quelle que soit son origine, est transmis à l’appareil interne
  • On ne peut pas compter sur ces protocoles
    • Ils peuvent ne pas être implémentés dans l’équipement
    • Ils peuvent être désactivés par défaut
    • Ils peuvent être désactivés par politique
  • D’anciennes vulnérabilités d’UPnP font qu’il est parfois désactivé par politique
    • Certains équipements désactivent aussi ensemble UPnP, NAT-PMP et PCP avec une seule case à cocher « UPnP »
  • Lorsqu’ils sont utilisables, un NAT disparaît quasiment du chemin de données, ce qui facilite la connexion

Double NAT et CGNAT

  • Dans un double NAT, où deux couches de NAT se trouvent devant un appareil, c’est le NAT le plus externe, juste avant Internet, dont le comportement est le plus important
    • Comme avec plusieurs couches de pare-feu à état, les couches NAT supplémentaires sont généralement invisibles
    • Les techniques existantes peuvent fonctionner quel que soit le nombre de couches NAT
  • Ce que le double NAT casse vraiment, ce sont les protocoles de mappage de ports
    • Le mappage de ports agit sur la couche NAT la plus proche du client
    • Mais ce que le pair distant doit traverser, c’est le NAT le plus externe
    • L’ip:port obtenu est donc une adresse du réseau intermédiaire, que le pair distant ne peut pas atteindre
  • Le double NAT est invisible pour la plupart des applications ordinaires qui ne font pas explicitement du NAT Traversal
    • Il peut toutefois dégrader le multijoueur de nombreux jeux et supprimer IPv6, réduisant les possibilités de connexion sans NAT
  • Le CGNAT (Carrier-Grade NAT) est une architecture dans laquelle le FAI applique un SNAT supplémentaire pour faire face à la pénurie d’adresses IPv4
    • Le routeur domestique SNAT les appareils vers une IP intermédiaire
    • Une deuxième couche de NAT dans le réseau du FAI mappe ces IP intermédiaires vers un plus petit nombre d’IP publiques
  • Avec CGNAT, l’utilisateur ne peut pas réinitialiser le NAT du FAI
    • Auparavant, les utilisateurs avancés pouvaient contourner le problème avec le port forwarding sur leur routeur domestique, mais avec CGNAT cette méthode est bloquée
  • Comme CGNAT est fondamentalement un double NAT, la plupart des techniques existantes continuent de fonctionner
    • Les protocoles de mappage de ports constituent l’exception, avec des limites particulières

Problème de hairpinning

  • Deux pairs situés derrière le même CGNAT, mais derrière des NAT domestiques différents, rencontrent un problème particulier
    • Le serveur STUN indique l’ip:port public vu depuis l’extérieur d’Internet
    • Mais ce dont les deux pairs ont réellement besoin, c’est de l’ip:port qui fonctionne dans le réseau intermédiaire interne au CGNAT
  • Si au moins l’un des NAT domestiques prend en charge un protocole de mappage de ports, la connexion peut devenir plus facile
    • À cause du double NAT, le fait que le protocole de mappage de ports indique l’ip:port du réseau intermédiaire devient au contraire utile
  • Si le mappage de ports n’est pas disponible, le hairpinning est nécessaire
    • Par exemple, le peer A envoie un paquet vers 2.2.2.2:5678, l’adresse de peer B obtenue via STUN
    • Le CGNAT doit renvoyer ce paquet en interne vers le mappage NAT de peer B, au lieu de l’envoyer vers l’Internet extérieur
  • De nombreux NAT ne prennent pas en charge le hairpinning
    • Certains équipements supposent que les paquets allant du réseau interne vers une IP non interne sortent toujours vers Internet
    • Cette hypothèse peut être inscrite dans le silicium de routage et impossible à corriger sans nouveau matériel
  • Quand CGNAT intervient, le hairpinning devient important pour la connectivité
    • Si le hairpinning et le mappage de ports échouent tous les deux, il faut utiliser un relais

IPv6 et NAT64

  • Dans un monde uniquement IPv6, le problème du NAT serait beaucoup plus simple
    • Tous les appareils pourraient avoir une adresse joignable sans NAT
    • Mais les pare-feu à état resteraient présents, donc la traversée de pare-feu et les canaux latéraux resteraient nécessaires
    • Un relais de fallback utilisant un protocole comme HTTP resterait aussi utile pour les réseaux qui bloquent l’UDP sortant
  • IPv6 seul ne suffit pas encore
    • Le monde est encore majoritairement IPv4, avec environ 33 % d’IPv6
    • Le déploiement d’IPv6 n’est pas uniforme : selon la combinaison de pairs, on peut avoir 100 % d’IPv6 ou 0 % d’IPv6
    • Si l’objectif est de se connecter à coup sûr, il faut continuer à gérer IPv4+NAT
  • La coexistence d’IPv6 et d’IPv4 crée un cas supplémentaire appelé NAT64
    • NAT44 traduit de l’IPv4 vers une autre IPv4
    • NAT64 traduit de l’IPv6 interne vers de l’IPv4 externe
    • Utilisé avec DNS64, il fournit un accès à l’Internet IPv4 tout en donnant au terminal l’impression d’être sur un réseau IPv6-only
  • Les applications qui n’utilisent que des noms DNS n’ont presque pas à se soucier de NAT64
    • Mais le NAT Traversal manipule directement des adresses IP et des ports précis, il nécessite donc un traitement spécifique
  • Si l’appareil prend en charge CLAT (Customer-side translator), le système d’exploitation fait comme s’il existait une connexion IPv4 directe et gère NAT64 en arrière-plan
    • CLAT est courant sur les appareils mobiles
    • Il est rare sur les postes de bureau, les ordinateurs portables et les serveurs
  • Sans CLAT, il faut détecter directement NAT64+DNS64
    • Envoyer une requête DNS à ipv4only.arpa.
    • Ce nom ne se résout que vers des adresses IPv4 fixes connues
    • Si une adresse IPv6 est renvoyée, elle a été traduite par DNS64, ce qui permet de déterminer le préfixe NAT64
  • Ensuite, pour communiquer avec une adresse IPv4, il suffit d’envoyer des paquets IPv6 vers {préfixe NAT64 + adresse IPv4}
    • Si l’on effectue STUN via NAT64 pour trouver l’ip:port public, on revient ensuite au problème classique du NAT Traversal

Regrouper les chemins candidats avec ICE

  • Chercher à classer précisément à l’avance la technique à utiliser parmi toutes celles disponibles passe mal à l’échelle
    • Parce que les ingénieurs réseau et les implémenteurs d’équipements NAT créent des comportements très variés
  • Le principe central d’ICE (Interactive Connectivity Establishment) est d’essayer simultanément tout ce qui est possible, puis de choisir le meilleur chemin parmi ceux qui fonctionnent
  • Au démarrage de la communication, on collecte la liste des endpoints candidats pour le socket local
    • ip:ports IPv6
    • ip:ports LAN IPv4
    • ip:ports WAN IPv4 découverts avec STUN
    • ip:ports WAN IPv4 découverts via un traducteur NAT64
    • ip:port WAN IPv4 attribué par un protocole de mappage de ports
    • Endpoint fourni par l’opérateur, comme un port forwarding configuré statiquement
  • On échange ensuite la liste des candidats via un canal latéral et on envoie des paquets de probe à tous les endpoints fournis par l’autre pair
    • Les paquets de probe servent à ouvrir les pare-feu et les NAT
    • Ils servent aussi, simultanément, à vérifier l’état sous forme de ping/pong
  • Après un certain délai, on choisit, parmi les chemins candidats dont le fonctionnement a été confirmé, celui qui est le meilleur selon une heuristique
    • ICE utilise généralement des scores prédéfinis du type LAN > WAN > WAN+NAT
    • Depuis la v0.100.0, Tailscale utilise la latence aller-retour plutôt qu’un ordre de préférence codé en dur
  • Tailscale ne sépare pas strictement la connexion en une phase de probe et une phase de communication
    • Toutes les connexions commencent avec DERP déjà sélectionné
    • L’utilisateur peut utiliser immédiatement la connexion via le chemin de fallback
    • La recherche de chemin s’exécute en parallèle et, si un meilleur chemin est découvert quelques secondes plus tard, la connexion est mise à niveau de façon transparente

Maintien des chemins en production et sécurité

  • Il faut faire attention aux chemins asymétriques
    • ICE s’efforce de faire en sorte que les deux pairs choisissent le même chemin réseau afin de maintenir un flux de paquets bidirectionnel
    • Même sans implémenter une procédure du même niveau, tous les chemins utilisés doivent transporter du trafic dans les deux sens
    • De simples probes ping/pong périodiques peuvent suffire à maintenir cela
  • Le chemin actuellement choisi peut échouer
    • Un exemple est la disparition de l’état à cause d’une maintenance du NAT
    • On peut continuer à sonder tous les chemins possibles afin de conserver des fallbacks chauds
    • Mais comme les downgrades sont rares, il peut être plus efficace de basculer vers le relais de dernier recours puis de relancer la recherche de chemin
  • Il est important de partir du principe que le protocole de niveau supérieur fournit sa propre sécurité
    • QUIC utilise des certificats TLS
    • WireGuard utilise ses propres clés publiques
  • Avec un changement dynamique de chemin, la sécurité fondée sur l’IP n’a plus de sens
    • Une authentification end-to-end est au minimum nécessaire
  • Si la couche supérieure dispose d’une sécurité end-to-end, même si les probes ping/pong peuvent être usurpés, dans le pire des cas l’attaquant ne peut qu’inciter le trafic à passer par lui
    • Il reste toutefois préférable d’authentifier et de chiffrer aussi les paquets de découverte de chemin

Composants d’un NAT Traversal robuste

  • Un NAT Traversal robuste nécessite les éléments suivants
    • Un protocole à étendre basé sur UDP
    • Un socket directement accessible depuis le programme
    • Un canal latéral pour communiquer avec les pairs
    • Quelques serveurs STUN
    • Un réseau de relais de fallback, optionnel mais fortement recommandé
  • Les étapes sont les suivantes
    • Énumérer tous les ip:ports du socket sur les interfaces directement connectées
    • Interroger les serveurs STUN pour trouver les ip:ports WAN et la difficulté du NAT
    • Trouver des ip:ports WAN supplémentaires avec un protocole de mappage de ports
    • S’il existe un NAT64, le détecter et trouver aussi un ip:port WAN via ce chemin
    • Échanger tous les ip:ports et les clés de chiffrement avec le pair via le canal latéral
    • Pour établir rapidement la connexion, il est possible de commencer par communiquer via le relais de fallback
    • Sonder tous les ip:ports de l’autre pair et, si nécessaire, effectuer une exploration fondée sur le paradoxe des anniversaires pour traverser un hard NAT
    • Si un chemin de connexion meilleur que le chemin actuel est trouvé, effectuer une mise à niveau transparente
    • Si le chemin actif s’arrête, effectuer un downgrade si nécessaire pour maintenir la connectivité
    • Toutes les communications doivent être chiffrées et authentifiées end-to-end

1 commentaires

 
GN⁺ 2025-01-06
Commentaires Hacker News
  • Excellent article. Il existe une sorte de savoir implicite selon lequel le hole punching basé sur TCP serait plus difficile qu’en UDP, et qu’il faudrait donc l’éviter ; en pratique, la complexité supplémentaire ne semble pas si grande par rapport aux flux UDP déjà complexes
    L’article reconnaît aussi que la traversée de NAT en TCP est possible, mais ajoute de la complexité et peut même nécessiter des modifications du noyau si l’on va dans les détails. Mais il me semble qu’il suffit de remplacer la partie qui démarre la connexion avec des paquets UDP bruts par des paquets TCP SYN et la prise en charge de l’ouverture simultanée (simultaneous open)
    Surtout quand on pense qu’il existe des réseaux, comme le Wi‑Fi invité de l’UC Berkeley, qui bloquent tout trafic UDP sortant sauf DNS, il est regrettable de balayer le hole punching TCP au motif que ce serait « plus difficile que l’UDP ». Cela me paraît réalisable presque de la même manière, avec une complexité supplémentaire limitée
    https://ttcplinux.sourceforge.net/documents/one/tcpstate/tcp...

    • Comme il existe des pare-feu à suivi d’état, et que la plupart des filtres NAT sont EDF plutôt qu’EIF, même en UDP il faut une ouverture simultanée, c’est-à-dire des envois simultanés
      La complexité supplémentaire liée à l’ouverture simultanée en TCP est donc assez faible. La difficulté principale consiste à transmettre le mapping public et à coordonner le punching/l’ouverture « simultanés », ce qui est généralement nécessaire aussi en UDP
      En TCP, une complexité de plus est qu’il faut effectuer un véritable appel connect() plutôt que fabriquer un faux paquet TCP SYN. Certains pare-feu examinent en effet les numéros de séquence
    • Très bonne remarque. J’ai implémenté moi-même le hole punching TCP et j’en ai aujourd’hui une implémentation assez correcte ; le grand avantage de TCP est qu’une fois le trou ouvert, on n’a pas besoin de réimplémenter par-dessus UDP une version pauvre de TCP
      Cela dit, le hole punching TCP peut ressembler beaucoup plus à un SYN flood que de simples paquets UDP, ce qui peut réduire le taux de réussite sur certains réseaux. En pratique, je n’ai pas encore vu beaucoup de filtrage
      Le hole punching TCP est assez amusant. Je l’ai implémenté en calculant, à partir de plusieurs mesures NTP, de combien l’horloge système s’écarte de NTP — le « décalage d’horloge » — puis en faisant choisir à l’initiateur une heure de rendez-vous future selon NTP. C’est plus précis qu’on ne le penserait, et le hole punching TCP fonctionne même entre sockets d’une même interface
      La raison pour laquelle j’ai pris en charge ce mode étrange de punching local, c’est que si le punching à l’intérieur de l’hôte réussit avec ce niveau d’efficacité, il y a de bonnes chances qu’il soit suffisamment rapide aussi sur un LAN et sur Internet. Le code est en Python, et le premier essai a été assez brutal. Comme le hole punching TCP est sensible au timing, mon ancienne gestion directe des sockets en Python, avec des threads et une boucle d’événements bricolée à partir de mon expérience des sockets C, échouait
      Pour faire fonctionner ce code, il fallait augmenter la priorité du processus Python afin qu’aucun autre processus ne crée de délai entre les tentatives de punching. Dans une implémentation inefficace, c’est à ce point sensible au temps. L’implémentation actuelle utilise un pool de processus, chacun avec sa propre boucle d’événements, construit une liste de tâches réparties dans le temps, puis chaque tâche réutilise le même socket pour ouvrir la connexion. Après l’avoir testée sur les principaux systèmes d’exploitation, j’ai estimé que c’était la meilleure approche en Python
      Je suis d’accord pour dire que la difficulté du hole punching TCP et UDP est similaire. Dans les deux cas, la partie la plus difficile est l’étape de prédiction du NAT. Je n’ai pas encore utilisé de code de contournement des NAT symétriques, mais je commence à voir comment l’intégrer ou en faire un nouveau plugin
    • Un autre inconvénient du punching TCP par rapport à l’UDP me vient à l’esprit. En TCP, le routeur doit enregistrer l’état de la connexion
      La table d’état du routeur est très petite, et certaines techniques de punching sont assez agressives. Par exemple, si l’on ouvre des centaines de connexions TCP comme dans un algorithme qui tente de contourner un NAT symétrique, on peut mettre le routeur en déni de service
      Avec UDP, grâce aux optimisations de gestion d’état, il est peut-être moins probable que le punching rende tout le routeur inutilisable. Mais ce n’est qu’une supposition
  • L’effet est étonnamment bon, mais dès qu’on parle de mettre ça dans un réseau d’entreprise en production, ça me met mal à l’aise
    On a l’impression de contourner le NAT et les pare-feu traditionnels, et de ne plus dépendre que d’une ACL logicielle, ce qui paraît risqué. Par exemple, si une VM abandonnée dans un environnement de test AWS a Tailscale et qu’un attaquant y accède, il semble qu’un chemin apparaisse jusqu’aux ordinateurs portables du réseau interne de l’entreprise, où seul le code ACL Tailscale en espace utilisateur, après le passage par le noyau, décide d’autoriser ou de bloquer
    Je ne sais pas si on pourrait même savoir qu’une personne non autorisée est arrivée jusque-là

    • C’est pour cela que beaucoup répètent sans cesse que le NAT n’est pas un mécanisme de sécurité
      Percer le NAT et la plupart des filtres à suivi d’état qui lui sont associés est très facile. J’ai implémenté ce genre de chose comme produit commercial dans de vrais environnements d’entreprise en production ; ce n’est pas de la magie, mais une technique bien connue des praticiens
      Si l’on veut un vrai filtrage de paquets, c’est-à-dire un pare-feu, il faut déployer une instance de pare-feu distincte du NAT et y mettre des règles appropriées. Cela dit, même ça aide surtout à réduire le volume de trafic ; le gain de sécurité réel d’un pare-feu lui-même est désormais faible. La plupart des attaques arrivent par des couches supérieures comme HTTP/HTTPS, POP/IMAP
    • Pour être juste, si tout le monde confond le NAT avec un mécanisme de sécurité, c’est parce qu’historiquement le NAT a été déployé avec un pare-feu à suivi d’état
      En réalité, c’est le pare-feu à suivi d’état qui fait l’essentiel du travail, mais le NAT en récolte le mérite. Tailscale ne supprime pas le pare-feu : il fournit une configuration basée sur des ACL correctes et beaucoup plus globale
      J’admets toutefois que les outils d’ACL de Tailscale pourraient être largement améliorés
    • Le réseau est depuis longtemps une sorte de décharge toxique de sécurité et de mauvaises configurations. À cela s’ajoutent maintenant les modèles modernes de réseau basé sur l’hôte pour les conteneurs
      En conséquence, la pile réseau de Windows a elle aussi beaucoup changé et s’est complexifiée. Depuis l’intégration de WireGuard dans Linux, tout le monde a plus ou moins un VPN qui se connecte à un VPS quelque part. À cause de ce qu’on ignore ignorer, la situation réelle est probablement pire qu’on ne le pense
    • C’est fait pour la traversée de NAT, des équipements conçus pour contourner la pénurie d’adresses IPv4
      Un pare-feu est un autre concept. Cela dit, si l’on associe connectivité et sécurité, il est triste et inquiétant de voir que la sécurité sur Internet s’est toujours reposée sur le fait de bloquer des paquets en fonction du port de destination
      On fait ce qui est facile plutôt que ce qui est correct, tout en appelant cela une « solution professionnelle »
    • La VoIP fonctionne depuis longtemps de cette manière, et il existe beaucoup d’infrastructures standard ouvertes pour faciliter cela, comme ICE, TURN
      Mais quelque chose à l’intérieur doit tout de même parler vers l’extérieur en premier ; un vrai pare-feu doit donc gérer en liste blanche à la fois les connexions sortantes et entrantes
      Autrement dit, si l’on dépend de la sécurité périmétrique, ce n’est qu’une question de temps avant que quelqu’un trouve ce qui correspond au « gilet fluorescent » dans son organisation
  • Pour les appareils qui chiffrent déjà au niveau applicatif, ce serait bien d’avoir une alternative à Tailscale sans chiffrement de la connexion. Comme presque tout Internet fonctionne ainsi, il n’est pas toujours nécessaire de chiffrer jusque dans les couches inférieures
    Sur les appareils basse consommation, par exemple les objets IoT qui font tourner un tunnel de type Tailscale, le coût de calcul est particulièrement important
    Les tunnels GRE existent et sont beaucoup utilisés en pratique, mais ils ne gèrent pas le hole punching UDP, ce qui impose une architecture en hub-and-spoke. Avec GRE, c’est-à-dire ip fou, on ne peut pas créer de maillage entre pairs
    Je me demande s’il existe une bibliothèque qui, après une poignée de main chiffrée pour vérifier l’identité, fournit du hole punching UDP et des tunnels GRE non chiffrés

    • Le standard établi dans ce domaine est ICE(Interactive Connectivity Establishment), sur lequel repose WebRTC. Il existe de bonnes bibliothèques qui l’implémentent ou en implémentent certains éléments
      Si l’on veut quelque chose de plus orienté connectivité générique, libp2p pourrait être proche de ce que vous cherchez
      https://datatracker.ietf.org/doc/html/rfc8445
      https://github.com/pion/webrtc
      https://github.com/algesten/str0m
      https://libp2p.io
    • Ce n’est pas de l’UDP, mais j’ai implémenté ici le hole punching TCP et d’autres grandes méthodes de traversée de NAT : https://github.com/robertsdotpm/p2pd
      C’est écrit en Python. Cela dit, contrairement à la plupart du code réseau, cela ne suppose pas l’utilisation de l’interface par défaut. Je voulais permettre de lancer des services sur n’importe quelle interface souhaitée, afin de pouvoir construire des choses plus variées et utiles
      C’est principalement basé sur les modules de la bibliothèque standard. Je n’aime pas les extensions C, car elles cassent souvent les paquets multiplateformes
    • En VoIP, ce sont TURN, STUN et ICE qui font le hole punching ; on peut donc réutiliser les bibliothèques de ce domaine
    • On peut aussi essayer de ressusciter Teredo
  • Le passage indiquant que les pairs doivent connaître à l’avance l’ip:port utilisé par l’autre, et qu’un serveur de coordination a été créé pour synchroniser cela, me fait penser que ce serait bien si SIP était à la hauteur de son nom
    SIP signifie Session Initiation Protocol ; avec un tel nom, il devrait pouvoir démarrer n’importe quelle session, comme un VPN, mais dans la pratique c’est un bazar tellement complexe que le coût n’en vaut plus vraiment la peine. À l’origine, je pense qu’il a été conçu comme un canal auxiliaire de communication pour établir des flux RTP en P2P

    • SIP fait tellement de choses qu’il est effrayant d’essayer de tout garder en tête en même temps
      C’est comme HTTP, mais avec de l’état, bidirectionnel, fédéré, et ça fonctionne aussi sur UDP
      Quand on voit la quantité que baresip doit implémenter juste pour faire du SIP, jusqu’à TLS over UDP, c’est énorme. Et ce n’est même pas du gonflement inutile : ces fonctionnalités sont réellement nécessaires
  • C’est un article de 2020. Les discussions précédentes sont les suivantes
    2022 : https://news.ycombinator.com/item?id=30707711
    2020 : https://news.ycombinator.com/item?id=24241105

  • C’est précisément l’article que j’envoyais aux gens pour expliquer la traversée de NAT
    Il se peut que nous continuions à dépendre de cette méthode quand nous créons des applis P2P. IPv6 n’a pas pris assez d’élan, et NAT ainsi que le routage SNI résolvent la plupart des problèmes pour la plupart des gens
    Du point de vue des FAI, il y a aussi peu d’incitations à faire changer cette situation

  • Je pense que c’est l’un des articles les plus détaillés sur la traversée de NAT de tout Internet. Il manque toutefois des informations sur le comportement delta
    Ce n’est pas un sujet compliqué : cela signifie que certains NAT présentent un motif observable lorsqu’ils attribuent des ports externes consécutifs. Le motif le plus courant consiste à préserver le port source, mais il peut aussi y avoir des motifs du type incrémentation de 1 par rapport au mappage précédent
    En théorie, c’est un très bon article, mais je me demande dans quelle mesure un ingénieur logiciel peut vraiment l’exploiter en pratique. Il explique beaucoup de choses, mais peut-être pas avec assez de détails pour écrire un algorithme. Par exemple, je ne sais pas si, à partir de cet article seul, on pourrait écrire un algorithme pour tester le type de NAT ou ajuster son propre code de hole punching
    Personnellement, j’ai vu des articles scientifiques où un simple tableau était plus utile qu’un texte aussi long. Cela dit, cela peut être un bon point de départ
    La dernière section de l’article est particulièrement importante. Elle ouvre la possibilité de contourner les NAT symétriques utilisés dans les systèmes mobiles. Les recherches récentes sur la traversée de NAT utilisent des techniques similaires et revendiquent un taux de réussite proche de 100 %

  • Un article intéressant qui rappelle le passé. En 2010, j’ai construit un réseau maillé P2P à oubli utilisant ce genre de méthode
    À l’époque, les gens ne se souciaient pas autant de la sécurité que nous le pensions, et aujourd’hui encore ils ne s’en soucient toujours pas assez. Les appareils sont plus nombreux et ont plus de valeur, mais restent encore assez peu sûrs
    Les racines de confiance matérielles, les chaînes de confiance sûres pour l’authentification/l’autorisation, et de vrais endpoints sécurisés avec un minimum de privilèges temporaires restent difficiles, et le théâtre de la sécurité périmétrique réseau continue, que ce soit dans les réseaux domestiques, les réseaux d’entreprise ou les grands réseaux de datacenters de production
    La seule raison pour laquelle ces éléments ne semblent pas être les principales causes racines des compromissions de sécurité, c’est qu’il existe encore quantité de vecteurs d’attaque plus faciles

  • C’est un peu hors sujet, mais il y a quelques semaines, sans rien connaître à ce domaine, j’ai lu quelques éléments
    L’impression que j’en ai retirée est que IPv6 élimine tout cela et rend la traversée de NAT inutile. Si c’est le cas, je me demande pourquoi IPv6 n’est pas plus largement utilisé, et comment s’y mettre sur un réseau domestique et avec un VPN Tailscale

    • Je ne sais pas quelle part cela représente parmi les raisons pour lesquelles IPv6 est moins populaire, mais le fait qu’il soit difficile à utiliser pour les humains a toujours été un défi
      Les incitations commerciales manquent aussi
  • Le simple fait qu’une chose comme celle-ci soit apparue à la place d’IPv6 montre bien la puissance des hacks suffisamment bons