我在家里运行了一个wireguard 服务器,以便能够从外部进入我的家庭网络。 Wireguard 使用 UDP。不幸的是,我不能像以前那样在我的 fritz box 中打开 IPv4 UDP 端口,因为我的提供商已将我置于 CG-NAT 后面,我必须使用 IPv6,但 IPv6 在我的工作场所尚不可用。
因此,我在家里使用 IPv6,在工作中使用 IPv4,但无法访问我的家庭网络。
我还有一个带有公共 IPv4 的 VPS,我可以为 TCP 流量建立反向 ssh 隧道。不幸的是,ssh 隧道仅支持 TCP,无论如何都不应该通过 TCP 进行 VPN 连接,这就是wireguard 仅使用 UDP 的原因。
VPN-Server VPN-Client
(UDP) | | (IPv4 UDP)
| | | not possible |
-----------|--------| <-------------------
| |
NAT CG-NAT
VPN-Server VPN-Client
(UDP) (IPv4 UDP)
| |
| |
inside agent outside agent
tunnel tunnel
|| | | ||
|| punch| punch| ||
| --------->|-------->|-------------------- |
-----------|<--------|<--------------------
| |
| |
NAT CG-NAT
外部代理在具有公共 IPv4 的 VPS 上运行
内部代理将向外部代理的公共 IP 和端口发送 UDP 数据报,这将在两个 NAT 上打孔,外部代理将接收这些数据报并从其源地址和端口了解如何将数据报发送回内部代理。
VPN 客户端可以将 UDP 发送到外部代理,这些数据报将转发到内部代理,然后从那里转发到 VPN 服务器,VPN 服务器可以回复内部代理,这些数据报将转发到外部代理,然后从那里转发到VPN 客户端。
外部代理将显示为客户端的 VPN 服务器,内部代理将显示为服务器的 VPN 客户端。
多个客户端连接是可能的,因为它将为每个连接到外部的新客户端使用内部代理上的隧道和新套接字,因此对于服务器来说,它看起来就像多个客户端正在内部代理的主机上运行一样。
在两台机器上克隆或解压代码并构建它。您至少需要 make 和 gcc。进入源码目录并使用命令
$ make
成功构建后,您最终会在同一文件夹中看到二进制udp-tunnel
。现在您可以直接从终端启动它(当然使用正确的选项)来进行一些快速测试,或者您可以在 makefile 的帮助下安装它。
编译后的二进制udp-tunnel
可以充当内部代理,也可以充当外部代理,具体取决于您在命令行上传递的选项。
假设 VPN 服务器正在侦听 UDP 1234,在本地主机上运行(与内部代理相同),外部计算机是 Jump.example.com,我们希望它侦听 UDP 端口 9999。
在内部主机上我们以
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999
在外部主机上我们以
$ ./udp-tunnel -l 9999
makefile 包含 3 个安装目标: install
仅安装二进制文件, install-outside
和install-inside
还安装 systemd 服务文件。后两者需要传递给 make 的变量才能正常工作。
要在跳转主机上安装外部代理(假设您需要端口 9999),请执行以下命令:
$ sudo make install-outside listen=9999
这会将二进制文件安装到/usr/local/bin/
并将 systemd 服务文件安装到/etc/systemd/system/
中,其中包含在外部代理模式下使用端口 9999 启动它所需的命令。
要在内部计算机上安装内部代理,请使用以下命令(假设您的 VPN 服务器是 localhost:1234,跳转主机是 Jump.example.com):
$ sudo make install-inside service=localhost:1234 outside=jump.example.com:9999
这将再次将二进制文件安装到/usr/local/bin/
并将 systemd 单元文件安装到/etc/systemd/system/
此时,您可能需要快速查看 systemd 单元文件,以了解二进制文件的使用方式并检查选项是否正确。这些选项应类似于上面快速测试中所述。
systemd 文件安装并确认正确后,尚未启用自动启动,您需要启用并启动它们。在内部机器上:
$ sudo systemctl enable udp-tunnel-inside.service
$ sudo systemctl start udp-tunnel-inside.service
在外部机器上
$ sudo systemctl enable udp-tunnel-outside.service
$ sudo systemctl start udp-tunnel-outside.service
没有加密。数据包按原样转发,假设您正在隧道传输的任何服务都知道如何自行保护或加密其数据。 VPN 连接通常就是这种情况。
此外,攻击者可能想要欺骗来自内部代理的保活数据包,以迷惑外部代理并将隧道转移到自己的计算机,从而导致服务中断。为了防止这种非常简单的攻击,可以使用基于散列的消息验证码来验证保活数据报。您可以在隧道两端使用 -k 选项来使用预共享密码来激活此功能。
在内部主机上你会像这样使用它
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999 -k mysecretpassword
在外部主机上,您可以使用以下命令启动它
$ ./udp-tunnel -l 9999 -k mysecretpassword
成功完成上述安装步骤后,您可能需要手动编辑两端的 systemd 文件并添加 -k 选项,然后在两端重新加载并重新启动。
然后,保活消息将包含对此密码的 SHA-256 以及严格递增的随机数,该随机数只能使用一次,以防止简单的重放攻击。
两个代理都维护一个连接列表,每个连接都存储与该特定客户端连接相关的套接字地址和套接字句柄。
启动时,内部代理将启动到外部代理的传出隧道,将其标记为未使用,并通过其发送保活数据包。外部代理将看到这些数据包,并将该隧道及其源地址添加到其自己的连接列表中。 keepalive 数据包使用随机数和身份验证代码进行签名,因此它们无法被欺骗或重放。
当客户端连接时,外部代理将沿着未使用的备用隧道发送客户端数据,内部代理将看到这一点,将隧道标记为活动状态,创建一个用于与服务通信的套接字并在两个方向上转发数据。然后,它还将立即创建另一个新的传出备用隧道,为下一个传入客户端做好准备。
当新的备用隧道到达外部代理时,它会将其添加到自己的连接列表中,以便能够立即为下一个连接客户端提供服务。
此时,两个代理的列表中都有 2 个隧道,一个处于活动状态,一个处于备用状态,等待下一个客户端连接。
不活动超时将确保死隧道(那些过去被标记为活动但一段时间内没有客户端数据的隧道)将被删除,并且它们的套接字将被关闭。内部代理将检测到长时间缺少转发数据,将其从自己的列表中删除并关闭其套接字,然后一段时间后,外部代理将检测到不再有任何保活到达此连接并将其从自己的列表中删除也列出来。
该代码仍处于高度实验阶段,因此不要将价值数百万美元的业务建立在它的基础上,至少现在还不是。它对我来说完美地满足了目的,但它可能会导致你的服务器崩溃、烧毁和爆炸。你已被警告过。