2019 年的 9 月和 10 月十分难熬。出于众所周知的原因,从 9 月开始又是一大波封端口封 IP 潮,AWS、Azure、GCP 几个大厂的 IP 从刚开始的几分钟阵亡,到后面一分钟就阵亡 ;小服务商们迫于现实也纷纷提高更换 IP 的价格。

翻越长城就这么难吗?在 SS、VMess 等协议早已被 GFW 攻破的情况下,还有什么能救一下我那岌岌可危的梯子?

与 V2Ray 插件的偶遇

SS 已经无力回天了,只能想办法转到 V2Ray 看看。在 V2Ray 浏览配置指南的时候看到了这一条:WebSocket+TLS+Web | 新 V2Ray 白话文指南,这一条的想法是利用 Web 服务器和 TLS 将流量伪装成正常的 HTTPS 流量,而建立连接使用 WebSocket,看起来有点“天衣无缝”的感觉,因为 HTTPS 使用443端口,还无法被嗅探内容。但是原文的 V2Ray 服务端使用了 VMess 协议,且 V2Ray 貌似并没有像 SS 那样广泛使用的 GUI,所以想找找看有没有办法让 SS 客户端通过 WS 连接到使用 SS 协议的 V2Ray 服务端。

巧的是,今年一月,Windows 端的 v2ray-plugin 和 Android 端的 v2ray-plugin-android 相继出现,之后又发现有人在 V2Ray 的 Github 上提出了和我一样的问题:ss+websocket不支持吗? 这样一来问题就全部解决了。

准备工作

  • 本文使用的系统是 Debian 9,Ubuntu 18.04 及更新版本也没有问题。
  • 默认你已安装了 V2Ray。如果没有请自行参阅 安装 | 新 V2Ray 白话文指南
  • 本文将使用 nginx 环境,所以请确认服务器已经安装了 Nginx。Apache 的配置请参阅 WebSocket+TLS+Web | 新 V2Ray 白话文指南
  • 拥有一个自己的域名,并已经设置好了 DNS 记录。
  • 为将要使用的(子)域名申请 SSL 证书并下载证书文件。推荐使用 FreeSSL 提供的 TrustAsia 或 Digicert Encryption Everywhere 证书,拥有一年的有效期。

服务器配置

Nginx 配置

