Como funciona o NAT Traversal (2020)
(tailscale.com)- O NAT Traversal é a tecnologia fundamental que faz dispositivos atrás de NAT e firewall trocarem pacotes UDP diretamente, permitindo que o Tailscale conecte túneis WireGuard sem um hub central
- A premissa principal é que o protocolo seja baseado em UDP e que os pacotes de descoberta de NAT e os pacotes da comunicação real possam ser enviados e recebidos pelo mesmo socket de rede
- Firewalls com estado só permitem respostas que correspondam a pacotes UDP enviados anteriormente, então, se os peers souberem o
ip:portum do outro e enviarem pacotes quase ao mesmo tempo, é possível abrir o estado do firewall - Como o NAT altera o IP e a porta de origem, são necessárias técnicas complementares como STUN, mapeamento de portas, tratamento de NAT64, exploração de portas baseada no paradoxo do aniversário e relay
- O ICE testa ao mesmo tempo os caminhos candidatos possíveis e escolhe o melhor; o Tailscale se conecta imediatamente por relay DERP e depois muda de forma transparente quando encontra um caminho direto melhor
Condições básicas do NAT Traversal
- O objetivo é criar um fluxo bidirecional de pacotes UDP entre dois dispositivos, sobre o qual protocolos como WireGuard, QUIC e WebRTC possam funcionar
- Para implementar isso diretamente, duas condições são importantes
- O protocolo precisa ser baseado em UDP
- Também é possível com TCP, mas a complexidade é maior e, dependendo da forma de implementação, pode ser necessário modificar o kernel
- Se for preciso uma conexão orientada a stream, pode-se considerar o QUIC, que roda sobre UDP
- O programa precisa controlar diretamente o socket de rede que envia e recebe os pacotes
- O NAT Traversal precisa enviar pacotes adicionais além do protocolo principal, então é difícil apenas encaixá-lo em uma biblioteca de rede existente
- É útil uma estrutura em que a lógica de NAT Traversal e o protocolo principal compartilhem o mesmo socket e funcionem em paralelo
- O protocolo precisa ser baseado em UDP
- Se o acesso direto ao socket for difícil, é possível usar um proxy local
- O protocolo original se comunica com o proxy
- O proxy fica responsável pelo NAT Traversal e pelo relay de pacotes para o peer
Atravessando firewalls com estado
- Firewalls com estado lembram dos pacotes vistos no passado para decidir se novos pacotes devem ou não ser permitidos
- Existem formas como Windows Defender Firewall,
ufwdo Ubuntu,pfdo BSD,pfdo macOS e AWS Security Groups - Uma configuração comum é permitir todas as conexões de saída e bloquear todas as conexões de entrada
- Existem formas como Windows Defender Firewall,
- No UDP, a regra é simples
- Se o firewall viu um pacote UDP sair de
2.2.2.2:1234para5.5.5.5:5678, ele permite o pacote no sentido inverso, de5.5.5.5:5678para2.2.2.2:1234 - Alguns firewalls mais permissivos podem permitir tráfego vindo de qualquer lugar para uma porta local que já se comunicou antes, mas isso é cada vez mais raro
- Se o firewall viu um pacote UDP sair de
- Em uma estrutura de servidor e cliente, o problema é menor porque o dispositivo atrás do firewall pode iniciar a conexão primeiro
- Em VPNs, isso vira uma estrutura hub-and-spoke, em que o hub não tem firewall e os spokes ficam atrás de firewall
- Para dois clientes se comunicarem diretamente, surge a situação em que os firewalls dos dois lados bloqueiam um ao outro
- Ambos precisam enviar primeiro para poder receber a resposta, mas o outro lado está na mesma condição
- Fazer o usuário configurar abertura de porta manualmente é inconveniente e, em redes mesh como o Tailscale, tem baixa escalabilidade
- Também há muitos firewalls que o usuário não pode controlar, como roteadores de aeroporto ou cafeteria
- A chave da solução é que as regras de firewall UDP não verificam se há uma relação real de resposta, apenas observam a combinação de IP e porta
- Se os dois peers souberem antecipadamente o
ip:portum do outro e enviarem pacotes UDP ao mesmo tempo, alguns pacotes iniciais podem ser bloqueados, mas o estado do firewall será aberto - Depois disso, os pacotes enviados pelo outro lado passam como se fossem respostas
- Se os dois peers souberem antecipadamente o
- Esse método precisa de um canal lateral
- As duas pontas precisam tentar se comunicar quase simultaneamente
- Basta um caminho de comunicação em que alguns segundos de latência sejam aceitáveis e seja possível transmitir alguns milhares de bytes
- O WebRTC exige um canal de sinalização, e o Tailscale usa o servidor de coordination e os servidores DERP como canal lateral
- O estado do firewall não é permanente
- Um valor comum para timeout de sessão UDP é 30 segundos
- Para manter a conexão, é preciso enviar pacotes periodicamente ou reiniciar a conexão quando necessário por um método out-of-band
- Mesmo que haja várias camadas de firewall com estado, se elas permitirem tráfego de saída, é possível atravessá-las com o método de envio simultâneo
Como o NAT torna o problema mais difícil
- O NAT (Network Address Translator) funciona de forma parecida com um firewall com estado, mas também altera o endereço IP ou a porta dos pacotes
- No NAT Traversal, o principal problema costuma ser o Source NAT (SNAT)
- O SNAT permite que vários dispositivos compartilhem um número menor de endereços IP, geralmente um único IPv4 público
- O DNAT também existe, mas tem pouca relação com o problema de NAT Traversal tratado aqui
- Por exemplo, se um notebook enviar um pacote UDP de
192.168.0.20:1234para o servidor na internet7.7.7.7:5678, o roteador residencial escolhe uma porta livre2.2.2.2:4242no IP público- O roteador cria um mapeamento NAT em que
192.168.0.20:1234e2.2.2.2:4242são equivalentes - Depois disso, os pacotes de saída passam a parecer enviados de
2.2.2.2:4242 - As respostas recebidas são então convertidas de volta para
192.168.0.20:1234
- O roteador cria um mapeamento NAT em que
- O mesmo princípio se aplica em redes corporativas
- A diferença é que a camada de NAT pode ser composta por vários equipamentos, por alta disponibilidade ou capacidade, e pode ter vários IPs públicos
STUN e descoberta de mapeamento NAT
- Um peer atrás de NAT não consegue saber qual é seu
ip:portpúblico visível para o outro lado, e o mapeamento NAT normalmente só é criado quando existe tráfego saindo para a internet - STUN é o protocolo usado para descobrir como um cliente atrás de NAT aparece na internet
- O cliente pergunta ao servidor STUN: “como o meu endpoint aparece para você?”
- O servidor STUN responde com o
ip:portpúblico de onde o pacote UDP foi visto
- Se o
ip:portpúblico informado pelo STUN for compartilhado com o peer, dá para aplicar a técnica de envio simultâneo usada na travessia de firewall - Esse também é o motivo de a lógica de NAT Traversal e o protocolo real precisarem usar o mesmo socket
- Equipamentos NAT criam mapeamentos diferentes para cada socket
- Se o STUN for feito com um socket diferente daquele usado na comunicação real, o
ip:portobtido não serve
- O STUN sozinho não resolve todos os NATs
- Pode funcionar na maioria dos roteadores residenciais
- Pode falhar em alguns gateways NAT corporativos
- Nem sempre é válida a suposição de que
2.2.2.2:4242, como visto pelo STUN, terá o mesmo significado para toda a internet
NAT fácil e NAT difícil
- Equipamentos NAT podem criar mapeamentos diferentes conforme o destino ou manter o mesmo mapeamento independentemente do destino
- A RFC 4787 chama a forma mais simples, em que o mapeamento é mantido sem depender do destino, de Endpoint-Independent Mapping (EIM)
- A forma mais difícil, em que o mapeamento muda conforme o destino, é chamada de Endpoint-Dependent Mapping (EDM)
- A mudança pode depender só do IP de destino ou do IP e da porta de destino juntos
- Do ponto de vista de NAT Traversal, ambas são ruins
- A terminologia antiga — Full Cone, Restricted Cone, Port-Restricted Cone e Symmetric NAT — mistura o comportamento do mapeamento NAT com o comportamento do firewall
- Em implementações práticas, a distinção “Symmetric versus o restante” ou EIM versus EDM é mais importante
- A técnica de envio simultâneo consegue atravessar vários tipos de firewall
- Em ambientes reais, firewalls dependentes de IP e porta são de longe os mais comuns
- Mas, se houver apenas um hard NAT em algum ponto do caminho, STUN e envio simultâneo por si só já passam a ter problemas
Relay quando a conexão direta falha
- A conexão direta pode falhar mesmo usando todas as técnicas
- Em redes com NAT difícil ou que bloqueiam UDP de saída, exceto DNS, como o guest Wi‑Fi da UC Berkeley, as técnicas de NAT não conseguem resolver
- Nesse caso, os pacotes podem ser trocados por meio de um relay acessível para ambos os lados
- Não é tão bom quanto a conexão direta, mas, se o relay estiver perto o suficiente do caminho e tiver largura de banda suficiente, a perda de qualidade da conexão pode não ser grande
- Mesmo que a latência aumente ou a largura de banda diminua, ainda é melhor do que não ter conexão nenhuma
- O protocolo de relay tradicional é o TURN
- O cliente se autentica no servidor TURN
- O servidor TURN aloca um
ip:portpara relay - O peer se comunica por esse
ip:port
- A Tailscale criou o DERP (Detoured Encrypted Routing Protocol) em vez de usar TURN
- O DERP funciona sobre HTTP
- É útil em redes com regras rígidas de saída
- Faz relay de payloads criptografados com base na chave pública do destino
- O DERP assume dois papéis
- Relay de dados quando o NAT Traversal falha
- Canal lateral para ajudar no NAT Traversal
- A estimativa é que, ao implementar STUN, envio simultâneo e relay, mais de 90% dos casos consigam conexão direta, e o relay possa sempre garantir algum tipo de conectividade
Técnicas adicionais para hard NAT
- Em um hard NAT, o peer do lado mais fácil não sabe quais portas o NAT do lado difícil abriu
- Em geral, pode-se assumir com STUN que o IP está correto
- O que não se sabe é a porta, e há 65.535 valores possíveis
- Simplesmente varrer todas as portas leva cerca de 10 minutos no pior caso, a 100 pacotes/segundo, e parece um port scan
- É possível reduzir o custo da busca usando o paradoxo do aniversário
- No lado do hard NAT, abrem-se 256 portas com 256 sockets, e no lado do NAT fácil faz-se a busca por portas de destino aleatórias
- Supondo que 256 portas estejam abertas, a probabilidade de sucesso é a seguinte
- 174 tentativas aleatórias: 50%
- 256 tentativas aleatórias: 64%
- 1024 tentativas aleatórias: 98%
- 2048 tentativas aleatórias: 99,9%
- A 100 portas/segundo, metade dos casos passa em até 2 segundos e, em cerca de 20 segundos, quase sempre dá certo mesmo tendo explorado menos de 4% do espaço total
- Se ambos os lados estiverem em hard NAT, fica muito mais difícil
- Agora é preciso acertar o par
{source port, destination port} - Nas mesmas condições, a probabilidade de sucesso após 20 segundos é de 0,01%
- Para atingir 99,9% de chance de sucesso, ambos os lados precisam enviar 170.000 probes cada, o que leva 28 minutos a 100 pacotes/segundo
- Agora é preciso acertar o par
- Esse método pode melhorar a conectividade em cenários home-office, home-cloud e alguns casos office-cloud ou cloud-cloud
- Roteadores residenciais tendem a ser NAT fácil, e hard NAT tende a aparecer em roteadores de escritório ou gateways NAT de nuvem
Protocolos de mapeamento de portas
- Existem protocolos para pedir diretamente ao NAT: “encaminhe esta porta WAN para este
ip:portda LAN” - Os três principais são os seguintes
- UPnP IGD: protocolo surgido no fim dos anos 1990 que usa tecnologias como XML, SOAP e HTTP multicast sobre UDP, com implementação e segurança difíceis
- NAT-PMP: o NAT Port Mapping Protocol criado pela Apple, que faz apenas port forwarding e é simples
- PCP: forma como o NAT-PMP v2 evoluiu para o Port Control Protocol
- É possível tentar UPnP IGD, NAT-PMP e PCP no gateway padrão local e, se houver resposta, solicitar um mapeamento de porta pública
- Se funcionar, além de descobrir o
ip:portpúblico como no STUN, também pode fazer o NAT se comportar de forma mais permissiva para aquela porta - Qualquer pacote que chegue à porta mapeada, independentemente de sua origem, será encaminhado ao dispositivo interno
- Se funcionar, além de descobrir o
- Não dá para depender desses protocolos
- O equipamento pode não implementá-los
- Eles podem vir desativados por padrão
- Podem estar desativados por política
- Por causa de vulnerabilidades históricas do UPnP, às vezes eles ficam desativados por política
- Alguns equipamentos desativam UPnP, NAT-PMP e PCP juntos com uma única caixa de seleção “UPnP”
- Se estiverem disponíveis, um NAT no caminho de dados praticamente deixa de existir, facilitando a conexão
Double NAT e CGNAT
- Em um double NAT, em que há duas camadas de NAT na frente de um dispositivo, o comportamento mais importante é o do NAT mais externo, isto é, o imediatamente anterior à internet
- Assim como várias camadas de firewall com estado, camadas adicionais de NAT em geral ficam invisíveis
- As técnicas existentes podem funcionar independentemente do número de camadas de NAT
- O que o double NAT mais atrapalha são os protocolos de mapeamento de portas
- O mapeamento de portas atua na camada de NAT mais próxima do cliente
- Mas o NAT que o peer remoto precisa atravessar é o mais externo
- Como resultado, o
ip:portobtido é um endereço da rede intermediária, ao qual o peer remoto não consegue chegar
- O double NAT passa despercebido para a maioria dos aplicativos comuns que não fazem NAT Traversal explícito
- Mas pode piorar o multiplayer de muitos jogos e eliminar o IPv6, reduzindo as opções de conexão sem NAT
- O CGNAT (Carrier-Grade NAT) é uma estrutura em que o ISP aplica mais uma camada de SNAT para lidar com a escassez de endereços IPv4
- O roteador residencial faz SNAT dos dispositivos para um IP intermediário
- Uma segunda camada de NAT dentro da rede do ISP mapeia esses IPs intermediários para um número menor de IPs públicos
- No CGNAT, o usuário não consegue redefinir o NAT do ISP
- No passado, usuários avançados podiam evitar o problema com port forwarding no roteador residencial, mas no CGNAT esse caminho fica bloqueado
- Como o CGNAT também é, em essência, um double NAT, a maioria das técnicas existentes continua funcionando
- Os protocolos de mapeamento de portas são a exceção, por terem limitações
Problema de hairpinning
- Dois peers atrás do mesmo CGNAT, mas cada um atrás de um NAT residencial diferente, enfrentam um problema específico
- O servidor STUN informa o
ip:portpúblico visto do lado de fora da internet - Mas o que os dois peers realmente precisam é do
ip:portválido na rede intermediária dentro do CGNAT
- O servidor STUN informa o
- Se pelo menos um dos NATs residenciais suportar protocolos de mapeamento de portas, a conexão pode ficar mais fácil
- Por causa do double NAT, o fato de os protocolos de mapeamento de portas informarem o
ip:portda rede intermediária acaba ajudando
- Por causa do double NAT, o fato de os protocolos de mapeamento de portas informarem o
- Se não for possível usar mapeamento de portas, será necessário hairpinning
- Por exemplo, o peer A envia um pacote para
2.2.2.2:5678do peer B, obtido via STUN - O CGNAT precisa não enviar esse pacote para a internet externa, mas sim devolvê-lo internamente para o mapeamento NAT do peer B
- Por exemplo, o peer A envia um pacote para
- Muitos NATs não suportam hairpinning
- Há equipamentos que assumem que pacotes saindo da rede interna para um IP não interno sempre vão para a internet
- Esse tipo de suposição pode estar gravado no silício de roteamento e talvez não possa ser corrigido sem hardware novo
- Quando o CGNAT entra em cena, o hairpinning se torna importante para a conectividade
- Se tanto o hairpinning quanto o mapeamento de portas falharem, é preciso usar relay
IPv6 e NAT64
- Em um mundo só com IPv6, o problema de NAT ficaria muito mais simples
- Todos os dispositivos poderiam ter endereços alcançáveis sem NAT
- Mas firewalls com estado continuariam existindo, então atravessar firewall e side channels ainda seriam necessários
- Um relay de fallback usando protocolos como HTTP também continua útil para redes que bloqueiam UDP de saída
- Só IPv6 ainda não basta
- O mundo ainda é majoritariamente IPv4, com cerca de 33% de IPv6
- A implantação de IPv6 não é uniforme, então dependendo da combinação de peers pode ser 100% IPv6 ou 0% IPv6
- Se o objetivo é conectar sempre, ainda é preciso continuar tratando IPv4+NAT
- A coexistência de IPv6 e IPv4 cria um caso adicional chamado NAT64
- NAT44 converte IPv4 para outro IPv4
- NAT64 converte IPv6 interno para IPv4 externo
- Em conjunto com DNS64, ele pode oferecer acesso à internet IPv4 enquanto parece ser uma rede IPv6-only para o dispositivo
- Aplicações que usam apenas nomes DNS quase não precisam se preocupar com NAT64
- Mas NAT Traversal lida diretamente com IP e porta específicos, então exige tratamento separado
- Se o dispositivo suportar CLAT (Customer-side translator), o sistema operacional faz parecer que existe conectividade IPv4 direta e lida com o NAT64 nos bastidores
- CLAT é comum em dispositivos móveis
- É raro em desktops, notebooks e servidores
- Sem CLAT, é preciso detectar NAT64+DNS64 diretamente
- Envie uma consulta DNS para
ipv4only.arpa. - Esse nome só resolve para endereços IPv4 fixos conhecidos
- Se voltar um endereço IPv6, ele foi traduzido pelo DNS64, então dá para descobrir o prefixo NAT64
- Envie uma consulta DNS para
- Depois, para se comunicar com um endereço IPv4, basta enviar pacotes IPv6 para
{NAT64 prefix + IPv4 address}- Se você fizer STUN via NAT64 para descobrir o
ip:portpúblico, volta ao problema normal de NAT Traversal
- Se você fizer STUN via NAT64 para descobrir o
Unificando caminhos candidatos com ICE
- Tentar classificar antecipadamente com precisão qual técnica usar entre todas as opções tem baixa escalabilidade
- Isso ocorre porque engenheiros de rede e implementadores de equipamentos NAT criam comportamentos variados
- A ideia central do ICE (Interactive Connectivity Establishment) é um algoritmo que tenta tudo o que for possível ao mesmo tempo e escolhe o melhor caminho entre os que funcionarem
- Ao iniciar a comunicação, reúne-se uma lista de endpoints candidatos para o socket local
- IPv6
ip:ports - IPv4 LAN
ip:ports - IPv4 WAN
ip:portsdescobertos por STUN - IPv4 WAN
ip:portsdescobertos por meio do tradutor NAT64 - IPv4 WAN
ip:portalocado por um protocolo de mapeamento de portas - Endpoints fornecidos pelo operador, como encaminhamento de porta configurado estaticamente
- IPv6
- Em seguida, troca-se a lista de candidatos por um side channel e envia-se pacotes de probe para todos os endpoints fornecidos pela outra ponta
- Os pacotes de probe servem para abrir firewalls e NATs
- Ao mesmo tempo, também funcionam como verificação de estado no formato ping/pong
- Depois de algum tempo, escolhe-se heurísticamente o melhor caminho entre os candidatos cujo funcionamento foi confirmado
- O ICE normalmente usa pontuações pré-definidas como LAN > WAN > WAN+NAT
- Desde a v0.100.0, o Tailscale usa tempo de ida e volta em vez de uma ordem de preferência fixa no código
- O Tailscale não separa a conexão em fases rígidas de probe e de comunicação
- Todas as conexões começam com o DERP já selecionado previamente
- O usuário pode usar imediatamente a conexão pelo caminho de fallback
- A descoberta de caminhos roda em paralelo e, se um caminho melhor for encontrado alguns segundos depois, a conexão é atualizada de forma transparente
Manutenção do caminho e segurança durante a operação
- É preciso tomar cuidado com caminhos assimétricos
- O ICE tenta fazer com que os dois peers escolham o mesmo caminho de rede para manter o fluxo bidirecional de pacotes
- Mesmo sem implementar exatamente o mesmo tipo de procedimento, deve haver tráfego nos dois sentidos em todos os caminhos em uso
- Só probes periódicos de ping/pong já podem manter isso
- O caminho atualmente selecionado pode falhar
- Um exemplo é quando o estado desaparece durante a manutenção do NAT
- É possível continuar fazendo probe em todos os caminhos para manter um fallback aquecido
- Mas, como downgrade é raro, pode ser mais eficiente cair para um relay de último recurso e reiniciar a descoberta de caminhos
- É importante assumir que o protocolo de camada superior fornece sua própria segurança
- O QUIC usa certificados TLS
- O WireGuard usa suas próprias chaves públicas
- Ao trocar de caminho dinamicamente, segurança baseada em IP deixa de fazer sentido
- No mínimo, é necessária autenticação end-to-end
- Se houver segurança end-to-end na camada superior, mesmo que probes de ping/pong possam ser forjados, no pior caso um atacante conseguiria apenas induzir o tráfego a passar por ele
- Ainda assim, é melhor autenticar e criptografar também os pacotes de descoberta de caminho
Componentes de um NAT Traversal robusto
- Um NAT Traversal robusto precisa dos seguintes elementos
- Um protocolo baseado em UDP para ser estendido
- Sockets acessíveis diretamente pelo próprio programa
- Um side channel para se comunicar com o peer
- Alguns servidores STUN
- Opcional, mas fortemente recomendada, uma rede de relay de fallback
- As etapas de execução são as seguintes
- Enumerar todos os
ip:portsdo socket nas interfaces conectadas diretamente - Consultar servidores STUN para descobrir
ip:portsWAN e o grau de dificuldade do NAT - Encontrar
ip:portsWAN adicionais com protocolos de mapeamento de portas - Se houver NAT64, detectá-lo e também descobrir um WAN
ip:portpor esse caminho - Trocar todos os
ip:portse chaves criptográficas com o peer pelo side channel - Para estabelecer a conexão rapidamente, pode-se começar primeiro por um relay de fallback
- Fazer probe em todos os
ip:portsda outra ponta e, se necessário, realizar exploração baseada no paradoxo do aniversário para atravessar hard NAT - Se encontrar um caminho de conexão melhor que o atual, fazer o upgrade de forma transparente
- Se o caminho ativo parar, fazer downgrade conforme necessário para manter a conectividade
- Toda comunicação deve ser criptografada e autenticada end-to-end
- Enumerar todos os
1 comentários
Opiniões no Hacker News
Excelente texto. Existe uma espécie de conhecimento tácito comum de que hole punching baseado em TCP é mais difícil do que UDP, então não se deve fazer; mas, na prática, a complexidade adicional não parece grande em comparação com o fluxo UDP, que já é complexo
O texto também reconhece que a travessia de NAT com TCP é possível, mas acrescenta complexidade e, se você for mais fundo, pode até exigir modificações no kernel. Ainda assim, vejo isso como substituir a parte que inicia a conexão com pacotes UDP brutos por pacotes TCP SYN e suporte a abertura simultânea (simultaneous open)
Considerando especialmente que existem redes, como o Wi‑Fi de convidados da UC Berkeley, que bloqueiam todo envio UDP exceto DNS, é uma pena tratar o hole punching TCP de forma superficial só porque “é mais difícil que UDP”. Vejo como algo praticamente tão viável quanto, com complexidade adicional limitada
https://ttcplinux.sourceforge.net/documents/one/tcpstate/tcp...
Portanto, a complexidade adicional de fazer abertura simultânea com TCP é bem pequena. A dificuldade central é transmitir o mapeamento público e coordenar o punching/abertura “simultâneo”, e isso normalmente também é necessário em UDP
Um ponto extra de complexidade em TCP é que, em vez de criar pacotes TCP SYN falsos, é preciso fazer uma chamada real a
connect(). Isso porque alguns firewalls olham os números de sequênciaDito isso, hole punching TCP pode se parecer muito mais com um SYN flood do que pacotes UDP, então em algumas redes a taxa de sucesso pode ser menor. Na prática, ainda não vi muita filtragem
Hole punching TCP é bem interessante. Implementei calculando o “deslocamento do relógio” — o quanto o relógio do sistema está divergente em relação ao NTP — por meio de várias medições NTP, e fazendo o iniciador definir um horário de encontro futuro com base no NTP. É mais preciso do que se imagina, e o hole punching TCP também funciona entre sockets da mesma interface
O motivo de dar suporte a esse modo estranho de punching local é que, se o punching dentro do próprio host funciona com esse nível de eficiência, há uma boa chance de que também seja rápido o suficiente na LAN e na internet. O código é em Python, e a primeira tentativa foi bem chocante. Como o hole punching TCP é sensível a timing, o uso em Python de gerenciamento manual de sockets à moda antiga, threading e um event loop improvisado com base em experiência com sockets em C acabou falhando
Para fazer aquele código funcionar, foi necessário aumentar a prioridade do processo Python, para que outros processos não criassem atrasos entre as tentativas de punching. Em uma implementação ineficiente, ele é sensível a tempo nesse nível. A implementação atual usa um pool de processos, cada um com seu próprio event loop, cria uma lista de tarefas distribuídas no tempo e faz cada tarefa reutilizar o mesmo socket para abrir a conexão. Depois de testar nos principais sistemas operacionais, concluí que essa é a melhor abordagem em Python
Concordo que a dificuldade de hole punching TCP e UDP é parecida. Em ambos, a parte mais difícil é a etapa de previsão de NAT. Ainda não usei código para contornar NAT simétrico, mas já começo a ver como integrá-lo ou transformá-lo em um novo plugin
A tabela de estados do roteador é muito pequena, e algumas técnicas de punching são bem agressivas. Por exemplo, ao abrir centenas de conexões TCP, como em algoritmos que tentam contornar NAT simétrico, você pode acabar colocando o roteador em estado de negação de serviço
Com UDP, por causa das otimizações de gerenciamento de estado, talvez seja menos provável que o punching derrube o roteador inteiro. Mas isso é só uma suposição
O efeito é curiosamente bom, mas quando alguém fala em colocar isso em uma rede corporativa de produção, por algum motivo dá uma certa insegurança
Parece arriscado porque a sensação é de contornar o NAT e os firewalls tradicionais e depender, em vez disso, de uma única ACL em software. Por exemplo, se houver Tailscale em uma VM abandonada no ambiente de testes da AWS e um atacante conseguir acesso a ela, parece que surge um caminho até um notebook na rede corporativa interna em que, depois de passar pelo kernel, apenas o código de ACL do Tailscale em espaço de usuário decide permitir/bloquear
Não sei se seria possível perceber caso alguém não autorizado chegasse até esse ponto
É muito fácil atravessar NAT e a maioria dos filtros com rastreamento de estado associados a ele. Já implementei coisas assim como produto comercial em ambientes corporativos reais de produção, e não é mágica; é uma técnica bem conhecida por profissionais da área
Se você quer filtragem real de pacotes, ou seja, um firewall, precisa colocar uma instância de firewall separada do NAT e definir regras adequadas. Mesmo assim, isso ajuda principalmente a reduzir o volume de tráfego; o ganho real de segurança do firewall em si hoje é pequeno. Isso porque a maioria dos ataques entra por camadas superiores, como HTTP/HTTPS, POP/IMAP
Na prática, é o firewall stateful que faz a maior parte do trabalho, mas o NAT leva o crédito. O Tailscale não elimina o firewall; ele oferece uma configuração muito mais abrangente, baseada em ACLs corretas
Dito isso, reconheço que as ferramentas de ACL do Tailscale ainda têm bastante espaço para melhorar
Como consequência, a pilha de rede do Windows também mudou bastante e ficou mais complexa. Depois que o WireGuard entrou no Linux, todo mundo passou a ter pelo menos uma VPN conectando a algum VPS em algum lugar. Por não sabermos o que não sabemos, a situação real provavelmente é pior do que parece
Firewall é outro conceito. Ainda assim, falando da relação entre conectividade e segurança, é triste e inquietante que a segurança na internet sempre tenha dependido de bloquear pacotes com base na porta de destino
É a realidade de fazer o que é fácil em vez do que é correto e ainda chamar isso de “solução profissional”
Ainda assim, algo de dentro precisa falar primeiro com o lado de fora; portanto, um firewall de verdade precisa gerenciar tanto conexões de saída quanto de entrada por listas de permissão
Em outras palavras, se você depende de segurança de perímetro, é só uma questão de tempo até alguém descobrir qual é o “colete refletivo” da sua organização
Seria bom ter uma alternativa parecida com o Tailscale sem criptografia de conexão para dispositivos que já criptografam na camada de aplicação. Como acontece com praticamente toda a internet, nem sempre é necessário criptografar também as camadas inferiores
Em dispositivos de baixo consumo, por exemplo dispositivos IoT rodando um túnel parecido com o Tailscale, o custo computacional é especialmente alto
Túneis GRE existem e são bastante usados na prática, mas não resolvem UDP hole punching, então exigem uma arquitetura hub-and-spoke. Com GRE, isto é,
ip fou, não dá para criar uma malha entre paresFico curioso se existe alguma biblioteca que, depois de um handshake criptográfico para verificação de identidade, forneça UDP hole punching e túneis GRE sem criptografia
Se você quer algo voltado a conectividade mais genérica, o libp2p pode ficar perto do que procura
https://datatracker.ietf.org/doc/html/rfc8445
https://github.com/pion/webrtc
https://github.com/algesten/str0m
https://libp2p.io
Foi escrito em Python. Só que, como a maior parte do código de rede, não parte do pressuposto de uso da interface padrão. Eu queria permitir rodar serviços em qualquer interface desejada, para possibilitar coisas mais variadas e úteis
É baseado principalmente em módulos da biblioteca padrão. Não gosto de extensões em C porque elas frequentemente quebram pacotes multiplataforma
Ao ver a parte em que os pares precisam saber de antemão o
ip:portusado pelo outro lado e que foi criado um servidor de coordenação para sincronizar isso, dá vontade de pensar que seria bom se o SIP fizesse jus ao nomeSIP significa Session Initiation Protocol, ou seja, pelo nome deveria ser capaz de iniciar também sessões arbitrárias, como uma VPN, mas na prática virou uma bagunça complexa demais para valer o custo. Imagino que ele tenha sido originalmente criado como um canal lateral de comunicação para estabelecer streams RTP P2P
Parece HTTP, mas tem estado, é bidirecional, federado e também funciona sobre UDP
A quantidade que o baresip implementa só para fazer SIP é enorme, incluindo até TLS over UDP. E nem é inchaço: esses recursos são realmente necessários
É um texto de 2020. As discussões anteriores são as seguintes
2022: https://news.ycombinator.com/item?id=30707711
2020: https://news.ycombinator.com/item?id=24241105
https://news.ycombinator.com/item?id=30707711
https://news.ycombinator.com/item?id=24241105
How NAT traversal works (2020) - https://news.ycombinator.com/item?id=36969018 - agosto de 2023, 106 comentários
How NAT traversal works (2020) - https://news.ycombinator.com/item?id=30707711 - março de 2022, 37 comentários
How NAT Traversal Works - https://news.ycombinator.com/item?id=24241105 - agosto de 2020, 28 comentários
Para referência, o motivo de os links não ficarem clicáveis era que linhas recuadas por dois espaços ou mais são formatadas como código: https://news.ycombinator.com/formatdoc
Este é justamente o texto que eu enviava às pessoas ao explicar NAT traversal
Talvez continuemos dependendo desse método ao criar apps P2P. O IPv6 não ganhou tração suficiente, e NAT e roteamento SNI resolvem a maior parte dos problemas para a maioria das pessoas
Do ponto de vista dos ISPs, também há pouco incentivo para fazer essa situação mudar
Acho que está entre os textos mais detalhados sobre NAT traversal em toda a internet. Só que faltam informações sobre comportamento de delta
Não é nada complicado: significa que alguns NATs têm padrões observáveis ao atribuir portas externas consecutivas. O padrão mais comum é preservar a porta de origem, mas também pode haver padrões como incrementar em 1 a partir do mapeamento anterior
Em teoria é um texto excelente, mas fico curioso sobre até que ponto um engenheiro de software conseguiria aproveitá-lo na prática. Ele explica muita coisa, mas talvez não traga detalhes suficientes para escrever um algoritmo. Por exemplo, não sei se, só com este texto, alguém conseguiria escrever um algoritmo para testar o tipo de NAT ou ajustar seu próprio código de hole punching
Pessoalmente, já vi artigos em que uma tabela simples foi mais útil do que um texto tão longo. Ainda assim, pode ser um bom ponto de partida
A última seção do texto é especialmente importante. Pode haver uma forma de contornar NAT simétrico usado em sistemas móveis. Pesquisas recentes sobre NAT traversal usam técnicas parecidas e afirmam ter taxas de sucesso próximas de 100%
É um texto interessante que faz lembrar o passado. Em 2010, criei uma rede mesh P2P oblivious que usava esse tipo de abordagem
Na época, as pessoas não se importavam com segurança tanto quanto imaginávamos, e ainda hoje continuam não se importando o bastante. Há mais dispositivos e mais valor envolvido, mas eles ainda são bastante inseguros
Raiz de confiança em hardware, uma cadeia de confiança segura para autenticação/autorização e endpoints realmente seguros com permissões temporárias mínimas ainda são difíceis; e, em redes domésticas, corporativas e de grandes data centers de produção, o teatro da segurança de perímetro de rede continua
A única razão pela qual essas coisas não parecem ser a principal causa-raiz das violações de segurança é que ainda há muitos caminhos de ataque mais fáceis por aí
Fugindo um pouco do assunto, há algumas semanas li um pouco sobre essa área sem saber praticamente nada antes
A impressão que tive foi que IPv6 eliminaria tudo isso e faria com que NAT traversal deixasse de ser necessário. Se for assim, fico me perguntando por que o IPv6 não é mais amplamente usado e como começar com ele na rede doméstica e na VPN Tailscale
Também faltam incentivos comerciais
O simples fato de algo assim ter surgido em vez do IPv6 mostra bem a força de um hack bom o bastante