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 与我们同在!

来源: habr.com

添加评论