ipipou: mai mult decât un simplu tunel neprotejat

Ce îi spunem lui Dumnezeu IPv6?

ipipou: mai mult decât un simplu tunel neprotejat
Corect, și lui Dumnezeu criptării îi vom spune același lucru astăzi.

Aici va fi vorba despre un tunel IPv4 necriptat, dar nu despre un "lampă caldă" ci despre unul modern "LED". Și aici apar socketuri brute, iar lucrul cu pachetele se desfășoară în spațiul utilizatorului.

Există N protocoale de tunelare pentru toate gusturile:

  • stilos, la modă, pentru tineri WireGuard
  • multifuncționale, precum cuțitele elvețiene, OpenVPN și SSH
  • vechi și mai puțin rău GRE
  • maxim simplu, rapid, deloc criptat IPIP
  • în continuă dezvoltare GENEVE
  • multe altele.

Dar eu sunt programator, așa că voi crește N doar cu o mică cotă, iar dezvoltarea protocoalelor adevărate o voi lăsa dezvoltatorilor profesioniști.

Într-un proiect care încă nu s-a născut la care lucrez acum, trebuie să ajung la host-uri din afară, prin NAT. Folosind pentru aceasta protocoale cu criptografie avansată, nu m-a părăsit niciodată senzația că e ca și cum ai folosi un tun de război pentru vrăbii. Deoarece tunelul este folosit în mare parte doar pentru a pătrunde în NAT, traficul intern este de obicei și el criptat, toată lumea susține HTTPS.Examinând diverse protocoale de tunelare, atenția perfecționistului meu interior a fost tot mai mult atrasă de IPIP datorită cheltuielilor sale minime. Dar are o jumătate de dezavantaj esențial pentru sarcinile mele:

necesită IP-uri publice de ambele părți,

  • și nu ai nicio autentificare.
  • Așa că perfecționistul a început să se retragă din nou în colțul întunecat al cutiei craniene, sau unde se află el.

Și așa, citind articole despre

tuneluri nativ suportate în Linux, am dat peste FOU (Foo-over-UDP), adică ceva împachetat în UDP. Până acum, dintre cele ce sunt suportate, doar IPIP și GUE (Generic UDP Encapsulation) sunt acceptate. "Iată-o, glonțul de argint! Mie îmi ajunge un IPIP simplu." — m-am gândit.

În realitate, glonțul s-a dovedit a fi nu complet din argint. Împachetarea în UDP rezolvă prima problemă — se poate conecta din exterior la clienții aflați în spatele NAT-ului folosind o conexiune stabilită anterior, dar aici partea a doua a dezavantajului IPIP înflorește sub o nouă lumină — în spatele IP-urilor publice vizibile și portului clientului se poate ascunde oricine dintr-o rețea privată (în IPIP-ul pur nu există această problemă).

Pentru a rezolva această problemă parțială, a fost creat utilitarul

ipipou ipipouAceasta include un mecanism homemade de autentificare a gazdelor la distanță, fără a afecta funcționarea FOU-ului nuclear, care va procesa rapid și eficient pachetele în spațiul nucleului.

Nu ai nevoie de scriptul tău!

Bine, dacă știi portul public și IP-ul clientului (de exemplu, aceștia nu se duc nicăieri, NAT-ul încearcă să mapeze porturile 1-1), poți crea un tunel IPIP-over-FOU cu următoarele comenzi, fără scripturi.

pe 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

pe client:

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

# Opțiunile local, peer, peer_port, dev pot să nu fie acceptate de nucleele mai vechi, le poți omite.
# peer și peer_port sunt utilizate pentru a crea o conexiune imediat la crearea FOU-listener-ului.
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

unde

  • ipipou* — numele interfeței de rețea tunnel locale
  • 203.0.113.1 — IP-ul public al serverului
  • 198.51.100.2 — IP-ul public al clientului
  • 192.168.0.2 — IP-ul clientului, atribuit interfeței eth0
  • 10001 — portul local al clientului pentru FOU
  • 20001 — portul public al clientului pentru FOU
  • 10000 — portul public al serverului pentru FOU
  • encap-csum — opțiune pentru adăugarea unei sume de control UDP în pachetele UDP înveșmântate; poate fi înlocuită cu noencap-csum, pentru a nu calcula, integritatea fiind controlată de stratul extern al înveșmântării (atâta vreme cât pachetul se află în tunel)
  • eth0 — interfața locală la care va fi legat tunelul ipip
  • 172.28.0.1 — IP-ul interfeței tunnel a clientului (privat)
  • 172.28.0.0 — IP-ul interfeței tunnel a serverului (privat)

