ipipou: più di un semplice tunnel non crittografato

Cosa diciamo a dio IPv6?

ipipou: più di un semplice tunnel non crittografato
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 WireGuard
  • 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 GENEVE
  • 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 progetto, 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 tunneling supportati nativamente 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à ipipou. È 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 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 ai pacchetti UDP incapsulati; può essere sostituito con noencap-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 ipip
  • 172.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 NetfilterQueue.

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, è possibile, 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 openvpn-manage

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

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

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