ipipou: més que un túnel sense xifrar

Què li estem dient al Déu de l'IPv6?

ipipou: més que un túnel sense xifrar
És cert, avui diem el mateix al déu del xifratge.

Aquí parlarem d'un túnel IPv4 no xifrat, però no d'un de "làmpada calenta", sinó d'un de "LED" modern. I també hi ha endolls en brut parpellejant aquí, i s'està treballant amb paquets a l'espai d'usuari.

Hi ha N protocols de túnel per a cada gust i color:

  • elegant, de moda, juvenil WireGuard
  • multifuncional, com els ganivets suïssos, OpenVPN i SSH
  • vell i no malvat GRE
  • l'IPIP més senzill, ràpid i completament sense xifrar
  • desenvolupant-se activament GENEVE
  • molts altres.

Però sóc programador, així que augmentaré N només en una fracció i deixaré el desenvolupament de protocols reals als desenvolupadors de Kommersant.

En un no nascut projecteEl que estic fent ara és arribar als amfitrions darrere de NAT des de l'exterior. Utilitzant protocols amb criptografia per a adults per a això, no vaig poder treure la sensació que era com disparar pardals amb un canó. Perquè el túnel s'utilitza en la seva major part només per fer forats a NAT-e, el trànsit intern també s'encripta, però encara s'ofeguen en HTTPS.

Mentre investigava diversos protocols de túnel, l'atenció del meu perfeccionista interior es va cridar una vegada i una altra a IPIP a causa de la seva mínima sobrecàrrega. Però té un inconvenient i mig significatiu per a les meves tasques:

  • requereix IP públiques d'ambdós costats,
  • i cap autenticació per a tu.

Per tant, el perfeccionista va ser conduït de nou al racó fosc del crani, o allà on s'asseu.

I després un dia, mentre llegia articles sobre túnels suportats de forma nativa a Linux em vaig trobar amb FOU (Foo-over-UDP), és a dir. sigui el que sigui, embolicat en UDP. Fins ara, només s'admeten IPIP i GUE (Encapsulació UDP genèrica).

"Aquí està la bala de plata! Un simple IPIP és suficient per a mi". - Vaig pensar.

De fet, la bala va resultar no ser completament platejada. L'encapsulació en UDP resol el primer problema: podeu connectar-vos als clients darrere de NAT des de l'exterior mitjançant una connexió preestablerta, però aquí la meitat del següent inconvenient de l'IPIP floreix amb una nova llum: qualsevol persona d'una xarxa privada pot amagar-se darrere del visible. IP pública i port de client (en IPIP pur aquest problema no existeix).

Per resoldre aquest problema i mig, va néixer la utilitat ipipou. Implementa un mecanisme casolà per autenticar un host remot, sense interrompre el funcionament de la FOU del nucli, que processarà de manera ràpida i eficient els paquets a l'espai del nucli.

No necessitem el teu guió!

D'acord, si coneixeu el port públic i la IP del client (per exemple, tots els que hi ha al darrere no van enlloc, NAT intenta mapejar els ports 1 en 1), podeu crear un túnel IPIP sobre FOU amb el següents ordres, sense cap script.

al 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

al client:

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

on

  • ipipou* — nom de la interfície de xarxa del túnel local
  • 203.0.113.1 — Servidor IP públic
  • 198.51.100.2 — IP pública del client
  • 192.168.0.2 — IP del client assignada a la interfície eth0
  • 10001 — Port de client local per a FOU
  • 20001 — Port públic client per a FOU
  • 10000 — port de servidor públic per a FOU
  • encap-csum — opció per afegir una suma de verificació UDP als paquets UDP encapsulats; es pot substituir per noencap-csum, sense oblidar, la integritat ja està controlada per la capa d'encapsulació exterior (mentre el paquet està dins del túnel)
  • eth0 — interfície local a la qual s'enllaçarà el túnel ipip
  • 172.28.0.1 — IP de la interfície del túnel del client (privada)
  • 172.28.0.0 — Interfície del servidor del túnel IP (privada)

Mentre la connexió UDP estigui viva, el túnel estarà en condicions de funcionament, però si es trenca, tindreu sort -si el port IP: del client segueix sent el mateix - viurà, si canvien - es trencarà.

La manera més senzilla de tornar-ho tot enrere és descarregar els mòduls del nucli: modprobe -r fou ipip

Encara que no sigui necessària l'autenticació, la IP i el port públics del client no sempre es coneixen i sovint són impredictibles o variables (segons el tipus de NAT). Si ometes encap-dport al costat del servidor, el túnel no funcionarà, no és prou intel·ligent per agafar el port de connexió remota. En aquest cas, ipipou també us pot ajudar, o WireGuard i altres com aquest us poden ajudar.

Com funciona?

