V2Ray进阶指南:WSS组合配置(WebSocket + TLS + Nginx + CDN)

余生、漫漫长路,有些人终其一生都在追寻虚无缥缈的迷途微光。余生很长,何必慌张。

——《往后余生》

GFW作为又一道万里长城,为了保护中国公民远离社交媒体荼毒立下了汗马功劳(迫真!)。大概计算机的某种鄙视链,用Google的看不上用Baidu的,用Tor的看不上用Google的,用V2Ray的鄙视用SSR的,用Trojan的斜眼看自己配V2Ray的,如此种种就当听个笑话~

回归正题,为了应对疫情过后注意力回归的GFW升级,想着提前升级到WSS组合拳上。之前用的基本的VMess + Google Cloud HK or Taiwan,也不知道是不是Google大环线堪比IPLC,简单实用一直稳定,VM价格$5/月 + 流量$20/月;最近精简开支,就Vultr SPG + V2Ray WSS (其实Vultr SPG也是日本中转线路,不过日本的IP基本都挂了,就……)

默认大家伙已经具备了搭建基础V2Ray功能的水平,这次线路就是Basic V2Ray ——> V2Ray WSS的升级路线。[V2Ray + WebSocket + TLS + Nginx + CloudFlare]

基本点

客户端 Ubuntu 19.10,服务器端 Ubuntu18.04。Shell默认使用ZSH。

默认大家伙已经具备了搭建基础V2Ray功能的水平,这次线路就是Basic V2Ray ——> V2Ray WSS的升级路线。

Linux包管理器是aptitude后端的多线程apt-fast,命令中的apt-fast都可以换成aptitude无伤大雅。

