Shadow Stride —— Shadowsocks的优化

Shadow Stride —— Shadowsocks的优化


1. 引言

Shadowsocks是流传极广的加密代理软件,用于应对GFW的网络封锁。Shadowsocks在使用AEAD加密模式(如aes-256-gcm)的情况下,其保密没有已知的安全问题。但是,Shadowsocks并不隐蔽,目前已知GFW可以通过“被动探测+主动探测”的方式识别并封锁Shadowsocks服务器。

参见:https://gfw.report/blog/gfw_shadowsocks/zh.html

此外,Shadowsocks使用TCP连接,还面临着国际出口网络丢包的问题。TCP协议在抵抗丢包这方面很差劲,一旦丢包稍有严重连接就会变得极为缓慢。

针对这两种问题,我的解决方案是:反向隧道和KCP隧道。


2. 传统方案

通常,我们采用中国以外的VPS服务器作为Shadowsocks(以下简称SS)服务器,而客户端跨越直连服务器。如下图所示

这样GFW就可以探测SS服务端。如果SS服务端被GFW探测到,可能IP和端口号就会被封禁。


3. 反向隧道

因此,我们要做的第一个优化是引入反向隧道,以防止GFW的探测。为了使用反向隧道,我们需要引入一个国内的跳板机。架构图如下:

这里的反向隧道和SSH中提供的 ssh -R 命令的功能是一样的,原理是:本来应该作为服务器的机器运行反向隧道的客户端,原来应该作为客户端的跳板机运行反向隧道的服务端。反向隧道的服务端和客户端之间建立TCP连接池。这样,原本的服务端成为了客户端,向原本的客户端发起建立TCP连接的请求。这样,境外的VPS服务器不再需要开放任何端口,GFW也就难以进行主动探测。只有国内的跳板机需要向公网开放端口,但是GFW并不会扫描国内机器的端口,同时,这台跳板机上也没有运行任何翻墙软件。

SSH的反向隧道仍然存在一个问题:TCP连接很容易断掉,而且不会自动重连。因此这里采用了另一个开源的反向隧道实现frp。在断线重连的场景下表现非常健壮。

参见:GitHub - fatedier/frp: A fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet.


4. KCP 隧道

TCP对丢包非常脆弱,约10%的丢包就可能导致连接几乎不可用。而跨越GFW的流量,丢包非常常见,这可能是GFW故意丢包,也有可能是境外出口带宽不足。这里不再分析具体原因。解决丢包的方法是更换传输层协议,例如用KCP。这里我用的是开源的kcptun。

GitHub - xtaci/kcptun: A Stable & Secure Tunnel based on KCP with N:M multiplexing and FEC.

KCP有前向纠错,可以较好地解决丢包严重时连接的稳定性。

将KCP隧道和反向隧道结合起来,就得到了下面的框图:

图中一共有三台设备:一台自己的终端设备,如自己的个人电脑或者手机、一台境内的跳板机、一台境外的服务器。

自己的个人设备上只需要运行Shadowsocks客户端(ss-local)就可以,这也是为了兼容性考虑。例如在苹果设备上,有很多种Shadowsocks的客户端,但是其他协议的客户端很难找到,自己开发的难度也相当大。

境内的跳板机需要运行:frp的服务端(frps)、kcp隧道的服务端(kcptun_server)。这台机器通常可以用国内的云服务商,例如阿里云、腾讯云等。我们的方案假设了这台机器是不可信的,上面不会保存和传输任何关键的密钥和明文流量。

境外的服务器上需要运行:frpc的客户端(frpc)、kcp隧道的客户端(kcptun_client)、Shadowsocks的服务端(ss-server)。


5. 小结

以手机访问谷歌的场景为例,我会解释一下整个系统是如何工作的。

一、kcptun_client和kcptun_server 交换密钥,建立KCP连接,搭建起KCP隧道;

二、frpc访问kcptun_client,经由KCP隧道到达kcptun_server,再由kcptun_server发送给frps,建立连接池,搭建起反向隧道。

三、用户在手机中打开Shadowsocks VPN,并在浏览器中访问 www.google.com,请求会发送给ss-local,加密后发送到frps,依次经过kcptun_server、kcptun_client、frpc到达ss-server。

四、由ss-server访问谷歌,并将http相应经过相反的顺序(ss-server、frpc、kcptun_client、kcptun_server、frps、ss-local)返回给手机浏览器。


6. 附录:配置文件及命令脚本

系统一共有六个模块,kcptun_client, kcptun_server, frpc, frps, ss-local, ss-server。

其中,ss-local由于运行在用户自己的终端上,不用操作系统下有所不同。例如:


其余模块都运行在Linux服务器上,见下方:

下面的 [frp密码]和[ss密码]要替换成自己的密码,两个密码需要不同。

下面均假设国内跳板机的ip是1.2.3.4,国外服务器ip是5.6.7.8。对应的ip需要替换成自己的IP。


1. frpc

下载地址: https://github.com/fatedier/frp/releases/download/v0.35.1/frp_0.35.1_linux_arm64.tar.gz

配置脚本: frpc.ini

[common]
server_addr = 127.0.0.1
server_port = 8000
authentication_method = token
token = [frp密码]

[ss]
type = tcp
local_ip = 127.0.0.1
local_port = 7070
remote_port = 8080

运行命令: ./frpc -c frpc.ini


2. frps

下载地址: https://github.com/fatedier/frp/releases/download/v0.35.1/frp_0.35.1_linux_arm64.tar.gz

配置脚本: frps.ini

[common]
bind_port = 8000
authentication_method = token
token = [frp密码]

运行命令: ./frps -c frps.ini


3. kcptun_server

下载地址: https://github.com/xtaci/kcptun/releases/download/v20210103/kcptun-linux-amd64-20210103.tar.gz

运行命令: ./server_linux_amd64 -t "127.0.0.1:8000" -l ":5353" -mode fast3 -nocomp -sockbuf 16777217 -dscp 46

4. kcptun_client

下载地址: https://github.com/xtaci/kcptun/releases/download/v20210103/kcptun-linux-amd64-20210103.tar.gz

运行命令: ./client_linux_amd64 -r "1.2.3.4:5353" -l "127.0.0.1:8000" -mode fast3 -nocomp -autoexpire 900 -sockbuf 16777217 -dscp 46

5. ss-server

下载地址: Ubuntu最新版本下运行 sudo apt install shadowsocks-libev 即可

运行命令: ss-server -s 127.0.0.1 -p 7070 -k [ss密码] -m aes-256-gcm

6. 后台运行

为了保证上述程序在后台运行,可以采用多种方式实现:

- 将他们加入到systemd中成为启动模块
- 直接运行在tmux里面
- 用nohup或者setsid命令使其在后台运行。

7. 客户端配置

IP: 1.2.3.4
端口: 8080
加密方式: aes-256-gcm
密码: [ss密码]