1 puntos por GN⁺ 2025-01-06 | 1 comentarios | Compartir por WhatsApp
  • NAT Traversal es la tecnología base que permite que dispositivos detrás de NAT y firewalls intercambien paquetes UDP directamente, haciendo posible que Tailscale conecte túneles de WireGuard sin un hub central.
  • La premisa clave es que el protocolo esté basado en UDP y que los paquetes para descubrimiento de NAT y los paquetes de comunicación real puedan enviarse y recibirse por el mismo socket de red.
  • Los firewalls con estado solo permiten respuestas que coincidan con paquetes UDP salientes previos, así que si los peers conocen el ip:port del otro y envían paquetes casi al mismo tiempo, pueden abrir el estado del firewall.
  • Como el NAT cambia la IP y el puerto de origen, se necesitan técnicas complementarias como STUN, mapeo de puertos, manejo de NAT64, exploración de puertos basada en la paradoja del cumpleaños y relays.
  • ICE prueba al mismo tiempo las rutas candidatas posibles y elige la mejor; Tailscale se conecta primero de inmediato mediante el relay DERP y luego cambia de forma transparente si encuentra una mejor ruta directa.

Condiciones básicas de NAT Traversal

  • El objetivo es crear un flujo bidireccional de paquetes UDP entre dos dispositivos, sobre el cual puedan funcionar protocolos como WireGuard, QUIC o WebRTC.
  • Si quieres implementarlo directamente, hay dos condiciones importantes.
    • El protocolo debe estar basado en UDP.
      • También se puede con TCP, pero la complejidad es mayor y, según la forma de implementarlo, puede requerir modificaciones al kernel.
      • Si necesitas una conexión orientada a flujo, puedes considerar QUIC, que funciona sobre UDP.
    • El programa debe controlar directamente el socket de red por el que envía y recibe paquetes.
      • NAT Traversal necesita enviar paquetes adicionales aparte del protocolo principal, así que es difícil simplemente añadirlo encima de una librería de red existente.
      • Es útil una estructura en la que la lógica de NAT Traversal y el protocolo principal compartan el mismo socket y funcionen en paralelo.
  • Si el acceso directo al socket es difícil, puedes usar un proxy local.
    • El protocolo original se comunica con el proxy.
    • El proxy se encarga del NAT Traversal y del relay de paquetes hacia el peer.

Atravesar firewalls con estado

  • Los firewalls con estado recuerdan los paquetes que han visto antes y deciden si permiten paquetes nuevos en función de eso.
    • Existen formas como Windows Defender firewall, ufw de Ubuntu, pf de BSD, pf de macOS y AWS Security Groups.
    • Una configuración común es permitir todas las conexiones outbound y bloquear todas las inbound.
  • En UDP, la regla es simple.
    • Si el firewall vio un paquete UDP que salió de 2.2.2.2:1234 hacia 5.5.5.5:5678, entonces permite el paquete que entra en sentido contrario, de 5.5.5.5:5678 hacia 2.2.2.2:1234.
    • Algunos firewalls más permisivos pueden permitir tráfico entrante desde cualquier origen hacia un puerto local que ya se usó una vez, pero cada vez son menos comunes.
  • En una estructura de servidor y cliente, el problema es menor porque el dispositivo detrás del firewall solo necesita iniciar primero la conexión.
    • En una VPN, esto se vuelve una estructura hub-and-spoke, donde el hub no tiene firewall y los spokes sí están detrás de uno.
  • Si dos clientes quieren comunicarse directamente, aparece una situación en la que ambos firewalls se bloquean mutuamente.
    • Ambos lados tienen que salir primero para poder recibir una respuesta, pero la contraparte está en la misma condición.
    • Hacer que el usuario configure manualmente la apertura de puertos es incómodo y, en una red mesh como Tailscale, no escala bien.
    • También hay muchos firewalls que el usuario no puede controlar, como los routers de aeropuertos o cafeterías.
  • La clave de la solución es que las reglas de firewall para UDP no verifican la relación real de respuesta, sino solo la combinación de IP y puerto.
    • Si ambos peers conocen de antemano el ip:port del otro y envían paquetes UDP al mismo tiempo, algunos paquetes iniciales pueden ser bloqueados, pero se abre el estado del firewall.
    • Después, los paquetes enviados por la contraparte parecen respuestas y pasan.
  • Este método necesita un canal lateral.
    • Ambos extremos deben intentar comunicarse casi al mismo tiempo.
    • Basta con una ruta de comunicación que tolere algunos segundos de latencia y solo necesite transmitir unos pocos miles de bytes.
    • WebRTC requiere un canal de señalización, y Tailscale usa el servidor de coordinación y los servidores DERP como canal lateral.
  • El estado del firewall no es permanente.
    • Un valor común del timeout de sesión UDP es de 30 segundos.
    • Para mantener la conexión hay que enviar paquetes periódicamente o reiniciarla cuando haga falta mediante un mecanismo out-of-band.
  • Aunque haya varias capas de firewalls con estado, si permiten tráfico outbound, se pueden atravesar con el método de envío simultáneo.

