diff --git a/vps-xray/.env.example b/vps-xray/.env.example index 64546fb..6d5a06c 100644 --- a/vps-xray/.env.example +++ b/vps-xray/.env.example @@ -13,9 +13,13 @@ # NAT / 端口转发的机器上出口 IP 与入站 IP 可能不同,此时必须手动指定 # SERVER_IP=1.2.3.4 -# Reality 伪装目标(留空则自动选择延迟最低的) -# REALITY_DEST=www.microsoft.com -# REALITY_SNI=www.microsoft.com +# Reality 伪装目标(留空则自动从候选中挑选,见 deploy.sh 的 select_reality_dest) +# 手动指定时务必确认新域名满足两个条件,否则客户端会刷 REALITY 报错: +# 1) TLS 1.3 且证书链 < 8192 字节(用 xray tls ping <域名> 查「with SNI」那段) +# 2) 该域名会被客户端路由判成「直连」(命中 geosite:cn) +# 已知不可用:dl.google.com(命中 geosite:google)、www.microsoft.com、www.amazon.com +# REALITY_DEST=www.apple.com +# REALITY_SNI=www.apple.com # 每日 04:00 自动重启 Xray(默认 false) # 崩溃恢复已由 systemd 的 Restart=always 覆盖(秒级响应), diff --git a/vps-xray/README.md b/vps-xray/README.md index 8006b71..6b393f2 100644 --- a/vps-xray/README.md +++ b/vps-xray/README.md @@ -163,6 +163,36 @@ 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 系域名。 + ### 查看状态 / 日志 ```bash diff --git a/vps-xray/client-config/lisa-vps-xray-optimized-info-4.md b/vps-xray/client-config/lisa-vps-xray-optimized-info-4.md index f237eb1..8ae181d 100644 --- a/vps-xray/client-config/lisa-vps-xray-optimized-info-4.md +++ b/vps-xray/client-config/lisa-vps-xray-optimized-info-4.md @@ -5,20 +5,20 @@ IP : 64.81.25.247 端口 : 443 协议 : VLESS - UUID : b2f1198d-9475-40af-a2c6-2738a69b4e06 + UUID : 7c7de15f-d408-4534-b367-f6253e850178 流控 : xtls-rprx-vision 传输 : tcp 安全 : reality SNI : www.apple.com Fingerprint : chrome - PublicKey : fKRVRMVlmmedi2rFk-cetAT397V-IzRifStKQcMTilM - ShortId : fca381b1ac6f4d02 + PublicKey : AvX70NC1Tqd1sFBvASY-2oitzS2OpCsZu-H1ucVDUBM + ShortId : fa4235504a09feeb ========================================================== >>> VLESS 分享链接(可直接导入客户端): -vless://b2f1198d-9475-40af-a2c6-2738a69b4e06@64.81.25.247:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=www.apple.com&fp=chrome&pbk=fKRVRMVlmmedi2rFk-cetAT397V-IzRifStKQcMTilM&sid=fca381b1ac6f4d02&type=tcp#vps-reality-lisa-1 +vless://7c7de15f-d408-4534-b367-f6253e850178@64.81.25.247:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=www.apple.com&fp=chrome&pbk=AvX70NC1Tqd1sFBvASY-2oitzS2OpCsZu-H1ucVDUBM&sid=fa4235504a09feeb&type=tcp#vps-reality-lisa-1 ========================================================== diff --git a/vps-xray/client-config/vps-xray-optimized-info-3.md b/vps-xray/client-config/vps-xray-optimized-info-3.md new file mode 100644 index 0000000..d5345cd --- /dev/null +++ b/vps-xray/client-config/vps-xray-optimized-info-3.md @@ -0,0 +1,31 @@ +========================================================== + Xray VLESS-Reality 部署完成 ✅ +========================================================== + + IP : 140.82.23.27 + 端口 : 443 + 协议 : VLESS + UUID : 3cac4c5e-c9ae-488f-9cde-0c65b2d23409 + 流控 : xtls-rprx-vision + 传输 : tcp + 安全 : reality + SNI : www.apple.com + Fingerprint : chrome + PublicKey : 5hukUZGoGAs4Fpj2Bc7_1nGQykdaYN1sznG-KsxYUD8 + ShortId : 4c5999dd1b15ff3c + +========================================================== + +>>> VLESS 分享链接(可直接导入客户端): + +vless://3cac4c5e-c9ae-488f-9cde-0c65b2d23409@140.82.23.27:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=www.apple.com&fp=chrome&pbk=5hukUZGoGAs4Fpj2Bc7_1nGQykdaYN1sznG-KsxYUD8&sid=4c5999dd1b15ff3c&type=tcp#vps-reality-3 + +========================================================== + +⚠️ 请妥善保存以上信息! + 配置文件: /usr/local/etc/xray/config.json + 凭据备份: /opt/vps-xray/.env + 再次查看: bash deploy.sh --show + 查看日志: journalctl -u xray -f + 重启服务: systemctl restart xray +========================================================== \ No newline at end of file diff --git a/vps-xray/deploy.sh b/vps-xray/deploy.sh index 03fd97f..3494c55 100644 --- a/vps-xray/deploy.sh +++ b/vps-xray/deploy.sh @@ -234,6 +234,39 @@ generate_keys() { fi } +# ===== 候选伪装站的技术校验 ===== +# 只看 xray tls ping 的「Pinging with SNI」那一段:REALITY 实际用的是带 SNI 的 +# 握手结果,无 SNI 段返回的往往是另一张证书(如 Google 的 invalid2.invalid, +# 才 895 字节),照它判会误判。 +REALITY_DEST_CERT_LEN="" +check_reality_dest() { + local site="$1" out len + REALITY_DEST_CERT_LEN="" + + local raw + raw=$(/usr/local/bin/xray tls ping "$site" 2>&1) + # 区分「当前 xray 没有 tls ping 子命令」和「这个域名校验不通过」: + # 前者不该阻断部署,后者必须判失败。有 "TLS ping" 头即说明子命令存在。 + if ! echo "$raw" | grep -q "TLS ping"; then + warn " ${site}: 当前 xray 不支持 tls ping,跳过技术校验" + return 0 + fi + + out=$(echo "$raw" | sed -n '/Pinging with SNI/,$p') + echo "$out" | grep -q "Handshake succeeded" || return 1 + echo "$out" | grep -q "TLS Version:.*TLS 1\.3" || return 1 + + len=$(echo "$out" | grep -oE "Certificate chain's total length:[[:space:]]*[0-9]+" \ + | grep -oE '[0-9]+$' | head -1) + [ -n "$len" ] || return 1 + # xtls/reality 对 target 握手报文有 8192 字节硬编码上限,超出直接握手失败 + # (XTLS/Xray-core#6356:www.microsoft.com 的证书记录 8273 字节即触发) + [ "$len" -lt 8192 ] || return 1 + + REALITY_DEST_CERT_LEN="$len" + return 0 +} + # ===== 3. 选择 Reality 伪装目标 ===== select_reality_dest() { if [ "$MODE" != "reality" ]; then @@ -242,7 +275,27 @@ select_reality_dest() { step "4/8 选择 Reality 伪装目标" - local candidates=("www.microsoft.com" "dl.google.com" "www.apple.com" "www.amazon.com") + # 候选伪装站必须同时满足两类条件。 + # + # 【技术条件】TLS 1.3 + 证书链总长 < 8192 字节。由 check_reality_dest 运行时实测。 + # + # 【客户端路由条件】SNI 必须会被客户端判成「直连」——这条不直观,但踩过坑。 + # TUN 模式下,客户端有时会把「连接本代理服务器」这件事本身也抓进代理 + # (v2rayN 的 sing-box+xray 双核心架构靠进程嗅探防回环,Windows 上有竞态, + # 实测约 2843:1584 的失败率)。这种回环连接的去向完全由 SNI 决定: + # + # SNI 被判直连 → 连接真的到达本服务器 → REALITY 握手成功 → 用户无感 + # SNI 被判走代理 → 进隧道 → 服务器替客户端连了真正的伪装站 + # → 客户端收到真证书 → 刷屏 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 系域名。更换前请先确认新域名命中客户端的直连规则。 + local candidates=("www.apple.com" "swdist.apple.com" "updates.cdn-apple.com" "itunes.apple.com") REALITY_DEST="${REALITY_DEST:-}" REALITY_SNI="${REALITY_SNI:-}" @@ -259,6 +312,10 @@ select_reality_dest() { warn "注意:更换会改变 SNI,届时所有客户端链接都必须同步更新" else log "伪装目标连通性正常 (${ms}ms)" + if ! check_reality_dest "$REALITY_DEST"; then + warn "伪装目标 ${REALITY_DEST} 未通过 REALITY 技术校验" + warn "(要求 TLS 1.3 且证书链 < 8192 字节),客户端可能握手失败" + fi fi return fi @@ -267,29 +324,35 @@ select_reality_dest() { warn "--redetect:将重新挑选伪装目标,原目标为 ${REALITY_DEST}" fi - local best_dest="" - local best_ms=9999 - + # 按列表顺序优先,只有前面的不可用才往后顺延——不按延迟排序。 + # 候选现在都是 Apple 系主机,彼此延迟差在噪声级(实测 1~2ms), + # 按延迟挑会让每台机器随机选出不同 SNI,徒增客户端管理成本; + # 而 SNI 在所有机器上保持一致,运维和排查都省事得多。 + local best_dest="" best_ms="" for site in "${candidates[@]}"; do local ms # curl 失败时 %{time_connect} 会输出 0.000000,必须要求 ms > 0, - # 否则连不通的站点会被当成「延迟最低」选中 + # 否则连不通的站点会被当成可用 ms=$(curl -so /dev/null -w '%{time_connect}' --max-time 3 "https://${site}" 2>/dev/null \ | awk '{printf "%d", $1*1000}') || true if [ -z "$ms" ] || ! [[ "$ms" =~ ^[0-9]+$ ]] || [ "$ms" -le 0 ]; then warn " ${site} 不可达,跳过" continue fi - log " ${site} → ${ms}ms" - if [ "$ms" -lt "$best_ms" ]; then - best_ms=$ms - best_dest=$site + if ! check_reality_dest "$site"; then + warn " ${site} (${ms}ms) 未通过技术校验,跳过" + continue fi + log " ${site} → ${ms}ms,证书链 ${REALITY_DEST_CERT_LEN:-?} 字节 ✔" + best_dest="$site" + best_ms="$ms" + break done if [ -z "$best_dest" ]; then - error "所有候选伪装目标都无法连接,请检查服务器网络 / DNS" + error "没有候选伪装目标能同时满足「可达」与「REALITY 技术要求」" error "候选列表: ${candidates[*]}" + error "可在 .env 中手动指定 REALITY_DEST / REALITY_SNI 后重试" exit 1 fi