ipipou: mai mult decât un tunel necriptat

Ce îi spunem zeului IPv6?

ipipou: mai mult decât un tunel necriptat
Așa este, vom spune același lucru și astăzi zeului criptării.

Aici vom vorbi despre un tunel IPv4 necriptat, dar nu despre unul „lampa calda”, ci despre unul modern „LED”. Și există, de asemenea, socketuri brute care clipesc aici și se lucrează cu pachete în spațiul utilizatorului.

Există N protocoale de tunel pentru fiecare gust și culoare:

  • stilat, la modă, tinerețe WireGuard
  • multifuncționale, cum ar fi cuțitele elvețiene, OpenVPN și SSH
  • vechi și nu rău GRE
  • cel mai simplu, rapid și complet necriptat IPIP
  • în curs de dezvoltare GENEVA
  • multe altele.

Dar sunt programator, așa că voi crește N doar cu o fracțiune și voi lăsa dezvoltarea de protocoale reale pe seama dezvoltatorilor Kommersant.

Într-unul nenăscut proiectCeea ce fac acum este să ajung la gazdele din spatele NAT din exterior. Folosind protocoale cu criptografie pentru adulți pentru asta, nu am putut scăpa de sentimentul că era ca și cum ai împușca vrăbii dintr-un tun. Deoarece tunelul este folosit în cea mai mare parte doar pentru a face găuri în NAT-e, traficul intern este de obicei criptat, dar încă se îneacă în HTTPS.

În timp ce cercetam diferite protocoale de tunel, atenția perfecționistului meu interior a fost atrasă de IPIP din nou și din nou datorită cheltuielilor minime. Dar are un dezavantaj și jumătate semnificative pentru sarcinile mele:

  • necesită IP-uri publice de ambele părți,
  • și nicio autentificare pentru tine.

Prin urmare, perfecționistul a fost alungat înapoi în colțul întunecat al craniului sau oriunde stă acolo.

Și apoi într-o zi, în timp ce citeam articole despre tuneluri suportate nativ în Linux am dat peste FOU (Foo-over-UDP), adică. orice, învelit în UDP. Până acum, sunt acceptate doar IPIP și GUE (Generic UDP Encapsulation).

„Iată glonțul de argint! Un simplu IPIP este suficient pentru mine.” - Am crezut.

De fapt, glonțul s-a dovedit a nu fi complet argintiu. Încapsularea în UDP rezolvă prima problemă - vă puteți conecta la clienții din spatele NAT din exterior folosind o conexiune prestabilită, dar aici jumătate din următorul dezavantaj al IPIP înflorește într-o lumină nouă - oricine dintr-o rețea privată se poate ascunde în spatele vizibilului. IP public și port client (în IPIP pur această problemă nu există).

Pentru a rezolva această problemă și jumătate, s-a născut utilitatea ipipou. Implementează un mecanism de casă pentru autentificarea unei gazde la distanță, fără a perturba funcționarea FOU nucleului, care va procesa rapid și eficient pachetele din spațiul nucleului.

Nu avem nevoie de scenariul tău!

Ok, dacă cunoașteți portul public și IP-ul clientului (de exemplu, toată lumea din spatele lui nu merge nicăieri, NAT încearcă să mapați porturile 1-în-1), puteți crea un tunel IPIP-over-FOU cu următoarele comenzi, fără niciun script.

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

asupra clientului:

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

unde

  • ipipou* — numele interfeței rețelei de tuneluri locale
  • 203.0.113.1 — server IP public
  • 198.51.100.2 — IP-ul public al clientului
  • 192.168.0.2 — IP-ul clientului atribuit interfeței eth0
  • 10001 — port client local pentru FOU
  • 20001 — port public client pentru FOU
  • 10000 — port de server public pentru FOU
  • encap-csum — opțiunea de a adăuga o sumă de control UDP la pachetele UDP încapsulate; poate fi înlocuit cu noencap-csum, ca să nu mai vorbim de faptul că integritatea este deja controlată de stratul exterior de încapsulare (în timp ce pachetul este în interiorul tunelului)
  • eth0 — interfață locală la care va fi legat tunelul ipip
  • 172.28.0.1 — IP-ul interfeței tunelului client (privat)
  • 172.28.0.0 — interfață de server IP tunel (privată)