假定 SSL 证书存放在 /etc/nginx/ssl.crt/ 目录下,nginx 配置如下。location 后的路径应与 V2Ray 配置中的 path 保持一致,端口号应与 V2Ray 配置中的任意门协议的 port 保持一致。当协议不为 WebSocket 时将返回 403 Forbidden。

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

    server_name domain.example.com;

    ssl on;
    ssl_certificate ssl.crt/domain.example.com.crt;
    ssl_certificate_key ssl.crt/domain.example.com.key;
    ssl_protocols TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;

    location /shadowsocks {
        if ($http_upgrade != "websocket") {
            return 403;
        }

        proxy_redirect off;
        proxy_pass http://127.0.0.1:10000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

V2Ray 配置

任意门(dokodemo-door)协议中的 port 以及 path 应与 nginx 配置保持一致。不要更改 settings 中的 address 项的值。SS 协议中的 9015 端口并没有使用,但是需要指定,所以无需在安全组或者 UFW 中开放 9015 端口。

{
    "log": {
        "access": "/var/log/v2ray/access.log",
        "error": "/var/log/v2ray/error.log",
        "loglevel": "warning"
    },
    "inbounds": [
        {
            "port": "10000",
            "listen": "127.0.0.1",
            "protocol": "dokodemo-door",
            "tag": "wsdoko",
            "settings": {
                "address": "v1.mux.cool",
                "followRedirect": false,
                "network": "tcp"
            },
            "streamSettings": {
                "network": "ws",
                "wsSettings": {
                    "path": "/shadowsocks"
                }
            }
        },
        {
            "port": 9015,
            "protocol": "shadowsocks",
            "settings": {
                "method": "aes-256-gcm",
                "ota": false,
                "password": "yourpassword",
                "network": "tcp,udp"
            },
            "streamSettings": {
                "network": "domainsocket"
            }
        }
    ],
    "outbounds": [
        {
            "protocol": "freedom",
            "settings": {}
        },
        {
            "protocol": "blackhole",
            "settings": {},
            "tag": "blocked"
        },
        {
            "protocol": "freedom",
            "tag": "ssmux",
            "streamSettings": {
                "network": "domainsocket"
            }
        }
    ],
    "transport": {
        "dsSettings": {
            "path": "/var/run/v2ray/ss-loop.sock"
        }
    },
    "routing": {
        "rules": [
            {
                "type": "field",
                "inboundTag": [
                    "wsdoko"
                ],
                "outboundTag": "ssmux"
            },
            {
                "type": "field",
                "ip": [
                    "geoip:private"
                ],
                "outboundTag": "blocked"
            }
        ]
    }
}

由于需要确保 /var/run/v2ray/ 目录在 V2Ray 进程启动前就存在,所以接下来在 /etc/systemd/system/v2ray.service 文件中的 ExecStart 之前新增一行:

ExecStartPre=/bin/mkdir -p /run/v2ray/

这样可以使 V2Ray 服务启动时自动创建目录。

客户端配置

Windows 客户端

Releases · shadowsocks/v2ray-plugin 中下载 32 位或 64 位的插件,并将插件解压到你的 Shadowsocks 的目录下,之后编辑服务器配置:

  • 服务器地址 填写你的服务器地址,如:domain.example.com
  • 服务器端口 填写 443
  • 密码 填写你设置的密码,如:yourpassword
  • 加密 选择 aes-256-gcm
  • 插件程序 填写插件的文件名,如:v2ray-plugin_windows_amd64.exe
  • 插件选项 填写 “tls;mode=websocket;host=domain.example.com;path=/shadowsocks”,没有引号

Android 客户端

Releases · shadowsocks/v2ray-plugin 中下载对应硬件平台的插件,并安装。一般的手机下载 v2ray-arm64-v8a 开头的 apk 即可。之后在 Shadowsocks 中编辑服务器配置:

  • 服务器 填写你的服务器地址,如:domain.example.com
  • 远程端口 填写 443
  • 密码 填写你设置的密码,如:yourpassword
  • 加密方式 选择 aes-256-gcm
  • 最下方的插件 选择 v2ray
  • 编辑 配置
    • Transport mode 选择 websocket-tls
    • Hostname 填写你的服务器地址,如:domain.example.com
    • Path 填写上文中指定的路径,如:/shadowsocks
    • 其他参数无需更改

iOS 客户端

非苹果用户所以没有办法测试 iOS 平台的情况,但是通过查看几个 issue 大概得知,Shadowrocket 客户端貌似已经集成了 v2ray-plugin,可以直接在插件页面选择 v2ray-plugin 来使用,参数与 Android 客户端基本一致。

macOS 客户端

Mac 平台也是一样无法测试,不过 9 月发布的 ShadowsocksX-NG 1.9.0 版及之后的版本已经内置了 v2ray-plugin,可以在这里下载:Releases · shadowsocks/ShadowsocksX-NG。插件填写 v2ray-plugin,插件选项与 Windows 客户端一致,应该就可以使用了。

结尾 & 一些问题

到这里应该就可以使用了,如果有问题的话可以把 V2Ray 的 loglevel 设为 info 或 debug,通过日志排查问题。还有一些小问题和想法,也一块儿放在这里讨论。

延迟问题

在套完盾以后,梯子打开网页的速度明显变慢了,这是因为 HTTPS 握手导致的延迟增加,没有办法避免,也是这个方案的代价,但是看视频或者下载的速度是不受影响的。

CDN

在 Google 的过程当中发现不少人的方案是 WebSocket+TLS+Web+CDN,我唯一能想到的情形就是使用了美国普通线路的 VPS,否则感觉并没有必要这样做,而且很多人用 CloudFlare,感觉这和摸奖也没有什么区别。美国线路本来就至少有 150+ 左右的延迟,这还是 CN2 GIA 线路,所以如果你使用的强度不低的话,建议考虑考虑香港的几家服务商,因为延迟实在是太香了……

既然使用了 SS 协议为什么不干脆使用 SS 服务端?

当然可以,而且就有现成的大神的博客手把手教你:Deploy Shadowsocks with v2ray-plugin Enabled,那么为什么不用呢?

首先你要看到,就算使用 SS 服务端,你也需要在服务端配置 v2ray-plugin,这样需要额外的步骤。而作为一个懒狗,只改动配置文件就能完成的事儿为什么要那么麻烦~以及看看这个插件的名字,v2ray-plugin,本来就是基于 v2ray 的东西,为什么不直接使用 V2Ray 服务端呢?