ipipou: meer as net 'n ongeënkripteerde tonnel

Wat sê ons vir die God van IPv6?

ipipou: meer as net 'n ongeënkripteerde tonnel
Dit is reg, ons sal vandag dieselfde vir die god van enkripsie sê.

Hier sal ons praat oor 'n ongeënkripteerde IPv4-tonnel, maar nie oor 'n "warm lamp" een nie, maar oor 'n moderne "LED" een. En hier flikker ook rou voetstukke, en werk is aan die gang met pakkies in gebruikersruimte.

Daar is N tonnelprotokolle vir elke smaak en kleur:

  • stylvol, modieus, jeug WireGuard
  • multifunksioneel, soos Switserse messe, OpenVPN en SSH
  • oud en nie boos nie GRE
  • die mees eenvoudige, vinnige, heeltemal ongeënkripteerde IPIP
  • aktief ontwikkel GENEVE
  • baie ander.

Maar ek is 'n programmeerder, so ek sal N slegs met 'n fraksie verhoog en die ontwikkeling van regte protokolle aan Kommersant-ontwikkelaars oorlaat.

In een ongebore die konsepWat ek nou doen, is om gashere agter NAT van buite af te bereik. Deur protokolle met volwasse kriptografie hiervoor te gebruik, kon ek nie die gevoel skud dat dit is soos om mossies uit 'n kanon te skiet nie. Omdat die tonnel word meestal net gebruik om gate in NAT-e te steek, interne verkeer word gewoonlik ook geïnkripteer, maar hulle verdrink steeds in HTTPS.

Terwyl ek verskeie tonnelprotokolle nagevors het, is my innerlike perfeksionis se aandag oor en oor op IPIP gevestig weens die minimale bokoste. Maar dit het een en 'n half beduidende nadele vir my take:

  • dit vereis openbare IP's aan beide kante,
  • en geen verifikasie vir jou nie.

Daarom is die perfeksionis teruggedryf in die donker hoek van die skedel, of waar hy ook al daar sit.

En dan eendag, terwyl jy artikels lees oor inheemse ondersteunde tonnels in Linux het ek op FOU (Foo-over-UDP) afgekom, m.a.w. wat ook al, toegedraai in UDP. Tot dusver word slegs IPIP en GUE (Generic UDP Encapsulation) ondersteun.

“Hier is die silwer koeël! ’n Eenvoudige IPIP is vir my genoeg.” - Ek dink.

Trouens, die koeël het geblyk nie heeltemal silwer te wees nie. Inkapseling in UDP los die eerste probleem op - jy kan van buite af met kliënte agter NAT koppel deur 'n voorafbepaalde verbinding, maar hier blom die helfte van die volgende nadeel van IPIP in 'n nuwe lig - enigiemand van 'n private netwerk kan agter die sigbare wegkruip publieke IP en kliëntpoort (in suiwer IPIP bestaan ​​hierdie probleem nie).

Om hierdie een en 'n halwe probleem op te los, is die nut gebore ipipou. Dit implementeer 'n tuisgemaakte meganisme om 'n afgeleë gasheer te staaf, sonder om die werking van die kern-FOU te ontwrig, wat pakkies vinnig en doeltreffend in kernspasie sal verwerk.

Ons het nie jou skrif nodig nie!

Goed, as jy die publieke poort en IP van die kliënt ken (byvoorbeeld, almal daaragter gaan nêrens heen nie, NAT probeer poorte 1-in-1 karteer), kan jy 'n IPIP-oor-FOU-tonnel skep met die volgende opdragte, sonder enige skrifte.

op bediener:

# Подгрузить модуль ядра 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

op die kliënt:

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

waar

  • ipipou* — naam van die plaaslike tonnelnetwerkkoppelvlak
  • 203.0.113.1 - openbare IP-bediener
  • 198.51.100.2 - publieke IP van die kliënt
  • 192.168.0.2 - kliënt IP toegewys aan koppelvlak eth0
  • 10001 — plaaslike kliëntpoort vir FOU
  • 20001 — publieke kliënt hawe vir FOU
  • 10000 — publieke bedienerpoort vir FOU
  • encap-csum - opsie om 'n UDP-kontrolesom by ingekapselde UDP-pakkies by te voeg; vervang kan word deur noencap-csum, om nie te praat nie, integriteit word reeds beheer deur die buitenste inkapselingslaag (terwyl die pakkie binne die tonnel is)
  • eth0 - plaaslike koppelvlak waaraan die ipip-tonnel gebind sal word
  • 172.28.0.1 - IP van die kliënt tonnel koppelvlak (privaat)
  • 172.28.0.0 - IP-tonnelbedienerkoppelvlak (privaat)

