ipipou: mais do que apenas um túnel não criptografado

O que estamos dizendo ao Deus do IPv6?

ipipou: mais do que apenas um túnel não criptografado
É 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:

  • elegante, moderno, jovem WireGuard
  • multifuncional, como canivetes suíços, OpenVPN e SSH
  • velho e não mau GRE
  • tão simples quanto possível, inteligente e nem um pouco criptografado IPIP
  • desenvolvendo ativamente GENEVE
  • muitos outros.

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", чтобы лишние пакеты не плодить и не портить производительность.

TCP

CPULOG=NAME.tcp.cpu.log; sar 10 6 >"$CPULOG" & iperf3 -c SERVER_IP -4 -t 60 -f m -i 10 -B LOCAL_IP -P 2; tail -1 "$CPULOG"

Latência ICMP

ping -c 10 SERVER_IP | tail -1

no servidor (executado simultaneamente com o cliente):

UDP

CPULOG=NAME.udp.cpu.log; sar 10 6 >"$CPULOG" & iperf3 -s -i 10 -f m -1; tail -1 "$CPULOG"

TCP

CPULOG=NAME.tcp.cpu.log; sar 10 6 >"$CPULOG" & iperf3 -s -i 10 -f m -1; tail -1 "$CPULOG"

Configuração do túnel

ipipou
servidor
/etc/ipipou/server.conf:

server
number 0
fou-dev eth0
fou-local-port 10000
tunl-ip 172.28.0.0
auth-remote-pubkey-b64 eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-secret topsecret
auth-lifetime 3600
reply-on-auth-ok
verb 3

systemctl start ipipou@server

cliente
/etc/ipipou/client.conf:

client
number 0
fou-local @eth0
fou-remote SERVER_IP:10000
tunl-ip 172.28.0.1
# pubkey of auth-key-b64: eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-key-b64 RuBZkT23na2Q4QH1xfmZCfRgSgPt5s362UPAFbecTso=
auth-secret topsecret
keepalive 27
verb 3

systemctl start ipipou@client

openvpn (sem criptografia, com autenticação)
servidor

openvpn --genkey --secret ovpn.key  # Затем надо передать ovpn.key клиенту
openvpn --dev tun1 --local SERVER_IP --port 2000 --ifconfig 172.16.17.1 172.16.17.2 --cipher none --auth SHA1 --ncp-disable --secret ovpn.key

cliente

openvpn --dev tun1 --local LOCAL_IP --remote SERVER_IP --port 2000 --ifconfig 172.16.17.2 172.16.17.1 --cipher none --auth SHA1 --ncp-disable --secret ovpn.key

openvpn (com criptografia, autenticação, via UDP, tudo conforme o esperado)
Configurado usando openvpn-gerenciar

guarda-fios
servidor
/etc/wireguard/server.conf:

[Interface]
Address=172.31.192.1/18
ListenPort=51820
PrivateKey=aMAG31yjt85zsVC5hn5jMskuFdF8C/LFSRYnhRGSKUQ=
MTU=1440

[Peer]
PublicKey=LyhhEIjVQPVmr/sJNdSRqTjxibsfDZ15sDuhvAQ3hVM=
AllowedIPs=172.31.192.2/32

systemctl start wg-quick@server

cliente
/etc/wireguard/client.conf:

[Interface]
Address=172.31.192.2/18
PrivateKey=uCluH7q2Hip5lLRSsVHc38nGKUGpZIUwGO/7k+6Ye3I=
MTU=1440

[Peer]
PublicKey=DjJRmGvhl6DWuSf1fldxNRBvqa701c0Sc7OpRr4gPXk=
AllowedIPs=172.31.192.1/32
Endpoint=SERVER_IP:51820

systemctl start wg-quick@client

Descobertas

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

ipipou: mais do que apenas um túnel não criptografado

ipipou: mais do que apenas um túnel não criptografado

canal por 1 Gbps otimista

ipipou: mais do que apenas um túnel não criptografado

ipipou: mais do que apenas um túnel não criptografado

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.

Que o IPv6 e o ​​NetPrickle estejam conosco!

Fonte: habr.com

Adicionar um comentário