涉及到域名的地方就用your.domain代替了,不会有人真的照抄吧……(笑

几种V2Ray进阶传输协议的对比和选择

先说结论,看完可以直接跳过后面大段描述:

  • WSS:有能力搞定域名和解析,推荐使用WebSocket+TLS (WSS) ,可以的话再增加CDN支持。
  • WS:没有办法搞定域名,推荐直接使用WebSocket。
  • mKCP:对于处在拥堵网络,如4G的用户,推荐使用mKCP伪装,并打开Mux多路复用。

WebSocket (WS)

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

简单说,WebSocket解决了HTTP协议的部分问题,比如每次求都携带状态信息(如身份认证等)、请求每次都要携带完整的头部等等。再加上由于WebSocket全双工通信,因此能够很好的进行实时通信。用在V2Ray上时,请求与应答的效率要远高于HTTP,而V2Ray的请求要远高于普通的请求,所以WebSocket可以说是一种不错的选择。同时由于WebSocket本身已经成熟,因此有Cloudflare这类云服务商支持基于WebSocket流量的CDN服务,这进一步增进了原本的服务器的安全性。

TLS

传输层安全性协议(英语:Transport Layer Security,缩写作TLS),及其前身安全套接层(Secure Sockets Layer,缩写作SSL)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。

对于TLS,其实大家不应该觉得陌生,TLS作用于HTTP便诞生出了现今最为常用的协议HTTPS,那么作用于WebSocket,自然便成了WSS,也就是WebSocket+TLS。

TLS的作用就好比是一个保险箱,不管在传输HTTP还是WebSocket的时候,传输中的内容均是以明文的方式传输的。也就是说,只要有人对传输数据拦截,是完全可以知道你在传输什么东西。而TLS就是用来交换最开始的“锁”的方式,保证传输的保密。

对于V2Ray来说,虽然经过了协议的加密,但是这类流量本身就并不“正常”。试想,你手机连接了V2ray,大量数据通过协议向一个固定IP请求,而数据内容与普通的数据格格不入。这自然会引起部分人的警觉。如果嵌套一个保险箱(TLS),让数据显得和其他请求的数据没什么两样,这样一方面减少了审查者的怀疑,另一方面又加强了数据的安全性。当然由于TLS本身存在一个加密解密的过程,因此势必会对传输的效率带来影响。不过影响是微乎其微的,想要追求安全,自然也需要一定的牺牲。

h2 (HTTP/2)

h2便是HTTP/2 (原名HTTP/2.0)即超文本传输协议 2.0,是下一代HTTP协议。对于V2Ray,使用h2必须同时使用TLS。h2本质是HTTP协议,对于传统http1.1协议传输速度快了不少,无主界便是基于h2的网站。h2的下层协议是TCP,因此大家应该知道这类协议的共同缺点了。

较之类似的WebSocket协议,传输效率略逊于WebSocket。但是它却有比WebSocket更加好的伪装,由于大部分网站使用HTTP协议,因此h2+TLS这种协议能使V2Ray流量伪装在正常流量中,并且难以察觉。

QUIC

2018年11月,国际互联网工程任务组(The Internet Engineering Task Force,简称 IETF )将HTTP-over-QUIC 实验性协议将被重命名为 HTTP/3,并有望成为 HTTP 协议的第三个正式版本,代表了互联网的未来。不久之后,无主界也许也会支持这个协议。

QUIC(Quick UDP Internet Connection)是谷歌制定的一种基于UDP的低时延的互联网传输层协议。此协议基于UDP,但较之KCP更为可靠。由于现今V2Ray的QUIC并不是真正意义上的HTTP/3,因此存在一定的兼容问题。

WSS (WebSocket + TLS + Web Server) 的完整安装与配置

使用 Nginx/Caddy/Apache 是因为 VPS 已经有 Nginx/Caddy/Apache 可以将 V2Ray 稍作隐藏,使用 WebSocket 是因为搭配 Nginx/Caddy/Apache 只能用 WebSocket,使用 TLS 是因为可以流量加密,看起来更像 HTTPS。 也许 WebSocket + TLS + Web 的配置组合相对较好,但不意味着这样的配置适合任何人。

注意: V2Ray 的 Websocket + TLS 配置组合并不依赖 Nginx/Caddy/Apache,只是能与其搭配使用而已,没有它们也可以正常使用。

域名注册

就像论文写了一页半的Introduction,我们终于进入正题!TLS 需要一个域名,域名有免费的和有付费的,如果你不舍得为一个域名每年花点钱,用个免费域名也可以,但总体来说付费的会优于免费的。

  • 免费的域名申请可以移步Freenom,不过谨记免费的就是最贵的

  • 付费域名租用,国内使用阿里云域名(曾经的万网),国外多币信用卡使用GoDaddy可以帮助降低审计风险(既然如此在GoDaddy就不要选择AliPay)。

注册好域名之后务必记得添加一个 A 记录指向你的 VPS!再次说明,以下涉及到域名的地方就用your.domain代替,域名的地方就用your.domain代替,域名的地方就用your.domain代替。

TLS证书生成和更新

TLS 是证书认证机制,所以使用 TLS 需要证书,证书也有免费付费的,同样的这里使用免费证书,证书认证机构为 Let’s Encrypt。 证书的生成有许多方法,这里使用的是比较简单的方法:使用 acme.sh 脚本生成,本部分说明部分内容参考于acme.sh README

证书有两种,一种是 ECC 证书(内置公钥是 ECDSA 公钥),一种是 RSA 证书(内置 RSA 公钥),建议使用 ECC 证书。简单来说,同等长度 ECC 比 RSA 更安全,也就是说在具有同样安全性的情况下,ECC 的密钥长度比 RSA 短得多(加密解密会更快)。ECC 的兼容性会差一些,Android 4.x 以下和 Windows XP 不支持。

证书生成只需在服务器上操作。以下将给出这两类证书的生成方法,请大家根据自身的情况自行选择其中一种证书类型。

acme.sh 的依赖项主要是 socat,我们通过以下命令来安装这些依赖项,然后安装一遍 acme.sh。执行以下命令,acme.sh 会安装到 ~/.acme.sh 目录下。

1
2
sudo apt-fast install -y openssl cron socat curl
curl https://get.acme.sh | sh

安装成功后执行 source ~/.bashrc (对应zsh source ~/.zshrc)以确保脚本所设置的命令别名生效,看见zshrc最后添加了. "/home/v2/.acme.sh/acme.sh.env"的别名解析。

使用 acme.sh 生成证书:执行以下命令生成证书,命令会临时监听 80 端口,请确保执行该命令前 80 端口没有使用。

1
sudo ~/.acme.sh/acme.sh --issue -d your.domain --standalone -k ec-256 --force

其中 -k 表示密钥长度,后面的值可以是 ec-256 、ec-384、2048、3072、4096、8192,带有 ec 表示生成的是 ECC 证书,没有则是 RSA 证书。在安全性上 256 位的 ECC 证书等同于 3072 位的 RSA 证书。这里使用sudo不加--force会触发一个警告,打开 https://github.com/acmesh-official/acme.sh/wiki/sudo 可以看到使用sudo可能导致续订失败,暂时还不知道效果,先酱紫。

手动更新 ECC 证书

由于 Let’s Encrypt 的证书有效期只有 3 个月,因此需要 90 天至少要更新一次证书,acme.sh 脚本会每 60 天自动更新证书。也可以手动更新。

1
2
3
4
5
# 如果是 RSA 证书
sudo ~/.acme.sh/acme.sh --renew -d your.domain --force --ecc

# 如果是 ECC 证书
sudo ~/.acme.sh/acme.sh --renew -d your.domain --force

安装证书和密钥

由于本例中将证书生成到 /etc/v2ray/ 文件夹,更新证书之后还得将新证书和密钥安装到 /etc/v2ray 中:

1
2
3
4
5
# ECC 证书
sudo ~/.acme.sh/acme.sh --installcert -d your.domain --fullchainpath /etc/v2ray/v2ray.crt --keypath /etc/v2ray/v2ray.key --ecc --force

# RSA 证书
sudo ~/.acme.sh/acme.sh --installcert -d your.domain --fullchainpath /etc/v2ray/v2ray.crt --keypath /etc/v2ray/v2ray.key --force

注意:无论什么情况,密钥(即上面的v2ray.key)都不能泄漏,如果你不幸泄漏了密钥,可以使用 acme.sh 将原证书吊销,再生成新的证书,吊销方法请自行参考 acme.sh手册

TLS配置验证

一般来说,按照以上步骤操作完成,V2Ray 客户端能够正常联网说明 TLS 已经成功启用。但要是有个可靠的方法来验证是否正常开启 TLS 无疑更令人放心。

打开 Qualys SSL Labs’s SSL Server Test,在 Hostname 中输入你的域名,点提交,过一会结果就出来了。

从图中可以看出关于证书的信息,我的这个证书有效期是从 21 Mar 2020 到 19 Jun 2020 ,密钥是 256 位的 ECC,证书签发机构是 Let’s Encrypt,重要的是最后一行,Trusted 为 Yes,表明我这个证书可信。

Web服务器的选择/安装/配置

Apache和Nginx的对比和选择

首先给出结论:一般来说,需要性能的Web服务,用Nginx 。如果不需要性能只求稳定,那就Apache吧。

  • Nginx相对于Apache的优点:轻量级,同样起Web服务,比Apache占用更少的内存及资源抗并发,Nginx处理请求是异步非阻塞的,而Apache则是阻塞型的,在高并发下Nginx能保持低资源低消耗高性能高度模块化的设计,编写模块相对简单社区活跃,各种高性能模块出品迅速。

  • Apache 相对于Nginx 的优点:rewrite,比Nginx的rewrite强大模块超多,基本想到的都可以找到;Bug少,超稳定,存在就是理由。

Nginx安装

1
2
sudo apt-fast update
sudo apt-fast install -y nginx

Nginx测试

安装成功后打开浏览器,打开你的域名(your.domain)或者输入服务器IP(x.x.x.x)地址,如果看到默认的Nginx登录页面,则安装成功。

Nginx Cheat Sheet

需求 命令
启动Nginx服务 sudo systemctl start nginx
停止Nginx服务 sudo systemctl stop nginx
重新启动Nginx服务 sudo systemctl restart nginx
重新加载Nginx服务 sudo systemctl reload nginx
禁用Nginx服务以在启动时启动 sudo systemctl disable nginx
重新启用Nginx服务以在启动时启动 sudo systemctl enable nginx

Nginx配置文件的结构和最佳做法

  • 所有Nginx配置文件都位于/etc/nginx目录中。
  • 主要的Nginx配置文件为/etc/nginx/nginx.conf。
  • 为使Nginx配置更易于维护,建议为每个域创建一个单独的配置文件。您可以根据需要拥有任意数量的服务器块文件。
  • Nginx服务器块文件存储在/etc/nginx/sites-available目录中。除非它们链接到/etc/nginx/sites-enabled目录,否则Nginx不会使用此目录中找到的配置文件。
  • 要激活服务器块,您需要从以下目录中的配置文件站点创建符号链接(指针)将sites-available目录移到sites-enabled目录。
  • 建议遵循标准命名约定,例如,如果您的域名是your.domain,则您的配置文件应命名为/etc/nginx/sites-available/your.domain.conf ]
  • /etc/nginx/snippets目录包含可包含在服务器块文件中的配置片段。如果使用可重复的配置段,则可以将这些段重构为片段,并将片段文件包括到服务器块中。
  • Nginx日志文件(access.log和error.log)位于/var/log/nginx目录中。建议每个服务器块使用不同的access和error日志文件。
  • 您可以将域文档根目录设置为所需的任何位置。 Webroot的最常见位置包括:
    /home//
    /var/www/
    /var/www/html/
    /opt/