Solank die UDP-verbinding lewendig is, sal die tonnel in 'n werkende toestand wees, maar as dit breek, sal jy gelukkig wees - as die kliënt se IP:-poort dieselfde bly - dit sal lewe, as hulle verander - sal dit breek.

Die maklikste manier om alles terug te draai, is om die kernmodules af te laai: modprobe -r fou ipip

Selfs al word stawing nie vereis nie, is die kliënt se openbare IP en poort nie altyd bekend nie en is dit dikwels onvoorspelbaar of veranderlik (na gelang van die NAT-tipe). As jy weglaat encap-dport aan die bedienerkant sal die tonnel nie werk nie, dit is nie slim genoeg om die afstandverbindingpoort te neem nie. In hierdie geval kan ipipou ook help, of WireGuard en ander soos dit kan jou help.

Hoe werk dit?

Die kliënt (wat gewoonlik agter NAT is) maak 'n tonnel oop (soos in die voorbeeld hierbo), en stuur 'n verifikasiepakkie na die bediener sodat dit die tonnel aan sy kant konfigureer. Afhangende van die instellings, kan dit 'n leë pakkie wees (net sodat die bediener die publieke IP: konneksiepoort kan sien), of met data waarmee die bediener die kliënt kan identifiseer. Die data kan 'n eenvoudige wagwoordfrase in duidelike teks wees (die analogie met HTTP Basic Auth kom na vore) of spesiaal ontwerpte data wat met 'n private sleutel onderteken is (soortgelyk aan HTTP Digest Auth net sterker, sien funksie client_auth in die kode).

Op die bediener (die kant met die publieke IP), wanneer ipipou begin, skep dit 'n nfqueue queue hanteerder en stel netfilter op sodat die nodige pakkies gestuur word waar hulle moet wees: pakkies wat die verbinding met die nfqueue queue inisieer, en [amper] al die res gaan reguit na die luisteraar FOU.

Vir diegene wat nie weet nie, is nfqueue (of NetfilterQueue) 'n spesiale ding vir amateurs wat nie weet hoe om kernmodules te ontwikkel nie, wat met behulp van netfilter (nftables/iptables) jou toelaat om netwerkpakkies na gebruikersruimte te herlei en dit daar te verwerk met primitiewe middel byderhand: wysig (opsioneel) en gee dit terug aan die kern, of gooi dit weg.

Vir sommige programmeertale is daar bindings om met nfqueue te werk, vir bash was daar geen (heh, nie verbasend nie), ek moes python gebruik: ipipou gebruik NetfilterWou.

As prestasie nie krities is nie, kan jy met hierdie ding relatief vinnig en maklik jou eie logika saamstel om met pakkies op 'n redelike lae vlak te werk, byvoorbeeld eksperimentele data-oordragprotokolle te skep, of plaaslike en afgeleë dienste met nie-standaard gedrag te trol.

Rou sokke werk hand aan hand met nfqueue, byvoorbeeld, wanneer die tonnel reeds gekonfigureer is en FOU luister op die verlangde poort, sal jy nie 'n pakkie van dieselfde poort op die gewone manier kan stuur nie - dit is besig, maar jy kan 'n ewekansige gegenereerde pakkie direk na die netwerkkoppelvlak neem en stuur deur 'n rou sok te gebruik, alhoewel die generering van so 'n pakkie 'n bietjie meer gepeuter sal verg. Dit is hoe pakkies met verifikasie in ipipou geskep word.

Aangesien ipipou slegs die eerste pakkies van die verbinding verwerk (en dié wat daarin geslaag het om in die tou te lek voordat die verbinding tot stand gebring is), ly prestasie amper nie daaronder nie.

Sodra die ipipou-bediener 'n geverifieerde pakkie ontvang, word 'n tonnel geskep en alle daaropvolgende pakkies in die verbinding word reeds verwerk deur die kern wat nfqueue omseil. As die verbinding misluk, sal die eerste pakkie van die volgende een na die nfqueue-tou gestuur word, afhangende van die instellings, as dit nie 'n pakkie met verifikasie is nie, maar vanaf die laaste onthou IP en kliëntpoort, kan dit óf deurgegee word op of weggegooi. As 'n geverifieerde pakkie van 'n nuwe IP en poort af kom, word die tonnel herkonfigureer om dit te gebruik.

