本文来自依云's Blog,转载请注明。
上篇成功让 mosh 走 UDP 洞,连接上了在 NAT 后边的主机。然而,很多有用的协议都是走 TCP 的,比如能传文件的 ssh、访问我的 MediaWiki 的 HTTP。TCP 洞难打,于是在想,OpenVPN 可以使用 UDP 协议,那么把双方用 OpenVPN 连起来,不是可以想用什么传输层的协议都可以了吗!于是,有了新的脚本。
与 mosh 相比,打洞部分主要的不同有:
- OpenVPN 的可配置性强,不需要 hack 即可让它绑定到需要的端口。
- OpenVPN 本身使用证书认证,因此把证书部分保存在客户端,余下的部分(包含双方使用的 IP 地址和端口号)可以通过打好的洞明文发送,不用怕被攻击。所以跑我这个脚本的话,当前工作目录要可写,以便保存双方即将使用的配置文件。
- OpenVPN 客户端会自动忽略对方发过来它不认识的配置信息,不用想办法避免。
- OpenVPN 需要 root 权限,因此脚本调用了 sudo,需要及时输入密码。
在实验过程中也遇到了一些坑:
-
Python 里没办法将已连接的 UDP socket「断开连接」,即将一个已经
connect
的 UDP socket 恢复到初始时可接收任意地址数据的状态。原本以为connect(('0.0.0.0', 0))
可以的,结果客户端这边始终收不到服务端发送的 OpenVPN 配置信息。Wireshark 抓包看到内核收到数据后发了 ICMP Port Unreachable 错误之后才明白过来。 -
MTU 的问题。默认值会导致刚开始传输正常,但随后收不到数据的情况。添加
mssfix 1400
配置解决。(其实这个 OpenVPN man 手册里有写。) -
超时的问题。先是没注意到 OpenVPN 服务端说没有配置
keepalive
的警告,结果连接空闲几分钟之后,「洞」就失效了。加上keepalive 10 60
解决。
配置中没有加默认路由,所以连接上之后唯一的效果就是,两个主机分别多出了同一网段的两个 IP 地址,相互间可以进行 TCP 通信了~~
对了,客户端连接时需要一个包含 OpenVPN 证书信息的文件,其格式为:
<ca> # ca.crt 文件内容 </ca> <cert> # crt 文件内容(只需要 BEGIN 和 END 标记的那部分) </cert> <key> # key 文件内容 </key>
PS: 有这个想法后不久,发现 None 已经做过类似的事情了。不过脚本有点多,是使用第三方服务器而不是像我这样手工交换地址的。
Oct 20, 2013 10:06:05 PM
我是想用这玩意,和同学玩 Minecraft 的..
想做成“傻瓜型”所以脚本有点多…
Oct 20, 2013 10:53:51 PM
你也玩 Minecraft 呀=w=
我是觉得,为了实现自动交换网络地址,专门弄个消息存取服务太浪费了,而且还涉及到认证之类的问题。
Oct 20, 2013 11:47:37 PM
自用所以认证什么的都偷懒省掉了..
不过最后还是用家里的路由器做中转了.. 宿舍路由器 PPTP 连回家,家里路由器做 DNAT 来我宿舍的本子…
这样同学那边比较方便.. 所以打洞也没继续完善..
(最近在玩 Terraria~
Oct 21, 2013 08:10:33 AM
感觉既然已经有第三方服务器了... 直接把第三方服务器做成VPN服务器,要打洞的机器都连接到VPN不就好了么 0.0
Oct 21, 2013 10:44:39 AM
1. 服务器在国外,转来转去延迟很高;
2. 服务器在国外,开 VPN 容易被墙。