OpenVPN 流量混淆补丁

最近墙抽风得比较厉害,OpenVPN 几乎全部阵亡,我自己架设的不用于翻墙用途的 OpenVPN 也都一并阵亡了。我只是用 OpenVPN 来组建一个虚拟局域网,以便从外网连接到我那被糟糕的移动宽带挡在内网里面的机器而已啊,这种人畜无害的服务都被咔嚓掉了,墙这次抽风抽得不轻。不过听说 有北邮的学生参与了墙的开发,考虑到天朝大学生的水平,出现这种情况也就不足为奇了。

虽然说我可以使用其他的虚拟专用网技术,但是比较起来还是 OpenVPN 更容易配置和方便部署。所以决定还是继续使用 OpenVPN,暂时不考虑更换到其他的虚拟专用网技术去。

据目前观察到的情况,墙只是会根据 OpenVPN 进行 TLS 握手时候的特征对特定的端口进行封杀,如果 OpenVPN 不使用 TLS 握手的话,墙就不会认出 OpenVPN 了。不过如果不使用 TLS 握手的话,OpenVPN 就只能配置成一对一的网络,没法组建局域网,故使用 TLS 是必须的(所以说你这墙放着只能用于翻墙用途的非 TLS 方式不管而去对可以作为正常应用的 TLS 协议下手你是要想怎样)。既然这样,那就只能对 TLS 握手过程进行一些模糊了。考虑到即使是握手之后的流量也会带有 OpenVPN 的表示,干脆顺便把握手之后的流量也一起做个模糊处理算了(好吧其实真正的原因修改代码的时候对所有流量进行处理要比仅仅对 TLS 握手流量进行处理更简单-_-b)。

于是这个补丁就出现了:

openvpn-2.2.2-obfs.patch <– 这个版本在 TCP 模式下会有问题,请使用下面的版本

OpenVPN-2.2.2-obfs.patch <– 这个版本的 TCP 和 UDP 模式均无问题,请使用此版本

同时这个补丁也发到了 gist 上: https://gist.github.com/4372285

使用说明

  1. 从 OpenVPN 官方下载 2.2.2 源码包,并解压
  2. 将此补丁保存到解压后的目录内,然后执行
  1. patch < openvpn-2.2.2-obfs.patch

另外建议手工修改源码目录中的 version.m4 文件,将 OpenVPN 的版本号 2.2.2 改为 2.2.2-obfs,以便和原始的 OpenVPN 区分

之后按正常的编译安装步骤进行

  1. ./configure –enable-password-save
  2. make
  3. make install

配置选项说明

该补丁有 2 个配置文件参数,分别如下

obfs-salt <secret>

混淆密钥,secret 是一个任意字符串,必须保证服务端的配置文件和客户端的配置文件的 secret 完全相同,否则客户端无法连接到服务器。

使用此选项即可开启流量混淆功能。

obfs-padlen <num>

num 是一个不大于 255 的正整数。如果使用了此选项,则本补丁会在每个数据包后面添加一个长度随机且不超过 num 的随机内容。

该选项只有在启用了  obfs-salt 之后才能使用。

注意事项

本补丁会在数据包在发送出去之前在数据包头部增加4字节的数据,如果启用了 obfs-padlen <num> 选项,还会在数据包尾部增加最多 num 字节的数据请使用 OpenVPN 自带的 mssfix 参数来指定最大的 TCP 包长度,保证原始TCP 包长度与随机增加的内容的长度之和不小于 MTU,如有必要,还应使用 fragment 来指定最大包长度,以确保数据包能够正常发送出去。

本补丁虽然使用了类似加密的方法对流量进行处理,但是我无法保证这种类似加密算法的安全性,不要尝试使用本补丁对流量进行加密。要对流量进行加密,请使用 OpenVPN 自带的加密功能。

选项示例

  1. ###示例壹####
  2. # 使用 threebody 作为混淆密钥,不改变数据包长度(但 obfs 补丁还是会在包头添加一段 4 字节的数据,实际上包的总长度会比原来多4,建议使用 mssfix 选项设置限制 TCP 包大小)
  3. obfs-salt threebody
  4. ###########
  5. ###示例二####
  6. # 使用 theansweris42 作为混淆密钥,随机将原始数据包增长0~10个字节
  7. # 假设 MTU 为 1400,因为原始数据包最多可能增加 10 个字节,另外 obfs 补丁会额外使用 4 个字节,因此要控制原始 TCP 包的大小在 1400 - 10 - 4 = 1386 以下
  8. obfs-salt theansweris42
  9. obfs-padlen 20
  10. mssfix 1386
  11. ##########

常见问题

问:使用了 obfs 补丁之后,速度变得很慢很慢

答:查看 OpenVPN 的 verb 3 级别的日志,检查其中是否包含有数据包错误之类的消息,如果有的话,可能是 mssfix 设置过大,将 mssfix 调小可解决问题

问:客户端已经向服务器发送请求,但服务器日志中看不到任何信息,客户端也无法连接

答:请检查服务端和客户端的 obfs-salt 以及 obfs-padlen 参数是否完全一致

问:使用 obfs 补丁后,性能是否会下降?

