ipipou: più di un semplice tunnel non crittografato

Cosa diciamo a Dio dell'IPv6?

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

Qui si parlerà di un tunnel IPv4 non crittografato, ma non di un 'caldo e accogliente', bensì di un moderno 'a LED'. E qui si fanno capolino socket grezzi e si lavora con i pacchetti nello spazio utente.

Ci sono N protocolli di tunneling per tutti i gusti e colori:

  • stiloso, trendy, giovanile WireGuard
  • multifunzione, come i coltellini svizzeri, OpenVPN e SSH
  • il vecchio e gentile GRE
  • massimo semplice, svelto, completamente non crittografato IPIP
  • in attivo sviluppo GENEVE
  • moltissimi altri.

Ma io sono un programmatore, quindi aumenterò N solo di una frazione, lasciando lo sviluppo di veri protocolli agli sviluppatori esperti.

In uno che non è ancora nato progetto, al quale sto lavorando adesso, bisogna contattare gli host dietro NAT dall'esterno. Usando per questo protocolli con crittografia seria, non ho potuto fare a meno di sentire che fosse come usare un cannone per i passeri. Poiché il tunnel è usato per lo più solo per creare un buco nel NAT, il traffico interno di solito è anche crittografato, si sostiene comunque l'uso di HTTPS.

Esplorando diversi protocolli di tunneling, l'attenzione del mio perfezionista interiore è stata ripetutamente attratta dall'IPIP per i suoi minimi costi di overhead. Ma ha un difetto e mezzo significativo per i miei scopi:

  • richiede indirizzi IP pubblici su entrambi i lati,
  • e non c'è autenticazione.

Quindi il perfezionista si è rinchiuso di nuovo nel buio della sua mente, o dove si trovi.

E così, un giorno leggendo articoli su tunneling supportato nativamente in Linux, mi sono imbattuto nel FOU (Foo-over-UDP), cioè qualcosa di generico impacchettato in UDP. Finora, di ciò che è generico sono supportati solo IPIP e GUE (Generic UDP Encapsulation).

«Ecco la pallottola d'argento! Per me anche un semplice IPIP è più che sufficiente.» — pensavo.

In realtà, la pallottola si è rivelata non del tutto d'argento. L'incapsulamento in UDP risolve il primo problema: i clienti dietro il NAT possono connettersi dall'esterno utilizzando una connessione già stabilita, ma qui la metà del prossimo difetto dell'IPIP si manifesta in una nuova luce: dietro gli indirizzi IP pubblici e la porta del cliente potrebbe nascondersi chiunque nella rete privata (con l'IPIP puro non c'è questo problema).

Per risolvere questo problema e mezzo è nata l'utilità ipipouImplementa un meccanismo di autenticazione per l'host remoto, senza compromettere il funzionamento del FOU sottostante, che gestirà rapidamente ed efficacemente i pacchetti nello spazio del kernel.

Non serve il tuo script!

Ok, se conosci la porta pubblica e l'IP del cliente (ad esempio, se non accede a tutte le sue connessioni, dove il NAT prova a mappare le porte 1-a-1), puoi creare un tunnel IPIP-over-FOU con i seguenti comandi, senza alcuno 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

# Le opzioni local, peer, peer_port, dev potrebbero non essere supportate da vecchie versioni del kernel, puoi ometterle.
# peer e peer_port sono usati per creare la connessione subito alla creazione del listener FOU.
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 — IP pubblico del server
  • 198.51.100.2 — IP pubblico del cliente
  • 192.168.0.2 — IP del cliente assegnato all'interfaccia eth0
  • 10001 — porta locale del cliente per FOU
  • 20001 — porta pubblica del cliente per FOU
  • 10000 — porta pubblica del server per FOU
  • encap-csum — opzione per aggiungere un checksum UDP nei pacchetti UDP incapsulati; può essere sostituito con noencap-csum, per non calcolarlo, poiché l'integrità è già controllata dallo strato di incapsulamento esterno (finché il pacchetto è all'interno del tunnel)
  • eth0 — interfaccia locale a cui sarà collegato il tunnel ipip
  • 172.28.0.1 — interfaccia di tunnel IP del client (privato)
  • 172.28.0.0 — interfaccia di tunnel IP del server (privato)

Finché la connessione UDP è attiva, il tunnel sarà funzionante, e se si interrompe, dipenderà dalla fortuna: se l'IP e la porta del client rimangono invariati, continuerà a funzionare; se cambiano, si interromperà.