Cómo el NAT complica más el problema

  • NAT (Network Address Translator) funciona de manera parecida a un firewall con estado, pero además cambia la dirección IP o el puerto de los paquetes.
  • En NAT Traversal, lo que más problema causa suele ser Source NAT (SNAT).
    • SNAT permite que varios dispositivos compartan menos direcciones IP, normalmente una sola IPv4 pública.
    • También existe DNAT, pero tiene poca relación con el problema de NAT Traversal tratado aquí.
  • Por ejemplo, si una laptop envía un paquete UDP desde 192.168.0.20:1234 al servidor de internet 7.7.7.7:5678, el router de casa elige un puerto libre 2.2.2.2:4242 en la IP pública.
    • El router crea un mapeo NAT donde 192.168.0.20:1234 y 2.2.2.2:4242 se consideran equivalentes.
    • Después, los paquetes salientes se cambian para que parezcan venir de 2.2.2.2:4242.
    • Las respuestas entrantes se vuelven a cambiar a 192.168.0.20:1234.
  • En redes corporativas se aplica el mismo principio.
    • La diferencia es que la capa NAT puede estar formada por varios equipos por razones de alta disponibilidad o capacidad, y puede tener varias IP públicas.

STUN y descubrimiento del mapeo NAT

  • Un peer detrás de NAT no puede saber cuál es su ip:port público visible para la contraparte, y el mapeo NAT normalmente solo se crea cuando hay tráfico saliente hacia internet.
  • STUN es un protocolo para descubrir cómo se ve un cliente detrás de NAT desde internet.
    • El cliente le pregunta al servidor STUN: “¿cómo se ve mi endpoint para ti?”.
    • El servidor STUN responde con el ip:port público desde el que llegó el paquete UDP.
  • Si compartes con el peer el ip:port público que reveló STUN, puedes aplicar la técnica de envío simultáneo usada para atravesar el firewall.
  • Esta también es la razón por la que la lógica de NAT Traversal y el protocolo de comunicación real deben usar el mismo socket.
    • Cada socket genera un mapeo distinto en el equipo NAT.
    • Si haces STUN con un socket distinto del que usarás para la comunicación real, obtendrás un ip:port inútil.
  • STUN por sí solo no puede resolver todos los NAT.
    • Puede funcionar en la mayoría de los routers domésticos.
    • Puede fallar en algunos gateways NAT corporativos.
    • La suposición de que 2.2.2.2:4242, como lo ve STUN, tiene el mismo significado en todo internet no siempre es correcta.

NAT fáciles y NAT difíciles

  • Un equipo NAT puede crear mapeos diferentes según el destino, o mantener el mismo mapeo sin importar el destino.
  • RFC 4787 llama Endpoint-Independent Mapping (EIM) a la forma fácil, en la que el mapeo se mantiene sin importar el destino.
  • La forma difícil, donde el mapeo cambia según el destino, se llama Endpoint-Dependent Mapping (EDM).
    • Puede variar solo según la IP de destino, o según la IP y el puerto de destino al mismo tiempo.
    • Desde la perspectiva de NAT Traversal, ambos casos son problemáticos.
  • La terminología antigua Full Cone, Restricted Cone, Port-Restricted Cone y Symmetric NAT mezcla el comportamiento del mapeo NAT con el comportamiento del firewall.
    • En implementaciones prácticas, es más importante la distinción entre “Symmetric frente al resto” o entre EIM y EDM.
  • La técnica de envío simultáneo puede atravesar varios tipos de firewalls.
    • En entornos reales, predominan ampliamente los firewalls dependientes de IP y puerto.
    • Pero si en algún punto del trayecto hay aunque sea un solo hard NAT, STUN y el envío simultáneo por sí solos ya no bastan.

