用 Nginx 转发 V2Ray 的 WebSocket 连接
Preface
有些地方网络环境很糟糕,比如:
-
防火墙 Drop 掉所有 UDP 包但是 DNS 除外
其实刚才写完才想到,没准防火墙只允许 53 端口的 UDP 包通过,毕竟这样配置起来简单而且也不怎么影响速度。但后来我想了想 暂时还是算了,因为让 V2Ray 监听 1~1023 端口需要 root 权限。
-
除 80 和 443 之类的端口外的数据包传输速率都很低
既然这样,我就使用 80 或者 443 端口进行通信就是了。于是我看了下 v2ray 官方文档,提供了以下跟 HTTP/HTTPS 有关的选项:
- TCP 并开启 HTTP 伪装
- WebSocket
因为我已经用 Nginx 监听 80 口了,所以新的连接必须通过 Nginx 分流到主机内的其他端口。这个 Issue 内提到了 “http 伪装没有完全遵守 http 协议,无法被常见的 http server 分流。如果你需要分流,可以使用 websocket 传输方式。",那我就用 WebSocket 吧。
哦对还有,有的更奇葩网络环境连 WebSocket 都不支持,具体可以到 websocketstest.com 测试一下。
弄个子域名
Nginx 支持使用域名来分流,也可以使用 URL 路径来分流,本节将使用域名来进行分流。
方法很简单:直接在域名提供商提供的DNS管理页面里 添加一个 CNAME
到自己的域名即可,本文将以 ws.example.com
为例。
配置 v2ray
客户端配置文件示例:
{
"log": {
"loglevel": "info"
},
"inbound": {
// 在 0.0.0.0:1080 上监听 SOCKS5 的入站连接
"protocol": "socks",
"listen": "0.0.0.0",
"port": 1080,
"settings": {
"auth": "noauth",
"udp": true,
"timeout": 30
}
},
"inboundDetour": [],
"outbound": {
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "ws.example.com", // 服务器的域名
"port": 80, // 访问服务器上监听在 80 端口的 Nginx
"users": [
{
"id": "00000000-0000-0000-0000-000000000000",
"alterId": 64,
"security": "aes-128-cfb"
}
]
}
]
},
"streamSettings": {
"network": "ws", // 使用 WebSocket 通信协议
"wsSettings": {
"connectionReuse": false,
"path": "/" // 路径。如果想让 Nginx 使用 URL 来分流的话,就来配置这个
}
}
},
"outboundDetour": [
{
"protocol": "freedom",
"settings": {},
"tag": "direct"
}
],
"dns": {
"servers": [
"8.8.8.8",
"8.8.4.4"
]
},
"routing": {
"strategy": "rules",
"settings": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": [
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.0.2.0/24",
"192.168.0.0/16",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"::1/128",
"fc00::/7",
"fe80::/10"
],
"outboundTag": "direct"
}
]
}
}
}
服务器配置文件示例:
{
"log": {
"loglevel": "info"
},
"inbound": {
"protocol": "vmess",
"listen": "127.0.0.1", // 这里要改成 loopback 地址,因为下面将配置 Nginx 把连接转发到 loopback
"port": 12345, // 监听端口号
"allocate": {
"strategy": "always"
},
// 下面的 settings 和 streamSettings 都要和客户端配置相同
"settings": {
"clients": [
{
"id": "00000000-0000-0000-0000-000000000000",
"alterId": 64,
"security": "aes-128-cfb"
}
]
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"connectionReuse": false,
"path": "/"
}
}
},
"inboundDetour": [],
"outbound": {
"protocol": "freedom",
"settings": {
"timeout": 30
}
},
"outboundDetour": [
{
"protocol": "blackhole",
"settings": {},
"tag": "blocked"
}
],
"routing": {
"strategy": "rules",
"settings": {
"rules": [
{
"type": "field",
"ip": [
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.0.2.0/24",
"192.168.0.0/16",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"::1/128",
"fc00::/7",
"fe80::/10"
],
"outboundTag": "blocked"
}
]
}
}
}
配置 Nginx
-
在
/etc/nginx/sites-available/
下创建文件ws.example.com
,文件内容如下:# V2Ray as WebSocket on "ws.excamle.com" server { server_name ws.example.com; listen 80; location / { proxy_redirect off; # 下面的 <端口号> 要和 v2ray 服务器端配置的 入站监听端口号 一致。 proxy_pass http://127.0.0.1:<端口号>; proxy_http_version 1.1; # Upgrade 和 Connection 都是必须的,这将告诉 Nginx 以 WebSocket 的形式对待入站连接 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # 下面这行可有可无 proxy_set_header Host $http_host; } }
-
将文件
ws.example.com
软链接到/etc/nginx/sites-enabled/
下:ln -s /etc/nginx/sites-available/ws.example.com /etc/nginx/sites-enabled/ws.example.com
-
测试配置文件语法是否正确 并 重新载入配置文件
nginx -t systemctl reload nginx systemctl restart v2ray
启动客户端并测试
像下面这样执行命令即可:
./v2ray -config ./config.json
然后配置网页浏览器等软件使用 V2Ray 提供的 SOCKS5 代理即可。
References
- 问题:关于HTTP伪装(非issue) #386
- REAL-TIME WEB TEST – DOES HTML5 WEBSOCKETS WORK FOR YOU?
- 底层传输配置 - v2ray.com
更新历史
11 Mar 2017:首次发布 11 Apr 2017:
- 改用 VMess 协议,因为 VMess 连接成功率比 Shadowsocks 高很多(GitHub Issue #416)
- 小修小改