ipipou: şifrelenmemiş bir tünelden daha fazlası

IPv6'nın Tanrısına ne diyoruz?

ipipou: şifrelenmemiş bir tünelden daha fazlası
Aynen öyle, bugün aynısını şifreleme tanrısına da söyleyeceğiz.

Burada şifrelenmemiş bir IPv4 tünelinden bahsedeceğiz, ancak "sıcak lambalı" bir tünelden değil, modern bir "LED" tünelinden bahsedeceğiz. Ayrıca burada yanıp sönen ham soketler de var ve kullanıcı alanındaki paketlerle ilgili çalışmalar sürüyor.

Her zevke ve renge uygun N tünelleme protokolü vardır:

  • şık, modaya uygun, gençlik WireGuard
  • İsviçre bıçakları, OpenVPN ve SSH gibi çok işlevli
  • eski ve kötü olmayan GRE
  • en basit, hızlı, tamamen şifrelenmemiş IPIP
  • aktif olarak gelişmekte CENEVRE
  • diğerleri.

Ama ben bir programcıyım, bu yüzden N'yi yalnızca bir miktar artıracağım ve gerçek protokollerin geliştirilmesini Kommersant geliştiricilerine bırakacağım.

Doğmamış birinde projeŞu an yaptığım şey NAT'ın arkasındaki hostlara dışarıdan ulaşmak. Bunun için yetişkin kriptografisine sahip protokoller kullandığımda, bunun serçeleri toptan fırlatmaya benzediği hissinden kurtulamadım. Çünkü tünel çoğunlukla yalnızca NAT-e'de delik açmak için kullanılıyor, dahili trafik de genellikle şifreleniyor, ancak yine de HTTPS'de boğuluyorlar.

Çeşitli tünel protokollerini araştırırken, içimdeki mükemmeliyetçinin dikkati minimum ek yük nedeniyle tekrar tekrar IPIP'e çekildi. Ancak görevlerim açısından bir buçuk önemli dezavantajı var:

  • her iki tarafta da genel IP'ler gerektirir,
  • ve sizin için kimlik doğrulama yok.

Bu nedenle mükemmeliyetçi, kafatasının karanlık köşesine veya nerede oturuyorsa oraya geri sürülür.

Ve sonra bir gün, konuyla ilgili makaleleri okurken yerel olarak desteklenen tüneller Linux'ta FOU (UDP üzerinden Foo) ile karşılaştım, yani. her neyse, UDP'ye sarılmış. Şu ana kadar yalnızca IPIP ve GUE (Genel UDP Kapsülleme) desteklenmektedir.

“İşte gümüş kurşun! Basit bir IPIP benim için yeterli.” - Düşündüm.

Aslında merminin tamamen gümüş olmadığı ortaya çıktı. UDP'de kapsülleme ilk sorunu çözer - NAT'ın arkasındaki istemcilere önceden belirlenmiş bir bağlantıyı kullanarak dışarıdan bağlanabilirsiniz, ancak burada IPIP'in bir sonraki dezavantajının yarısı yeni bir ışıkta çiçek açar - özel bir ağdaki herkes görünenin arkasına saklanabilir genel IP ve istemci bağlantı noktası (saf IPIP'de bu sorun mevcut değildir).

Bu bir buçuk sorunu çözmek için yardımcı program doğdu ipipou. Çekirdek alanındaki paketleri hızlı ve verimli bir şekilde işleyecek olan çekirdek FOU'nun çalışmasını kesintiye uğratmadan uzak bir ana bilgisayarın kimliğini doğrulamak için ev yapımı bir mekanizma uygular.

Senaryonuza ihtiyacımız yok!

Tamam, istemcinin genel bağlantı noktasını ve IP'sini biliyorsanız (örneğin, arkasındaki herkes hiçbir yere gitmez, NAT bağlantı noktalarını 1'i 1 arada eşleştirmeye çalışır), FOU üzerinden IPIP tüneli oluşturabilirsiniz. herhangi bir komut dosyası olmadan komutları takip etmek.

sunucuda:

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

istemcide:

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

nerede

  • ipipou* — yerel tünel ağ arayüzünün adı
  • 203.0.113.1 — genel IP sunucusu
  • 198.51.100.2 — istemcinin genel IP'si
  • 192.168.0.2 — eth0 arayüzüne atanan istemci IP'si
  • 10001 — FOU için yerel istemci bağlantı noktası
  • 20001 - FOU için genel istemci bağlantı noktası
  • 10000 - FOU için genel sunucu bağlantı noktası
  • encap-csum — kapsüllenmiş UDP paketlerine UDP sağlama toplamı ekleme seçeneği; ile değiştirilebilir noencap-csumAyrıca bütünlük zaten dış kapsülleme katmanı tarafından kontrol ediliyor (paket tünelin içindeyken)
  • eth0 — ipip tünelinin bağlanacağı yerel arayüz
  • 172.28.0.1 — İstemci tüneli arayüzünün IP'si (özel)
  • 172.28.0.0 — IP tünel sunucusu arayüzü (özel)