Atâta timp cât conexiunea UDP este vie, tunelul va fi în stare de funcționare, dar dacă se rupe, vei avea noroc - dacă IP-ul clientului: portul rămâne același - va funcționa, dacă se schimbă - se va rupe.

Cel mai simplu mod de a întoarce totul este să descărcați modulele nucleului: modprobe -r fou ipip

Chiar dacă nu este necesară autentificarea, IP-ul public și portul clientului nu sunt întotdeauna cunoscute și sunt adesea imprevizibile sau variabile (în funcție de tipul NAT). Dacă omiteți encap-dport pe partea serverului, tunelul nu va funcționa, nu este suficient de inteligent pentru a lua portul de conectare la distanță. În acest caz, ipipou vă poate ajuta, sau WireGuard și alții ca acesta vă pot ajuta.

Cum funcționează?

Clientul (care se află de obicei în spatele NAT) deschide un tunel (ca în exemplul de mai sus) și trimite un pachet de autentificare către server, astfel încât acesta să configureze tunelul pe partea sa. În funcție de setări, acesta poate fi un pachet gol (doar pentru ca serverul să poată vedea IP-ul public: portul de conectare), sau cu date prin care serverul poate identifica clientul. Datele pot fi o simplă frază de acces în text clar (imi vine în minte analogia cu HTTP Basic Auth) sau date special concepute, semnate cu o cheie privată (asemănătoare cu HTTP Digest Auth doar mai puternică, vezi funcția client_auth în cod).

Pe server (partea cu IP-ul public), când ipipou pornește, creează un handler de coadă nfqueue și configurează netfilter astfel încât pachetele necesare să fie trimise acolo unde ar trebui să fie: pachete care inițializează conexiunea la coada nfqueue și [aproape] toate celelalte merg direct la ascultător FOU.

Pentru cei care nu știu, nfqueue (sau NetfilterQueue) este un lucru special pentru amatorii care nu știu să dezvolte module kernel, care folosind netfilter (nftables/iptables) vă permit să redirecționați pachetele de rețea în spațiul utilizatorului și să le procesați acolo folosind primitiv înseamnă la îndemână: modificați (opțional) și dați-l înapoi nucleului sau aruncați-l.

Pentru unele limbaje de programare există legături pentru lucrul cu nfqueue, pentru bash nu a existat niciunul (heh, nu este surprinzător), a trebuit să folosesc python: ipipou folosește NetfilterQueue.

Dacă performanța nu este critică, folosind acest lucru vă puteți inventa relativ rapid și ușor propria logică pentru a lucra cu pachete la un nivel destul de scăzut, de exemplu, creați protocoale experimentale de transfer de date sau găsiți servicii locale și la distanță cu un comportament nestandard.

Prizele brute funcționează mână în mână cu nfqueue, de exemplu, când tunelul este deja configurat și FOU ascultă pe portul dorit, nu veți putea trimite un pachet din același port în mod obișnuit - este ocupat, dar puteți lua și trimite un pachet generat aleatoriu direct la interfața de rețea folosind un socket brut, deși generarea unui astfel de pachet va necesita puțin mai multă reparație. Așa sunt create pachetele cu autentificare în ipipou.

Deoarece ipipou procesează doar primele pachete de la conexiune (și cele care au reușit să se scurgă în coadă înainte de stabilirea conexiunii), performanța aproape că nu are de suferit.

De îndată ce serverul ipipou primește un pachet autentificat, este creat un tunel și toate pachetele ulterioare din conexiune sunt deja procesate de kernel care ocolește nfqueue. Dacă conexiunea eșuează, atunci primul pachet din următorul va fi trimis în coada nfqueue, în funcție de setări, dacă nu este un pachet cu autentificare, ci de la ultimul IP memorat și port client, acesta poate fi fie trecut pe sau aruncat. Dacă un pachet autentificat provine de la un nou IP și port, tunelul este reconfigurat pentru a le utiliza.

