ipipou: molto più di un semplice tunnel non crittografato

Cosa stiamo dicendo al Dio di IPv6?

ipipou: molto più di un semplice tunnel non crittografato
Esatto, diremo la stessa cosa oggi al dio della crittografia.

Qui parleremo di un tunnel IPv4 non crittografato, ma non di uno “lampada calda”, ma di un moderno “LED”. E qui lampeggiano anche socket grezzi e il lavoro è in corso con i pacchetti nello spazio utente.

Esistono protocolli di tunneling N per ogni gusto e colore:

  • elegante, alla moda, giovanile Gabbia di protezione
  • multifunzionali, come Swiss Knife, OpenVPN e SSH
  • GRE vecchio e non malvagio
  • l'IPIP più semplice, veloce e completamente non crittografato
  • sviluppandosi attivamente GENEVE
  • molti altri.

Ma sono un programmatore, quindi aumenterò N solo di una frazione e lascerò lo sviluppo di protocolli reali agli sviluppatori di Kommersant.

In un nascituro progettoQuello che sto facendo ora è raggiungere gli host dietro NAT dall’esterno. Utilizzando protocolli con crittografia per adulti per questo, non potevo liberarmi della sensazione che fosse come sparare ai passeri da un cannone. Perché il tunnel viene utilizzato per la maggior parte solo per creare buchi nel NAT-e, di solito anche il traffico interno è crittografato, ma annega comunque nell'HTTPS.

Durante la ricerca su vari protocolli di tunneling, l'attenzione del mio perfezionista interiore è stata attirata più e più volte da IPIP a causa del suo sovraccarico minimo. Ma presenta uno svantaggio e mezzo significativo per i miei compiti:

  • richiede IP pubblici su entrambi i lati,
  • e nessuna autenticazione per te.

Pertanto, il perfezionista è stato ricacciato nell'angolo oscuro del cranio, o dovunque si sieda lì.

E poi un giorno, mentre leggevo articoli su tunnel supportati nativamente in Linux mi sono imbattuto in FOU (Foo-over-UDP), ovvero qualunque cosa, avvolta in UDP. Finora sono supportati solo IPIP e GUE (Generic UDP Encapsulation).

“Ecco la soluzione miracolosa! A me basta un semplice IPIP.” - Ho pensato.

In effetti, il proiettile si è rivelato non completamente d'argento. L'incapsulamento in UDP risolve il primo problema: è possibile connettersi ai client dietro NAT dall'esterno utilizzando una connessione prestabilita, ma qui metà del successivo inconveniente di IPIP sboccia sotto una nuova luce: chiunque appartenga a una rete privata può nascondersi dietro il visibile IP pubblico e porta client (in IPIP puro questo problema non esiste).

Per risolvere questo problema e mezzo, è nata l'utilità ipipou. Implementa un meccanismo fatto in casa per autenticare un host remoto, senza interrompere il funzionamento del kernel FOU, che elaborerà i pacchetti in modo rapido ed efficiente nello spazio del kernel.

Non abbiamo bisogno della tua sceneggiatura!

Ok, se conosci la porta pubblica e l'IP del client (ad esempio, tutti quelli dietro non vanno da nessuna parte, NAT tenta di mappare le porte 1 in 1), puoi creare un tunnel IPIP-over-FOU con il seguenti comandi, senza script.

sul server:

# Подгрузить модуль ядра 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

sul 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

dove

  • ipipou* — nome dell'interfaccia di rete del tunnel locale
  • 203.0.113.1 — server IP pubblico
  • 198.51.100.2 — IP pubblico del client
  • 192.168.0.2 — IP client assegnato all'interfaccia eth0
  • 10001 — porta client locale per FOU
  • 20001 — porta client pubblica per FOU
  • 10000 — porta del server pubblico per FOU
  • encap-csum — opzione per aggiungere un checksum UDP ai pacchetti UDP incapsulati; può essere sostituito da noencap-csum, per non parlare del fatto che l'integrità è già controllata dallo strato di incapsulamento esterno (mentre il pacchetto è all'interno del tunnel)
  • eth0 — interfaccia locale a cui verrà legato il tunnel ipip
  • 172.28.0.1 — IP dell'interfaccia tunnel client (privato)
  • 172.28.0.0 — Interfaccia server tunnel IP (privata)

