Debian & Ubuntu 搭建部署 V2Ray + WebSocket + TLS + Nginx 并使用 CloudFlare

Contents
  1. 1. 准备工作
    1. 1.1. 服务器与域名
    2. 1.2. 内容准备
    3. 1.3. CloudFlare 设置
    4. 1.4. 安装所需基本工具
    5. 1.5. 服务器防火墙配置
    6. 1.6. 查看 nobody 的用户组
    7. 1.7. 添加用户脚本
  2. 2. 安装并配置 V2Ray
    1. 2.1. 安装
    2. 2.2. 配置
  3. 3. 安装并配置 Nginx
    1. 3.1. 安装 nginx
    2. 3.2. 配置
    3. 3.3. 启用配置
  4. 4. 利用 acme.sh 生成证书
    1. 4.1. 准备工作
    2. 4.2. 生成证书
    3. 4.3. 安装证书
  5. 5. 设置定时任务
  6. 6. 启动服务
  7. 7. 客户端配置文件
  8. 8. References

本文最后更新于 2024 年 2 月 25 日


本文以 Debian 12 为例,介绍如何搭建 VLESS + WebSocket + TLS 服务端,使用 Nginx 建立 Web,并利用 CloudFlare 隐藏源站 IP,最后说明对应的客户端配置文件的格式。本文同样完全适用于 Ubuntu 22.04 系统。

注:请先参照 Debian & Ubuntu 服务器的初始化配置 一文对服务器进行各种必要的配置。本文以 sammy 用户为例,进行 V2Ray 的部署,并默认已按初始化配置文章对服务器进行了配置。


准备工作

服务器与域名

  1. 可用的公网 IP 服务器(例如在 BandwagonHostVultr 等处购买的 VPS)
  2. 注册一个域名,本文以 example.com 为例

内容准备

  1. 起一个随机的路径名,本文使用 /random
  2. Online UUID Generator Tool 上生成一个 UUID,并记录之
  3. 准备自定义的 Web 页面,用于浏览器正常访问 example.com 显示的网页
  4. 准备自定义的 404 页面(可选)
  5. 起一个文件夹路径(可选),本文使用 /files,用于存放一些客户端软件包以供有限的授权人员下载

CloudFlare 设置

  1. 将域名的 Namesever 指向 CloudFlare 所提供的地址,等待生效
  2. NS 记录更新后,将 CloudFlare 中域名的 A 记录指向服务器 IP,确保云朵为橙色(Proxied)
  3. SSL/TLS 版块中的 Overview 里,将加密模式调整为 Full (strict)
  4. SSL/TLS 版块中的 Edge Certificates 里,将 Minimum TLS Version 调整为 TLS 1.3,并在下方确保开启对 TLS 1.3 的支持
  5. Firewall 版块中的 Firewall Rules 里,添加一个规则,允许 /random 路径的访问(Allow URI path)
  6. 在 CloudFlare 上获取域名的 Zone ID,记录之
  7. 在 CloudFlare 的 My Profile 中生成一个 API Token,权限为 Zone DNS EditZone Resources 特指区域为 example.com,完成后记下 Token
  8. 如果像本文一样准备了一个文件夹路径 /files,则需在 Access 版块中添加 Access Policy,只允许授权的用户访问该路径的资源
  9. 根据自己的需要在 CloudFlare 上进行其他设置(可选),例如配置 Always Use HTTPSHSTSAutomatic HTTPS RewritesAuto Minify 等等,主要影响浏览器访问网站的效果

安装所需基本工具

1
sudo apt update && sudo apt install curl unzip vim wget -y

服务器防火墙配置

此处限制服务器只允许 CloudFlare 的 IP 访问 443 端口。

1
2
sudo mkdir -p /root/scripts/ufw
sudo vim /root/scripts/ufw/add.sh
/root/scripts/ufw/add.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

cd /root/scripts/ufw

for ipv4 in `curl -sL https://www.cloudflare.com/ips-v4/ | tee ips-v4`
do
ufw allow from $ipv4 to any port 443
done

for ipv6 in `curl -sL https://www.cloudflare.com/ips-v4/ | tee ips-v6`
do
ufw allow from $ipv6 to any port 443
done
1
sudo vim /root/scripts/ufw/remove.sh
/root/scripts/ufw/remove.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

cd /root/scripts/ufw

for ipv4 in `cat ips-v4`
do
ufw delete allow from $ipv4 to any port 443
done

for ipv6 in `cat ips-v6`
do
ufw delete allow from $ipv6 to any port 443
done
1
sudo vim /root/scripts/ufw/update.sh
/root/scripts/ufw/update.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

cd /root/scripts/ufw

curl -sL https://www.cloudflare.com/ips-v4/ -o ips-v4.new
curl -sL https://www.cloudflare.com/ips-v6/ -o ips-v6.new

if ! cmp -s ips-v4 ips-v4.new || ! cmp -s ips-v6 ips-v6.new || [ ! -f ips-v4 ] || [ ! -f ips-v6 ]; then
bash remove.sh
bash add.sh
fi

rm -f ips-v4.new ips-v6.new

添加规则:

1
sudo bash /root/scripts/ufw/add.sh

设置 cron 定时任务:

1
sudo crontab -e

首行开始,添加如下内容,代表每日服务器时间 03:05 自动更新 ip 列表:

1
2
MAILTO=""
5 3 * * * /bin/bash /root/scripts/ufw/update.sh

查看 nobody 的用户组

执行命令:

1
id nobody

输出的结果可能是:

1
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)

输出的结果也可能是:

1
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)

请记住输出的用户组是 nobody 还是 nogroup,接下来会用到这个信息,这决定了我们是使用 nobody:nogroup 还是 nobody:nobody

添加用户脚本

执行以下命令:

1
2
mkdir -p ~/scripts
vim ~/scripts/acme.sh

添加如下内容,注意替换其中的 sammynobody:nogroup(如有必要):

~/scripts/acme.sh
1
2
3
4
5
6
#!/bin/bash

sudo chown -R sammy:sammy ~/certs
"/home/sammy/.acme.sh"/acme.sh --cron --home "/home/sammy/.acme.sh" > /dev/null
sudo chown -R nobody:nogroup ~/certs
sudo systemctl restart v2ray

执行以下命令:

1
vim ~/scripts/cert.sh

添加如下内容:

~/scripts/cert.sh
1
2
3
#!/bin/bash

/usr/bin/openssl req -newkey rsa:4096 -nodes -keyout ~/certs/default.key -x509 -days 365 -out ~/certs/default.crt -subj "/C=US"

至此,准备工作已完成。


安装并配置 V2Ray

安装

设置一个本地的安装脚本:

1
2
mkdir -p ~/scripts
vim ~/scripts/v2ray.sh

添加如下内容:

~/scripts/v2ray.sh
1
2
3
4
5
6
#!/bin/bash

cd ~/scripts
wget https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh
sudo bash install-release.sh
rm -f install-release.sh*
1
2
sudo chmod -R 400 ~/scripts && chmod 700 ~/scripts
bash ~/scripts/v2ray.sh

配置

编辑配置文件,此处示例额外配置了禁止 BT 流量,注意替换其中的 自定义端口号生成的 UUID/random

1
2
sudo mkdir -p /usr/local/etc/v2ray
sudo vim /usr/local/etc/v2ray/config.json
/usr/local/etc/v2ray/config.json
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{
"log":{
"loglevel": "warning"
},
"inbounds": [
{
"port": 自定义端口号,
"listen":"127.0.0.1",
"protocol": "vless",
"settings": {
"clients": [
{
"id": "生成的 UUID"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"path": "/random"
}
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
},
{
"protocol": "blackhole",
"settings": {},
"tag": "blocked"
}
],
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "blocked"
},
{
"type": "field",
"protocol": ["bittorrent"],
"outboundTag": "blocked"
}
]
}
}

注:关于禁止 BT 或 P2P 流量的讨论,请详见 利用 iptables 或 UFW 禁止 BT 流量 一文。


安装并配置 Nginx

安装 nginx

1
sudo apt update && sudo apt install nginx -y

配置

生成一个默认自签证书:

1
2
3
sudo apt update && sudo apt install openssl -y
mkdir -p ~/certs
bash ~/scripts/cert.sh

配置 ngnix,设置默认的 catch-all:

1
2
3
cd /etc/nginx/sites-enabled
sudo rm -f default
sudo vim default

添加如下内容,注意替换其中的 sammy 为当前用户名:

/etc/nginx/sites-enabled/default
1
2
3
4
5
6
7
8
9
10
11
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;

server_name _;

ssl_certificate /home/sammy/certs/default.crt;
ssl_certificate_key /home/sammy/certs/default.key;

return 400;
}

创建文件夹,注意提供换其中的 example.com

1
2
3
sudo mkdir -p /var/www/example.com/html
sudo chown -R $USER:$USER /var/www/example.com/html
sudo chmod -R 755 /var/www/example.com/html

编辑 example.com 站点配置文件:

1
sudo vim /etc/nginx/sites-available/example.com

注意替换其中的 sammy 为当前用户名,替换其中的 example.com 为实际域名,替换其中的 /random自定义端口(V2Ray 中设置的)、/files

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

ssl_certificate /home/sammy/certs/example.com.crt;
ssl_certificate_key /home/sammy/certs/example.com.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;

ssl_protocols 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;

root /var/www/example.com/html;
index index.html index.htm index.nginx-debian.html;
error_page 404 /404/index.html;

server_name example.com;

location /random {
if ($http_upgrade != "websocket") {
return 404;
}
proxy_redirect off;
proxy_pass http://127.0.0.1:自定义端口;
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;
}

location /files {
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}

注意,如果不需要设置自定义 404 页面,则删去:

1
error_page 404 /404/index.html;

注意,如果不需要设置自定义的文件资源共享目录,则删去:

1
2
3
4
5
location /files {
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}

接着,通过 scprsync 上传之前在本地准备好的 Web 页面文件、资源文件夹到 /var/www/example.com/html 中去。例如:

1
rsync -ruhP --delete public/ sammy_host:/var/www/example.com/html/

其中,网页文件和 files 文件夹都存放在本地 public 文件夹中,服务器别名为 sammy_hostpublic 文件夹结构为:

  • public/
    • 404/
      • ...
      • index.html
    • files/
    • ...
    • index.html

启用配置

1
sudo ln -s /etc/nginx/sites-available/exmaple.com /etc/nginx/sites-enabled/

至此,Nginx 部分已经配置完毕。


利用 acme.sh 生成证书

准备工作

安装依赖:

1
sudo apt update && sudo apt install socat -y

安装 acme.sh

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

修改 sudoer 文件:

1
sudo visudo

最后一行新增,注意替换其中的 sammy 为当前的真实用户名:

1
2
3
4
sammy ALL=(ALL) NOPASSWD: /usr/sbin/service nginx force-reload
sammy ALL=(ALL) NOPASSWD: /bin/systemctl restart v2ray
sammy ALL=(ALL) NOPASSWD: /bin/chown -R sammy\:sammy /home/sammy/certs
sammy ALL=(ALL) NOPASSWD: /bin/chown -R nobody\:nogroup /home/sammy/certs

生成证书

设置环境变量:

1
2
export CF_Token="此处填写之前创建的 CloudFlare API Token"
export CF_Zone_ID="此处填写 CloudFlare 中域名的 Zone ID"

生成证书:

1
acme.sh --issue --dns dns_cf -d example.com

安装证书

注意替换其中的 example.com 为实际域名:

1
2
3
4
acme.sh --install-cert -d example.com \
--key-file ~/certs/example.com.key \
--fullchain-file ~/certs/example.com.crt \
--reloadcmd "sudo service nginx force-reload"

设置定时任务

编辑定时任务:

1
crontab -e

删除掉其中 acme.sh 自动生成的定时任务行。

添加如下内容,保存并退出:

1
9 0 * * * /bin/bash ~/scripts/acme.sh

启动服务

1
sudo systemctl restart v2ray

至此,服务端已部署完成。


客户端配置文件

注意替换下方的 example.com生成的 UUID/random

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "socks",
"port": 10808,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": {
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
},
{
"tag": "http",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "example.com",
"port": 443,
"users": [
{
"id": "生成的 UUID",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"wsSettings": {
"path": "/random"
}
}
},
{
"protocol": "freedom",
"tag": "direct",
"settings": {}
}
],
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
}
]
}
}

设置的代理监听在 socks5://127.0.0.1:10808 上,此设置同样可导入 v2rayNG 的 Android 客户端中。

而如果是利用了 socat 来中转流量,那么客户端的配置则可见下方,注意替换下方的 中转服务器 IP中转服务器端口example.com生成的 UUID/random

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "socks",
"port": 10808,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": {
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
},
{
"tag": "http",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "中转服务器 IP",
"port": 中转服务器端口,
"users": [
{
"id": "生成的 UUID",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "example.com"
},
"wsSettings": {
"path": "/random",
"headers": {
"Host": "example.com"
}
}
}
},
{
"protocol": "freedom",
"tag": "direct",
"settings": {}
}
],
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
}
]
}
}

设置的代理监听在 socks5://127.0.0.1:10808 上,此设置同样可导入 v2rayNG 的 Android 客户端中。


References

VLESS

v2fly / fhs-install-v2ray

Migrate from the old script to this

Access (Setup & Usage)

CDN | 新 V2Ray 白话文指南

websocket + tls + nginx + cdn 断流严重

“(CRON) info (No MTA installed, discarding output)” error in the syslog

How do I make cron create cron.log and monitor it in real time?

Project V · Project V 官方网站

搭建备用梯子:V2Ray + WebSocket + TLS + CloudFlare

V2ray + WebSocket + Nginx + TLS 纯手工配置极简教程

V2RAY + Nginx + Ws + Tls + Host + Path,HTTPS/HTTP2 协议伪装,可选开启 CDN

V2Ray + WebSocket + TLS + Nginx 配置与使用教程

v2ray +tls + websocket + nginx 配置与使用

shuanghua / ss v2ray-plugin.md

禁用 BT | 新 V2Ray 白话文指南

233boy-v2ray/config/server/ws.json

WebSocket + TLS + Web | 新 V2Ray 白话文指南

How to Install Nginx on Debian 10

Install Nginx with Server Blocks (Virtual Hosts) on Debian 10

Enabling the Nginx Directory Index Listing

Redirect HTTP to HTTPS in Nginx

Nginx block for multiple domains to redirect all traffic to https?

nginx: How to prevent an exactly named SSL server block from acting as the catchall for all SSL

Why is nginx responding to any domain name?

Setting Nginx to catch all unhandled vhosts

Generating a self-signed certificate using OpenSSL

How to create a self-signed certificate with OpenSSL

HowTo: Create CSR using OpenSSL Without Prompt (Non-Interactive)

OpenSSL Essentials: Working with SSL Certificates, Private Keys and CSRs

crontab guru

acmesh-official / acme.sh

DNS manual mode

How to use DNS API

[DNS mode] CloudFlare New API Tokens

TLS | 新 V2Ray 白话文指南

acme 工具的使用与 cloudflare

使用 acme.sh 给 Nginx 安装 Let’ s Encrypt 提供的免费 SSL 证书

Get started with V2Ray

Mastodon