Critical-1 timer 时区:[Timer] 段没有 TimeZone= 这个 key,systemd 只会记一条 "Unknown key name" 然后按系统本地时区解析 OnCalendar。VPS 出厂多为 UTC, 用户设「04:00 Asia/Shanghai」会在北京时间 12:00 触发且毫无提示。 改为把时区写进日历表达式本身:OnCalendar=*-*-* HH:MM:00 <TZ>(systemd >= 242)。 Critical-2 前导零:^[0-9]+$ 放行 08/09/010,该值原样写进 guard 的 DAYS=, 而 guard 里是算术展开——010 被当八进制解析成 8 天,08/09 直接报 "value too great for base",使间隔判断求值为假,落进重启分支变成每天重启。 校验通过后统一 $((10#$days)) 归一化。 Important-3 状态文件写入失败:mkdir 与写时间戳都不检查返回值,/var 写满或 只读时写入失败却照样重启,「先写时间戳」的保护完全失效,退化成每天硬重启且 无限持续。改为 fail-safe:写不进去就报错并跳过本轮。 update_env_field 的三处写入同样补上返回值检查,调用方改在条件位置。 Important-4 未来时间戳:RTC 错乱或 NTP 大幅向后校正时会写下未来的时间戳, 时钟校回后 now-last 恒为负,此后永远跳过且无自愈路径。与「内容非法按 0 处理」 同策略,读到 last > now 时按 0 处理。 Important-5 静默中止:configure_restart_timer 被 harden_system、main 一路裸调用, set -e 下 systemctl enable 失败会让部署在「8/8 加固」之后无声退出—— start_service / save_env / print_result 全不执行,config.json 已换、防火墙已改, 但 xray 没重启、.env 没更新、链接没打印。三处 systemctl 全部改到条件位置, enable 不再吞 stderr(规格本就要求靠 systemctl 报错暴露非法时区)。 Important-6 NEXT 语义:list-timers 的 NEXT 是 timer 的「下次唤醒」,每天一次, 不是下次重启时间。新增 print_next_restart,--restart 输出里直接算出并显示 「下次实际重启时间」,README 同步说明差异。 Important-7 安全不变式测试:configure_restart_timer 与 apply_restart_only 原本 零覆盖,「不重启 xray、不碰 vless 链接」这个最重要的承诺恰恰没有回归保护。 补上启用/关闭两个方向的断言,以及 .env 逐字段 diff 与 systemctl 调用日志断言。 Minor-9 README 补充取舍:完整部署的 systemctl restart xray 不更新状态文件。 Minor-11 save_env 注释「这两项」→「这几项」。 测试 61 → 101 项,全部通过。
398 lines
13 KiB
Markdown
398 lines
13 KiB
Markdown
# Xray VPS 部署指南
|
||
|
||
> 一键部署 Xray 代理,支持 VLESS-Reality(抗封锁)和 VLESS TCP(极速)两种模式。无需域名和证书。
|
||
|
||
## 功能特性
|
||
|
||
- 两种部署模式可选:Reality(推荐)/ Fast TCP
|
||
- 自动安装 Xray + 生成密钥 + 配置防火墙
|
||
- BBR 拥塞控制 + TCP 深度调优
|
||
- 自动选择延迟最低的 Reality 伪装目标
|
||
- 部署完成自动输出 VLESS 分享链接
|
||
- 一键备份 / 卸载脚本
|
||
|
||
## 模式对比
|
||
|
||
| 特性 | Reality(推荐) | Fast TCP |
|
||
|------|-----------------|----------|
|
||
| **加密** | Reality TLS 1.3 | 无,明文传输 |
|
||
| **抗检测** | 伪装为真实 HTTPS 流量 | 极易被 DPI 识别 |
|
||
| **需要域名** | 否 | 否 |
|
||
| **需要证书** | 否 | 否 |
|
||
| **速度** | Vision 流控避免双重加密,差距 < 5% | 裸 TCP 无开销,理论最快 |
|
||
| **被封风险** | 极低 | 极高 |
|
||
|
||
## 技术栈
|
||
|
||
| 组件 | 说明 |
|
||
|------|------|
|
||
| Xray | VLESS 代理核心 |
|
||
| BBR | TCP 拥塞控制算法 |
|
||
| ufw / firewalld | 防火墙 |
|
||
|
||
## 前置条件
|
||
|
||
1. 一台境外 Linux VPS(Debian 12 推荐)
|
||
2. root 权限
|
||
3. 服务器 443 端口可从外网访问
|
||
|
||
### 推荐配置
|
||
|
||
| 项目 | 最低 | 推荐 |
|
||
|------|------|------|
|
||
| CPU | 1 核 | 1 核 |
|
||
| 内存 | 256 MB | 512 MB |
|
||
| 硬盘 | 5 GB | 10 GB |
|
||
| 带宽 | 200 Mbps | 1 Gbps |
|
||
| 流量 | 500 GB/月 | 1 TB+/月 |
|
||
| 位置 | — | 日本 / 新加坡 / 美西 |
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
vps-xray/
|
||
├── deploy.sh # 一键部署脚本
|
||
├── uninstall.sh # 完全卸载脚本
|
||
├── backup.sh # 备份脚本
|
||
├── upload.sh # 上传脚本(仅上传服务端必要文件)
|
||
├── .env.example # 配置模板
|
||
├── README.md # 本文件
|
||
├── vps-xray-optimized.md # Reality 方案详细文档
|
||
├── vps-xray-fast.md # Fast TCP 方案详细文档
|
||
└── client-config/ # 客户端配置(本地使用,不上传服务器)
|
||
├── gen-clash.sh # 由节点信息生成 Clash.Meta 配置
|
||
└── client-macos.md # macOS 客户端指南(模板,可提交)
|
||
```
|
||
|
||
> `client-config/` 下含真实 UUID / 服务器 IP 的节点配置(`*-info*.md`、`*.yaml`)已在根 `.gitignore` 中排除,不要提交到版本库。
|
||
|
||
服务器上的文件位置:
|
||
|
||
```
|
||
/usr/local/bin/xray # Xray 可执行文件
|
||
/usr/local/etc/xray/config.json # Xray 配置文件
|
||
/etc/sysctl.d/99-xray-turbo.conf # 网络调优参数
|
||
/var/backups/xray/ # 备份文件
|
||
```
|
||
|
||
## 快速部署
|
||
|
||
### 第一步:上传文件到 VPS
|
||
|
||
使用 `upload.sh` 只上传服务端必要文件(`deploy.sh`、`uninstall.sh`、`.env.example`),跳过文档和客户端配置:
|
||
|
||
```bash
|
||
# 在本地 vps-xray/ 目录下执行
|
||
bash upload.sh <VPS_IP>
|
||
```
|
||
|
||
### 第二步:登录 VPS 执行部署
|
||
|
||
```bash
|
||
ssh root@<VPS_IP>
|
||
cd /opt/vps-xray
|
||
|
||
# Reality 模式部署(推荐)
|
||
bash deploy.sh
|
||
|
||
# 或者 Fast TCP 模式
|
||
bash deploy.sh --mode fast
|
||
```
|
||
|
||
部署完成后会输出:
|
||
- 连接参数(IP、端口、UUID、密钥等)
|
||
- VLESS 分享链接(可直接导入客户端)
|
||
|
||
凭据保存在 `/opt/vps-xray/.env`(权限 600)。**信息不会丢** —— 随时可以重新打印:
|
||
|
||
```bash
|
||
cd /opt/vps-xray && bash deploy.sh --show
|
||
```
|
||
|
||
`--show` 只读取 `.env` 输出连接信息和分享链接,不安装、不改配置、不重启服务。
|
||
|
||
> 即使 `.env` 被误删,只要 `/usr/local/etc/xray/config.json` 还在,UUID、shortId、SNI 都能从中读出,公钥可用 `xray x25519 -i <privateKey>` 从私钥反推。
|
||
|
||
### 第三步:客户端配置
|
||
|
||
将部署脚本输出的 `vless://` 链接导入客户端即可:
|
||
|
||
| 平台 | 推荐客户端 |
|
||
|------|-----------|
|
||
| Windows | v2rayN(6.x+) |
|
||
| macOS | V2BOX / NekoRay,详见 [client-macos.md](client-macos.md) |
|
||
| iOS | Shadowrocket / Streisand |
|
||
| Android | v2rayNG |
|
||
|
||
#### 生成 Clash 配置
|
||
|
||
把每台 VPS 的 `bash deploy.sh --show` 输出保存成 `client-config/xxx-info.md`,然后:
|
||
|
||
```bash
|
||
cd client-config
|
||
bash gen-clash.sh # 交互式勾选要写入的节点
|
||
bash gen-clash.sh --all # 全部节点
|
||
```
|
||
|
||
脚本解析文件里的 `vless://` 链接,生成 `vpn-vps-xray-optimized.yaml`(多节点自动组成 url-test 自动选择组,原文件会先备份为 `.bak`)。
|
||
|
||
> 生成的配置需要 **Clash.Meta / mihomo** 内核(Clash Verge Rev、ClashX Meta、FlClash 等)。原版 Clash / Clash Premium 不支持 VLESS + Reality。
|
||
|
||
详细的 Clash Meta / Sing-Box 配置参见 [vps-xray-optimized.md](vps-xray-optimized.md)。
|
||
|
||
## 日常运维
|
||
|
||
### 查看连接信息 / 分享链接
|
||
|
||
```bash
|
||
cd /opt/vps-xray
|
||
bash deploy.sh --show
|
||
```
|
||
|
||
只读操作,不会改动任何服务。
|
||
|
||
### 更换 Reality 伪装目标
|
||
|
||
伪装目标在首次部署时按握手延迟自动挑选,之后固定写入 `.env` 不再变动。后续每次部署会检查它是否仍然可达,不可达时告警但**不会自动更换**——因为更换会改变 SNI,导致所有客户端链接静默失效。
|
||
|
||
确认要换时显式执行:
|
||
|
||
```bash
|
||
bash deploy.sh --redetect
|
||
```
|
||
|
||
> ⚠️ 换完后 SNI 变了,**所有客户端都必须用新的分享链接重新配置**。脚本会在更换时打印醒目提示。
|
||
|
||
#### 自定义伪装目标时的两个硬条件
|
||
|
||
在 `.env` 里手动指定 `REALITY_DEST` / `REALITY_SNI` 前,必须确认新域名同时满足:
|
||
|
||
**1. 技术条件** — TLS 1.3,且证书链总长 < 8192 字节。上限是 `xtls/reality` 里的硬编码值,超出会直接握手失败(见 [XTLS/Xray-core#6356](https://github.com/XTLS/Xray-core/issues/6356),`www.microsoft.com` 的 8273 字节即触发)。在服务器上自查:
|
||
|
||
```bash
|
||
xray tls ping www.apple.com # 只看「Pinging with SNI」那一段
|
||
```
|
||
|
||
**2. 客户端路由条件** — 该域名必须会被客户端判成「直连」。
|
||
|
||
这条不直观。TUN 模式下客户端偶尔会把「连接本代理服务器」这件事本身也抓进代理(v2rayN 的 sing-box + xray 双核心靠进程嗅探防回环,Windows 上存在竞态)。这种回环连接的去向完全由 SNI 决定:
|
||
|
||
| SNI 被判为 | 结果 |
|
||
|---|---|
|
||
| 直连 | 连接真的到达本服务器,REALITY 握手成功,**用户无感** |
|
||
| 走代理 | 进隧道 → 服务器替客户端连了真正的伪装站 → 客户端收到真证书 → 刷屏 `REALITY: received real certificate` |
|
||
|
||
实测(v2rayN 默认规则模板,`geosite:google → proxy` 排在 `geosite:cn → direct` 之前):
|
||
|
||
| 域名 | 命中规则 | 可用性 |
|
||
|---|---|---|
|
||
| `www.apple.com`、`*.apple.com` | `geosite:cn` | ✅ 安全 |
|
||
| `dl.google.com` | `geosite:google` | ❌ 不安全 |
|
||
| `www.microsoft.com` | 都不命中 → `final:proxy` | ❌ 不安全 |
|
||
| `www.amazon.com` | 都不命中 → `final:proxy` | ❌ 不安全 |
|
||
|
||
脚本的候选列表因此只保留 Apple 系域名。
|
||
|
||
### 定时重启
|
||
|
||
默认**关闭**。原因是:
|
||
|
||
- 崩溃恢复已由 systemd 的 `Restart=always` + `RestartSec=3` 覆盖(秒级响应),不必等到凌晨
|
||
- Xray 无已知需要定期重启的缺陷(内存、句柄上限均已配置)
|
||
- 而硬重启会切断全部活动连接——跨夜下载、备份、CI 都会中断
|
||
|
||
即"为假想问题付出真实代价"。仅在确实观察到长期运行后性能退化时才启用。
|
||
|
||
需要时用轻量入口调整,**不会重装 Xray、不会重写配置、不会重启服务,现有 vless 链接完全不受影响**:
|
||
|
||
```bash
|
||
bash deploy.sh --restart 7 04:00 # 每 7 天 04:00 重启
|
||
bash deploy.sh --restart 3 # 每 3 天,时间沿用 .env
|
||
bash deploy.sh --restart 0 # 关闭
|
||
bash deploy.sh --restart # 按 .env 现有值重建 timer
|
||
```
|
||
|
||
对应 `.env` 三个字段:
|
||
|
||
| 字段 | 含义 | 默认 |
|
||
|---|---|---|
|
||
| `XRAY_RESTART_EVERY_DAYS` | 0 = 关闭;N = 每 N 天 | `0` |
|
||
| `XRAY_RESTART_TIME` | 24 小时制 `HH:MM` | `04:00` |
|
||
| `XRAY_RESTART_TIMEZONE` | 时区 | `Asia/Shanghai` |
|
||
|
||
#### 为什么不是纯 systemd timer
|
||
|
||
`OnCalendar` 表达不了任意 N 天:`*-*-1/7 04:00:00` 是「每月 1、8、15、22、29 号」,**跨月会重置**——29 号到下月 1 号只隔 2~3 天。
|
||
|
||
所以实现是 timer 每天到点唤醒,由 `/usr/local/bin/xray-restart-guard` 读 `/var/lib/xray/last-restart`,满 N 天才真正重启。间隔因此是「距上次实际重启」的真实天数,`N=3` 与 `N=7` 同样准确。
|
||
|
||
查看唤醒计划与执行记录:
|
||
|
||
```bash
|
||
systemctl list-timers xray-restart.timer
|
||
journalctl -u xray-restart.service -n 20
|
||
```
|
||
|
||
> ⚠️ `list-timers` 的 `NEXT` 是 **timer 下次「唤醒」的时间**,不是下次重启的时间。timer 每天唤醒一次,NEXT 因此恒为次日的 `XRAY_RESTART_TIME`;是否真的重启由守卫按间隔判断。设了「每 7 天」且 5 天前刚重启过时,NEXT 显示明天凌晨,但实际还要再等 2 天。
|
||
>
|
||
> 想直接看「下次实际重启时间」,跑一次不带参数的 `bash deploy.sh --restart`(不改任何配置、不重启服务),输出里会算好打印出来。
|
||
|
||
#### 已知取舍
|
||
|
||
- **间隔锚点是「上次由守卫执行的重启」,不是任何一次 xray 重启。** 完整部署(`bash deploy.sh`)里的 `systemctl restart xray` 不会更新 `/var/lib/xray/last-restart`——设了每 7 天、距上次守卫重启已 6.9 天时跑一次完整部署,次日守卫仍会判定满 7 天而再重启一次。介意的话可在部署后手动执行 `date +%s > /var/lib/xray/last-restart` 把锚点对齐。
|
||
- **重启失败时时间戳仍已更新**,本轮被跳过,下一轮照常——这是为了避免陷入每天硬重启的循环。但若时间戳**写不进去**(磁盘满、只读挂载),守卫会跳过本轮重启并在日志里报错,宁可不重启也不退化成每日重启。
|
||
|
||
> 旧字段 `XRAY_DAILY_RESTART` 已废弃。三个新字段都未设置时它仍作为兼容回退(`true` → 每 1 天,其它 → 关闭),重跑部署后 `.env` 会自动换写成新字段。
|
||
|
||
### 查看状态 / 日志
|
||
|
||
```bash
|
||
systemctl status xray
|
||
journalctl -u xray -f
|
||
journalctl -u xray -n 100
|
||
```
|
||
|
||
### 备份
|
||
|
||
```bash
|
||
cd /opt/vps-xray
|
||
bash backup.sh
|
||
```
|
||
|
||
备份内容包括:
|
||
- Xray 配置文件(`config.json`)
|
||
- 部署配置(`.env` + 脚本)
|
||
- 网络调优参数
|
||
|
||
备份文件保存在 `/var/backups/xray/`,自动清理 30 天前的旧备份。
|
||
|
||
### 恢复备份
|
||
|
||
```bash
|
||
# 查看可用备份
|
||
ls /var/backups/xray/
|
||
|
||
# 恢复 Xray 配置
|
||
tar xzf /var/backups/xray/xray_config_<日期>.tar.gz -C /usr/local/etc/
|
||
|
||
# 恢复网络调优
|
||
cp /var/backups/xray/sysctl_<日期>.conf /etc/sysctl.d/99-xray-turbo.conf
|
||
sysctl --system
|
||
|
||
# 重启服务
|
||
systemctl restart xray
|
||
```
|
||
|
||
### 更新 Xray
|
||
|
||
```bash
|
||
# 备份当前配置
|
||
cd /opt/vps-xray && bash backup.sh
|
||
|
||
# 更新 Xray 核心(配置文件保留)
|
||
bash <(curl -fsSL https://raw.githubusercontent.com/XTLS/Xray-install/main/install-release.sh)
|
||
|
||
# 重启服务
|
||
systemctl restart xray
|
||
```
|
||
|
||
### 重启 / 停止
|
||
|
||
```bash
|
||
systemctl restart xray # 重启
|
||
systemctl stop xray # 停止
|
||
systemctl start xray # 启动
|
||
```
|
||
|
||
### 验证
|
||
|
||
```bash
|
||
# 确认 BBR 启用
|
||
sysctl net.ipv4.tcp_congestion_control
|
||
|
||
# 确认端口监听
|
||
ss -tlnp | grep 443
|
||
|
||
# 测试配置语法
|
||
/usr/local/bin/xray run -test -config /usr/local/etc/xray/config.json
|
||
```
|
||
|
||
## 完全卸载
|
||
|
||
```bash
|
||
cd /opt/vps-xray
|
||
bash uninstall.sh
|
||
```
|
||
|
||
脚本会**交互式确认**每个危险操作:
|
||
|
||
| 步骤 | 操作 | 确认方式 |
|
||
|------|------|----------|
|
||
| 0 | 卸载前备份(可选) | y/N |
|
||
| 1 | 停止并禁用 Xray 服务 | 输入 YES |
|
||
| 2 | 卸载 Xray 程序 + 删除配置 | 自动 |
|
||
| 3 | 移除网络调优配置 | 自动 |
|
||
| 4 | 关闭防火墙端口 | 自动 |
|
||
| 5 | 删除部署目录 `/opt/vps-xray` | y/N |
|
||
|
||
**备份目录 `/var/backups/xray/` 始终保留**。
|
||
|
||
## 多用户
|
||
|
||
在 Xray 配置的 `clients` 数组中添加更多用户:
|
||
|
||
```bash
|
||
vi /usr/local/etc/xray/config.json
|
||
```
|
||
|
||
```json
|
||
"clients": [
|
||
{ "id": "原始-uuid", "flow": "xtls-rprx-vision" },
|
||
{ "id": "新用户-uuid", "flow": "xtls-rprx-vision" }
|
||
]
|
||
```
|
||
|
||
生成新 UUID:`cat /proc/sys/kernel/random/uuid`
|
||
|
||
修改后重启:`systemctl restart xray`
|
||
|
||
## 故障排查
|
||
|
||
### 客户端连接失败
|
||
|
||
```bash
|
||
# 确认 Xray 运行
|
||
systemctl status xray
|
||
|
||
# 确认端口监听
|
||
ss -tlnp | grep 443
|
||
|
||
# 查看错误日志
|
||
journalctl -u xray -n 50
|
||
```
|
||
|
||
### 速度慢
|
||
|
||
```bash
|
||
# 确认 BBR 启用
|
||
sysctl net.ipv4.tcp_congestion_control
|
||
# 应输出: net.ipv4.tcp_congestion_control = bbr
|
||
|
||
# 确认网络调优生效
|
||
sysctl net.core.rmem_max
|
||
# 应输出: net.core.rmem_max = 16777216
|
||
```
|
||
|
||
### IP 被封
|
||
|
||
Reality 能大幅降低被封概率,但如果 IP 已被墙:
|
||
1. 更换 VPS IP
|
||
2. 重新部署:`bash deploy.sh`
|
||
|
||
## 安全说明
|
||
|
||
- `PrivateKey` 只存在服务端配置中,切勿泄露
|
||
- `PublicKey` 是客户端参数,可公开
|
||
- `.env` 文件权限已设为 600(仅 root 可读)
|
||
- BT 流量已在路由规则中屏蔽,降低 VPS 被投诉风险
|