Cosa diciamo a dio IPv6?

Corretto, e a dio della crittografia oggi diremo lo stesso.
Qui si parlerà di un tunnel IPv4 non crittografato, ma non di quello "caldo e accogliente", bensì di un moderno "a LED". E qui si intravedono socket grezzi, con lavori sui pacchetti nello spazio utente.
Ci sono N protocolli di tunneling per tutti i gusti e colori:
- elegante, alla moda, giovanile
- multifunzione, come i coltellini svizzeri, OpenVPN e SSH
- il vecchio e non malvagio GRE
- il massimo della semplicità, veloce, e completamente non crittografato IPIP
- in continua evoluzione
- molti altri.
Ma io sono un programmatore, quindi aumenterò N solo di una frazione, lasciando lo sviluppo di veri protocolli ai veri sviluppatori.
In uno ancora non nato , a cui mi sto attualmente dedicando, bisogna raggiungere gli host dietro NAT dall'esterno. Usando protocolli con crittografia matura, non mi ha mai abbandonato la sensazione che fosse come usare un cannone contro un passero. Poiché il tunnel è utilizzato principalmente per fare un buco nel NAT, il traffico interno è di solito anch'esso crittografato, dato che si preferisce HTTPS.
Esplorando vari protocolli di tunneling, l'attenzione del mio perfezionista interiore è stata più e più volte attratta da IPIP per i suoi ridotti costi di overhead. Ma ha un difetto e mezzo per le mie esigenze:
- richiede indirizzi IP pubblici su entrambe le estremità,
- e non c'è alcuna autenticazione.
Quindi il perfezionista si è rinchiuso nuovamente in un angolo buio della scatola cranica, o dove si trovi.
E così, una volta leggendo articoli sui in Linux, mi sono imbattuto in FOU (Foo-over-UDP), ossia qualunque cosa avvolta in UDP. Finora tra le cose supportate ci sono solo IPIP e GUE (Generic UDP Encapsulation).
"Ecco la pallottola d'argento! Anche solo IPIP mi basta e avanza." pensavo.
In realtà, la pallottola si è rivelata non completamente d'argento. L'incapsulamento in UDP risolve il primo problema: è possibile connettersi da fuori ai client dietro NAT utilizzando una connessione già stabilita, ma qui metà del successivo difetto di IPIP fiorisce in una nuova luce: dietro gli indirizzi IP pubblici visibili e la porta del client può nascondersi chiunque all'interno della rete privata (in IPIP puro questo problema non esiste).
Per risolvere questo problema semi-completo è nata l'utilità . È stato implementato un meccanismo di autenticazione per host remoto senza compromettere il funzionamento del FOU, che elaborerà pacchetti nello spazio del kernel in modo rapido ed efficiente.
Non serve il tuo script!
Ok, se conosci la porta pubblica e l'IP del cliente (ad esempio, se tutti i propri pacchetti non escono a caso, NAT tenterà di 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 kernel vecchi, possono essere omesse.
# peer e peer_port vengono utilizzati per stabilire una connessione immediatamente quando si crea un 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 locale203.0.113.1— IP pubblico del server198.51.100.2— IP pubblico del cliente192.168.0.2— IP del cliente assegnato all'interfaccia eth010001— porta locale del cliente per FOU20001— porta pubblica del cliente per FOU10000— porta pubblica del server per FOUencap-csum— opzione per aggiungere un checksum UDP ai pacchetti UDP incapsulati; può essere sostituito connoencap-csum, per non calcolare, l'integrità è comunque controllata dallo strato esterno di incapsulazione (finché il pacchetto è all'interno del tunnel)eth0— interfaccia locale alla quale sarà associato il tunnel ipip172.28.0.1— IP dell'interfaccia tunnel del cliente (privato)172.28.0.0— IP dell'interfaccia tunnel del server (privato)
Finché la connessione UDP è attiva, il tunnel sarà funzionante; una volta interrotta, dipenderà dalle circostanze: se l'IP e la porta del cliente rimangono gli stessi, continuerà a funzionare, altrimenti si interromperà.
Tornare indietro è più facile scaricando i moduli del kernel: modprobe -r fou ipip
Anche se l'autenticazione non è richiesta, gli IP pubblici e la porta del cliente non sono sempre noti e spesso sono imprevedibili o variabili (a seconda del tipo di NAT). Se si omette encap-dport dal lato server, il tunnel non funzionerà, non è così intelligente da prendere la porta remota della connessione. In questo caso, ipipou può comunque aiutare, o ti può servire WireGuard e simili.
Come funziona?
Il client (dietro NAT, di solito) stabilisce un tunnel (come nell'esempio sopra) e invia un pacchetto di autenticazione al server, affinché questo configuri il tunnel dal suo lato. A seconda delle impostazioni, questo può essere un pacchetto vuoto (solo per far vedere al server l'IP pubblico: la porta di connessione), oppure con dati che consentono al server di identificare il client. I dati possono essere una semplice frase di password in chiaro (mi viene in mente l'analogia con l'HTTP Basic Auth) oppure dati firmati con una chiave privata, appositamente formattati (simile all'HTTP Digest Auth, ma più robusto, vedi la funzione client_auth nel codice).
Sul server (lato con l'IP pubblico), all'avvio ipipou crea un gestore per la coda nfqueue e configura netfilter affinché i pacchetti necessari siano diretti 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) è una cosa speciale per i dilettanti, che non sanno sviluppare moduli kernel, che con mezzi di netfilter (nftables / iptables) permette di reindirizzare i pacchetti di rete nello spazio utente e di elaborarli lì con mezzi rudimentali: modificarli (opzionalmente) e restituiti al kernel, oppure scartarli.
Esistono binding per alcuni linguaggi di programmazione per lavorare con nfqueue, ma per bash non ne ho trovato (eheh, non sorprende), ho dovuto usare python: ipipou utilizza .
Se le prestazioni non sono critiche, con questa cosa è possibile elaborare logiche proprie per la gestione dei pacchetti a un livello piuttosto basso, ad esempio creare protocolli di trasmissione dati sperimentali o prendersela con servizi locali e remoti con comportamenti non standard.
A stretto contatto con nfqueue funzionano i socket raw (raw sockets), ad esempio quando il tunnel è già configurato e il FOU ascolta sulla porta necessaria, non è possibile inviare un pacchetto da quella stessa porta in modo consueto — occupato, ma si può prendere e iniettare un pacchetto generato casualmente direttamente nell'interfaccia di rete usando un socket raw, anche se sulla generazione di tale pacchetto dovrai impegnarti un po' di più. Così vengono creati in ipipou pacchetti di autenticazione.
Poiché ipipou elabora solo i primi pacchetti di una connessione (e quelli che sono riusciti a filtrare nella coda prima della creazione della connessione), le prestazioni quasi non ne risentono.
Non appena il server ipipou riceve un pacchetto autenticato, si crea un tunnel e tutti i pacchetti successivi nella connessione vengono elaborati direttamente dal kernel, bypassando nfqueue. Se la connessione è scaduta, il primo pacchetto successivo verrà inviato alla coda di nfqueue, a seconda delle impostazioni; se non è un pacchetto di autenticazione, ma proviene dall'ultimo IP e porta salvati del cliente, potrebbe essere o ignorato o scartato. Se arriva un pacchetto autenticato da un nuovo IP e porta, il tunnel si riconfigura per utilizzare questi.
IPIP-over-FOU presenta un ulteriore problema quando si lavora con il NAT: non è possibile creare due tunnel IPIP incapsulati in UDP con lo stesso IP, poiché i moduli FOU e IPIP sono sufficientemente isolati l'uno dall'altro. Ciò significa che una coppia di clienti con un unico IP pubblico non potrà connettersi contemporaneamente a un server in questo modo. In futuro, , sarà risolto a livello di kernel, ma non è certo. Nel frattempo, i problemi legati al NAT possono essere risolti con il NAT: se si verifica che una coppia di indirizzi IP sia già occupata da un altro tunnel, ipipou applicherà un NAT dall'IP pubblico a un IP privato alternativo, voilà! — si possono creare tunnel finché ci sono porte disponibili.
Poiché non tutti i pacchetti nella connessione sono firmati, una semplice protezione del genere è vulnerabile agli attacchi MITM; se c'è un malintenzionato tra il cliente e il server che può ascoltare e manipolare il traffico, può reindirizzare i pacchetti di autenticazione tramite un altro indirizzo e creare un tunnel da un host non fidato.
Se qualcuno ha idee su come risolvere questo problema mantenendo la maggior parte del traffico nel kernel, non esiti a farsi avanti.
A proposito, l'incapsulamento in UDP si è dimostrato molto efficace. Rispetto all'incapsulamento sopra IP, è decisamente più stabile e spesso più veloce nonostante l'overhead aggiuntivo dell'intestazione UDP. Questo è dovuto al fatto che nella maggior parte di Internet la maggior parte degli host funziona decentemente solo con i tre protocolli più popolari: TCP, UDP, ICMP. Una parte significativa può addirittura scartare tutto il resto, oppure elaborarlo più lentamente, poiché è ottimizzato solo per questi tre.
Ad esempio, per questo QUICK, su cui si basa HTTP/3, è stato creato esattamente sopra UDP, e non sopra IP.
Bene, basta parole, è tempo di vedere come funziona nel "mondo reale".
Battaglia
Per simulare il mondo reale si utilizza iperf3. Per quanto riguarda la vicinanza alla realtà, è circa come simulare il mondo reale in Minecraft, ma va bene per ora.
Nel concorso partecipano:
- canale principale di riferimento
- eroe di questo articolo ipipou
- OpenVPN con autenticazione, ma senza crittografia
- OpenVPN in modalità "tutto incluso"
- WireGuard senza PresharedKey, con MTU=1440 (dato che è solo IPv4)
Dati tecnici per geek
Le metriche vengono raccolte 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"
# Dove "-b 12M" è la larghezza di banda del canale principale, divisa per il numero di flussi "-P", per non generare pacchetti superflui e non 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 (lanciato contemporaneamente con il 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
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
un 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 (senza crittografia, con autenticazione)
server
openvpn --genkey --secret ovpn.key # Poi bisogna inviare ovpn.key al client
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
un 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 ci si aspetta)
Configurato utilizzando
wireguard
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
un 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 a volte consumano risorse:
larghezza di banda[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# canale da 20 Mbps da un microcomputer (4 core) a un VPS (1 core) attraverso l'Atlantico
# puro
UDP 20.4 99.80 93.34
TCP 19.2 99.67 96.68
ICMP latenza 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 latenza min/avg/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/avg/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (crittografia completa, autenticazione, ecc)
UDP 19.6 99.75 72.35
TCP 17.0 94.47 87.99
ICMP latenza 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 latenza min/avg/max/mdev = 217.925/223.601/230.696/3.266 ms
## canale di circa 1 Gbps tra VPS in Europa e USA (1 core)
# puro
UDP 729 73.40 39.93
TCP 363 96.95 90.40
ICMP latenza 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 latenza min/avg/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/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 latenza min/avg/max/mdev = 107.616/107.788/108.038/0.128 ms
canale da 20 Mbps


canale da 1 Gbps ottimistico


In tutti i casi, ipipou è piuttosto vicino ai parametri del canale di base, e questo è fantastico!
Il tunnel openvpn non crittografato si è comportato in modo piuttosto strano in entrambi i casi.
Se qualcuno vuole testare, sarà interessante sentire feedback.
Che l'IPv6 e il NetPrickle siano con noi!
Fonte: habr.com