Die gewone IPIP-oor-FOU het nog een probleem wanneer jy met NAT werk - dit is onmoontlik om twee IPIP-tonnels te skep wat in UDP ingekapsuleer is met dieselfde IP, want die FOU- en IPIP-modules is redelik geïsoleer van mekaar. Dié. 'n paar kliënte agter dieselfde openbare IP sal nie gelyktydig op hierdie manier aan dieselfde bediener kan koppel nie. In die toekoms, miskien, sal dit op kernvlak opgelos word, maar dit is nie seker nie. Intussen kan NAT-probleme deur NAT opgelos word - as dit gebeur dat 'n paar IP-adresse reeds deur 'n ander tonnel beset word, sal ipipou NAT van publiek na 'n alternatiewe private IP doen, voila! - jy kan tonnels skep totdat die poorte opraak.

Omdat Nie alle pakkies in die verbinding is onderteken nie, dan is hierdie eenvoudige beskerming kwesbaar vir MITM, so as daar 'n skurk op die pad tussen die kliënt en die bediener skuil wat na die verkeer kan luister en dit kan manipuleer, kan hy gewaarmerkte pakkies herlei deur 'n ander adres en skep 'n tonnel van 'n onbetroubare gasheer.

As iemand idees het oor hoe om dit reg te stel terwyl jy die grootste deel van die verkeer in die kern laat, moet asseblief nie huiwer om te praat nie.

Terloops, inkapseling in UDP het homself baie goed bewys. In vergelyking met inkapseling oor IP, is dit baie meer stabiel en dikwels vinniger ten spyte van die bykomende bokoste van die UDP-opskrif. Dit is te wyte aan die feit dat die meeste gashere op die internet net goed werk met die drie gewildste protokolle: TCP, UDP, ICMP. Die tasbare deel kan alles anders heeltemal weggooi, of dit stadiger verwerk, want dit is net vir hierdie drie geoptimaliseer.

Dit is byvoorbeeld hoekom QUICK, waarop HTTP/3 gebaseer is, bo-op UDP geskep is, en nie bo-op IP nie.

Wel, genoeg woorde, dit is tyd om te sien hoe dit in die "regte wêreld" werk.

Slag

Word gebruik om die regte wêreld na te boots iperf3. Wat die mate van nabyheid aan die werklikheid betref, is dit ongeveer dieselfde as om die werklike wêreld in Minecraft na te boots, maar vir eers sal dit deug.

Deelnemers aan die kompetisie:

  • verwys hoofkanaal
  • die held van hierdie artikel is ipipou
  • OpenVPN met verifikasie, maar geen enkripsie nie
  • OpenVPN in alles-insluitende modus
  • WireGuard sonder PresharedKey, met MTU=1440 (slegs vanaf IPv4)

Tegniese data vir geeks
Metrieke word geneem met die volgende opdragte:

op die kliënt:

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"

ICMP latency

ping -c 10 SERVER_IP | tail -1

op die bediener (loop gelyktydig met die kliënt):

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"

Tonnelkonfigurasie

ipipou
bediener
/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

kliënt
/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 (geen enkripsie, met verifikasie)
bediener

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

kliënt

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 (met enkripsie, verifikasie, via UDP, alles soos verwag)
Gekonfigureer met behulp van openvpn-bestuur

draadbeskerming
bediener
/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

kliënt
/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

Bevindinge

Klam lelike teken
Bediener SVE-lading is nie baie aanduidend nie, want... Daar is baie ander dienste wat daar loop, soms eet hulle hulpbronne op:

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

20 Mbps kanaal

ipipou: meer as net 'n ongeënkripteerde tonnel

ipipou: meer as net 'n ongeënkripteerde tonnel

kanaal per 1 optimistiese Gbps

ipipou: meer as net 'n ongeënkripteerde tonnel

ipipou: meer as net 'n ongeënkripteerde tonnel

In alle gevalle is ipipou redelik naby aan die basiskanaal in prestasie, wat wonderlik is!

Die ongeënkripteerde openvpn-tonnel het in albei gevalle nogal vreemd opgetree.

As iemand dit gaan toets, sal dit interessant wees om terugvoer te hoor.

Mag IPv6 en NetPrickle met ons wees!

Bron: will.com

Voeg 'n opmerking