Nginx服务器配置

这次 TLS 的配置将写入 Nginx 配置中,由ta来监听 443 端口(443 比较常用,并非 443 不可),然后将流量转发到 V2Ray 的 WebSocket 所监听的内网端口(本例是14400),V2Ray 服务器端则不需要配置 TLS。

服务器V2Ray和Nginx配置

服务器 V2Ray 配置

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
{
"inbounds": [
{
"port": 14400,
"listen":"127.0.0.1",//只监听 127.0.0.1,避免除本机外的机器探测到开放了 14400 端口
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
"alterId": 64
}
]
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"path": "/ray"
}
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
}
]
}

Nginx 配置

注意替换配置中的是域名和证书。将以下代码以v2ray_ws_tls.conf 保存在 /etc/nginx/conf.d路径下作为Nginx的一个单独模块加载。如果出现任何Nginx启动失败的提示,请在/var/log/nginx 自行查看错误日志 error.log

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
server {
listen 443 ssl;
listen [::]:443 ssl;

ssl_certificate /etc/v2ray/v2ray.crt;
ssl_certificate_key /etc/v2ray/v2ray.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;

ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

server_name your.domain;
location /ray { # 与 V2Ray 配置中的 path 保持一致
if ($http_upgrade != "websocket") { # WebSocket协商失败时返回404
return 404;
}
proxy_redirect off;
proxy_pass http://127.0.0.1:14400; # 假设WebSocket监听在环回地址的14400端口上
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# Show real IP in v2ray access.log
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

注意事项

  1. V2Ray 自4.18.1后支持TLS1.3,如果开启并强制 TLS1.3 请注意v2ray客户端版本.

  2. 较低版本的nginx的location需要写为 /ray/ 才能正常工作

  3. 如果在设置完成之后不能成功使用,可能是由于 SElinux 机制(如果你是 CentOS 7 的用户请特别留意 SElinux 这一机制)阻止了 Nginx 转发向内网的数据。如果是这样的话,在 V2Ray 的日志里不会有访问信息,在 Nginx 的日志里会出现大量的 “Permission Denied” 字段,要解决这一问题需要在终端下键入以下命令:setsebool -P httpd_can_network_connect 1

  4. 请保持服务器和客户端的 wsSettings 严格一致,对于 V2Ray,/ray 和 /ray/ 是不一样的

  5. 较低版本的系统/浏览器可能无法完成握手. 如 Chrome 49/XP SP3, Safari 8/iOS 8.4, Safari 8/OS X 10.10 及更低的版本. 如果你的设备比较旧, 则可以通过在配置中添加较旧的 TLS 协议以完成握手.

CDN的配置和使用

目前和 V2Ray 兼容的 CDN 国外有 Cloudflare,国内阿里云,这两家的 CDN 是支持 WebSocket 的。剩下的几家不支持 WebSocket,也不会 keep TCP connection。因此 HTTP/2 回源也不支持(访问支持 HTTP/2 和回源支持 HTTP/2 是两回事)。另外,使用国内 CDN 需要域名备案并服务商实名认证。使用有风险,入坑需谨慎。

确保 Cloudflare 的 Crypto 选项卡的 SSL 为 Full,并且请确保 SSL 选项卡有显示 Universal SSL Status Active Certificate 这样的字眼,如果你的 SSL 选项卡没有显示这个,不要急,只是在申请证书,24 小时内可以搞定。

有关 Cloudflare 的 Firewall(防火墙)问题
最近突然有遇见经过 CDN 的代理完全无法通讯的情况哦。 有趣的是,在 VPS 里没有任何相关日志,并且不走 CDN 的话就可以正常通讯并出现相关日志了呢;而走 CDN 的 Web 可以正常访问,但 Path 界面却出现了 Challenge(Captcha)。也就是说,问题就在于 Cloudflare 啦。在 Firewall 界面有看见大量 IP 被拦截的记录,问题 GET👍。

稍稍总结一下不同的解决方案:

  1. Firewall => Settings => Security Level,设置为 Essentially Off (默认是 Medium,个人实测改为 Low 仍被拦截)。
  2. 无需修改 Security Level,而是 Firewall => Tools,将 China(或被 Block 的 IP/ASN)作为 Whitelist。
  3. Issues 里有提到,可以 Firewall => Firewall Rules,添加 Country => China 或 IP Address =>(被 Block 的 IP)或 URL Path => (wsSettings 里 的那个 path),为 Allow 即可,但个人实测,Activity log 里会显示,先是被 Firewall Rules 所 Allow,随后又被 Security Level 所 Block。
  4. 针对解决方案 3,建议各位在出现被拦截的问题之前就进行此操作,本文所提的个人实测是在已经出现被拦截的情况下所进行的哦。

总结和回顾

整体方法就是:客户端V2Ray使用TLS协议访问domain443端口,服务器端Nginx监听443端口来信并转发至14400端口,服务器端V2Ray监听14400的Nginx转发信息并进一步向外传递,形成完整信息交互链。因为使用域名访问,自然而然可以附加CDN隐藏服务器IP。

祝诸位事毕功成!!!

注意以下几点:

  1. 开启了 TLS 之后 path 参数是被加密的,GFW 看不到;
  2. 主动探测一个 path 产生 Bad request 不能证明是 V2Ray;
  3. 使用 Header 分流并不比 path 安全, 不要迷信。

TODO

  • 我的第一次……长篇,后续慢慢改进。当前主题似乎是不适合图文太多,而且缺少目录 (懒得用hexo-toc,会全部生成),更换主题提上calendar。[Done]

Reference

  1. https://baike.baidu.com/item/IPLC
  2. https://cloud.google.com/
  3. https://www.idleleo.com/05/2071.html
  4. http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
  5. https://zh.wikipedia.org/wiki/%E5%82%B3%E8%BC%B8%E5%B1%A4%E5%AE%89%E5%85%A8%E5%8D%94%E8%AD%B0
  6. https://github.com/acmesh-official/acme.sh
  7. https://github.com/Neilpang/acme.sh/wiki/Options-and-Params
  8. https://juejin.im/post/5dd5fac6f265da47dd1af995
  9. http://www.zhangjiee.com/blog/2019/apache-vs-nginx.html
  10. https://www.v2ray.com/
  11. https://nginx.org/cn/