ipipou:不僅僅是一個未加密的隧道

我們要對 IPv6 之神說什麼?

ipipou:不僅僅是一個未加密的隧道
是的,我們今天也對加密之神說同樣的話。

在這裡,我們將討論未加密的 IPv4 隧道,但不是「暖燈」隧道,而是現代「LED」隧道。 這裡還有閃爍的原始套接字,並且正在處理用戶空間中的資料包。

每種口味和顏色都有 N 種隧道協定:

  • 時尚、時尚、青春 WireGuard
  • 多功能,如瑞士刀、OpenVPN 和 SSH
  • 老而不邪惡的GRE
  • 最簡單、快速、完全未加密的IPIP
  • 積極發展 日內瓦
  • 好多其它的。

但我是一名程式設計師,所以我只會將N增加一小部分,並將真正協議的開發留給Kommersant開發人員。

在一個未出生的 項目我現在正在做的是從外部存取 NAT 後面的主機。 使用成人密碼學協議來實現這一點,我無法擺脫這種感覺,就像用大砲射出麻雀一樣。 因為隧道大部分僅用於在 NAT-e 中戳洞,內部流量通常也被加密,但它們仍然淹沒在 HTTPS 中。

在研究各種隧道協定時,我內心的完美主義者的注意力一次又一次地被 IPIP 吸引,因為它的開銷最小。 但對於我的任務來說,它有一個半明顯的缺點:

  • 它需要雙方的公共IP,
  • 並且沒有對您進行身份驗證。

因此,完美主義者被趕回了頭骨的黑暗角落,或者他坐在那裡的任何地方。

然後有一天,當我閱讀有關的文章時 原生支援的隧道 在Linux中我遇到了FOU(Foo-over-UDP),即不管怎樣,封裝在UDP中。 到目前為止,僅支援 IPIP 和 GUE(通用 UDP 封裝)。

「這就是銀彈! 一個簡單的 IPIP 對我來說就足夠了。” - 我想。

事實上,子彈並不是完全銀色的。 UDP 封裝解決了第一個問題- 您可以使用預先建立的連接從外部連接到NAT 後面的客戶端,但是IPIP 的下一個缺點的一半在這裡以新的方式展現- 來自專用網路的任何人都可以隱藏在可見的後面公共IP和客戶端連接埠(在純IPIP中不存在此問題)。

為了解決這個半問題,實用程式誕生了 伊皮普。 它實作了一種自製的機制來驗證遠端主機,而不會中斷核心 FOU 的操作,這將快速有效地處理核心空間中的資料包。

我們不需要你的劇本!

好的,如果您知道客戶端的公共連接埠和 IP(例如,它後面的每個人都不會去任何地方,NAT 嘗試映射連接埠 1-in-1),您可以使用以下命令建立 IPIP-over-FOU 隧道遵循命令,無需任何腳本。

在伺服器上:

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

在客戶端:

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

哪裡

  • ipipou* — 本機隧道網路介面的名稱
  • 203.0.113.1 — 公用IP伺服器
  • 198.51.100.2 — 用戶端的公共IP
  • 192.168.0.2 — 指派給介面 eth0 的客戶端 IP
  • 10001 — FOU 的本機用戶端端口
  • 20001 — FOU 的公共用戶端端口
  • 10000 — FOU 的公共伺服器端口
  • encap-csum — 在封裝的 UDP 封包中新增 UDP 校驗和的選項; 可以替換為 noencap-csum更不用說,完整性已經由外部封裝層控制(當資料包位於隧道內部時)
  • eth0 — ipip 隧道將綁定到的本地接口
  • 172.28.0.1 — 用戶端隧道介面的 IP(私有)
  • 172.28.0.0 — IP 隧道伺服器介面(專用)

只要 UDP 連線處於活動狀態,隧道就會處於工作狀態,但如果它中斷,您會很幸運 - 如果客戶端的 IP:連接埠保持不變 - 它將繼續存在,如果它們發生變化 - 它將中斷。

恢復一切最簡單的方法是卸載核心模組: modprobe -r fou ipip

即使不需要身份驗證,客戶端的公共 IP 和連接埠也並不總是已知,並且通常是不可預測的或可變的(取決於 NAT 類型)。 如果你省略 encap-dport 在伺服器端,隧道將不起作用,它不夠智能,無法佔用遠端連線連接埠。 在這種情況下,ipipou 也可以提供幫助,或者 WireGuard 和其他類似的工具可以幫助您。

它是如何工作的呢?

用戶端(通常位於 NAT 之後)開啟隧道(如上例所示),並向伺服器發送身份驗證封包,以便伺服器在其一側配置隧道。 根據設置,這可以是一個空資料包(只是為了讓伺服器可以看到公共 IP:連接埠),也可以是包含伺服器可以識別客戶端的資料。 資料可以是明文形式的簡單密碼(類似 HTTP Basic Auth),也可以是用私鑰簽署的專門設計的資料(類似 HTTP Digest Auth,只是更強,請參閱函數 client_auth 在代碼中)。

在伺服器(具有公共IP 的一側)上,當ipipou 啟動時,它會建立一個nfqueue 佇列處理程序並配置netfilter,以便將必要的封包傳送到它們應該傳送到的位置:初始化與nfqueue 佇列的連接的資料包,以及[幾乎]其餘的都直接發送給聽眾 FOU。

