Deploy Shadowsocks with v2ray-plugin Enabled

本文将介绍如何搭建自己的 shadowsocks 服务器并启用新的 v2ray-plugin 插件实现 Websocket 伪装。将会使用到 shadowsocks-libev 以及 v2ray-plugin
本文假定读者已经拥有了自己的 vps,且系统是 Debian 9+ 或者 Ubuntu 18.04+,并能够进行远程操作(VNC 或 SSH 或者你喜欢的任何方式)以及修改文本文件。

Install Requirements

shadowsocks-libev

此处只给出 Debian 系列的安装方法,推荐从 backports 源进行安装:

1
2
3
4
5
# add backports source
sh -c "printf 'deb http://deb.debian.org/debian $(lsb_release -sc)-backports main' > /etc/apt/sources.list.d/backports.list"

# update cache & install
apt update && apt -t $(lsb_release -sc)-backports install shadowsocks-libev

如果使用其它的 Linux 发行版,可以参考 shadowsocks-libev 的官方 README

v2ray-plugin

该插件没有进入 Debian 的包管理,但是得益于它由 Go 语言编写,可以方便的生成用于分发的二进制文件,因此我们可以直接从其 Github 的 Release 页面找到最新版适合自己系统的下载链接,如果是想本文一样搭建服务端的话,基本就是在 v2ray-plugin-linux-386-*v2ray-plugin-linux-amd64-* 中根据系统是 32 位还是 64 位选一个。截至本文,最新的 v2ray-plugin-linux-amd64 的地址是 https://github.com/shadowsocks/v2ray-plugin/releases/download/v1.2.0/v2ray-plugin-linux-amd64-v1.2.0.tar.gz

安装的话,只需下载,解压再移动到 /usr/local/bin 下即可:

1
2
3
4
5
6
7
8
pushd /tmp > /dev/null
VP_URL="https://github.com/shadowsocks/v2ray-plugin/releases/download/v1.2.0/v2ray-plugin-linux-amd64-v1.2.0.tar.gz"

wget -O- "$VP_URL" | tar zx
mv v2ray-plugin* /usr/local/bin/v2ray-plugin

unset VP_URL
popd > /dev/null

Configure Service

configure file

编辑 /etc/shadowsocks-libev/config.json 文件,填写以下内容:

1
2
3
4
5
6
7
8
9
{
"server": "0.0.0.0",
"server_port": 80,
"method": "aes-256-gcm",
"password": "your_password",
"timeout": 300,
"plugin": "/usr/local/bin/v2ray-plugin",
"plugin_opts": "server;path=/your-path"
}

其中 server_port 即服务端监听的端口,由于我们需要做伪装,因此建议使用 80 或者 8080 来伪装 HTTP,而如果开启了 TLS(见后文)则建议使用 443 端口。 plugin_opts 会被传递给插件,其中的 path 需要设置为一个合法的 HTTP 路径,它是 websocket 混淆模式下需要的参数,可以与后文的 nginx 反代配合。

enable and start service

1
2
systemctl enable shadowsocks-libev
systemctl start shadowsocks-libev

其中 systemctl enable shadowsocks-libev 只需要安装过后执行一次即可,该命令用来设置 shadowsocks-libev 开机自启动。

Advanced

work with nginx

由于伪装了 http 协议,因此可以使用一些 web server 作为反向代理,这里以 nginx 举例。
将 shadowsocks 隐藏在反向代理后面有很多好处,包括但不限于:

  1. 不同的 path 对应不同的 shadowsocks 实例来实现多用户;
  2. 在访问该域名的其它路径时可以返回一个正常的网站。

其中第二点可以实现更好的伪装。

install nginx

从 apt 安装 nginx 包:

1
apt install nginx-full

configure file

首先调整前述 /etc/shadowsocks-libev/config.json 的配置:

1
2
3
4
5
6
7
8
9
{
"server": "127.0.0.1",
"server_port": 10000,
"method": "aes-256-gcm",
"password": "your_password",
"timeout": 300,
"plugin": "/usr/local/bin/v2ray-plugin",
"plugin_opts": "server;path=/your-path"
}

其中监听地址改成了 127.0.0.1 ,这样可以避免不必要的端口暴露在公网。而 server_port 改成了其它端口,因为一会 80 要被 nginx 占用,这里改成了 10000。

接下来修改 nginx 的配置,编辑 /etc/nginx/sites-enabled/default 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 80;
listen [::]:80;

root /var/www/html;

server_name your.domain;

add_header X-Robots-Tag "noindex, nofollow";

location / {
resolver 1.0.0.1;
proxy_pass http://github.com;
}