Atâta vreme cât conexiunea UDP este activă, tunelul va funcționa, iar când se rupe, va depinde — dacă IP-ul: portul clientului rămân aceleași — va funcționa, dacă se schimbă — se va rupe.

Cel mai simplu mod de a răsturna totul este încărcând modulele nucleului: modprobe -r fou ipip

Chiar dacă autentificarea nu este necesară, IP-urile publice și portul clientului nu sunt întotdeauna cunoscute și adesea imprevizibile sau variabile (în funcție de tipul de NAT). Dacă omitem encap-dport de partea serverului, tunelul nu va funcționa, nu este atât de inteligent să ia portul de conectare la distanță. În acest caz, ipipou poate ajuta de asemenea, sau WireGuard și alte soluții similare pot fi de ajutor.

Cum funcționează?

Clientul (ceea ce este de obicei în spatele NAT-ului) ridică un tunel (ca în exemplul de mai sus) și trimite un pachet cu autentificare către server, pentru ca acesta să configureze tunelul de partea sa. În funcție de setări, acest lucru poate fi un pachet gol (doar pentru ca serverul să vadă IP-urile publice: portul de conexiune), sau cu date prin care serverul poate să identifice clientul. Datele pot fi o simplă frază parolă în text simplu (se poate face o analogie cu HTTP Basic Auth) sau date semnate cu o cheie privată, special pregătite (asemănătoare cu HTTP Digest Auth, dar mai puternice, vezi funcția client_auth în cod).

Pe server ( partea cu IP-ul public), la pornirea ipipou, se creează un handler pentru coada nfqueue și se configurează netfilter astfel încât pachetele necesare să fie direcționate corespunzător: pachetele care inițiază conexiunea în coada nfqueue, iar [aproape] toate celelalte direct în listener-ul FOU.

Cine nu este familiarizat, nfqueue (sau NetfilterQueue) este un mecanism special pentru începători, care nu știu să dezvolte module de kernel, care, folosind netfilter (nftables/iptables), permite redirecționarea pachetelor de rețea în spațiul utilizatorului și procesarea acestora acolo cu instrumente primitive: modificarea (opțional) și returnarea la kernel, sau abandonarea acestora.

Pentru anumite limbaje de programare există binding-uri pentru lucrul cu nfqueue, însă pentru bash nu s-au găsit (huh, nu e surprinzător), a fost necesar să folosim python: ipipou folosește NetfilterQueue.

Dacă performanța nu este critică, cu ajutorul acestui mecanism poți crea rapid și ușor propria logică de lucru cu pachetele la un nivel destul de inferior, de exemplu să dezvolți protocoale experimentale de transfer de date sau să troll-ezi servicii locale și externe cu comportamente neobișnuite.

La unison cu nfqueue lucrează socket-urile brute (raw sockets), de exemplu, când tunelul este deja configurat, și FOU ascultă pe portul dorit, nu poți trimite un pachet din acest port folosind metoda obișnuită — este ocupat, dar poți lua și să trimiți un pachet generat aleatoriu direct în interfața de rețea folosind un socket brut, chiar dacă va trebui să te ocupi puțin mai mult de generarea unui astfel de pachet. Astfel de pachete cu autentificare sunt create în ipipou.

Deoarece ipipou procesează doar primele pachete din conexiune (și cele care au reușit să se strecoare în coadă înainte de a stabili conexiunea), performanța nu este aproape deloc afectată.

Când serverul ipipou primește un pachet care a trecut autentificarea, tunelul este creat, iar toate pachetele următoare din conexiune sunt procesate de nucleu, ocolind nfqueue. Dacă conexiunea expiră, primul pachet următor va fi direcționat în coada nfqueue, în funcție de setări; dacă nu este un pachet de autentificare, dar provine de la ultimul IP și port cunoscut al clientului, acesta poate fi fie trecut mai departe, fie abandonat. Dacă un pachet autentificat vine de la un IP și port noi, tunelul va fi reconfigurat pentru a-i utiliza.

IPIP-over-FOU obișnuit are o altă problemă atunci când lucrează cu NAT — nu se pot crea două tuneluri IPIP încapsulate în UDP cu aceleași IP-uri, deoarece modulele FOU și IPIP sunt suficient de izolate una de alta. Asta înseamnă că o pereche de clienți ce folosesc același IP public nu se pot conecta simultan la același server în acest mod. În viitor, se poate, aceasta va fi rezolvată la nivelul nucleului, dar nu este sigur. Până atunci, problemele NAT-ului pot fi rezolvate prin NAT — dacă se întâmplă ca o pereche de adrese IP să fie deja ocupate de un alt tunel, ipipou va face NAT de pe IP-ul public pe un IP privat alternativ, voila! — pot fi create tuneluri până când porțile se vor termina.