Relay cuando falla la conexión directa

  • La conexión directa puede fallar incluso usando todas las técnicas disponibles
    • Si el NAT es complicado, o en redes que bloquean el UDP saliente salvo DNS, como el guest Wi-Fi de UC Berkeley, las técnicas de NAT no pueden resolverlo
  • En ese caso, los paquetes pueden intercambiarse a través de un relay accesible para ambos lados
    • No es mejor que una conexión directa, pero si el relay está lo bastante cerca de la ruta y tiene suficiente ancho de banda, la degradación en la calidad de la conexión puede no ser grande
    • Aunque aumente la latencia o disminuya el ancho de banda, sigue siendo mejor que no tener conexión alguna
  • El protocolo de relay tradicional es TURN
    • El cliente se autentica ante el servidor TURN
    • El servidor TURN asigna un ip:port para el relay
    • Los peers se comunican con ese ip:port
  • Tailscale creó DERP (Detoured Encrypted Routing Protocol) en lugar de TURN
    • DERP funciona sobre HTTP
    • Es útil en redes con reglas estrictas de salida
    • Reenvía payloads cifrados según la clave pública del destino
  • DERP cumple dos funciones
    • Relay de datos cuando falla el NAT Traversal
    • Canal lateral para ayudar con el NAT Traversal
  • Se estima que, al implementar STUN, envío simultáneo y relay, más del 90% de los casos pueden conectarse directamente, y que el relay siempre puede garantizar algún tipo de conectividad

Técnicas adicionales para hard NAT

  • En un hard NAT, el peer del lado fácil no sabe qué puerto abrió el NAT difícil
    • Con STUN se puede asumir que la IP suele ser correcta
    • Lo desconocido es el puerto, y hay 65,535 valores posibles
  • Si simplemente se recorren todos los puertos, en el peor caso toma unos 10 minutos a 100 paquetes/segundo, y además parece un escaneo de puertos
  • Se puede reducir el costo de búsqueda usando la paradoja del cumpleaños
    • Del lado del hard NAT se abren 256 puertos con 256 sockets, y del lado del NAT fácil se prueban puertos de destino al azar
    • Suponiendo que hay 256 puertos abiertos, la probabilidad de éxito es la siguiente
      • 174 intentos aleatorios: 50%
      • 256 intentos aleatorios: 64%
      • 1024 intentos aleatorios: 98%
      • 2048 intentos aleatorios: 99.9%
    • A 100 puertos/segundo, la mitad de los casos pasan en menos de 2 segundos, y hacia los 20 segundos casi siempre se logra aun habiendo explorado menos del 4% del espacio total
  • Si ambos lados están detrás de hard NAT, es mucho más difícil
    • Ahora tiene que coincidir el par {source port, destination port}
    • En las mismas condiciones, la probabilidad de éxito después de 20 segundos es de 0.01%
    • Para una probabilidad de éxito de 99.9%, ambos lados deben enviar 170,000 probes cada uno, lo que toma 28 minutos a 100 paquetes/segundo
  • Este método puede mejorar la conectividad en escenarios home-office, home-cloud y algunos office-cloud o cloud-cloud
    • Los routers domésticos tienden a ser easy NAT, mientras que los hard NAT suelen ser routers de oficina o gateways NAT en la nube

Protocolos de mapeo de puertos

  • Existen protocolos para pedirle directamente al NAT: “redirige este puerto WAN a este ip:port de la LAN”
  • Los tres más representativos son los siguientes
    • UPnP IGD: protocolo surgido a fines de los años 90 que usa tecnologías como XML, SOAP y HTTP multicast sobre UDP, con implementación y seguridad complejas
    • NAT-PMP: NAT Port Mapping Protocol creado por Apple, simple y dedicado solo al port forwarding
    • PCP: forma en que NAT-PMP v2 evolucionó hacia Port Control Protocol
  • Si se prueba UPnP IGD, NAT-PMP o PCP en el gateway local por defecto y hay respuesta, se puede solicitar un mapeo de puerto público
    • Si funciona, no solo permite conocer el ip:port público como con STUN, sino también hacer que el NAT opere de forma más permisiva para ese puerto
    • Cualquier paquete que llegue al puerto mapeado, sin importar de dónde venga, se reenvía al dispositivo interno
  • No se puede depender de estos protocolos
    • Puede que el equipo no los implemente
    • Pueden venir desactivados por defecto
    • Pueden estar deshabilitados por política
  • A veces se desactivan por política debido a vulnerabilidades históricas de UPnP
    • Algunos equipos incluso apagan juntos UPnP, NAT-PMP y PCP con una sola casilla de “UPnP”
  • Si están disponibles, en la práctica un NAT desaparece del camino de datos y la conexión se vuelve más fácil

Double NAT y CGNAT

  • En un double NAT, donde hay dos capas de NAT delante de un dispositivo, lo más importante es el comportamiento del NAT externo, es decir, el que está justo antes de Internet
    • Igual que con varias capas de firewalls con estado, las capas adicionales de NAT normalmente pasan desapercibidas
    • Las técnicas existentes pueden funcionar sin importar cuántas capas de NAT haya
  • Lo que sí rompe de forma importante el double NAT son los protocolos de mapeo de puertos
    • El mapeo de puertos actúa sobre la capa de NAT más cercana al cliente
    • Pero la que el peer remoto realmente tiene que atravesar es la capa de NAT más externa
    • Como resultado, el ip:port obtenido pertenece a una red intermedia y el peer remoto no puede alcanzarlo
  • El double NAT suele ser invisible para la mayoría de las aplicaciones comunes que no hacen NAT Traversal explícito
    • Pero puede empeorar el multijugador de muchos juegos y eliminar IPv6, reduciendo así las opciones de conexión sin NAT
  • CGNAT (Carrier-Grade NAT) es una estructura en la que el ISP aplica una capa extra de SNAT para resolver la escasez de direcciones IPv4
    • El router doméstico hace SNAT de los dispositivos hacia una IP intermedia
    • Una segunda capa de NAT dentro de la red del ISP mapea esas IP intermedias a un número menor de IP públicas
  • En CGNAT, el usuario no puede reiniciar el NAT del ISP
    • Antes, los usuarios avanzados podían evitar el problema con port forwarding en el router de casa, pero con CGNAT ese método queda bloqueado
  • Como CGNAT es básicamente un double NAT, la mayoría de las técnicas existentes siguen funcionando
    • Los protocolos de mapeo de puertos son la excepción y tienen limitaciones

Problemas de hairpinning

  • Dos peers que están detrás del mismo CGNAT, pero cada uno detrás de un NAT doméstico distinto, enfrentan un problema especial
    • El servidor STUN informa el ip:port público visto desde fuera de Internet
    • Pero lo que esos dos peers realmente necesitan es el ip:port válido dentro de la red intermedia del CGNAT
  • Si al menos uno de los NAT domésticos soporta protocolos de mapeo de puertos, la conexión puede facilitarse
    • Debido al double NAT, el hecho de que el protocolo de mapeo de puertos entregue el ip:port de la red intermedia resulta, de hecho, útil
  • Si no se puede usar mapeo de puertos, se necesita hairpinning
    • Por ejemplo, el peer A envía un paquete al 2.2.2.2:5678 del peer B obtenido con STUN
    • El CGNAT no debe enviar ese paquete hacia Internet externo, sino devolverlo internamente al mapeo NAT del peer B
  • Muchos NAT no soportan hairpinning
    • Hay equipos que asumen que los paquetes dirigidos a una IP no interna desde una red interna siempre salen a Internet
    • Esa suposición puede estar grabada en el silicio de enrutamiento y tal vez no pueda corregirse sin hardware nuevo
  • Cuando interviene CGNAT, el hairpinning se vuelve importante para la conectividad
    • Si fallan tanto el hairpinning como el mapeo de puertos, hay que usar un relay

IPv6 y NAT64

  • En un mundo con solo IPv6, el problema de NAT se vuelve mucho más simple
    • Todos los dispositivos podrían tener direcciones alcanzables sin NAT
    • Pero los firewalls con estado seguirían existiendo, así que seguirían siendo necesarios el atravesado de firewalls y los canales laterales
    • Un relay de fallback que use protocolos como HTTP también sigue siendo útil para redes que bloquean el UDP saliente
  • Solo con IPv6 todavía no alcanza
    • El mundo sigue siendo mayormente IPv4 y ronda 33% de IPv6
    • El despliegue de IPv6 no es uniforme, así que según la combinación de peers puede ser 100% IPv6 o 0% IPv6
    • Si el objetivo es conectarse pase lo que pase, hay que seguir manejando IPv4+NAT
  • La coexistencia de IPv6 e IPv4 crea un caso adicional llamado NAT64
    • NAT44 traduce IPv4 a otro IPv4
    • NAT64 traduce IPv6 interno a IPv4 externo
    • Si se usa junto con DNS64, para el dispositivo parece una red solo IPv6 mientras sigue teniendo acceso a Internet IPv4
  • Las aplicaciones que solo usan nombres DNS casi no tienen que preocuparse por NAT64
    • Pero NAT Traversal maneja directamente IP y puertos concretos, así que requiere tratamiento aparte
  • Si el dispositivo soporta CLAT (Customer-side translator), el sistema operativo hace que parezca que existe conectividad IPv4 directa y resuelve NAT64 por detrás
    • CLAT es común en dispositivos móviles
    • Es raro en desktops, laptops y servidores
  • Si no hay CLAT, hay que detectar NAT64+DNS64 de forma directa
    • Se envía una consulta DNS a ipv4only.arpa.
    • Ese nombre solo se resuelve a direcciones IPv4 fijas conocidas
    • Si vuelve una dirección IPv6, entonces fue traducida por DNS64 y se puede descubrir el prefijo NAT64
  • Después, para comunicarse con una dirección IPv4, basta con enviar paquetes IPv6 a {NAT64 prefix + IPv4 address}
    • Si se hace STUN a través de NAT64 para encontrar el ip:port público, se vuelve al problema normal de NAT Traversal

Integrar rutas candidatas con ICE

  • Un enfoque que intente clasificar de antemano con exactitud qué técnica usar entre todas las posibles escala mal
    • Porque los ingenieros de red y quienes implementan equipos NAT generan comportamientos muy variados
  • La idea central de ICE (Interactive Connectivity Establishment) es un algoritmo que prueba al mismo tiempo todo lo posible y elige la mejor ruta entre las que funcionan
  • Al iniciar la comunicación, se reúne una lista de endpoints candidatos para el socket local
    • ip:ports de IPv6
    • ip:ports de LAN IPv4
    • ip:ports de WAN IPv4 descubiertos con STUN
    • ip:ports de WAN IPv4 descubiertos a través del traductor NAT64
    • ip:port de WAN IPv4 asignado por protocolos de mapeo de puertos
    • endpoints proporcionados por el operador, como un port forwarding configurado estáticamente
  • Luego se intercambian las listas de candidatos por un canal lateral y se envían paquetes de prueba a todos los endpoints que dio la otra parte
    • Los paquetes de prueba sirven para abrir el firewall y el NAT
    • Al mismo tiempo también sirven como verificación de estado tipo ping/pong
  • Después de cierto tiempo, se elige la ruta candidata que heurísticamente parezca mejor entre las que sí funcionaron
    • ICE normalmente usa puntajes predefinidos como LAN > WAN > WAN+NAT
    • Tailscale usa latencia de ida y vuelta desde la v0.100.0 en lugar de un orden de preferencia hardcodeado
  • Tailscale no divide la conexión en una etapa estricta de prueba y otra de comunicación
    • Todas las conexiones comienzan con DERP ya seleccionado de antemano
    • El usuario puede usar de inmediato la conexión por la ruta de fallback
    • La exploración de rutas corre en paralelo y, si unos segundos después se descubre una ruta mejor, se actualiza de forma transparente

Mantenimiento de rutas y seguridad durante la operación

  • Hay que tener cuidado con las rutas asimétricas
    • ICE intenta que ambos peers elijan la misma ruta de red para mantener el flujo bidireccional de paquetes
    • Incluso si no se implementa un procedimiento del mismo nivel, debe haber tráfico bidireccional en todas las rutas en uso
    • Incluso probes periódicos de ping/pong pueden bastar para mantener esto
  • La ruta seleccionada actualmente puede fallar
    • Un ejemplo es cuando desaparece el estado por mantenimiento del NAT
    • Se pueden seguir probando todas las rutas posibles para mantener un fallback listo
    • Pero como el downgrade es raro, puede ser más eficiente caer a un relay de último recurso y luego reiniciar la exploración de rutas
  • Es importante asumir que el protocolo de nivel superior aporta su propia seguridad
    • QUIC usa certificados TLS
    • WireGuard usa sus propias claves públicas
  • Si se cambian rutas dinámicamente, la seguridad basada en IP deja de tener sentido
    • Como mínimo, se necesita autenticación end-to-end
  • Si hay seguridad end-to-end en la capa superior, aunque los probes de ping/pong puedan falsificarse, en el peor caso un atacante solo podría hacer que el tráfico pase por él
    • Aun así, conviene autenticar y cifrar también los paquetes de exploración de rutas

Componentes de un NAT Traversal robusto

  • Un NAT Traversal robusto necesita los siguientes elementos
    • Un protocolo basado en UDP que se vaya a escalar
    • Sockets accesibles directamente desde el programa
    • Un canal lateral para comunicarse con el peer
    • Algunos servidores STUN
    • Una red de relay de fallback, opcional pero muy recomendada
  • Los pasos de ejecución son los siguientes
    • Enumerar todos los ip:ports del socket en interfaces conectadas directamente
    • Consultar a los servidores STUN para encontrar los ip:ports WAN y la dificultad del NAT
    • Encontrar ip:ports WAN adicionales con protocolos de mapeo de puertos
    • Si hay NAT64, detectarlo y encontrar también por esa ruta el ip:port WAN
    • Intercambiar con el peer todos los ip:ports y las claves de cifrado por el canal lateral
    • Para establecer la conexión rápido, se puede empezar comunicándose por el relay de fallback
    • Hacer probes a todos los ip:ports de la otra parte y, si hace falta, usar exploración basada en la paradoja del cumpleaños para atravesar hard NAT
    • Si se encuentra una ruta de conexión mejor que la actual, actualizarla de forma transparente
    • Si la ruta activa se detiene, hacer downgrade según sea necesario para mantener la conectividad
    • Toda la comunicación debe estar cifrada y autenticada end-to-end

1 comentarios

 
GN⁺ 2025-01-06
Opiniones de Hacker News
  • Excelente artículo. Suele existir una especie de conocimiento tácito de que el hole punching basado en TCP es más difícil que con UDP, así que mejor no hacerlo, pero en la práctica no parece agregar mucha complejidad frente al flujo de UDP, que ya es complejo
    El artículo también reconoce que atravesar NAT con TCP es posible, pero añade complejidad y, si se profundiza, incluso podría requerir modificar el kernel. Aun así, creo que basta con reemplazar la parte que inicia la conexión con paquetes UDP sin procesar por paquetes TCP SYN y soporte de apertura simultánea (simultaneous open)
    Sobre todo considerando que existen redes, como el Wi‑Fi de invitados de UC Berkeley, que bloquean todo envío UDP salvo DNS, es una lástima pasar por alto el hole punching TCP solo porque “es más difícil que UDP”. Me parece casi igual de viable y con una complejidad adicional limitada
    https://ttcplinux.sourceforge.net/documents/one/tcpstate/tcp...

    • Debido a la existencia de firewalls con seguimiento de estado y a que la mayoría de los filtros NAT son EDF y no EIF, también en UDP se necesita apertura simultánea, es decir, envío simultáneo
      Por eso, la complejidad adicional de hacer apertura simultánea con TCP es bastante pequeña. La dificultad central es comunicar el mapeo público y coordinar el punching/la apertura “simultáneos”, y eso normalmente también se necesita con UDP
      Un punto extra de complejidad en TCP es que, en vez de fabricar paquetes TCP SYN falsos, hay que hacer una llamada real a connect(). Algunos firewalls miran los números de secuencia
    • Muy buen punto. Implementé hole punching TCP por mi cuenta y ahora tengo una implementación bastante decente; una gran ventaja de usar TCP es que, una vez abierto el agujero, no hace falta volver a montar una versión pobre de TCP encima de UDP
      Sin embargo, el hole punching TCP puede parecerse mucho más a un SYN flood que los paquetes UDP, así que en algunas redes la tasa de éxito puede bajar. En la práctica, todavía no he visto mucho filtrado
      El hole punching TCP es bastante interesante. Lo implementé calculando, mediante varias mediciones NTP, cuánto se desvía el reloj del sistema respecto de NTP, es decir, el “desfase del reloj”, y haciendo que el iniciador fije una hora futura de encuentro basada en NTP. Es más preciso de lo que parece, y el hole punching TCP también funciona entre sockets de la misma interfaz
      La razón para soportar este extraño modo de punching basado en lo local es que, si el punching dentro del host tiene éxito con ese nivel de eficiencia, es muy probable que también sea lo bastante rápido en LAN e Internet. El código está en Python, y el primer intento fue bastante impactante. Como el hole punching TCP es sensible al timing, al usar en Python gestión manual de sockets al estilo antiguo, threading y un loop de eventos improvisado basado en experiencia con sockets en C, fallaba
      Para hacer funcionar ese código tuve que subir la prioridad del proceso de Python para que otros procesos no introdujeran retrasos entre los intentos de punching. En una implementación ineficiente, es así de sensible al tiempo. La implementación actual usa un pool de procesos, cada uno con su propio loop de eventos, crea una lista de tareas distribuidas en el tiempo y hace que cada tarea reutilice el mismo socket para abrir la conexión. Tras probarlo en los principales sistemas operativos, concluí que este enfoque es el mejor en Python
      Coincido en que la dificultad del hole punching TCP y UDP es similar. En ambos, la parte más difícil es la etapa de predicción de NAT. Todavía no he usado código para evadir NAT simétrico, pero empiezo a ver formas de integrarlo o convertirlo en un nuevo plugin
    • Se me ocurrió otra desventaja por la que el punching TCP está en peor posición que UDP. En TCP, el router tiene que registrar el estado de la conexión
      La tabla de estados del router es muy pequeña, y algunas técnicas de punching son bastante agresivas. Por ejemplo, si se abren cientos de conexiones TCP, como en un algoritmo que intenta evadir NAT simétrico, se podría llevar al router a un estado de denegación de servicio
      Gracias a las optimizaciones de gestión de estado en UDP, quizá sea menos probable que el punching deje inutilizable a todo el router. Aunque esto es una conjetura
  • El efecto es interesantemente bueno, pero cuando alguien sugiere meter esto en una red corporativa de producción, por alguna razón me da inquietud
    Se siente riesgoso, como si se estuvieran eludiendo el NAT y los firewalls tradicionales, y en su lugar se dependiera solo de una ACL de software. Por ejemplo, si una VM abandonada en un entorno de pruebas de AWS tiene Tailscale y un atacante logra acceder a ella, parece que se crea una ruta hasta las laptops de la red corporativa interna donde solo el código de ACL de Tailscale en espacio de usuario, pasando por el kernel, decide si permitir o bloquear
    No sé si siquiera se podría saber si alguien no autorizado llegó hasta ese punto

    • Por eso tanta gente repite una y otra vez que NAT no es un mecanismo de seguridad
      Atravesar NAT y la mayoría de los filtros con seguimiento de estado asociados es muy fácil. He implementado cosas así como producto comercial en entornos corporativos reales de producción, y no es magia, sino una técnica bien conocida por los profesionales
      Si quieres filtrado real de paquetes, es decir, un firewall, debes desplegar una instancia de firewall separada del NAT y poner reglas adecuadas. Dicho eso, incluso eso ayuda sobre todo a reducir el volumen de tráfico; el beneficio real de seguridad del propio firewall ya es pequeño. La mayoría de los ataques entran por capas superiores como HTTP/HTTPS, POP/IMAP
    • Para ser justos, la razón por la que todo el mundo confunde NAT con un mecanismo de seguridad es que tradicionalmente NAT se desplegaba junto con un firewall con seguimiento de estado
      En realidad, el firewall con seguimiento de estado hace la mayor parte del trabajo, pero NAT se lleva el crédito. Tailscale no elimina el firewall, sino que ofrece una configuración mucho más integral basada en ACL correctas
      Aunque admito que las herramientas de ACL de Tailscale tienen mucho margen de mejora
    • Desde hace mucho, redes ha sido una especie de vertedero tóxico de seguridad y configuraciones incorrectas. A eso se le sumaron los modelos modernos de redes basadas en host para contenedores
      Como consecuencia, el stack de red de Windows también cambió bastante y se volvió más complejo. Desde que WireGuard entró en Linux, cualquiera tiene al menos una VPN que se conecta a algún VPS en alguna parte. Por el hecho de no saber lo que no sabemos, es probable que la situación real sea peor de lo que pensamos
    • Esto es para atravesar NAT, equipos creados para sortear la escasez de direcciones IPv4
      Un firewall es otro concepto. Dicho eso, si hablamos de conectividad y seguridad juntas, es triste e inquietante que la seguridad de Internet siempre haya dependido de bloquear paquetes según el puerto de destino
      Es la realidad de hacer lo fácil en lugar de lo correcto, y aun así llamarlo una “solución profesional”
    • VoIP ha funcionado así desde hace mucho, y existe mucha infraestructura pública estándar para facilitarlo. Cosas como ICE, TURN
      Aun así, algo interno debe hablar primero hacia afuera, por lo que un firewall real debería gestionar con listas de permitidos tanto las conexiones salientes como las entrantes
      En otras palabras, si dependes de la seguridad perimetral, es cuestión de tiempo que alguien descubra cuál es el “chaleco fluorescente” de su propia organización
  • Sería bueno tener una alternativa parecida a Tailscale sin cifrado de conexión para dispositivos que ya cifran en la capa de aplicación. Como funciona casi todo Internet, no siempre hace falta cifrar también las capas inferiores
    En dispositivos de bajo consumo, por ejemplo dispositivos IoT que ejecutan un túnel parecido a Tailscale, el costo computacional es especialmente alto
    Existen los túneles GRE y de hecho se usan mucho, pero no manejan UDP hole punching, así que se necesita una estructura hub-and-spoke. Con GRE, es decir ip fou, no se puede construir una malla entre pares
    Me pregunto si existe alguna librería que, tras un handshake criptográfico para verificación de identidad, ofrezca UDP hole punching y un túnel GRE sin cifrar

    • El estándar establecido en este campo es ICE(Interactive Connectivity Establishment), del que depende WebRTC. Hay buenas librerías que lo implementan o implementan parte de sus elementos
      Si quieres algo orientado a conectividad más general, libp2p podría acercarse a lo que buscas
      https://datatracker.ietf.org/doc/html/rfc8445
      https://github.com/pion/webrtc
      https://github.com/algesten/str0m
      https://libp2p.io
    • No es UDP, pero aquí implementé TCP hole punching y otros métodos principales de NAT traversal: https://github.com/robertsdotpm/p2pd
      Está escrito en Python. Eso sí, como la mayoría del código de red, no asume el uso de la interfaz predeterminada. Quise permitir ejecutar servicios en cualquier interfaz que se quiera, para poder crear cosas más variadas y útiles
      Se basa mayormente en módulos de la biblioteca estándar. No me gustan las extensiones en C porque a menudo rompen los paquetes multiplataforma
    • En VoIP, TURN, STUN e ICE son los que hacen hole punching, así que se pueden reutilizar librerías de ese lado
    • Otra opción es intentar revivir Teredo
  • Al ver la parte donde los pares tienen que conocer de antemano el ip:port que usa el otro, y que se creó un servidor de coordinación para sincronizarlo, me da por pensar que ojalá SIP hubiera estado a la altura de su nombre
    SIP significa Session Initiation Protocol, un nombre que implica que debería poder iniciar sesiones arbitrarias como una VPN, pero en la práctica se volvió un caos tan complejo que vale poco la pena soportarlo. Creo que originalmente se creó como un canal lateral de comunicación para establecer streams RTP P2P

    • SIP hace tantas cosas que da miedo intentar tenerlas todas en la cabeza al mismo tiempo
      Es como HTTP, pero con estado, bidireccional, federado y también funciona sobre UDP
      Si ves la cantidad de cosas que baresip implementa apenas para hacer SIP, hasta TLS sobre UDP, es enorme. Y ni siquiera está inflado: esas funciones realmente son necesarias
  • Es un artículo de 2020. Las discusiones anteriores son las siguientes:
    2022: https://news.ycombinator.com/item?id=30707711
    2020: https://news.ycombinator.com/item?id=24241105

  • Este es justamente el artículo que enviaba a la gente cuando explicaba NAT traversal.
    Puede que sigamos dependiendo de este enfoque al crear apps P2P. IPv6 no ha logrado suficiente tracción, y NAT y el enrutamiento SNI resuelven la mayoría de los problemas para la mayoría de la gente.
    Desde el punto de vista de los ISP, tampoco hay muchos incentivos para hacer que esa situación cambie.

  • Me parece que está entre los artículos más detallados de todo Internet sobre NAT traversal. Eso sí, le falta información sobre el comportamiento delta.
    No es nada complicado: significa que algunos NAT tienen patrones observables al asignar puertos externos consecutivos. El patrón más común es preservar el puerto de origen, y también puede haber patrones como incrementar en 1 respecto del mapeo anterior.
    En teoría es un muy buen artículo, pero me pregunto hasta qué punto un ingeniero de software podría aprovecharlo en la práctica. Explica mucho, pero quizá no con suficiente detalle como para escribir un algoritmo. Por ejemplo, no sé si con solo leer este artículo se podría escribir un algoritmo para probar el tipo de NAT o ajustar el propio código de hole punching.
    Personalmente, he visto papers donde una tabla sencilla resultaba más útil que un texto tan largo. Aun así, puede ser un buen punto de partida.
    La última sección del artículo es especialmente importante. Podría permitir sortear el NAT simétrico usado en sistemas móviles. La investigación moderna sobre NAT traversal usa técnicas parecidas y afirma tasas de éxito cercanas al 100%.

  • Es un artículo interesante que me hace recordar el pasado. En 2010 construí una red mesh P2P de tipo oblivious que usaba este enfoque.
    En ese entonces la gente no se preocupaba por la seguridad tanto como pensábamos, y todavía hoy no se preocupa lo suficiente. Hay más dispositivos y más valor, pero siguen siendo bastante inseguros.
    Las raíces de confianza en hardware, cadenas de confianza seguras para autenticación/autorización y endpoints realmente seguros con privilegios temporales mínimos siguen siendo difíciles, y en redes domésticas, redes empresariales y grandes redes de data centers en producción, el teatro de seguridad del perímetro de red continúa.
    La única razón por la que estas cosas no parecen ser la causa raíz principal de las brechas de seguridad es que todavía hay por todos lados vías de ataque más fáciles.

  • Un poco fuera de tema, pero hace unas semanas leí un poco sobre esto sin saber nada del área.
    La impresión que me quedó es que IPv6 elimina todo esto y hace que NAT traversal ya no sea necesario. Si es así, me pregunto por qué IPv6 no se usa más ampliamente y cómo debería empezar con mi red doméstica y una VPN de Tailscale.

    • No sé qué tan importante sea como una de las razones por las que IPv6 es menos popular, pero el hecho de que sea difícil de usar para las personas siempre ha sido un desafío.
      También faltan incentivos comerciales.
  • El simple hecho de que haya surgido algo así en lugar de IPv6 muestra muy bien el poder de un hack suficientemente bueno.