答:CPU 占用率会有一定提高,但除非是古董级 CPU,否则基本没影响。网络性能会有一定下降,如果没开启 obfs-padlen 选项,则下降不大,基本没影响。如果开启了 obfs-padlen <num> 选项,则 num 越大,影响越大。根据实测,num 选择 20 在 4Mbps 的线路下感觉不到速度降低。

补丁原理

本补丁在 OpenVPN 发送包之前以及接收包之后对数据包进行混淆,混淆方法是将数据包与一个混淆密钥进行异或操作。混淆密钥的产生方法如下:

  1. 从包头中提取一个随机数 R
  2. 密钥 C0=SHA1(R+salt)
  3. SHA1 计算结果为 20 字节,使用 C0 对数据包前 20 个字节进行异或运算
  4. 若数据包长度大于 20 字节,则计算 Cn=SHA1(Cn−1),然后用 Cn 继续对原始数据包进行异或运算

为保证每个数据包使用的密钥都不相同,补丁会在每个数据包的开头添加 4 字节的随机数据,这个随机数据就是随机数 R,随机数本身就是随机的,不需要进行混淆操作。平均大约发送 42 亿个数据包之后才会出现相同的密钥,假设数据包平均大小 100 字节,则流量平均大约达到 400G 后才会出现相同的密钥。

混淆之后,OpenVPN 的流量看上去就是完全无意义的随机流量了,但此时还有包大小信息是可识别的。为了把包大小信息也一并混淆,可以在包末尾增加一些随机长度的随机内容,这样包长度信息就也被混淆了。

由于 OpenVPN 在处理数据包时,会丢弃大小不正确的数据包,所以在接收端要把数据包中随机添加的内容去掉,这就要求接收端知道发送端添加了多长的内容,所以 obfs-padlen 选项随机增加的长度应该是伪随机的,也就是可以计算的。计算方法如下:

  1. 取 C=MD5(R+salt),其中 R 是随机数,和混淆时使用的随机数相同
  2. 取 MD5 值的第一个字节,作为无符号数,取 256 除的余数,作为随机增加的内容的长度,即 随机长度 L = (unsigned char)C[0] % 256

在单个连接流量较小的情况下,OpenVPN 的 ping 包是按照固定时间间隔发送的,这同样也是一个有用的信息。本补丁未针对此情况进行数据包混淆处理,用户可以通过在服务器端和客户端都禁用 OpenVPN 的 keepalive 及 ping 系列选项来避免此类信息被识别。

性能估算

最近装了个 MathJax 插件,不写几个公式手就痒,于是弄几个公式出来玩玩。

下面的计算均假设最大传输单元为 1500,且数据包长度服从均匀分布。

在启用了混淆补丁,但不开启 obfs-padlen 选项时,吞吐量与原始吞吐量的百分比的最好、最坏和平均情况分别为:

 

最好情况:1500−41500×10≈99.73%最坏情况:11+4=20%平均情况:∑1500−4i=1i∑1500k=4k≈99.46%

这结果比我预想中的要好啊,我估计平均会有20%~30%左右的性能下降,也就是性能仅为原始情况的 80%~70%,没想到结果竟然比我预想的要好得多。

在启用了 obfs-padlen 选项的情况下,最好、最坏和平均性能与原始吞吐量的百分比为:

 

最好情况:1500−4−n1500=1496−n1500×100%,最坏情况:11+4+n=1n+5×100%,平均情况:∑1500−4−ni=1i∑1500k=n+4k≈(1494−n)(1495−n)(1504+n)(1497−n)×100%,(0<n<256)(0<n<256)(0<n<256)

我现在在服务器上使用的参数是 n=20, 代入计算得到最好情况 98.40%,最坏情况 4%,平均情况 96.59%. 开了 20 个字节长度的随机内容填充,性能竟然还是这么好,真出乎我意料。不过这个计算结果确实也和实际情况相符。

其实上面的计算并不精确,因为没有考虑到当包的大小超过最大传输单元时拆分包后多出来的 TCP 头和 IP 头大小。不过要考虑这种情况的话就比较麻烦,所以就不算了。


截止到写这篇文章为止,我已经在两台服务器上运行这个补丁超过 5 天时间,暂时还没有观察到墙有任何反应,这比使用原版 OpenVPN 的时候运行不到一天就被墙的情况强多了。期间只出现了一个小插曲,其中一台服务器的 IP 地址被局部墙掉,主要是在非电信线路上被墙,不过这有可能和我在此期间在手机上使用联通网络去访问这台服务器的 SSH 引起的,此猜测有待证实。另外和这台服务器上 OpenVPN 产生的流量主要都是和一台位于电信宽带上的服务器进行的,估计这台服务器局部被墙和 OpenVPN 的关系不大。

也就是说到目前为止,这个补丁运行得还不错,至少没看到墙有什么实质性的动静。不过既然我把这个补丁发出来了,如果日后有其他人使用的话,到时候墙会作出何种反应就不得而知了,总之保持观察然后根据其反应作出调整即可。

最后要黑一下 Windows。我写这个补丁花了两天时间,在 Windows 上研究怎样编译 OpenVPN 又花了另外两天时间。在 Windows 上研究编译方法的时间竟然和我写补丁的时间一样长!于是得出结论:企图在 Windows 下进行开发=自虐。


若转载本文,无须遵守本站的授权许可,即转载时无须注明出处,同时,转载本文时不得注明本文出处。


原文:http://igfw.net/archives/13018
发表评论