4. 安全解决方案:使用 ssh 穿透

4.1. 原理

假设你的防火墙管理员允许透明 TCP 连接到某台服务器机器的某个端口(无论是标准的 SSH 端口 22,还是备用目标端口,如 HTTP 端口 80 或其他端口),或者你设法将防火墙一侧的某个端口重定向到另一侧的端口(使用 httptunnelmailtunnel、通过 telnet 的某些隧道,或其他方式)。

然后,你可以在服务器端端口上运行 sshd,并在客户端端口上使用 ssh 连接到它。在 ssh 连接的两端,你运行 IP 模拟器(pppd),这样你就拥有了 VPN,即虚拟公共网络,它可以绕过愚蠢的防火墙限制,并额外提供加密以保护隐私(注意:防火墙管理员仍然知道隧道的另一端,以及你在运行 ssh 之前可能发送的任何身份验证信息)。

相同的技术可以用于构建 VPN,即虚拟专用网络,从而将物理站点安全地连接到一个逻辑网络中,而不会牺牲站点之间传输网络的安全。

4.2. 示例会话

以下是一个示例脚本,供你根据自己的需求进行调整。它使用了 zsh 的数组功能,但你可以轻松地将其调整为你喜欢的 shell。使用 ssh-p 选项来尝试端口 22 以外的其他端口(但请务必在同一端口上运行 sshd)。

请注意,该脚本假定 ssh 可以登录,而无需你交互式地键入密码(实际上,它的控制 tty 将连接到 pppd,因此如果它要求输入密码,你将失败)。这可以通过你的˜/.ssh/authorized_keys中的 ssh 密钥来完成,这些密钥要么不需要密码,要么你可以使用 ssh-agentssh-askpass 解锁。请参阅你的 SSH 文档。实际上,你也可以使用 chat 脚本输入密码,但这绝对不是正确的方法。

如果你不是服务器端的 root 用户,或者只是想屏蔽客户端网络的出站连接,你可以使用 slirp 而不是 pppd 作为服务器的 PPP 模拟器。只需取消注释相关行即可。

#!/bin/zsh -f
SERVER_ACCOUNT=root@server.fqdn.tld
SERVER_PPPD="pppd ipcp-accept-local ipcp-accept-remote"
#SERVER_PPPD="pppd" ### This usually suffices if it's in /usr/sbin/
#SERVER_PPPD="/home/joekluser/bin/slirp ppp"
CLIENT_PPPD=( pppd
	silent
	10.0.2.15:10.0.2.2
	### For debugging purposes, you may uncomment the following:
	# updetach debug
	### Another potentially useful option (see section on Routing):
	# defaultroute
)
$CLIENT_PPPD pty "ssh -t $SERVER_ACCOUNT $SERVER_PPPD"

请注意,来自你的/etc/ppp/options˜/.slirprc的默认选项可能会破坏此脚本,因此请从中删除任何不需要的选项。

另请注意,10.0.2.2slirp 的默认设置,可能适合也可能不适合你的特定设置。在任何情况下,你都应该最有可能使用 RFC 1918 为私有网络保留的范围内的某个地址10.0.0.0/8, 172.16.0.0/12192.168.0.0/16。受防火墙保护的 LAN 可能已经在使用其中的一些地址,避免冲突是你的责任。有关更多自定义设置,请阅读相应的文档。

如果你的客户端 pppd 较旧或非 Linux(例如 BSD)且没有 pty 选项,请使用
cotty -d -- $CLIENT_PPPD -- ssh -t $SERVER_ACCOUNT $SERVER_PPPD
注意:不要在给 cotty 的命令周围加上引号,因为它们只是按原样 exec() 执行的,并且不要忘记指定服务器 pppd 的完整路径(如果它不在 ssh 设置的标准路径中)。

自动重新连接留给读者作为练习(提示:pppdnodetach 选项可能对此有所帮助)。