UDP bağlantısı canlı olduğu sürece tünel çalışır durumda olacaktır, ancak kesilirse şanslısınız - eğer istemcinin IP: bağlantı noktası aynı kalırsa - yaşayacak, değişirse - kopacaktır.

Her şeyi geri döndürmenin en kolay yolu çekirdek modüllerini boşaltmak: modprobe -r fou ipip

Kimlik doğrulama gerekmese bile, istemcinin genel IP'si ve bağlantı noktası her zaman bilinmez ve genellikle tahmin edilemez veya değişkendir (NAT türüne bağlı olarak). Eğer ihmal ederseniz encap-dport sunucu tarafında tünel çalışmayacak, uzak bağlantı portunu alacak kadar akıllı değil. Bu durumda ipipou da yardımcı olabilir veya WireGuard ve onun gibi diğerleri size yardımcı olabilir.

Nasıl çalışır?

İstemci (genellikle NAT'ın arkasındadır) bir tünel açar (yukarıdaki örnekte olduğu gibi) ve sunucuya bir kimlik doğrulama paketi göndererek tüneli kendi tarafında yapılandırır. Ayarlara bağlı olarak bu, boş bir paket (sunucunun genel IP: bağlantı portunu görebilmesi için) veya sunucunun istemciyi tanımlayabileceği verileri içeren bir paket olabilir. Veriler, açık metin halinde basit bir parola (akla HTTP Temel Kimlik Doğrulaması benzetmesi geliyor) veya özel bir anahtarla imzalanmış özel olarak tasarlanmış veriler (HTTP Digest Auth'a benzer, yalnızca daha güçlü, bkz. işlev) olabilir. client_auth kodda).