Rivoltare tutto indietro è più semplice scaricando i moduli del kernel: modprobe -r fou ipip

Anche se l'autenticazione non è richiesta, gli IP pubblici e la porta del client non sono sempre noti e spesso imprevedibili o variabili (a seconda del tipo di NAT). Se si lascia encap-dport lato server, il tunnel non funzionerà, non è così intelligente da prendere la porta remota della connessione. In questo caso, ipipou può aiutare, oppure WireGuard e simili possono essere di aiuto.

Come funziona?

Il client (che di solito è dietro a un NAT) stabilisce un tunnel (come nell'esempio sopra) e invia un pacchetto di autenticazione al server affinché possa configurare il tunnel dal suo lato. A seconda delle impostazioni, questo pacchetto può essere vuoto (solo per far vedere al server l'indirizzo IP pubblico: la porta di connessione) oppure contenere dati che il server può utilizzare per identificare il cliente. I dati possono essere una semplice password in chiaro (mi viene in mente l'analogia con l'HTTP Basic Auth) o dati opportunamente firmati con una chiave privata (analogamente all'HTTP Digest Auth, ma più sicuro, vedi la funzione client_auth nel codice).

Sul server (lato con IP pubblico) all'avvio ipipou crea un gestore della coda nfqueue e configura netfilter affinché i pacchetti necessari vengano indirizzati dove devono: i pacchetti che inizializzano la connessione nella coda nfqueue, mentre [quasi] tutti gli altri direttamente nel listener FOU.

Per chi non lo sapesse, nfqueue (o NetfilterQueue) è uno strumento speciale per dilettanti che non sanno sviluppare moduli del kernel, che consente di reindirizzare i pacchetti di rete nello spazio utente e gestirli con strumenti rudimentali tramite netfilter (nftables/iptables): modificarli (opzionalmente) e restituirli al kernel, oppure scartarli.

Per alcuni linguaggi di programmazione esistono binding per lavorare con nfqueue, ma per bash non ne ho trovati (eh, non sorprende), quindi ho dovuto usare python: ipipou utilizza NetfilterQueue.

Se la performance non è critica, con questo strumento è possibile costruire relativamente velocemente e facilmente la propria logica di gestione dei pacchetti a un livello piuttosto basso, ad esempio inventare protocolli sperimentali di trasmissione dei dati o trollare servizi locali e remoti con comportamenti non standard.

Insieme a nfqueue, lavorano i socket raw, ad esempio quando il tunnel è già configurato e FOU ascolta sulla porta necessaria; non è possibile inviare un pacchetto dalla stessa porta in modo convenzionale — è già occupata, ma è possibile generare un pacchetto casuale direttamente nell'interfaccia di rete utilizzando un socket raw, anche se per generare tale pacchetto sarà necessario un po' più di impegno. Così si creano pacchetti autenticati in ipipou.

Poiché ipipou elabora solo i primi pacchetti di una connessione (e quelli che sono riusciti a filtrare nella coda prima dell'instaurazione della connessione), le prestazioni non ne risentono quasi affatto.

Una volta che il server ipipou riceve un pacchetto autenticato, si crea un tunnel e tutti i pacchetti successivi nella connessione vengono elaborati dal kernel bypassando nfqueue. Se la connessione scade, il primo pacchetto successivo sarà inviato nella coda nfqueue. A seconda delle impostazioni, se non si tratta di un pacchetto di autenticazione ma di uno dell'ultimo IP e porta memorizzati del cliente, può essere sia inoltrato che scartato. Se un pacchetto autenticato arriva da un nuovo IP e porta, il tunnel viene riconfigurato per il loro utilizzo.

L'IPIP-over-FOU normale ha un ulteriore problema quando si lavora con NAT: non è possibile creare due tunnel IPIP incapsulati in UDP con gli stessi IP, poiché i moduli FOU e IPIP sono piuttosto isolati l'uno dall'altro. Ciò significa che una coppia di clienti dietro uno stesso IP pubblico non può collegarsi contemporaneamente a un unico server in questo modo. In futuro, è possibile, questo problema sarà risolto a livello di kernel, ma non è certo. Nel frattempo, i problemi di NAT possono essere risolti con il NAT stesso: se si verifica che una coppia di indirizzi IP sia già occupata da un altro tunnel, ipipou farà NAT da un IP pubblico a un IP privato alternativo, et voilà! — si possono creare tunnel fino a esaurire le porte.

Poiché non tutti i pacchetti nella connessione sono firmati, questa semplice protezione è vulnerabile agli attacchi MITM. Quindi, se un malintenzionato si nasconde nel percorso tra il client e il server, può ascoltare il traffico e gestirlo, redirigendo i pacchetti di autenticazione attraverso un altro indirizzo e creando un tunnel da un host non fidato.

Se qualcuno ha idee su come risolvere il problema mantenendo la maggior parte del traffico nel kernel, non esitate a farvi sentire.

A proposito, l'incapsulamento in UDP si è dimostrato molto efficace. Rispetto all'incapsulamento sopra IP, è molto più stabile e spesso più veloce, nonostante i costi aggiuntivi dell'intestazione UDP. Questo è dovuto al fatto che nella maggior parte delle reti, molti host funzionano solo con i tre protocolli più popolari: TCP, UDP e ICMP. Una parte considerevole potrebbe anche scartare tutto il resto o gestirlo più lentamente, in quanto ottimizzati solo per questi tre.

Ad esempio, per questo motivo, QUICK, su cui si basa HTTP/3, è stato sviluppato specificamente su UDP, e non su IP.

Bene, basta parole, è tempo di vedere come funziona nel "mondo reale".

Battaglia

Per emulare il mondo reale viene utilizzato iperf3. In termini di realismo, è circa come un'emulazione del mondo reale in Minecraft, ma per il momento va bene.

Nel concorso partecipano:

  • canale principale di riferimento
  • il protagonista di questo articolo ipipou
  • OpenVPN con autenticazione, ma senza crittografia
  • OpenVPN in modalità "tutto compreso"
  • WireGuard senza PresharedKey, con MTU=1440 (poiché solo IPv4)

Dati tecnici per i geek
Le metriche vengono raccolte con questi 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"
# Dove "-b 12M" rappresenta la larghezza di banda del canale principale, divisa per il numero di flussi "-P", per evitare di generare pacchetti extra e compromettere le prestazioni.

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 (avviato 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 dei tunnel

ipipou
un 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

client
/etc/ipipou/client.conf:

numero del cliente
0
fou-local @eth0
fou-remote SERVER_IP:10000
tunl-ip 172.28.0.1
# pubkey dell'auth-key-b64: eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-key-b64 RuBZkT23na2Q4QH1xfmZCfRgSgPt5s362UPAFbecTso=
auth-secret topsecret
keepalive 27
verb 3

systemctl start ipipou@client

openvpn (senza crittografia, con autenticazione)
un server

openvpn --genkey --secret ovpn.key  # Poi bisogna trasferire ovpn.key al cliente
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 (con crittografia, autenticazione, tramite UDP, tutto come dovrebbe essere)
Configurato utilizzando openvpn-manage

wireguard
un 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

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

Risultati

Tabella grezza e brutta
Il carico della CPU del server non è molto indicativo, poiché ci sono molti altri servizi in esecuzione che consumano risorse:

proto larghezza di banda[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps canale da microcomputer (4 core) a VPS (1 core) attraverso l'Atlantico
# puro
UDP 20.4      99.80 93.34
TCP 19.2      99.67 96.68
ICMP latenza min/media/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 latenza min/media/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (solo autenticazione, senza crittografia)
UDP 19.3      99.89 72.90
TCP 16.1      95.95 88.46
ICMP latenza min/media/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (cifratura completa, autenticazione, ecc)
UDP 19.6      99.75 72.35
TCP 17.0      94.47 87.99
ICMP latenza min/media/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 latenza min/media/max/mdev = 217.925/223.601/230.696/3.266 ms

## canale circa-1Gbps tra VPS Europa e Stati Uniti (1 core)
# puro
UDP 729      73.40 39.93
TCP 363      96.95 90.40
ICMP latenza min/media/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 latenza min/media/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (solo autenticazione, senza crittografia)
UDP 193      17.51  1.62
TCP  12      95.45 92.80
ICMP latenza min/media/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 latenza min/media/max/mdev = 107.616/107.788/108.038/0.128 ms

canale da 20 Mbps

ipipou: più di un semplice tunnel non crittografato

ipipou: più di un semplice tunnel non crittografato

canale da 1 Gbps ottimistico

ipipou: più di un semplice tunnel non crittografato

ipipou: più di un semplice tunnel non crittografato

In tutti i casi, ipipou è piuttosto vicino ai valori del canale base, ed è fantastico!

Il tunnel openvpn non cifrato si è comportato piuttosto in modo strano in entrambi i casi.

Se qualcuno vuole testare, sarà interessante sentire i feedback.

Che l'IPv6 e il NetPrickle siano con noi!

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster