V2Ray 配置 WebSocket + TLS + Web 教程

暑假回中国了,虽然手机卡有漫游可以回去,但毕竟有流量限制,于是自然而然的需要一个梯子来方便回去。以前有在用 Trojan,不过好像最近 V2Ray 比较流行所以也来配一个玩玩好了。

在阅读本文之前,建议先阅读官方白话文指南以了解 V2Ray 的基本配置方法(或许阅读后你会发现没有必要看本文了(笑))。

那么我们就开始吧w

1. 事前准备

在配置 V2Ray 前,我想你需要先准备以下内容和知识

  • 一台境外的 Linux 服务器
  • 基本的服务器操作知识和相关软件
  • 一个域名(如果你需要使用 TLS 的话)

2. V2Ray 的基本配置

让我们先配置一个最基本的 VMess 来熟悉一下 V2Ray 的配置方法。
到这里可能会有小伙伴有疑问了:说好的 V2Ray 怎么又变成 VMess 了呢?

你看这就是不读官方文档的坏处(敲打),人家文档上都已经写了:“VMess 协议是由 V2Ray 原创并使用于 V2Ray 的加密传输协议,如同 Shadowsocks 一样为了对抗墙的深度包检测 (opens new window)而研发的。在 V2Ray 上客户端与服务器的通信主要是通过 VMess 协议通信。”

那么就让我们来安装 V2Ray 并配置一个基本的 VMess 吧w

注意:VMess 协议的认证基于时间,一定要保证服务器和客户端的系统时间相差要在 90 秒以内。

# 安装一些需要的包
sudo apt install curl unzip vim
# 先提权
sudo -i
# 我们使用 https://github.com/v2fly/fhs-install-v2ray 的脚本进行安装
# 运行安装脚本
bash <(curl -L https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh)
# 安装完成后会询问是否要 remove 掉 curl 和 unzip, 个人觉得没必要
logout
sudo systemctl enable v2ray

安装完成后我们对服务器端的配置文件进行编辑,命令是 vim /usr/local/etc/v2ray/config.json,修改完成后按 esc 并输入 :wq 保存并退出。如果你不习惯使用 vim 也可以本地编辑后再传上去。另外需注意使用双斜杠进行注释是 JSON5 的功能,别在其他不确定版本的 JSON 中尝试 = =。下面的配置中的 id 字段使用的是 Linux 的 UUID,建议使用此网站生成:Online UUID Generator Tool

{                             
  "inbounds": [
    {
      "port": 10081, // 服务器监听端口,防火墙记得打开
      "protocol": "vmess",    // 主传入协议
      "settings": {
        "clients": [
          {
            "id": "c36581ab-7992-48c3-b25c-be6f9eec5242",  // 用户 ID,客户端与服务器必须相同
            "alterId": 0
          }
        ]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",  // 主传出协议, 此处的 freedom 代表数据直接出去
      "settings": {}
    }
  ]
}
/usr/local/etc/v2ray/config.json

编辑完成后检查一下格式对不对 sudo v2ray test -config /usr/local/etc/v2ray/config.json ,提示 Configuration OK. 代表配置正确,然后重启 v2ray。

sudo systemctl restart v2ray

客户端个人建议使用 Clash,文档可参考 Clash 官方文档。当然 Clash 也有很多(GUI)实现,我自己在用的是 ClashX 的 Pro 版本,Windows 用户可以使用 Clash for Windows,有开源洁癖(开玩笑)的可以使用 Clash.Meta

Clash 的配置文件使用 YAML 格式,这是一种对缩进敏感的格式,并且字符串不需要包在引号之内,建议在本地编辑器(推荐使用 Visual Studio Code )进行编辑,否则大概率你的缩进会出问题导致无法使用

# 前面的内容省略, 会单独开一篇文章来讲 Clash 的配置
proxies:

# VMess
- name: "My v2ray"
  type: vmess
  server: your-server-domain/ip
  port: 10081
  uuid: c36581ab-7992-48c3-b25c-be6f9eec5242
  alterId: 0
  cipher: auto
  # udp: true
  
  # 后面的内容也省略
$HOME/.config/clash/myconfig.yaml

如果可以正常连接的话代表配置成功,如果你并不想折腾的话到这里就可以了。

3. 创建服务账户(可选)

由于我们需要用 acme.shNginx 生成证书,为了减小对系统的影响和风险,我们可以给 acme 创建一个没有 sudo 权限的账户

# 创建一个 certusers 用户组, 方便我们后面让 Nginx 使用证书文件
sudo groupadd certusers
# 创建 acme 用户并添加至 certusers 用户组
sudo useradd -r -m -G certusers acme

4. 安装依赖

# 安装 acme 所需要的依赖
sudo apt install socat cron openssl
# 启动crontab
sudo systemctl start cron
sudo systemctl enable cron
# 安装Nginx
sudo apt install nginx

5. 配置 Nginx

如果你在此前已经有配置 Nginx 虚拟主机,则需先将已有的虚拟主机文件备份并关闭已有的虚拟主机,后面的章节会讲如何与现有的虚拟主机集成

5.1. 关闭默认虚拟主机

Nginx 的虚拟主机配置文件在 /etc/nginx/sites-available/ 文件夹中,如果要开启某一个虚拟主机,则可使用 mv 命令将配置文件移动至 /etc/nginx/sites-enabled/ 文件夹并重启 Nginx 服务即可(当然你也可以使用软连接进行操作,但我觉得直接 mv 过去会更方便一些)。安装 Nginx 后会有一个默认的虚拟主机文件在 /etc/nginx/sites-enabled/ 中,直接 rm 掉即可。然后我们创建一个 acme 申请证书用的虚拟主机文件。

# 删除默认虚拟主机文件
sudo rm /etc/nginx/sites-enabled/default
# 创建 acme 申请证书用的虚拟主机文件
#自己的虚拟主机文件, 注意 uuz_moe 改成你自己的域名, 此处的文件名仅用作方便识别
sudo vim /etc/nginx/sites-enabled/acme.conf

5.2. 编写 acme 用的虚拟主机文件

server {
    listen 0.0.0.0:80;
    listen [::]:80;

    server_name _; 

    # 这个块接收所有 80 端口的流量并重定向到 443 端口
    # 这样便开启了全站 https, 可防止恶意探测, 如果你不需要可以删去
    location / {
        return 301 https://$host$request_uri;
    }

    # 这个块是为 acme 申请证书准备的
    location /.well-known/acme-challenge {
       root /var/www/acme-challenge;
    }
}
/etc/nginx/sites-enabled/acme.conf

5.3. 测试并重启 Nginx

“启动之前测试一下总是好事”

# 启动之前测试一下总是好事, 没有报错就行
sudo nginx -t
# 重启 Nginx
sudo systemctl restart nginx
# 查询 Nginx 服务的状态
sudo systemctl status nginx

6. 配置证书

使用 acme 从 Let’s Encrypt 获得证书时,Let’s Encrypt 会验证证书中域名的控制权。一般采用 HTTP-01 或 DNS-01 方式来验证,详情可参考官方文档的 challenge-types。本文使用 HTTP-01 方式验证,若需要使用 DNS-01 方式验证,请参考 acme.sh 官方文档的 How to use DNS API

6.1. 创建证书文件夹

我们前面创建了 acme 用户来方便使用证书,下面就来创建证书文件夹并修改所有者吧。

# 创建证书文件夹
sudo mkdir -p /etc/mycerts
# 修改所有者为 acme, 使 acme 有权限写入文件
# 如果你没有为 acme 创建账户则需将 acme:acme 替换成你自己的用户名和用户组, 例如 uuz:uuz
sudo chown -R acme:acme /etc/mycerts

6.2. 创建 webroot 并修改所有者

本文使用 acme.sh 的 HTTP 方式申请证书,HTTP 方式需要在网站根目录下放置一个文件来验证域名所有权,故需要 acme.sh 和 Nginx 均对 webroot 目录有权限,故将运行 Nginx 的 worker 进程加入 certusers 组,下文再将 webroot 目录附加给 certusers 组即可。

在不同的 Linux 发新版本中,Nginx 可能使用不同的用户运行 worker process,可能为 www-datanginxnobody 中的一个,可查看 /etc/nginx/nginx.conf 文件中 user 字段(千夏的是 user www-data; ,因此可知是用 www-data 运行的)或运行下述命令查找 nginx: worker process 所属用户。

# 查找 nginx worker process 的用户
ps -eo user,command|grep nginx

# 以下是千夏这边的返回结果, 在第二行可见 nginx: worker process 为 www-data
root     nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
www-data nginx: worker process

# 然后根据查找到的用户执行以下命令, 添加 nginx 用户至 certusers 用户组
# www-data 记得替换成你自己在上面查找到的
# 如果你没有在第三步中创建 certusers 用户组, 则将 certusers 替换成你所在的用户组
sudo usermod -aG certusers www-data

运行下面两条命令,新建文件夹用于存放 acme 的域名验证文件并更改所有者。

# 新建一个文件夹用于给 acme.sh 存放域名验证文件
sudo mkdir -p  /var/www/acme-challenge
# 将文件夹所有者改为 acme, 使得用户 acme 有权写入文件并当验证时 Nginx 可以读取该文件
# 如果你没有在第三步中创建用户和用户组, 则将 acme:certusers 改成你自己的用户名和组
sudo chown -R acme:certusers /var/www/acme-challenge

6.3. 安装 acme.sh 并申请证书

下面我们来安装 acme.sh 并申请证书。

# 切换至用户 acme
# 如果你没有在第三步中创建账户则直接从第 4 行开始看
sudo su -l -s /bin/bash acme
# 安装 acme.sh, acme.sh 会安装到 ~/.acme.sh 目录下
curl  https://get.acme.sh | sh
# 安装成功后执行以下命令以确保脚本所设置的命令别名生效
source ~/.bashrc

如果以上命令没有报错的话则恭喜你已经安装好 acme.sh 了,那么我们就来申请证书吧。
申请证书前需先确保有正确配置 Nginx 并配置好相关权限以使 acme 可以写入切 Nginx 可以读取相关文件。

# 先把默认 CA 修改为 letsencrypt
acme.sh --set-default-ca --server letsencrypt
# 使用以下命令申请证书, 注意替换 uuz.moe 为你自己要申请的域名
# --keylength 表示密钥长度, 带 ec 代表生成的是 ECC (ECDHE) 证书, 没有则是 RSA 证书
~/.acme.sh/acme.sh --issue -d uuz.moe -w /var/www/acme-challenge --keylength ec-256
# 以下输出则表示正确生成了证书
[Wed Aug 30 10:30:29 UTC 2023] Your cert is in  /home/acme/.acme.sh/uuz.moe_ecc/uuz.moe.cer
[Wed Aug 30 10:30:29 UTC 2023] Your cert key is in  /home/acme/.acme.sh/uuz.moe_ecc/uuz.moe.key
[Wed Aug 30 10:30:29 UTC 2023] The intermediate CA cert is in  /home/acme/.acme.sh/uuz.moe_ecc/ca.cer
[Wed Aug 30 10:30:29 UTC 2023] And the full chain certs is there:  /home/acme/.acme.sh/uuz.moe_ecc/fullchain.cer
# 如果申请失败则请检查 1.域名解析是否生效 2.文件夹权限是否有误 3.加上 --debug 和 --staging 再次执行看看问题在哪里

如果已经成功申请了证书,则可使用以下命令安装证书

# 将证书文件安装到 /etc/mycerts/ 文件夹, 注意文件名改成你自己的域名
~/.acme.sh/acme.sh --installcert -d uuz.moe --ecc \
                          --fullchain-file /etc/mycerts/uuz.moe.crt \
                          --key-file /etc/mycerts/uuz.moe.key
# 更新并设置自动更新
acme.sh  --upgrade  --auto-upgrade

# 由于 Let's Encrypt 的证书有效期只有 3 个月,因此需要 90 天至少要更新一次证书, acme.sh 脚本会每 60 天自动更新证书。也可以手动更新。
# 手动强制更新证书
~/.acme.sh/acme.sh --renew -d uuz.moe --force --ecc

安装完成后还需要修改一下权限

# 将证书文件夹所在用户组改为 certusers
chown -R acme:certusers /etc/mycerts
# 赋予证书文件夹组内用户读取权限, 这样 Nginx 就有权限读取证书了
chmod -R 750 /etc/mycerts
# 安装完成, 退出 acme 用户
exit

当 acme.sh 重新安装证书之后,Nginx 需要重新加载证书才行,执行 sudo crontab -u root -e 编辑 root 用户的 crontab 文件并加入以下内容保存

# 每月 1 日 4:00 重载 Nginx
00 04 01 * * systemctl reload nginx
crontab

至于为何选用 ECC 证书?因为在安全性上 256 位的 ECC 证书等同于 3072 位的 RSA 证书, 且 256 位的 ECC 证书比 2048 位的 RSA 证书至少快 20 倍以上, 具体可以参考以下两篇文章:

短い暗号鍵長「ECC」でパフォーマンスとセキュリティの両立図るベリサイン — ITmedia

A (Relatively Easy To Understand) Primer on Elliptic Curve Cryptography — The Cloudflare Blog

# 以下为 rsa 和 ecdsa 的对比, 供参考
# MacBook Pro 13-inch, 2020, 2 GHz クアッドコアIntel Core i5 with 16 GB 3733 MHz LPDDR4X
# openssl speed rsa
Doing 2048 bits private rsa's for 10s: 14311 2048 bits private RSA's in 9.72s
# openssl speed ecdsa
Doing 256 bits sign ecdsa's for 10s: 384133 256 bits ECDSA signs in 9.71s

7. 服务器 V2Ray 配置

编辑 V2Ray 配置文件

sudo vim /usr/local/etc/v2ray/config.json

"listen":"127.0.0.1" 只监听 127.0.0.1,避免除本机外的机器探测到开放了 10080 端口

"id": 记得换成你自己生成的,切勿使用示例中的 id

"network": "ws" 表示使用 WebSocket

"path": "/v2ray" 表示路径为 /v2ray,建议换成只有你知道的

{
  "inbounds": [
    {
      "port": 10080,
      "listen":"127.0.0.1",
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "faf171ee-d211-4e25-a645-6ac087e48bfb",
            "alterId": 0
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
        "path": "/v2ray"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}
/usr/local/etc/v2ray/config.json

8. 配置 Nginx 证书和反代

新建一个 Nginx 虚拟主机 sudo vim /etc/nginx/sites-enabled/uuz_moe.conf,然后按照注释修改

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  server_name uuz.moe; # 改成你自己的域名
  
  # ssl 设置
  ssl_certificate       /etc/mycerts/uuz.moe.crt; #你自己的证书路径
  ssl_certificate_key   /etc/mycerts/uuz.moe.key;
  ssl_session_timeout 1d;
  ssl_session_cache shared:MozSSL:10m;
  ssl_session_tickets off;
  
  ssl_protocols         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;
  
  location /v2ray { # 与 V2Ray 配置中的 path 保持一致
    if ($http_upgrade != "websocket") { # WebSocket协商失败时返回404
        return 404;
    }
    proxy_redirect off;
    proxy_pass http://127.0.0.1:10080; # 假设 V2Ray 的 WebSocket 监听在环回地址的10080端口上
    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 if log on
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}
/etc/nginx/sites-enabled/uuz_moe.conf

9. Clash 配置

- name: uuz-ws
    type: vmess
    server: uuz.moe
    port: 443
    uuid: faf171ee-d211-4e25-a645-6ac087e48bfb
    alterId: 0
    cipher: auto
    udp: true
    tls: true
    servername: uuz.moe
    network: ws
    ws-opts:
      headers:
        Host: uuz.moe
      path: /v2ray
$HOME/.config/clash/myconfig.yaml

10. 注意事项

以下内容摘自 新 V2Ray 白话文指南

  • V2Ray 自 4.18.1 后支持 TLS1.3,如果开启并强制 TLS1.3 请注意 v2ray 客户端版本.
  • 较低版本的 nginx 的 location 需要写为 /ray/ 才能正常工作
  • 请保持服务器和客户端的 wsSettings (path) 严格一致,对于 V2Ray,/ray 和 /ray/ 是不一样的
  • 较低版本的系统/浏览器可能无法完成握手. 如 Chrome 49/XP SP3, Safari 8/iOS 8.4, Safari 8/OS X 10.10 及更低的版本. 如果你的设备比较旧, 则可以通过在配置中添加较旧的 TLS 协议以完成握手
  • 如果在设置完成之后不能成功使用,可能是由于 SElinux 机制(如果你是 CentOS 7 的用户请特别留意 SElinux 这一机制)阻止了 Nginx 转发向内网的数据。如果是这样的话,在 V2Ray 的日志里不会有访问信息,在 Nginx 的日志里会出现大量的 “Permission Denied” 字段,要解决这一问题需要在终端下键入以下命令:
setsebool -P httpd_can_network_connect 1

11. 参考资料

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注