Sunucuda (genel IP'nin olduğu tarafta), ipipou başladığında, bir nfqueue kuyruk işleyicisi oluşturur ve netfilter'ı gerekli paketlerin olması gereken yere gönderileceği şekilde yapılandırır: nfqueue kuyruğuna bağlantıyı başlatan paketler ve [neredeyse] geri kalan her şey doğrudan dinleyici FOU'ya gider.

Bilmeyenler için, nfqueue (veya NetfilterQueue), çekirdek modüllerini nasıl geliştireceğini bilmeyen amatörler için özel bir şeydir; netfilter (nftables/iptables) kullanarak ağ paketlerini kullanıcı alanına yeniden yönlendirmenize ve bunları orada işlemenize olanak tanır. ilkel olmanın anlamı şudur: değiştirin (isteğe bağlı) ve onu çekirdeğe geri verin veya atın.

Bazı programlama dilleri için nfqueue ile çalışmaya yönelik bağlamalar vardır, bash için ise hiçbiri yoktu (heh, şaşırtıcı değil), python kullanmak zorunda kaldım: ipipou kullanır Netfilter Sırası.

Performans kritik değilse, bu şeyi kullanarak oldukça düşük düzeydeki paketlerle çalışmak için kendi mantığınızı nispeten hızlı ve kolay bir şekilde oluşturabilirsiniz, örneğin deneysel veri aktarım protokolleri oluşturabilir veya standart dışı davranışa sahip yerel ve uzak hizmetleri trolleyebilirsiniz.

Ham soketler nfqueue ile el ele çalışır; örneğin, tünel zaten yapılandırılmışsa ve FOU istenen bağlantı noktasını dinliyorsa, aynı bağlantı noktasından her zamanki gibi paket gönderemezsiniz - meşgul, ancak Rastgele oluşturulmuş bir paketi ham bir soket kullanarak doğrudan ağ arayüzüne alıp gönderebilirsiniz, ancak böyle bir paketin oluşturulması biraz daha fazla müdahale gerektirecektir. İpipou'da kimlik doğrulamalı paketler bu şekilde oluşturulur.

ipipou yalnızca bağlantıdan gelen ilk paketleri (ve bağlantı kurulmadan önce kuyruğa sızmayı başaran paketleri) işlediğinden, performans neredeyse hiç etkilenmez.

İpipou sunucusu kimliği doğrulanmış bir paket alır almaz, bir tünel oluşturulur ve bağlantıdaki sonraki tüm paketler, nfqueue'yu atlayarak çekirdek tarafından zaten işlenir. Bağlantı başarısız olursa, bir sonraki paketin ilk paketi ayarlara bağlı olarak nfqueue kuyruğuna gönderilir, kimlik doğrulamalı bir paket değilse, ancak son hatırlanan IP ve istemci bağlantı noktasından geçirilebilir. açık veya atılır. Kimliği doğrulanmış bir paket yeni bir IP ve bağlantı noktasından gelirse tünel bunları kullanacak şekilde yeniden yapılandırılır.

Her zamanki FOU üzerinden IPIP'nin NAT ile çalışırken bir sorunu daha vardır - aynı IP ile UDP'de kapsüllenmiş iki IPIP tüneli oluşturmak imkansızdır çünkü FOU ve IPIP modülleri birbirlerinden oldukça yalıtılmıştır. Onlar. aynı genel IP'nin arkasındaki bir çift istemci aynı sunucuya bu şekilde aynı anda bağlanamayacaktır. Gelecekte, belkikernel seviyesinde çözülecek ama bu kesin değil. Bu arada, NAT sorunları NAT tarafından çözülebilir - eğer bir çift IP adresi zaten başka bir tünel tarafından kullanılıyorsa, ipipou NAT'ı genelden alternatif bir özel IP'ye yapacaktır, işte! - Bağlantı noktaları bitene kadar tüneller oluşturabilirsiniz.

Çünkü Bağlantıdaki tüm paketler imzalanmadıysa, bu basit koruma MITM'e karşı savunmasızdır, dolayısıyla istemci ile sunucu arasındaki yolda trafiği dinleyip yönetebilen bir kötü adam varsa, kimliği doğrulanmış paketleri aracılığıyla yeniden yönlendirebilir. başka bir adrese gidin ve güvenilmeyen bir ana bilgisayardan bir tünel oluşturun.

Trafiğin büyük kısmını merkezde bırakarak bu durumu nasıl düzeltebileceğinize dair fikri olan varsa, konuşmaktan çekinmeyin.

Bu arada, UDP'deki kapsülleme kendini çok iyi kanıtladı. IP üzerinden kapsülleme ile karşılaştırıldığında, UDP başlığının ek yüküne rağmen çok daha kararlı ve genellikle daha hızlıdır. Bunun nedeni, İnternet'teki çoğu ana bilgisayarın yalnızca en popüler üç protokolle iyi çalışmasıdır: TCP, UDP, ICMP. Somut kısım, diğer her şeyi tamamen atabilir veya daha yavaş işleyebilir çünkü yalnızca bu üçü için optimize edilmiştir.

Örneğin, HTTP/3'ü temel alan QUICK'in IP'nin değil, UDP'nin üzerinde oluşturulmasının nedeni budur.

Yeterince kelime, "gerçek dünyada" nasıl çalıştığını görmenin zamanı geldi.

Savaş

Gerçek dünyayı taklit etmek için kullanılır iperf3. Gerçekliğe yakınlık derecesi açısından bu, Minecraft'ta gerçek dünyayı taklit etmekle yaklaşık olarak aynıdır, ancak şimdilik işe yarayacaktır.

Yarışmaya katılanlar:

  • referans ana kanalı
  • bu makalenin kahramanı ipipou
  • Kimlik doğrulamalı ancak şifrelemesiz OpenVPN
  • Her şey dahil modunda OpenVPN
  • PresharedKey'siz WireGuard, MTU=1440 (yalnızca IPv4'ten beri)

Meraklıları için teknik veriler
Metrikler aşağıdaki komutlarla alınır:

istemcide:

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 gecikmesi

ping -c 10 SERVER_IP | tail -1

sunucuda (istemciyle aynı anda çalışır):

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"

Tünel konfigürasyonu

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

müşteri
/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 (şifreleme yok, kimlik doğrulamalı)
sunucu

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

müşteri

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 (şifreleme, kimlik doğrulama, UDP aracılığıyla, her şey beklendiği gibi)
Kullanılarak yapılandırıldı openvpn-yönet

telgraf
sunucu
/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

müşteri
/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

Bulgular

Nemli çirkin işaret
Sunucu CPU yükü pek belirleyici değildir, çünkü... Orada çalışan birçok başka hizmet var, bazen kaynakları tüketiyorlar:

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 Mb/sn kanal

ipipou: şifrelenmemiş bir tünelden daha fazlası

ipipou: şifrelenmemiş bir tünelden daha fazlası

1 iyimser Gbps başına kanal

ipipou: şifrelenmemiş bir tünelden daha fazlası

ipipou: şifrelenmemiş bir tünelden daha fazlası

Her durumda, ipipou'nun performansı temel kanala oldukça yakındır ve bu harikadır!

Şifrelenmemiş openvpn tüneli her iki durumda da oldukça tuhaf davrandı.

Birisi bunu test edecekse, geri bildirimleri duymak ilginç olacaktır.

IPv6 ve NetPrickle bizimle olsun!

Kaynak: habr.com

Yorum ekle