El client (que normalment està darrere de NAT) obre un túnel (com a l'exemple anterior) i envia un paquet d'autenticació al servidor perquè configuri el túnel al seu costat. Segons la configuració, aquest pot ser un paquet buit (només perquè el servidor pugui veure la IP pública: port de connexió), o amb dades per les quals el servidor pugui identificar el client. Les dades poden ser una frase de contrasenya senzilla en text clar (em ve a la ment l'analogia amb HTTP Basic Auth) o dades dissenyades especialment signades amb una clau privada (similar a HTTP Digest Auth només més forta, vegeu la funció client_auth al codi).

Al servidor (al costat de la IP pública), quan s'inicia ipipou, crea un gestor de cues nfqueue i configura netfilter perquè s'enviïn els paquets necessaris on haurien d'estar: paquets que inicialitzen la connexió a la cua nfqueue, i [quasi] tota la resta va directament a l'oient FOU.

Per a aquells que no ho saben, nfqueue (o NetfilterQueue) és una cosa especial per als aficionats que no saben com desenvolupar mòduls del nucli, que amb netfilter (nftables/iptables) us permet redirigir paquets de xarxa a l'espai de l'usuari i processar-los allà fent servir primitius significa a mà: modificar (opcional) i tornar-lo al nucli, o descartar-lo.

Per a alguns llenguatges de programació hi ha enllaços per treballar amb nfqueue, per a bash no n'hi havia cap (je, no és sorprenent), vaig haver d'utilitzar python: ipipou utilitza NetfilterQueue.

Si el rendiment no és crític, amb aquesta cosa podeu inventar la vostra pròpia lògica de manera relativament ràpida i senzilla per treballar amb paquets a un nivell força baix, per exemple, crear protocols de transferència de dades experimentals o trobar serveis locals i remots amb un comportament no estàndard.

Els endolls en brut funcionen mà a mà amb nfqueue, per exemple, quan el túnel ja està configurat i FOU està escoltant al port desitjat, no podreu enviar un paquet des del mateix port de la manera habitual: està ocupat, però podeu agafar i enviar un paquet generat aleatòriament directament a la interfície de xarxa mitjançant un sòcol en brut, tot i que generar aquest paquet requerirà una mica més de retoc. Així és com es creen els paquets amb autenticació a ipipou.

Com que ipipou només processa els primers paquets de la connexió (i els que van aconseguir filtrar-se a la cua abans d'establir la connexió), el rendiment gairebé no es ressent.

Tan bon punt el servidor ipipou rep un paquet autenticat, es crea un túnel i tots els paquets posteriors de la connexió ja són processats pel nucli sense passar nfqueue. Si la connexió falla, el primer paquet del següent s'enviarà a la cua nfqueue, depenent de la configuració, si no es tracta d'un paquet amb autenticació, sinó des de l'última IP recordada i el port del client, es pot passar. activat o descartat. Si un paquet autenticat prové d'una IP i un port nous, el túnel es reconfigura per utilitzar-los.

L'IPIP-over-FOU habitual té un problema més quan es treballa amb NAT: és impossible crear dos túnels IPIP encapsulats en UDP amb la mateixa IP, perquè els mòduls FOU i IPIP estan bastant aïllats entre si. Aquells. un parell de clients darrere de la mateixa IP pública no es podran connectar simultàniament al mateix servidor d'aquesta manera. En el futur, potser, es resoldrà a nivell del nucli, però això no és segur. Mentrestant, els problemes de NAT es poden resoldre mitjançant NAT: si passa que un parell d'adreces IP ja està ocupada per un altre túnel, ipipou farà NAT d'una IP pública a una privada alternativa, voilà! - pots crear túnels fins que s'acabin els ports.

Perquè No tots els paquets de la connexió estan signats, aleshores aquesta protecció senzilla és vulnerable a MITM, de manera que si hi ha un dolent a l'aguait al camí entre el client i el servidor que pot escoltar el trànsit i manipular-lo, pot redirigir els paquets autenticats a través de una altra adreça i creeu un túnel des d'un amfitrió no fiable.

Si algú té idees sobre com solucionar-ho tot deixant la major part del trànsit al nucli, no dubteu a parlar.

Per cert, l'encapsulació en UDP s'ha demostrat molt bé. En comparació amb l'encapsulació per IP, és molt més estable i sovint més ràpid malgrat la sobrecàrrega addicional de la capçalera UDP. Això es deu al fet que la majoria dels amfitrions a Internet només funcionen bé amb els tres protocols més populars: TCP, UDP, ICMP. La part tangible pot descartar completament tota la resta, o processar-la més lentament, perquè només està optimitzada per a aquests tres.

Per exemple, aquesta és la raó per la qual QUICK, en què es basa HTTP/3, es va crear sobre UDP, i no sobre IP.

Bé, prou paraules, és hora de veure com funciona al "món real".

Batalla

S'utilitza per emular el món real iperf3. Pel que fa al grau de proximitat a la realitat, això és aproximadament el mateix que emular el món real a Minecraft, però de moment ho farà.

Participants al concurs:

  • canal principal de referència
  • l'heroi d'aquest article és ipipou
  • OpenVPN amb autenticació però sense xifratge
  • OpenVPN en mode tot inclòs
  • WireGuard sense PresharedKey, amb MTU=1440 (només des d'IPv4)

Dades tècniques per frikis
Les mètriques es prenen amb les ordres següents:

al client:

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

al servidor (s'executa simultàniament amb el client):

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"

Configuració del 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

client
/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 (sense xifratge, amb autenticació)
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

client

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 (amb xifratge, autenticació, mitjançant UDP, tot com s'esperava)
Configurat utilitzant openvpn-manage

protector de fil
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

client
/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

Troballes

Signe lleig humit
La càrrega de la CPU del servidor no és gaire indicativa, perquè... Hi ha molts altres serveis que hi funcionen, de vegades es consumeixen 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: més que un túnel sense xifrar

ipipou: més que un túnel sense xifrar

canal per 1 Gbps optimista

ipipou: més que un túnel sense xifrar

ipipou: més que un túnel sense xifrar

En tots els casos, ipipou s'acosta bastant en rendiment al canal base, que és genial!

El túnel openvpn sense xifrar es va comportar de manera força estranya en ambdós casos.

Si algú el prova, serà interessant rebre comentaris.

Que IPv6 i NetPrickle ens acompanyin!

Font: www.habr.com

Afegeix comentari