ipipou: molto più di un semplice tunnel non crittografato
Cosa stiamo dicendo al Dio di IPv6?
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:
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", чтобы лишние пакеты не плодить и не портить производительность.
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
canale per 1 Gbps ottimistico
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.