location = /your-path {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:10000;
}
}

其中的 server_name 就是要伪装的域名,推荐自己去 freenom 免费申请一个。而后面的 location /your-path 即对应了前文 v2ray-plugin 中的 path 参数,当客户端使用 /your-path 访问服务器时该区域的配置生效,nginx 会代理 websocket 流量到本地的 127.0.0.1:10000 ,如果前面调整 shadowsocks 时填写了其它的 server_port 请在 proxy_pass http://127.0.0.1:10000; 出作出相应调整。

location / 段的配置使得访问其它 path 的 http 流量被代理到http://github.com 。这样可以实现伪装成一个普通网站的需求。当然,你也可以选择在本地搭个人网站来伪装(比如一个 blog 哈哈哈)。

restart services

1
2
systemctl restart shadowsocks-libev
nginx -t && nginx -s reload

其中 nginx -t 会检查当前的配置是否有语法错误,如果有错误请按照提示修改,再执行 nginx -s reload 命令重载 nginx 即可。

enable TLS

由于 shadowsocks 已经提供了基于预共享密钥的安全性保障,只要设置的密码足够复杂,那么安全性是足够的。在我们的伪装流量的需求下,开启 TLS 的最大作用是可以隐藏前面的 path 参数,即一个嗅探者没办法从 TLS 流量中解析出正在访问该服务器的哪个 path,其次是可以享受一些运营商针对 TLS 流量的一些 QOS。

setup DNS records

在申请 TLS 证书前,需要先配置好 DNS 解析。一般来说,在域名提供商的域名管理页面就会提供基本的 DNS 配置方法,比如 freenom 即在 Services->My Domains->Manage Domain->Manage Freenom DNS

如果你的 VPS 有一个 IPv4 地址,那么你需要添加一个 A 记录来将要混淆的域名(这里是 your.domain)指向服务器的地址。如果需要配置 IPv6 地址则再添加一个相同域名的 AAAA 记录。配置好后可以在任意电脑上执行:

1
nslookup your.domain

如果上述命令正确地返回了刚才配置的地址,那么就可以进行下一步了。

install acme.sh

要开启 TLS,我们需要有自己的证书,本文选择 acme.sh 来实现证书的自动申请和更新的自动化流程。当然,也可以选择自签证书,但是强烈不建议这么做,这会使得客户端的配置变得麻烦,如果配置不当还存在被中间人的风险。

安装 acme.sh 十分简单:

1
wget -O- https://get.acme.sh | sh

issue a cert

由于我们使用了 nginx 来做反向代理,申请证书的时候也可以使用 nginx mode,这里假定我们拥有的域名是 your.domain

1
2
3
4
5
acme.sh --issue --nginx \
-d your.domain -d other.your.domain \
--fullchain-file /etc/ssl/private/your.domain/fullchain.cer \
--key-file /etc/ssl/private/your.domain/private.key \
--reloadcmd 'systemctl reload nginx'

这样在申请好证书之后,完整证书链文件会被复制到 /etc/ssl/private/your.domain/fullchain.cer ,私钥在/etc/ssl/private/your.domain/private.key ;这两个路径在后面的 nginx 中需要使用,请根据自己的需求调整。

安装 acme.sh 时会自动添加一个 crontab 任务来每天检查证书的更新情况,因此后续的更新等就不需要操心了。在 acme.sh 成功更新了证书会自动执行命令中 --reloadcmd 的命令。

更多 acme.sh 的功能和用法请去其官网了解吧。

configure nginx

再次修改 /etc/nginx/sites-enabled/default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
server {
listen 80;
listen [::]:80;

server_name _;

location / {
return 301 https://$http_host$request_uri;
}
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

server_name your.domain;

ssl_protocols TLSv1.2;
ssl_certificate /etc/ssl/private/your.domain/fullchain.cer;
ssl_certificate_key /etc/ssl/private/your.domain/private.key;

ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5:!ECDHE-ECDSA-AES256-SHA384:!ECDHE-ECDSA-CAMELLIA256-SHA384:!ECDHE-ECDSA-AES128-SHA256:!ECDHE-ECDSA-CAMELLIA128-SHA256:!ECDHE-ECDSA-AES256-SHA:!ECDHE-ECDSA-AES128-SHA;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
keepalive_timeout 70;

add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';

location / {
proxy_pass https://github.com;
resolver 1.0.0.1;
}

location = /your-path {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:10000;
}
}

restart service

接下来重载 nginx 即可:

1
nginx -t && nginx -s reload

最后在客户端开启 TLS 选项,这样就实现了 Websocket over TLS 的伪装。