對於那些不了解的人來說,nfqueue(或NetfilterQueue)對於不知道如何開發核心模組的業餘愛好者來說是一個特殊的東西,它使用netfilter(nftables/iptables)允許您將網路封包重定向到使用者空間並在那裡處理它們原始的意思是:修改(可選)並將其傳回給內核,或丟棄它。

對於某些程式語言,有與 nfqueue 一起使用的綁定,對於 bash 則沒有(呵呵,這並不奇怪),我不得不使用 python:ipipou 使用 網路過濾隊列.

如果性能並不重要,那麼使用這個東西,您可以相對快速且輕鬆地構建自己的邏輯,以在相當低的級別上處理數據包,例如,創建實驗性數據傳輸協議,或以非標準行為控製本地和遠端服務。

原始套接字與 nfqueue 協同工作,例如,當隧道已配置並且 FOU 正在偵聽所需連接埠時,您將無法以通常的方式從相同連接埠發送資料包 - 它很忙,但是您可以使用原始套接字直接將隨機產生的資料包發送到網路接口,儘管產生這樣的資料包需要更多的修改。 這就是在 ipipou 中建立帶有身份驗證的資料包的方式。

由於 ipipou 僅處理來自連接的第一個資料包(以及在建立連接之前設法洩漏到佇列中的資料包),因此效能幾乎不會受到影響。

一旦 ipipou 伺服器收到經過驗證的封包,就會建立隧道,並且連接中的所有後續封包都已由核心繞過 nfqueue 進行處理。 如果連接失敗,那麼下一個的第一個資料包將發送到 nfqueue 佇列,根據設置,如果不是帶有身份驗證的資料包,而是從最後記住的 IP 和客戶端連接埠開始,則可以透過開啟或丟棄。 如果經過驗證的封包來自新的 IP 和端口,則會重新配置隧道以使用它們。

通常的 IPIP-over-FOU 在使用 NAT 時還有一個問題 - 不可能創建兩個封裝在具有相同 IP 的 UDP 中的 IPIP 隧道,因為 FOU 和 IPIP 模組彼此完全隔離。 那些。 透過這種方式,同一公用 IP 後面的一對客戶端將無法同時連接到同一台伺服器。 在未來, 也許,會在核心層面解決,但這個還不確定。 同時,NAT問題可以透過NAT來解決——如果碰巧一對IP位址已經被另一個隧道佔用,ipipou將進行從公共IP到替代私有IP的NAT,瞧! - 您可以建立隧道,直到連接埠用完。

因為並非連接中的所有資料包都經過簽名,那麼這種簡單的保護很容易受到MITM 的攻擊,因此,如果客戶端和伺服器之間的路徑上潛伏著一個可以偵聽流量並操縱流量的惡棍,他可以透過以下方式重新導向經過驗證的封包:另一個位址並從不受信任的主機建立隧道。

如果有人對如何解決此問題同時將大部分流量留在核心中的想法有任何想法,請毫不猶豫地說出來。

順便說一下,UDP 封裝已經很好地證明了自己。 與 IP 封裝相比,儘管 UDP 標頭有額外的開銷,但它更加穩定且更快。 這是因為 Internet 上的大多數主機只能使用三種最受歡迎的協定才能正常運作:TCP、UDP、ICMP。 有形部分可以完全丟棄其他所有內容,或者更慢地處理它,因為它只針對這三者進行了最佳化。

例如,這就是為什麼 HTTP/3 所基於的 QUICK 是在 UDP 之上創建的,而不是在 IP 之上創建的。

好了,話夠多了,是時候看看它在「現實世界」中是如何運作的了。

戰鬥

用於模擬現實世界 iperf3。 就接近現實的程度而言,這與《我的世界》中模擬現實世界大致相同,但目前來說就可以了。

比賽參加者:

  • 參考主通路
  • 本文的主角是 ipipou
  • OpenVPN 具有身份驗證但不加密
  • OpenVPN全包模式
  • 不含 PresharedKey 的 WireGuard,MTU=1440(因為僅限 IPv4)

極客技術數據
使用以下命令取得指標:

在客戶端:

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

ping -c 10 SERVER_IP | tail -1

在伺服器上(與客戶端同時運行):

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"

隧道配置

伊皮普
服務器
/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

顧客
/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(無加密,有驗證)
服務器

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

顧客

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(加密、驗證、透過 UDP,一切如預期)
配置使用 openvpn 管理

線衛
服務器
/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

顧客
/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

Результаты

潮濕醜陋的標誌
伺服器 CPU 負載不是很有指示性,因為... 還有許多其他服務在那裡運行,有時它們會消耗資源:

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

20Mbps頻道

ipipou:不僅僅是一個未加密的隧道

ipipou:不僅僅是一個未加密的隧道

每 1 樂觀 Gbps 的通道

ipipou:不僅僅是一個未加密的隧道

ipipou:不僅僅是一個未加密的隧道

在所有情況下,ipipou 的性能都非常接近基本通道,這非常棒!

在這兩種情況下,未加密的 openvpn 隧道的行為都非常奇怪。

如果有人要測試它,聽到回饋將會很有趣。

願 IPv6 和 NetPrickle 與我們同在!

來源: www.habr.com

添加評論