IPIP-over-FOU obișnuit mai are o problemă când se lucrează cu NAT - este imposibil să se creeze două tuneluri IPIP încapsulate în UDP cu același IP, deoarece modulele FOU și IPIP sunt destul de izolate unul de celălalt. Acestea. o pereche de clienți din spatele aceluiași IP public nu se va putea conecta simultan la același server în acest fel. In viitor, este posibil, se va rezolva la nivel de kernel, dar acest lucru nu este sigur. Între timp, problemele NAT pot fi rezolvate prin NAT - dacă se întâmplă ca o pereche de adrese IP să fie deja ocupată de un alt tunel, ipipou va face NAT de la IP public la un IP privat alternativ, voila! - puteți crea tuneluri până când porturile se epuizează.

Deoarece Nu toate pachetele din conexiune sunt semnate, atunci această protecție simplă este vulnerabilă la MITM, așa că dacă există un tip rău care pândește pe calea dintre client și server care poate asculta traficul și îl poate manipula, el poate redirecționa pachetele autentificate. printr-o altă adresă și creați un tunel de la o gazdă nede încredere.

Dacă cineva are idei despre cum să remedieze acest lucru, lăsând cea mai mare parte a traficului în nucleu, nu ezitați să vorbiți.

Apropo, încapsularea în UDP s-a dovedit foarte bine. În comparație cu încapsularea prin IP, este mult mai stabil și adesea mai rapid, în ciuda supraîncărcării suplimentare a antetului UDP. Acest lucru se datorează faptului că majoritatea gazdelor de pe Internet funcționează bine doar cu cele mai populare trei protocoale: TCP, UDP, ICMP. Partea tangibilă poate elimina complet orice altceva sau o poate procesa mai lent, deoarece este optimizată doar pentru aceste trei.

De exemplu, acesta este motivul pentru care QUICK, pe care se bazează HTTP/3, a fost creat pe UDP și nu pe IP.

Ei bine, destule cuvinte, este timpul să vedem cum funcționează în „lumea reală”.

Luptă

Folosit pentru a emula lumea reală iperf3. În ceea ce privește gradul de apropiere de realitate, este aproximativ același lucru cu emularea lumii reale în Minecraft, dar deocamdată va fi bine.

Participanti la concurs:

  • canalul principal de referință
  • eroul acestui articol este ipipou
  • OpenVPN cu autentificare, dar fără criptare
  • OpenVPN în modul all-inclusive
  • WireGuard fără PresharedKey, cu MTU=1440 (numai pentru IPv4)

Date tehnice pentru tocilari
Valorile sunt preluate cu următoarele comenzi:

asupra clientului:

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", чтобы лишние пакеты не плодить и не портить производительность.

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"

Latența ICMP

ping -c 10 SERVER_IP | tail -1

pe server (se rulează 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 tunelului

ipipou
serverului
/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)
serverului

openvpn --genkey --secret ovpn.key  # Затем надо передать ovpn.key клиенту
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, totul conform așteptărilor)
Configurat folosind openvpn-manage

sârmă de protecție
serverului
/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

Constatări

Semn urât umed
Încărcarea procesorului serverului nu este foarte indicativă, deoarece... Există multe alte servicii care rulează acolo, uneori consumă resurse:

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

Canal de 20 Mbps

ipipou: mai mult decât un tunel necriptat

ipipou: mai mult decât un tunel necriptat

canal pe 1 Gbps optimist

ipipou: mai mult decât un tunel necriptat

ipipou: mai mult decât un tunel necriptat

În toate cazurile, ipipou este destul de aproape ca performanță de canalul de bază, ceea ce este grozav!

Tunelul openvpn necriptat s-a comportat destul de ciudat în ambele cazuri.

Dacă cineva are de gând să-l testeze, va fi interesant să audă feedback.

Fie ca IPv6 și NetPrickle să fie cu noi!

Sursa: www.habr.com

Adauga un comentariu