Eu tenho um servidor wireguard rodando em casa para poder entrar na minha rede doméstica de fora. Wireguard está usando UDP. Infelizmente não posso simplesmente abrir uma porta IPv4 UDP na minha fritz box como funcionava antigamente porque meu provedor me colocou atrás do CG-NAT, tenho que usar o IPv6, mas o IPv6 ainda não pode ser usado no meu local de trabalho.
Então em casa tenho IPv6 e no trabalho tenho IPv4 e não tenho como acessar minha rede doméstica.
Também tenho um VPS com IPv4 público no qual posso estabelecer túneis SSH reversos para tráfego TCP. Infelizmente, os túneis ssh suportam apenas TCP e não se deve fazer conexões VPN sobre TCP de qualquer maneira, e é por isso que o wireguard está usando apenas UDP.
VPN-Server VPN-Client
(UDP) | | (IPv4 UDP)
| | | not possible |
-----------|--------| <-------------------
| |
NAT CG-NAT
VPN-Server VPN-Client
(UDP) (IPv4 UDP)
| |
| |
inside agent outside agent
tunnel tunnel
|| | | ||
|| punch| punch| ||
| --------->|-------->|-------------------- |
-----------|<--------|<--------------------
| |
| |
NAT CG-NAT
O agente externo está rodando em um VPS com IPv4 público
O agente interno enviará datagramas UDP para o IP público e a porta do agente externo, isso perfurará ambos os NATs, o agente externo receberá esses datagramas e aprenderá com seu endereço de origem e porta como enviar datagramas de volta para o agente interno .
O cliente VPN pode enviar UDP para o agente externo e esses datagramas serão encaminhados para o agente interno e de lá para o servidor VPN, o servidor VPN pode responder ao agente interno, estes serão encaminhados para o agente externo e de lá para o cliente VPN.
O agente externo aparecerá como o servidor VPN para o cliente e o agente interno aparecerá como o cliente VPN para o servidor.
Múltiplas conexões de cliente são possíveis porque ele usará um túnel e um novo soquete no agente interno para cada novo cliente conectado do lado de fora, então para o servidor aparecerá como se vários clientes estivessem rodando no host do agente interno.
Clone ou descompacte o código em ambas as máquinas e construa-o. Você precisa de pelo menos make e gcc. Digite o diretório de origem e use o comando
$ make
Após a compilação bem-sucedida, você terá o udp-tunnel
na mesma pasta. Agora você pode iniciá-lo diretamente de um terminal (com as opções certas, é claro) para fazer alguns testes rápidos, ou pode instalá-lo com a ajuda do makefile.
O binário compilado udp-tunnel
pode atuar como agente interno ou externo, dependendo de quais opções você passa na linha de comando.
Suponha como exemplo que o servidor VPN está escutando em UDP 1234, rodando em localhost (o mesmo que o agente interno) e a máquina externa é jump.example.com e queremos que ela escute na porta UDP 9999.
No host interno, começamos com
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999
No host externo, começamos com
$ ./udp-tunnel -l 9999
O makefile contém 3 alvos de instalação: install
para instalar apenas o binário, install-outside
e install-inside
para instalar também os arquivos de serviço do systemd. Os dois últimos precisam de variáveis passadas para fazer funcionar corretamente.
Para instalar o agente externo no host de salto (assumindo que você deseja a porta 9999), execute este comando:
$ sudo make install-outside listen=9999
Isso instalará o binário em /usr/local/bin/
e instalará um arquivo de serviço systemd em /etc/systemd/system/
contendo o comando necessário para iniciá-lo no modo de agente externo com porta 9999.
Para instalar o agente interno na máquina interna use o seguinte comando (assumindo como exemplo seu servidor VPN é localhost:1234 e seu host de salto é jump.example.com):
$ sudo make install-inside service=localhost:1234 outside=jump.example.com:9999
Isso instalará novamente o binário em /usr/local/bin/
e um arquivo de unidade systemd em /etc/systemd/system/
Neste ponto, você pode querer dar uma olhada rápida nos arquivos da unidade systemd para ver como o binário é usado e verificar se as opções estão corretas. As opções devem ser semelhantes às descritas acima no teste rápido.
Depois que os arquivos systemd forem instalados e confirmados como corretos, eles ainda não estão habilitados para inicialização automática, você precisa habilitá-los e iniciá-los. Na máquina interna:
$ sudo systemctl enable udp-tunnel-inside.service
$ sudo systemctl start udp-tunnel-inside.service
e na máquina externa
$ sudo systemctl enable udp-tunnel-outside.service
$ sudo systemctl start udp-tunnel-outside.service
Não há criptografia. Os pacotes são encaminhados como estão, presume-se que qualquer serviço que você esteja encapsulando saiba como proteger ou criptografar seus dados por conta própria. Geralmente este é o caso das conexões VPN.
Além disso, um invasor pode querer falsificar os pacotes de manutenção de atividade do agente interno para confundir o agente externo e desviar o túnel para sua própria máquina, resultando na interrupção do serviço. Para evitar este ataque muito simples, os datagramas keepalive podem ser autenticados com um código de autenticação de mensagem baseado em hash. Você pode usar uma senha pré-compartilhada usando a opção -k em ambas as extremidades do túnel para ativar esse recurso.
No host interno você usaria assim
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999 -k mysecretpassword
No host externo você começaria
$ ./udp-tunnel -l 9999 -k mysecretpassword
Depois que as etapas de instalação acima funcionarem com sucesso, você pode querer editar manualmente seus arquivos systemd em ambas as extremidades e adicionar uma opção -k, recarregar e reiniciar em ambas as extremidades.
A mensagem keepalive conterá então um SHA-256 sobre esta senha e sobre um nonce estritamente crescente que só pode ser usado exatamente uma vez para evitar ataques simples de repetição.
Ambos os agentes mantêm uma lista de conexões, cada conexão armazena endereços de soquete e identificadores de soquete associados a essa conexão de cliente específica.
Na inicialização, o agente interno iniciará um túnel de saída para o agente externo, marcará-o como não utilizado e enviará pacotes de manutenção de atividade por ele. O agente externo verá esses pacotes e adicionará esse túnel com seu endereço de origem à sua própria lista de conexões. Os pacotes keepalive são assinados com um nonce e um código de autenticação, portanto, não podem ser falsificados ou reproduzidos.
Quando um cliente se conecta, o agente externo enviará os dados do cliente por aquele túnel sobressalente não utilizado, o agente interno verá isso, marcará o túnel como ativo, criará um soquete para comunicação com o serviço e encaminhará os dados em ambas as direções. Em seguida, ele também criará imediatamente outro novo túnel sobressalente de saída para estar pronto para o próximo cliente de entrada.
Quando o novo túnel sobressalente chegar ao agente externo, ele o adicionará à sua própria lista de conexões para poder atender imediatamente o próximo cliente conectado.
Neste ponto ambos os agentes possuem 2 túneis em sua lista, um está ativo e outro sobressalente, aguardando a próxima conexão do cliente.
os tempos limite de inatividade garantirão que os túneis mortos (aqueles que foram marcados como ativos no passado, mas sem dados do cliente por algum tempo) serão excluídos e seus soquetes serão fechados. O agente interno irá detectar a falta de dados encaminhados por um tempo prolongado, removê-los de sua própria lista e fechar seus soquetes, então algum tempo depois o agente externo detectará que não há mais keepalives chegando para esta conexão e removê-los de sua própria lista também.
Este código ainda é altamente experimental, então não baseie nele um negócio multimilionário, pelo menos não ainda. Ele atende perfeitamente ao meu propósito, mas pode travar, queimar e explodir seu servidor para você. Você foi avisado.