Finché la connessione UDP è attiva, il tunnel funzionerà, ma se si rompe, sarai fortunato - se l'IP del client: la porta rimane la stessa - sopravviverà, se cambiano - si romperà.

Il modo più semplice per ripristinare tutto è scaricare i moduli del kernel: modprobe -r fou ipip

Anche se l'autenticazione non è richiesta, l'IP pubblico e la porta del client non sono sempre noti e sono spesso imprevedibili o variabili (a seconda del tipo NAT). Se ometti encap-dport lato server, il tunnel non funzionerà, non è abbastanza intelligente da prendere la porta di connessione remota. In questo caso, anche ipipou può aiutarti, oppure WireGuard e altri simili possono aiutarti.

Come funziona?

Il client (che di solito si trova dietro NAT) apre un tunnel (come nell'esempio sopra) e invia un pacchetto di autenticazione al server in modo che configuri il tunnel dalla sua parte. A seconda delle impostazioni, questo può essere un pacchetto vuoto (solo così che il server possa vedere l'IP pubblico: porta di connessione), o con dati con cui il server può identificare il client. I dati possono essere una semplice passphrase in chiaro (mi viene in mente l'analogia con HTTP Basic Auth) o dati appositamente progettati firmati con una chiave privata (simile a HTTP Digest Auth solo più forte, vedere la funzione client_auth nel codice).

Sul server (il lato con l'IP pubblico), quando ipipou si avvia, crea un gestore della coda nfqueue e configura netfilter in modo che i pacchetti necessari vengano inviati dove dovrebbero essere: pacchetti che inizializzano la connessione alla coda nfqueue e [quasi] tutto il resto va direttamente all'ascoltatore FOU.

Per chi non lo sapesse, nfqueue (o NetfilterQueue) è una cosa speciale per i dilettanti che non sanno come sviluppare moduli del kernel, che utilizzando netfilter (nftables/iptables) consente di reindirizzare i pacchetti di rete nello spazio utente ed elaborarli lì utilizzando primitivo significa a portata di mano: modifica (opzionale) e restituiscilo al kernel, oppure scartalo.

Per alcuni linguaggi di programmazione ci sono collegamenti per lavorare con nfqueue, per bash non ce n'erano (eh, non sorprende), ho dovuto usare python: ipipou usa NetfilterQueue.

Se le prestazioni non sono critiche, utilizzando questa cosa puoi inventare in modo relativamente rapido e semplice la tua logica per lavorare con i pacchetti a un livello abbastanza basso, ad esempio, creare protocolli di trasferimento dati sperimentali o trollare servizi locali e remoti con comportamenti non standard.

I socket grezzi funzionano mano nella mano con nfqueue, ad esempio, quando il tunnel è già configurato e FOU è in ascolto sulla porta desiderata, non sarai in grado di inviare un pacchetto dalla stessa porta nel solito modo: è occupata, ma puoi prendere e inviare un pacchetto generato casualmente direttamente all'interfaccia di rete utilizzando un socket grezzo, anche se generare un pacchetto di questo tipo richiederà un po' più di armeggiare. Ecco come vengono creati i pacchetti con autenticazione in ipipou.

Poiché ipipou elabora solo i primi pacchetti dalla connessione (e quelli che sono riusciti a entrare nella coda prima che la connessione fosse stabilita), le prestazioni quasi non ne risentono.

Non appena il server ipipou riceve un pacchetto autenticato, viene creato un tunnel e tutti i pacchetti successivi nella connessione vengono già elaborati dal kernel bypassando nfqueue. Se la connessione fallisce, il primo pacchetto di quello successivo verrà inviato alla coda nfqueue, a seconda delle impostazioni, se non è un pacchetto con autenticazione, ma dall'ultimo IP e porta client ricordati, può essere passato acceso o scartato. Se un pacchetto autenticato proviene da un nuovo IP e da una nuova porta, il tunnel viene riconfigurato per utilizzarli.

Il solito IPIP-over-FOU ha un ulteriore problema quando si lavora con NAT: è impossibile creare due tunnel IPIP incapsulati in UDP con lo stesso IP, perché i moduli FOU e IPIP sono piuttosto isolati l'uno dall'altro. Quelli. una coppia di client dietro lo stesso IP pubblico non sarà in grado di connettersi contemporaneamente allo stesso server in questo modo. In futuro, forse, verrà risolto a livello di kernel, ma questo non è certo. Nel frattempo, i problemi NAT possono essere risolti dal NAT: se succede che una coppia di indirizzi IP è già occupata da un altro tunnel, ipipou eseguirà il NAT da un IP pubblico a un IP privato alternativo, voilà! - puoi creare tunnel finché le porte non si esauriscono.

Perché Non tutti i pacchetti nella connessione sono firmati, quindi questa semplice protezione è vulnerabile al MITM, quindi se c'è un malintenzionato in agguato sul percorso tra il client e il server che può ascoltare il traffico e manipolarlo, può reindirizzare i pacchetti autenticati attraverso un altro indirizzo e creare un tunnel da un host non attendibile.

Se qualcuno ha idee su come risolvere questo problema lasciando la maggior parte del traffico nel core, non esitate a parlare.

A proposito, l'incapsulamento in UDP si è dimostrato molto efficace. Rispetto all'incapsulamento su IP, è molto più stabile e spesso più veloce nonostante il sovraccarico aggiuntivo dell'intestazione UDP. Ciò è dovuto al fatto che la maggior parte degli host su Internet funziona bene solo con i tre protocolli più diffusi: TCP, UDP, ICMP. La parte tangibile può scartare completamente tutto il resto, oppure elaborarla più lentamente, perché ottimizzata solo per questi tre.

Per questo motivo, ad esempio, QUICK, su cui si basa HTTP/3, è stato creato su UDP e non su IP.

Bene, basta parole, è ora di vedere come funziona nel “mondo reale”.

Battaglia

Utilizzato per emulare il mondo reale iperf3. In termini di grado di vicinanza alla realtà, è più o meno come emulare il mondo reale in Minecraft, ma per ora va bene.

Partecipanti al concorso:

  • canale principale di riferimento
  • l'eroe di questo articolo è ipipou
  • OpenVPN con autenticazione ma senza crittografia
  • OpenVPN in modalità tutto compreso
  • WireGuard senza PresharedKey, con MTU=1440 (solo IPv4)

Dati tecnici per geek
Le metriche vengono acquisite con i seguenti comandi:

sul 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"

Latenza ICMP

ping -c 10 SERVER_IP | tail -1

sul server (funziona contemporaneamente al 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"

Configurazione del tunnel

ipipou
Server
/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 (nessuna crittografia, con autenticazione)
Server

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 (con crittografia, autenticazione, tramite UDP, tutto come previsto)
Configurato utilizzando openvpn-manage

protezione del cavo
Server
/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

Giudizio

Brutto segno umido
Il carico della CPU del server non è molto indicativo, perché... Ci sono molti altri servizi in esecuzione lì, a volte consumano risorse:

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

Canale da 20Mbps

ipipou: molto più di un semplice tunnel non crittografato

ipipou: molto più di un semplice tunnel non crittografato

canale per 1 Gbps ottimistico

ipipou: molto più di un semplice tunnel non crittografato

ipipou: molto più di un semplice tunnel non crittografato

In tutti i casi, ipipou ha prestazioni abbastanza vicine al canale base, il che è fantastico!

Il tunnel openvpn non crittografato si è comportato in modo abbastanza strano in entrambi i casi.

Se qualcuno lo metterà alla prova, sarà interessante sentire il feedback.

Che IPv6 e NetPrickle siano con noi!

Fonte: habr.com

Aggiungi un commento