ipipou: mais do que apenas um túnel não criptografado
O que estamos dizendo ao Deus do IPv6?
É isso mesmo, diremos o mesmo ao deus da criptografia hoje.
Aqui falaremos sobre um túnel IPv4 não criptografado, mas não sobre um túnel de “lâmpada quente”, mas sobre um túnel moderno de “LED”. E também há soquetes brutos piscando aqui, e o trabalho está em andamento com pacotes no espaço do usuário.
Existem N protocolos de tunelamento para todos os gostos e cores:
Mas eu sou um programador, então aumentarei N apenas uma fração e deixarei o desenvolvimento de protocolos reais para os desenvolvedores do Kommersant.
Em um não nascido projetoO que estou fazendo agora é alcançar hosts por trás do NAT de fora. Usando protocolos com criptografia adulta para isso, não consegui afastar a sensação de que era como atirar em pardais com um canhão. Porque o túnel é usado em sua maior parte apenas para abrir buracos no NAT-e, o tráfego interno geralmente também é criptografado, mas ainda se afoga em HTTPS.
Ao pesquisar vários protocolos de tunelamento, a atenção do meu perfeccionista interior foi atraída repetidamente para o IPIP devido à sua sobrecarga mínima. Mas tem uma desvantagem significativa e meia para minhas tarefas:
requer IPs públicos de ambos os lados,
e nenhuma autenticação para você.
Portanto, o perfeccionista foi empurrado de volta para o canto escuro do crânio, ou onde quer que ele esteja sentado.
E uma vez, enquanto lia artigos sobre túneis com suporte nativo no Linux me deparei com FOU (Foo-over-UDP), ou seja, tanto faz, embrulhado em UDP. Até agora, apenas IPIP e GUE (Encapsulamento UDP Genérico) são suportados.
“Aqui está a bala de prata! Um simples IPIP é suficiente para mim.” - Eu pensei.
Na verdade, a bala não era totalmente prateada. O encapsulamento em UDP resolve o primeiro problema - você pode se conectar a clientes por trás do NAT de fora usando uma conexão pré-estabelecida, mas aqui metade da próxima desvantagem do IPIP floresce sob uma nova luz - qualquer pessoa de uma rede privada pode se esconder atrás do visível IP público e porta cliente (em IPIP puro esse problema não existe).
Para resolver esse problema e meio, nasceu o utilitário ipipou. Ele implementa um mecanismo caseiro para autenticar um host remoto, sem interromper a operação do kernel FOU, que processará pacotes no espaço do kernel de forma rápida e eficiente.
Não precisamos do seu roteiro!
Ok, se você conhece a porta pública e o IP do cliente (por exemplo, todos por trás dele não vão a lugar nenhum, o NAT tenta mapear as portas 1 em 1), você pode criar um túnel IPIP sobre FOU com o seguintes comandos, sem nenhum script.
no servidor:
# Подгрузить модуль ядра FOU
modprobe fou
# Создать IPIP туннель с инкапсуляцией в FOU.
# Модуль ipip подгрузится автоматически.
ip link add name ipipou0 type ipip
remote 198.51.100.2 local 203.0.113.1
encap fou encap-sport 10000 encap-dport 20001
mode ipip dev eth0
# Добавить порт на котором будет слушать FOU для этого туннеля
ip fou add port 10000 ipproto 4 local 203.0.113.1 dev eth0
# Назначить IP адрес туннелю
ip address add 172.28.0.0 peer 172.28.0.1 dev ipipou0
# Поднять туннель
ip link set ipipou0 up
no cliente:
modprobe fou
ip link add name ipipou1 type ipip
remote 203.0.113.1 local 192.168.0.2
encap fou encap-sport 10001 encap-dport 10000 encap-csum
mode ipip dev eth0
# Опции local, peer, peer_port, dev могут не поддерживаться старыми ядрами, можно их опустить.
# peer и peer_port используются для создания соединения сразу при создании FOU-listener-а.
ip fou add port 10001 ipproto 4 local 192.168.0.2 peer 203.0.113.1 peer_port 10000 dev eth0
ip address add 172.28.0.1 peer 172.28.0.0 dev ipipou1
ip link set ipipou1 up
onde
ipipou* — nome da interface de rede do túnel local
203.0.113.1 — servidor IP público
198.51.100.2 — IP público do cliente
192.168.0.2 — IP do cliente atribuído à interface eth0
10001 — porta cliente local para FOU
20001 — porta cliente pública para FOU
10000 — porta do servidor público para FOU
encap-csum — opção para adicionar uma soma de verificação UDP a pacotes UDP encapsulados; pode ser substituído por noencap-csum, sem falar que a integridade já é controlada pela camada de encapsulamento externa (enquanto o pacote está dentro do túnel)
eth0 — interface local à qual o túnel ipip será vinculado
172.28.0.1 — IP da interface do túnel do cliente (privado)
172.28.0.0 — IP da interface do túnel do servidor (privado)
Enquanto a conexão UDP estiver ativa, o túnel estará funcionando, mas se ele quebrar, você terá sorte - se a porta IP: do cliente permanecer a mesma - ele permanecerá ativo, se eles mudarem - ele irá quebrar.
A maneira mais fácil de reverter tudo é descarregar os módulos do kernel: modprobe -r fou ipip
Mesmo que a autenticação não seja necessária, o IP público e a porta do cliente nem sempre são conhecidos e muitas vezes são imprevisíveis ou variáveis (dependendo do tipo de NAT). Se você omitir encap-dport no lado do servidor, o túnel não funcionará, não é inteligente o suficiente para usar a porta de conexão remota. Nesse caso, o ipipou também pode ajudar, ou o WireGuard e outros semelhantes podem ajudá-lo.
Como isso funciona?
O cliente (que geralmente está atrás do NAT) abre um túnel (como no exemplo acima) e envia um pacote de autenticação ao servidor para que ele configure o túnel ao seu lado. Dependendo das configurações, pode ser um pacote vazio (apenas para que o servidor possa ver o IP público: porta de conexão), ou com dados pelos quais o servidor possa identificar o cliente. Os dados podem ser uma senha simples em texto não criptografado (a analogia com HTTP Basic Auth vem à mente) ou dados especialmente projetados assinados com uma chave privada (semelhante ao HTTP Digest Auth, apenas mais forte, consulte a função client_auth no código).
No servidor (lado com IP público), quando o ipipou é iniciado, ele cria um manipulador de fila nfqueue e configura o netfilter para que os pacotes necessários sejam enviados para onde deveriam estar: pacotes inicializando a conexão com a fila nfqueue, e [quase] todo o resto vai direto para o ouvinte FOU.
Para quem não sabe, nfqueue (ou NetfilterQueue) é uma coisa especial para amadores que não sabem desenvolver módulos de kernel, que usando netfilter (nftables/iptables) permite redirecionar pacotes de rede para o espaço do usuário e processá-los lá usando primitivo significa disponível: modificar (opcional) e devolvê-lo ao kernel ou descartá-lo.
Para algumas linguagens de programação existem ligações para trabalhar com nfqueue, para bash não havia nenhuma (heh, não é surpreendente), tive que usar python: ipipou usa NetfilterQueue.
Se o desempenho não for crítico, usando isso você pode criar sua própria lógica de forma relativamente rápida e fácil para trabalhar com pacotes em um nível bastante baixo, por exemplo, criar protocolos experimentais de transferência de dados ou rastrear serviços locais e remotos com comportamento fora do padrão.
Os soquetes brutos trabalham lado a lado com o nfqueue, por exemplo, quando o túnel já está configurado e o FOU está escutando na porta desejada, você não poderá enviar um pacote da mesma porta da maneira usual - está ocupado, mas você pode pegar e enviar um pacote gerado aleatoriamente diretamente para a interface de rede usando um soquete bruto, embora a geração de tal pacote exija um pouco mais de ajustes. É assim que os pacotes com autenticação são criados no ipipou.
Como o ipipou processa apenas os primeiros pacotes da conexão (e aqueles que conseguiram vazar para a fila antes de a conexão ser estabelecida), o desempenho quase não é prejudicado.
Assim que o servidor ipipou recebe um pacote autenticado, um túnel é criado e todos os pacotes subsequentes na conexão já são processados pelo kernel ignorando o nfqueue. Se a conexão falhar, o primeiro pacote do próximo será enviado para a fila nfqueue, dependendo das configurações, se não for um pacote com autenticação, mas do último IP e porta do cliente lembrado, pode ser passado ligado ou descartado. Se um pacote autenticado vier de um novo IP e porta, o túnel será reconfigurado para utilizá-los.
O IPIP sobre FOU usual tem mais um problema ao trabalhar com NAT - é impossível criar dois túneis IPIP encapsulados em UDP com o mesmo IP, pois os módulos FOU e IPIP são bastante isolados um do outro. Aqueles. um par de clientes atrás do mesmo IP público não poderá se conectar simultaneamente ao mesmo servidor dessa forma. No futuro, talvez, será resolvido no nível do kernel, mas isso não é certo. Enquanto isso, os problemas de NAT podem ser resolvidos pelo NAT - se acontecer de um par de endereços IP já estar ocupado por outro túnel, o ipipou fará o NAT do IP público para um IP privado alternativo, voila! - você pode criar túneis até que as portas acabem.
Porque Nem todos os pacotes na conexão são assinados, então essa proteção simples é vulnerável ao MITM, portanto, se houver um bandido à espreita no caminho entre o cliente e o servidor que possa ouvir o tráfego e manipulá-lo, ele poderá redirecionar os pacotes autenticados através de outro endereço e crie um túnel a partir de um host não confiável.
Se alguém tiver ideias sobre como consertar isso e deixar a maior parte do tráfego no núcleo, não hesite em falar.
A propósito, o encapsulamento em UDP provou ser muito bom. Comparado ao encapsulamento sobre IP, é muito mais estável e muitas vezes mais rápido, apesar da sobrecarga adicional do cabeçalho UDP. Isso se deve ao fato de que a maioria dos hosts na Internet funciona bem apenas com os três protocolos mais populares: TCP, UDP, ICMP. A parte tangível pode descartar completamente todo o resto, ou processá-lo mais lentamente, porque está otimizado apenas para esses três.
Por exemplo, é por isso que o QUICK, no qual o HTTP/3 é baseado, foi criado sobre UDP, e não sobre IP.
Bem, chega de palavras, é hora de ver como funciona no “mundo real”.
Batalha
Usado para emular o mundo real iperf3. Em termos do grau de proximidade com a realidade, isso é aproximadamente o mesmo que emular o mundo real no Minecraft, mas por enquanto servirá.
Participantes da competição:
canal principal de referência
o herói deste artigo é ipipou
OpenVPN com autenticação, mas sem criptografia
OpenVPN em modo tudo incluído
WireGuard sem PresharedKey, com MTU=1440 (desde somente IPv4)
Dados técnicos para geeks As métricas são obtidas com os seguintes comandos:
no cliente:
UDP
CPULOG=NAME.udp.cpu.log; sar 10 6 >"$CPULOG" & iperf3 -c SERVER_IP -4 -t 60 -f m -i 10 -B LOCAL_IP -P 2 -u -b 12M; tail -1 "$CPULOG"
# Где "-b 12M" это пропускная способность основного канала, делённая на число потоков "-P", чтобы лишние пакеты не плодить и не портить производительность.
Sinal úmido e feio
A carga da CPU do servidor não é muito indicativa, porque... Existem muitos outros serviços em execução lá, às vezes eles consomem recursos:
proto bandwidth[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps канал с микрокомпьютера (4 core) до VPS (1 core) через Атлантику
# pure
UDP 20.4 99.80 93.34
TCP 19.2 99.67 96.68
ICMP latency min/avg/max/mdev = 198.838/198.997/199.360/0.372 ms
# ipipou
UDP 19.8 98.45 99.47
TCP 18.8 99.56 96.75
ICMP latency min/avg/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (auth only, no encryption)
UDP 19.3 99.89 72.90
TCP 16.1 95.95 88.46
ICMP latency min/avg/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (full encryption, auth, etc)
UDP 19.6 99.75 72.35
TCP 17.0 94.47 87.99
ICMP latency min/avg/max/mdev = 202.168/202.377/202.900/0.451 ms
# wireguard
UDP 19.3 91.60 94.78
TCP 17.2 96.76 92.87
ICMP latency min/avg/max/mdev = 217.925/223.601/230.696/3.266 ms
## около-1Gbps канал между VPS Европы и США (1 core)
# pure
UDP 729 73.40 39.93
TCP 363 96.95 90.40
ICMP latency min/avg/max/mdev = 106.867/106.994/107.126/0.066 ms
# ipipou
UDP 714 63.10 23.53
TCP 431 95.65 64.56
ICMP latency min/avg/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (auth only, no encryption)
UDP 193 17.51 1.62
TCP 12 95.45 92.80
ICMP latency min/avg/max/mdev = 107.191/107.334/107.559/0.116 ms
# wireguard
UDP 629 22.26 2.62
TCP 198 77.40 55.98
ICMP latency min/avg/max/mdev = 107.616/107.788/108.038/0.128 ms
Canal de 20 Mbps
canal por 1 Gbps otimista
Em todos os casos, o ipipou tem desempenho bastante próximo do canal base, o que é ótimo!
O túnel openvpn não criptografado se comportou de maneira bastante estranha em ambos os casos.
Se alguém for testá-lo, será interessante ouvir feedback.