Deoarece nu toate pachetele din conexiune sunt semnate, această protecție simplă este vulnerabilă la MITM, așa că, dacă între client și server se află un adversar care poate asculta traficul și să-l controleze, acesta poate redirecționa pachetele de autentificare printr-o altă adresă și poate crea un tunel de la un gazdă neîncredere.

Dacă cineva are idei despre cum să rezolve aceasta lăsând cea mai mare parte a traficului în nucleu, nu ezitați — exprimați-vă.

Ca dovadă, încapsularea în UDP s-a dovedit a fi foarte eficientă. Comparativ cu încapsularea peste IP, este mult mai stabilă și adesea mai rapidă, în ciuda costurilor suplimentare ale antetului UDP. Acest lucru se datorează faptului că, pe internet, majoritatea gazdelor funcționează decent doar cu cele trei protocoale cele mai populare: TCP, UDP, ICMP. O parte semnificativă poate respinge complet tot restul sau să-l proceseze mai încet, deoarece este optimizată doar pentru aceste trei.

De exemplu, de aceea QUICK, pe baza căruia a fost creat HTTP/3, a fost dezvoltat exact peste UDP, și nu peste IP.

Ei bine, destul cu vorbele, este timpul să vedem cum funcționează în «lumea reală».

Bătălie

Pentru a emula lumea reală se folosește iperf3. Din punct de vedere al apropierii de realitate, este cam ca emularea lumii reale în Minecraft, dar deocamdată este suficient.

În competiție participă:

  • canalul de referință principal
  • eroarea acestui articol ipipou
  • OpenVPN cu autentificare, dar fără criptare
  • OpenVPN în modul «totul inclus»
  • WireGuard fără PresharedKey, cu MTU=1440 (deoarece doar IPv4)

Date tehnice pentru geeki
Metricile sunt obținute cu comenzile următoare

pe client:

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"
# Unde "-b 12M" este lățimea de bandă a canalului principal, împărțită la numărul de fire "-P", pentru a nu genera pachete suplimentare și a nu afecta performanța.

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"

latenta ICMP

ping -c 10 SERVER_IP | tail -1

pe server (pornește simultan cu clientul):

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"

Configurarea tunelurilor

ipipou
serverul
/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:

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 (fără criptare, cu autentificare)
serverul

openvpn --genkey --secret ovpn.key  # Apoi trebuie să transferi ovpn.key clientului
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 (cu criptare, autentificare, prin UDP, cum ar trebui să fie)
Configurat folosind openvpn-manage

wireguard
serverul
/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

Rezultate

Un tabel neîngrijit și urât
Încărcarea CPU-ului serverului nu este foarte relevantă, deoarece rulează multe alte servicii care uneori consumă resurse:

lățimea de bandă[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps canal de microcomputers (4 core) până la VPS (1 core) prin Atlantica
# pur
UDP 20.4      99.80 93.34
TCP 19.2      99.67 96.68
ICMP latență min/med/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 latență min/med/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (doar autentificare, fără criptare)
UDP 19.3      99.89 72.90
TCP 16.1      95.95 88.46
ICMP latență min/med/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (criptare completă, autentificare, etc)
UDP 19.6      99.75 72.35
TCP 17.0      94.47 87.99
ICMP latență min/med/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 latență min/med/max/mdev = 217.925/223.601/230.696/3.266 ms

## canal aproape de 1Gbps între VPS Europa și SUA (1 core)
# pur
UDP 729      73.40 39.93
TCP 363      96.95 90.40
ICMP latență min/med/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 latență min/med/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (doar autentificare, fără criptare)
UDP 193      17.51  1.62
TCP  12      95.45 92.80
ICMP latență min/med/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 latență min/med/max/mdev = 107.616/107.788/108.038/0.128 ms

canal de 20 Mbps

ipipou: mai mult decât un simplu tunel neprotejat

ipipou: mai mult decât un simplu tunel neprotejat

canal de 1 Gbps optimist

ipipou: mai mult decât un simplu tunel neprotejat

ipipou: mai mult decât un simplu tunel neprotejat

În toate cazurile, ipipou este destul de aproape de performanțele canalului de bază, ceea ce este minunat!

Tunelul openvpn necriptat a avut un comportament destul de ciudat în ambele cazuri.

Dacă cineva intenționează să testeze, ar fi interesant să auzim păreri.

Să fie cu noi IPv6 și NetPrickle!

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster