diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..55890d0 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,15 @@ +{ + "permissions": { + "allow": [ + "WebSearch", + "WebFetch(domain:xtls.github.io)", + "WebFetch(domain:deepwiki.com)", + "WebFetch(domain:api.github.com)", + "Bash(bats tests/test_config_generation.bats)", + "Bash(npx *)", + "Bash(wsl.exe *)", + "PowerShell(wsl -u root -e bash -c \"apt install -y jq 2>&1 | tail -3\")", + "PowerShell(wsl -u root -e bash -c \"apt install -y shellcheck 2>&1 | tail -3\")" + ] + } +} diff --git a/.env.example b/.env.example index 3c42856..e005f8b 100644 --- a/.env.example +++ b/.env.example @@ -6,177 +6,148 @@ # ./scripts/deploy.sh # # 或直接传环境变量: -# EASYNET_SERVICE_CHOICE=0 EASYNET_DOMAIN=proxy.example.com ./scripts/deploy.sh +# EASYNET_DOMAIN=proxy.example.com ./scripts/deploy.sh +# +# 大多数变量有合理的默认值,你通常只需配置 # 1. 必填配置 即可。 # --------------------------------------------------------- -# 1. 部署选择 +# 1. 必填配置 # --------------------------------------------------------- -# 优先级:EASYNET_PROFILE > EASYNET_MODULE > EASYNET_SERVICE_CHOICE。 -# -# EASYNET_SERVICE_CHOICE: -# 0: 全部部署 -# 1-N: 按抗 DPI 能力从高到低排列(当前:1=xray-reality, 2=hysteria2, 3=shadowsocks, 4=wireguard) -# N+1: 退出安装 -# -# 新增协议模块后编号会顺延,建议优先使用 EASYNET_MODULE 或 EASYNET_PROFILE。 -# EASYNET_SERVICE_CHOICE=0 -# 按单个模块部署,可选: -# xray-reality / hysteria2 / shadowsocks / wireguard -# EASYNET_MODULE=xray-reality +# 域名(必填——Hysteria2 需要域名,Edge Gateway 自动签发证书并生成订阅链接) +# 请确保此域名已经 A 记录解析到当前服务器公网 IP。 +# EASYNET_DOMAIN=proxy.example.com -# 按策略部署,可选:strict / balanced / compat。 -# strict: xray-reality(最高抗 DPI,仅部署 Reality) -# balanced: xray-reality + hysteria2(推荐默认,均衡抗 DPI 与速度) +# 部署策略(默认 balanced,可选 strict / balanced / compat) +# strict: 仅部署 Xray+Reality(极致抗 DPI) +# balanced: Xray+Reality + Hysteria2(均衡,推荐默认) # compat: 全部已发现模块 EASYNET_PROFILE=balanced -# --------------------------------------------------------- -# 1.2 安全回滚 -# --------------------------------------------------------- -# 部署前自动备份 state 目录,部署过程中出错时自动恢复。 -# 默认关闭,设置为 true 启用。 -# EASYNET_AUTO_ROLLBACK=true +# 也可直接指定单个模块(优先级高于 PROFILE): +# EASYNET_MODULE=xray-reality # --------------------------------------------------------- -# 1.3 卸载选择(独立变量空间,不与部署变量混淆) +# 2. 常用调优 # --------------------------------------------------------- -# EASYNET_UNINSTALL_CHOICE: -# 0: 卸载全部协议与 Edge Gateway -# 1-N: 按字母序排列(当前:1=edge, 2=hysteria2, 3=shadowsocks, 4=wireguard, 5=xray-reality) -# N+1: 退出卸载 -# EASYNET_UNINSTALL_CHOICE=0 - -# 按单个模块卸载,可选: -# xray-reality / hysteria2 / shadowsocks / wireguard / edge -# EASYNET_UNINSTALL_MODULE=xray-reality +# 以下变量可以按需调整,不设置也使用合理的默认值。 -# 如需保留配置用于迁移或排障,设置为 true。 -# EASYNET_UNINSTALL_KEEP_CONFIG=true - -# 确认依赖只能由 EasyNet 使用时,可设置为 true。 -# EASYNET_UNINSTALL_PURGE_PACKAGES=true - -# --------------------------------------------------------- -# 2. 域名配置 -# --------------------------------------------------------- -# Hysteria2 需要域名。配置域名后会自动启用 Edge Gateway 并打印订阅链接和二维码。 -# 请确保此域名已经 A 记录解析到当前服务器公网 IP。 -# EASYNET_DOMAIN=proxy.example.com - -# 如果订阅域名与部署域名不同,可单独指定。 -# EASYNET_SUBSCRIPTION_DOMAIN=proxy.example.com - -# Edge Gateway 默认占用 TCP/80(HTTP)和 TCP/443(HTTPS), -# 并使用首次部署时生成的稳定随机路径承载订阅。 -# 可运行 ./scripts/show_subscription.sh 随时重新显示订阅链接和二维码。 -# 如果 EASYNET_DOMAIN 与 EASYNET_SUBSCRIPTION_DOMAIN 不同,两者都需要解析到服务器。 - -# 默认可通过 /sub、/clash、/singbox 直接路径访问订阅文件(同时保留随机路径兼容)。 -# 如需关闭直接路径,设置 EASYNET_SUBSCRIPTION_DIRECT_PATHS=false。 - -# 如需自定义订阅路径前缀(而非自动生成),设置: -# EASYNET_SUBSCRIPTION_PATH_PREFIX=/mysecret +# Reality 传输方式:tcp(默认,推荐)或 xhttp(HTTP/3 伪装) +# EASYNET_REALITY_TRANSPORT=tcp +# EASYNET_REALITY_TRANSPORT=xhttp -# Edge Gateway 根路径默认反代到 https://www.bing.com 消除指纹, -# 可通过以下变量自定义伪装目标。 -# EASYNET_EDGE_MASQUERADE_URL=https://www.bing.com +# Reality 多 SNI,逗号分隔多个域名分散流量特征 +EASYNET_REALITY_SERVER_NAME=www.microsoft.com,cloudflare.com,www.apple.com -# 如需调整 Edge 端口(不推荐,部分 VPS 可能屏蔽非标准端口): -# EASYNET_EDGE_HTTP_PORT=80 -# EASYNET_EDGE_HTTPS_PORT=443 +# Hysteria2 端口跳跃(默认 20000-30000/udp),留空禁用 +EASYNET_HYSTERIA2_PORT_HOPPING=20000-30000 -# Edge 证书目录 -# EASYNET_EDGE_CERT_DIR=/etc/ssl/easynet-edge -# EASYNET_EDGE_CERT_FILE=/etc/ssl/easynet-edge/fullchain.crt -# EASYNET_EDGE_KEY_FILE=/etc/ssl/easynet-edge/private.key +# 手动指定公网 IP(覆盖自动检测,多网卡或 NAT 场景使用) +# EASYNET_PUBLIC_IP=203.0.113.1 # --------------------------------------------------------- -# 3. 可选:公共状态和输出目录 +# 3. 抗 DPI 调优 # --------------------------------------------------------- -# EASYNET_STATE_DIR=/var/lib/easynet -# EASYNET_WEB_ROOT=/var/www/html +# Reality 默认使用 TCP + flow:xtls-rprx-vision(TLS 指纹模仿 + Vision 流控)。 +# 如需极致抗 DPI,可改用 EASYNET_PROFILE=strict(仅部署 Xray-Reality)。 +# XHTTP 传输(HTTP/3)适合需要过 CDN 的场景,配合 XMUX 多路复用。 # --------------------------------------------------------- -# 4. 可选:通用参数 +# 4. 部署选择(仅自动化部署时需要) # --------------------------------------------------------- -# 手动指定公网 IP(覆盖自动检测,多网卡或 NAT 场景使用) -# EASYNET_PUBLIC_IP=203.0.113.1 +# 以下变量用于非交互式部署(CI/CD、脚本调用)。 +# 手动部署时脚本会提示选择,无需设置。 + +# EASYNET_SERVICE_CHOICE: 0=全部 / 1=xray-reality / 2=hysteria2 / 3=shadowsocks / 4=wireguard +# EASYNET_SERVICE_CHOICE=0 + +# 安全回滚:部署失败时自动恢复 state 目录 +# EASYNET_AUTO_ROLLBACK=true +# 严格预检:部署前检查 DNS 解析、端口可用性等 +# EASYNET_STRICT_PRECHECK=false # --------------------------------------------------------- -# 4.5 抗 DPI 调优推荐配置 +# 5. 卸载配置(独立变量空间,用于自动化卸载) # --------------------------------------------------------- -# 以下配置在保证多协议兼容的同时最大化抗 DPI 能力: -# -# - Xray Reality 有两种传输方式可选: -# · TCP(默认):配合 Fragment 包分片,将 TLS Client Hello 拆小包防 DPI -# · XHTTP(HTTP/3 over QUIC):流量混入浏览器 QUIC,但**与 Fragment 不兼容** -# - Reality 伪装目标增加到三个主流 CDN,多 SNI 减少指纹特征 -# - Hysteria2 启用端口跳跃(20000-30000/udp),每 10 秒跳变一次 -# -# 如需极致抗 DPI,可改用 EASYNET_PROFILE=strict(仅部署 Xray-Reality)。 +# EASYNET_UNINSTALL_CHOICE: 0=全部 / 1=edge / 2=hysteria2 / 3=shadowsocks / 4=wireguard / 5=xray-reality +# EASYNET_UNINSTALL_CHOICE=0 +# EASYNET_UNINSTALL_MODULE=xray-reality +# EASYNET_UNINSTALL_KEEP_CONFIG=true +# EASYNET_UNINSTALL_PURGE_PACKAGES=true # --------------------------------------------------------- -# 5. 可选:协议参数 +# 6. 高级配置 # --------------------------------------------------------- +# 以下变量很少需要修改,取消注释并修改值即可生效。 -# === Xray+Reality === +# --- 协议端口 --- # EASYNET_REALITY_PORT=8443 +# EASYNET_HYSTERIA2_PORT=443 +# EASYNET_SHADOWSOCKS_PORT=8388 +# EASYNET_WIREGUARD_PORT=51820 + +# --- Reality 细节 --- # EASYNET_REALITY_DEST=www.microsoft.com:443 -# EASYNET_REALITY_SERVER_NAME 支持逗号分隔多个目标 -EASYNET_REALITY_SERVER_NAME=www.microsoft.com,cloudflare.com,www.apple.com -# -# 传输方式:tcp(默认,支持 Fragment 包分片)或 xhttp(HTTP/3 伪装) -# ⚠️ xhttp 与 Fragment 不兼容,设 xhttp 时系统自动跳过 Fragment -# 取消下面其中一行的注释即可切换: -EASYNET_REALITY_TRANSPORT=tcp -# EASYNET_REALITY_TRANSPORT=xhttp -# -# XHTTP 专属参数(仅 TRANSPORT=xhttp 时生效): -# EASYNET_REALITY_XHTTP_MODE=auto +# EASYNET_REALITY_XHTTP_MODE=stream-one # EASYNET_REALITY_XMUX_CONCURRENCY=0 -# -# Fragment 包分片(仅 TRANSPORT=tcp 时生效,xhttp 下自动跳过) -EASYNET_REALITY_FRAGMENT=tlshello -# EASYNET_REALITY_FRAGMENT_LENGTH 分片长度范围(默认 100-200 字节) -EASYNET_REALITY_FRAGMENT_LENGTH=40-120 -# EASYNET_REALITY_FRAGMENT_INTERVAL 分片间隔范围(默认 10-20 毫秒) -EASYNET_REALITY_FRAGMENT_INTERVAL=5-15 - -# === Hysteria2 === -# EASYNET_HYSTERIA2_PORT=443 -# EASYNET_HYSTERIA2_PORT_HOPPING 端口跳变范围(如 "20000-30000"),留空禁用 -EASYNET_HYSTERIA2_PORT_HOPPING=20000-30000 -# EASYNET_HYSTERIA2_PORT_HOP_INTERVAL 跳变间隔,默认 30s -EASYNET_HYSTERIA2_PORT_HOP_INTERVAL=10s +# EASYNET_REALITY_XMUX_CONN_IDLE=60 -# === Shadowsocks 2022 Edition (shadowsocks-rust) === -# EASYNET_SHADOWSOCKS_PORT=8388 -# EASYNET_SHADOWSOCKS_INSTALL_SHA256 可选,用于校验 shadowsocks-rust 二进制包 +# --- Hysteria2 细节 --- +# EASYNET_HYSTERIA2_PORT_HOP_INTERVAL=30s +# EASYNET_HYSTERIA2_PASSWORD= (随机生成,留空自动生成) +# EASYNET_HYSTERIA2_OBFS_PASSWORD= (随机生成,留空自动生成) +# EASYNET_HYSTERIA2_MASQUERADE_URL=https://www.bing.com/ -# === WireGuard === -# EASYNET_WIREGUARD_PORT=51820 -# EASYNET_WIREGUARD_OBFS AmneziaWG 混淆(默认 true,启用 Jc/Jmin/Jmax 垃圾包填充) +# --- WireGuard 细节 --- # EASYNET_WIREGUARD_OBFS=true -# EASYNET_WIREGUARD_JC 垃圾包数量(默认 5) -# EASYNET_WIREGUARD_JMIN 最小垃圾包大小(默认 50 字节) -# EASYNET_WIREGUARD_JMAX 最大垃圾包大小(默认 1000 字节) -# EASYNET_WIREGUARD_SERVER_IP 服务器子网(默认 10.0.0.1/24) -# EASYNET_WIREGUARD_CLIENT 客户端配置名称(默认 client1) +# EASYNET_WIREGUARD_JC=5 +# EASYNET_WIREGUARD_JMIN=50 +# EASYNET_WIREGUARD_JMAX=1000 +# EASYNET_WIREGUARD_SERVER_IP=10.0.0.1/24 +# EASYNET_WIREGUARD_CLIENT=client1 -# --------------------------------------------------------- -# 6. 可选:安装脚本/安装包 SHA256 校验 -# --------------------------------------------------------- -# 默认使用 HTTPS 下载官方安装脚本或发布包。 -# 如需进一步收紧供应链风险,可填写对应 SHA256,下载后会先校验再执行或安装。 +# --- Edge Gateway --- +# EASYNET_EDGE_HTTP_PORT=80 +# EASYNET_EDGE_HTTPS_PORT=443 +# EASYNET_EDGE_MASQUERADE_URL=https://www.bing.com +# EASYNET_EDGE_CERT_DIR=/etc/ssl/easynet-edge +# EASYNET_EDGE_CERT_FILE=/etc/ssl/easynet-edge/fullchain.crt +# EASYNET_EDGE_KEY_FILE=/etc/ssl/easynet-edge/private.key +# EASYNET_EDGE_STATE_DIR=/var/lib/easynet/exposure/edge +# EASYNET_EDGE_RENEW_HOOK= (默认使用内置 cert_renew_hook.sh) +# EASYNET_SUBSCRIPTION_PATH_PREFIX=/mysecret +# EASYNET_SUBSCRIPTION_DIRECT_PATHS=true +# EASYNET_SUBSCRIPTION_SCHEME=https +# EASYNET_SUBSCRIPTION_URL_PORT= (默认使用 Edge HTTPS 端口) +# EASYNET_SUBSCRIPTION_ROTATION_GRACE=false +# EASYNET_SKIP_NGINX_RELOAD=false + +# --- 订阅域名(与部署域名不同时使用) --- +# EASYNET_SUBSCRIPTION_DOMAIN=proxy.example.com + +# --- 状态和输出目录 --- +# EASYNET_STATE_DIR=/var/lib/easynet +# EASYNET_WEB_ROOT=/var/www/html + +# --- SHA256 供应链校验 --- +# EASYNET_XRAY_VERSION=26.3.27 # EASYNET_ACME_INSTALL_SHA256= # EASYNET_XRAY_INSTALL_SHA256= # EASYNET_HYSTERIA2_INSTALL_SHA256= # EASYNET_SHADOWSOCKS_INSTALL_SHA256= # EASYNET_SINGBOX_INSTALL_SHA256= -# --------------------------------------------------------- -# 7. 可选:系统维护 -# --------------------------------------------------------- +# --- sing-box 客户端(端设备安装时使用) --- +# EASYNET_SINGBOX_MODE=mixed +# EASYNET_SINGBOX_INSTALL_DIR=/usr/local/bin +# EASYNET_SINGBOX_CONFIG_DIR=/etc/sing-box +# EASYNET_SINGBOX_STATE_DIR=/etc/easynet +# EASYNET_SINGBOX_SERVICE_NAME=easynet-singbox +# EASYNET_SINGBOX_UPDATE_NAME=easynet-singbox-update +# EASYNET_SINGBOX_DOWNLOAD_URL= (默认从 GitHub Releases 自动检测) +# EASYNET_SINGBOX_RELEASE_API=https://api.github.com/repos/SagerNet/sing-box/releases/latest +# EASYNET_SINGBOX_PROBE_URL=https://www.gstatic.com/generate_204 + +# --- 系统维护 --- # EASYNET_JOURNALD_MAX_USE=500M # EASYNET_NGINX_LOGROTATE_FILE=/etc/logrotate.d/easynet-nginx diff --git a/CHANGELOG.md b/CHANGELOG.md index b6700be..393ac30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 本项目遵循 [语义化版本](https://semver.org/spec/v2.0.0.html)。 +## [0.0.5] - 2026-06-22 + +### 移除 +- **Xray Reality Fragment 包分片**:`EASYNET_REALITY_FRAGMENT` 及相关环境变量全部移除。Fragment 与 + `flow: xtls-rprx-vision` 存在已知冲突(两者同时修改 TLS Client Hello 导致间歇性连接失败),且 + Vision 的 uTLS 指纹伪装 + padding 已完整覆盖抗 DPI 功能,Fragment 不再需要。 + +### 修复 +- **Xray Reality XHTTP 配置修复**:XHTTP 传输现在正确添加 `flow: xtls-rprx-vision` 和 + `encryption: none`(VLESS Encryption),消除 Xray-core v26.1.18+ 的 "VLESS without flow is + deprecated" 警告。 +- **XHTTP 默认 mode 改为 `stream-one`**:纯 TCP HTTP/2,避免 `mode=auto` 默认走 UDP/QUIC 被云商 QoS 限速。 +- **XMUX connIdleTime 可配置**:新增 `EASYNET_REALITY_XMUX_CONN_IDLE` 环境变量(默认 60 秒),替代硬编码值。 + +### 文档 +- 清理所有 Fragment 环境变量、配置示例、兼容性表格、诊断命令等文档。 + ## [0.0.4] - 2026-06-19 ### 优化 diff --git a/README.md b/README.md index 0d2e775..2a7e4d5 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,11 @@ ## 核心特性 - 🚀 支持 4 种代理协议,全部内置混淆对抗 DPI: - - **Xray+Reality** — TLS 指纹模仿 + XHTTP/HTTP3 传输 + Fragment 包分片 + XMUX 多路复用 + - **Xray+Reality** — TLS 指纹模仿 + XHTTP/HTTP3 传输 + XMUX 多路复用 - **Hysteria2** — QUIC/UDP + Salamander 混淆 + Port Hopping 端口跳变 - **Shadowsocks 2022** — BLAKE3-AES-256-GCM 加密,完整重放保护 - **WireGuard (+Amnezia obfs)** — 默认启用 Jc/Jmin/Jmax 垃圾包填充消除 UDP 指纹 -- 🔒 强安全架构:REALITY 无证书 TLS、Finalmask Fragment、Edge Gateway 反代伪装 +- 🔒 强安全架构:REALITY 无证书 TLS、Edge Gateway 反代伪装 - ⚡ 性能优化:BBR 拥塞控制、XHTTP 多路复用 (XMUX)、QUIC 0-RTT - 🔄 自动化运维:系统更新、证书续期 hook(自动修正权限并重启服务)、日志限额与 logrotate - 🤖 无交互部署:支持 `.env` 或环境变量进行一键安装 @@ -36,7 +36,7 @@ | 协议 | 传输/混淆 | 优点 | 防探测等级 | |------|-----------|------|-----------| -| **Xray+Reality** | TCP/XHTTP + REALITY + Fragment + XMUX | 无需域名,TLS 指纹模仿,包分片抗 ML,多路复用 | 🥇 极高(推荐) | +| **Xray+Reality** | TCP/XHTTP + REALITY + XMUX | 无需域名,TLS 指纹模仿,包分片抗 ML,多路复用 | 🥇 极高(推荐) | | **Hysteria2** | QUIC/UDP + Salamander + Port Hopping | 端口跳变抗封锁,HTTP/3 伪装 | 🥇 高(推荐补充) | | **Shadowsocks 2022** | TCP+UDP / BLAKE3-AES-256-GCM | 2022 Edition 强加密,重放保护 | 🥈 中等+ | | **WireGuard (+Amnezia obfs)** | UDP + Jc/Jmin/Jmax 垃圾包 | 可启用混淆消除 UDP 指纹 | 🥈 中等(启用混淆后) | @@ -158,7 +158,6 @@ EASYNET_SERVICE_CHOICE=0 EASYNET_DOMAIN=proxy.example.com ./scripts/deploy.sh EASYNET_PROFILE=balanced \ EASYNET_DOMAIN=proxy.example.com \ EASYNET_REALITY_TRANSPORT=xhttp \ - EASYNET_REALITY_FRAGMENT=tlshello \ EASYNET_REALITY_XMUX_CONCURRENCY=4 \ EASYNET_HYSTERIA2_PORT_HOPPING=20000-30000 \ EASYNET_WIREGUARD_OBFS=true \ diff --git a/docs/clients.md b/docs/clients.md index 6777195..540167a 100644 --- a/docs/clients.md +++ b/docs/clients.md @@ -93,7 +93,6 @@ sudo bash easynet-singbox-client.sh doctor |----------|--------------| | Shadowsocks 2022 (BLAKE3) | Clash Verge Rev ≥1.6, Shadowrocket ≥2.2.38, sing-box ≥1.8 | | Xray XHTTP 传输 | Clash Verge Rev ≥1.7, sing-box ≥1.11 | -| Xray Fragment 混淆 | 客户端自动适配(服务端配置) | | Hysteria2 Port Hopping | 需客户端支持 `port_hopping` 参数 | | WireGuard AmneziaWG | Clash Verge Rev (支持 jc/jmin/jmax), AmneziaWG 客户端 | diff --git a/docs/deployment.md b/docs/deployment.md index 03c9668..ccc85ea 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -29,7 +29,7 @@ | 协议 | 推荐度 | 核心混淆 | 适用场景 | |------|--------|----------|----------| -| Xray+Reality | 高 | REALITY + Fragment + XHTTP | 抗封锁优先,TLS 指纹模仿 + 包分片抗 ML | +| Xray+Reality | 高 | REALITY + XHTTP | 抗封锁优先,TLS 指纹模仿 + 包分片抗 ML | | Hysteria2 | 高 | Salamander + Port Hopping | UDP/QUIC 场景,端口跳变抗封锁 | | Shadowsocks 2022 | 中 | BLAKE3-AES-256-GCM | 兼容性场景,2022 Edition 强加密 | | WireGuard (+Amnezia obfs) | 中 | Jc/Jmin/Jmax 垃圾包填充 | 启用混淆后适合中转、低延迟、独立 VPN | @@ -59,7 +59,6 @@ | TLS 指纹模仿 (REALITY) | ✅ | — | — | — | | HTTP/3 伪装 (XHTTP) | ✅ | ✅ (QUIC) | — | — | | XMUX 多路复用 | ✅ | — | — | — | -| 包分片混淆 (Fragment) | ✅ | — | — | — | | QUIC 混淆 (Salamander) | — | ✅ | — | — | | 端口跳变 (Port Hopping) | — | ✅ | — | — | | 垃圾包填充 (AmneziaWG) | — | — | — | ✅ | @@ -67,11 +66,9 @@ ### 协议混淆增强 -协议混淆增强(Fragment 和 AmneziaWG 默认已启用,以下为显式配置示例): +协议混淆增强(AmneziaWG 默认已启用,以下为显式配置示例): ```bash -# Xray+Reality: Fragment 包分片(默认 tlshello,设空禁用) -EASYNET_REALITY_FRAGMENT=tlshello # Xray+Reality: XHTTP/HTTP3 传输(需客户端支持,默认 tcp) EASYNET_REALITY_TRANSPORT=xhttp # Xray+Reality: XMUX 多路复用并发数(0 = 禁用,默认) @@ -84,8 +81,6 @@ EASYNET_HYSTERIA2_PORT_HOPPING=20000-30000 EASYNET_WIREGUARD_OBFS=true ``` -> **⚠️ XHTTP 与 Fragment 不兼容:** `EASYNET_REALITY_FRAGMENT`(包分片)只能在 `EASYNET_REALITY_TRANSPORT=tcp` 时生效。 -> 如果 `TRANSPORT=xhttp`,系统会自动跳过 Fragment 设置并在日志中提示。切换传输方式后,无需手动删除对方的环境变量。 ## 快速部署 @@ -349,12 +344,10 @@ openssl x509 -in /etc/ssl/easynet-edge/fullchain.crt -noout -enddate | `EASYNET_REALITY_PORT` | Xray 监听端口 | `8443` | | `EASYNET_REALITY_DEST` | REALITY 目标/伪装服务器地址 | `www.microsoft.com:443` | | `EASYNET_REALITY_SERVER_NAME` | 逗号分隔的 SNI 名称列表 | `www.microsoft.com,cloudflare.com,www.apple.com` | -| `EASYNET_REALITY_TRANSPORT` | 传输层协议:`tcp` 或 `xhttp`(HTTP/3 伪装);与 Fragment 包分片互斥 | `tcp` | -| `EASYNET_REALITY_XHTTP_MODE` | XHTTP 多路复用模式:`auto` / `stream-one` / `stream-up` / `packet-up` | `auto` | +| `EASYNET_REALITY_TRANSPORT` | 传输层协议:`tcp` 或 `xhttp`(HTTP/3 伪装) | `tcp` | +| `EASYNET_REALITY_XHTTP_MODE` | XHTTP 多路复用模式:`stream-one` / `auto` / `stream-up` / `packet-up` | `stream-one` | | `EASYNET_REALITY_XMUX_CONCURRENCY` | XMUX 多路复用并发数(`0` = 禁用) | `0` | -| `EASYNET_REALITY_FRAGMENT` | Finalmask 包分片混淆(仅 TCP 传输有效;与 XHTTP 不兼容,设值但传输为 xhttp 时自动跳过) | `tlshello` | -| `EASYNET_REALITY_FRAGMENT_LENGTH` | 分片长度范围(字节) | `100-200` | -| `EASYNET_REALITY_FRAGMENT_INTERVAL` | 分片间隔(毫秒) | `10-20` | +| `EASYNET_REALITY_XMUX_CONN_IDLE` | XMUX 空闲连接超时(秒) | `60` | | `EASYNET_XRAY_INSTALL_SHA256` | Xray 安装脚本 SHA256 校验(可选) | 未设置(不校验) | #### Hysteria2 diff --git a/docs/troubleshooting-guide.md b/docs/troubleshooting-guide.md index d7c7102..6926a4c 100644 --- a/docs/troubleshooting-guide.md +++ b/docs/troubleshooting-guide.md @@ -174,5 +174,4 @@ ss -ltnup wg show ufw status verbose jq '.inbounds[0].streamSettings.network' /usr/local/etc/xray/config.json -jq '.inbounds[0].streamSettings.fragmentSettings' /usr/local/etc/xray/config.json ``` diff --git a/scripts/protocols/xray-reality/deploy.sh b/scripts/protocols/xray-reality/deploy.sh old mode 100755 new mode 100644 index 8efcc96..7e34541 --- a/scripts/protocols/xray-reality/deploy.sh +++ b/scripts/protocols/xray-reality/deploy.sh @@ -38,7 +38,8 @@ write_xray_config_template() { "settings": { "clients": [ { - "id": "$uuid" + "id": "$uuid", + "flow": "xtls-rprx-vision" } ], "decryption": "none" @@ -134,11 +135,9 @@ configure_reality() { mkdir -p "$XRAY_DIR" local transport="${EASYNET_REALITY_TRANSPORT:-tcp}" - local xhttp_mode="${EASYNET_REALITY_XHTTP_MODE:-auto}" + local xhttp_mode="${EASYNET_REALITY_XHTTP_MODE:-stream-one}" local xmux_concurrency="${EASYNET_REALITY_XMUX_CONCURRENCY:-0}" - local fragment="${EASYNET_REALITY_FRAGMENT:-tlshello}" - local fragment_length="${EASYNET_REALITY_FRAGMENT_LENGTH:-100-200}" - local fragment_interval="${EASYNET_REALITY_FRAGMENT_INTERVAL:-10-20}" + local xmux_conn_idle="${EASYNET_REALITY_XMUX_CONN_IDLE:-60}" if [ -f "$XRAY_DIR/config.json" ] && grep -q "privateKey" "$XRAY_DIR/config.json"; then log_info "检测到已有的 Xray 配置,跳过生成新密钥,直接使用现有配置。" @@ -162,20 +161,15 @@ configure_reality() { write_xray_config_template "$transport" "$UUID" "$PORT" "$existing_dest" "$existing_server_names_arr" "$xhttp_mode" - # Inject preserved keys + optional fragment/xmux + # Inject preserved keys + optional xmux JQ_ARGS=(--arg pk "$existing_private_key" --arg sid "$existing_short_id") # shellcheck disable=SC2016 # $pk, $sid etc. are jq --arg vars, not bash JQ_FILTER='.inbounds[0].streamSettings.realitySettings.privateKey = $pk | .inbounds[0].streamSettings.realitySettings.shortIds[0] = $sid' - if [ -n "$fragment" ] && [ "$transport" = "tcp" ]; then - JQ_ARGS+=(--arg f_packets "$fragment" --arg f_length "$fragment_length" --arg f_interval "$fragment_interval") - # shellcheck disable=SC2016 # $f_* are jq --arg vars - JQ_FILTER+=' | .inbounds[0].streamSettings.fragmentSettings = { "packets": $f_packets, "length": $f_length, "interval": $f_interval }' - fi if [ "$transport" = "xhttp" ] && [ "$xmux_concurrency" -gt 0 ] 2>/dev/null; then - JQ_ARGS+=(--argjson xmux_cc "$xmux_concurrency") - # shellcheck disable=SC2016 # $xmux_cc is a jq --argjson var - JQ_FILTER+=' | .inbounds[0].streamSettings.xhttpSettings.xmux = { "concurrency": $xmux_cc, "connIdleTime": 60 }' + JQ_ARGS+=(--argjson xmux_cc "$xmux_concurrency" --argjson xmux_idle "$xmux_conn_idle") + # shellcheck disable=SC2016 # $xmux_cc, $xmux_idle are jq --argjson vars + JQ_FILTER+=' | .inbounds[0].streamSettings.xhttpSettings.xmux = { "concurrency": $xmux_cc, "connIdleTime": $xmux_idle }' fi jq "${JQ_ARGS[@]}" "$JQ_FILTER" "$XRAY_DIR/config.json" > "${XRAY_DIR}/config.json.tmp" && \ mv "${XRAY_DIR}/config.json.tmp" "$XRAY_DIR/config.json" @@ -185,20 +179,6 @@ configure_reality() { return # Skip subsequent logic (already fully handled) fi - # Update Fragment settings on existing config (only applies to tcp transport) - if [ -n "$fragment" ]; then - if [ "$transport" = "tcp" ]; then - jq --arg packets "$fragment" \ - --arg length "$fragment_length" \ - --arg interval "$fragment_interval" \ - '.inbounds[0].streamSettings.fragmentSettings = { "packets": $packets, "length": $length, "interval": $interval }' \ - "$XRAY_DIR/config.json" > "${XRAY_DIR}/config.json.tmp" && mv "${XRAY_DIR}/config.json.tmp" "$XRAY_DIR/config.json" - log_info "Fragment 混淆已启用: packets=$fragment length=$fragment_length interval=$fragment_interval" - systemctl restart xray - else - log_info "注意: Fragment 不适用于 $transport 传输方式,已跳过(仅支持 tcp)" - fi - fi else UUID=$(generate_uuid) DEST="${EASYNET_REALITY_DEST:-www.microsoft.com:443}" @@ -229,34 +209,22 @@ configure_reality() { SHORT_ID=$(openssl rand -hex 8) - # Single combined jq — inject privateKey, shortId, optional fragment, optional xmux + # Single combined jq — inject privateKey, shortId, optional xmux JQ_ARGS=(--arg pk "$PRIVATE_KEY" --arg sid "$SHORT_ID") # shellcheck disable=SC2016 # $pk, $sid etc. are jq --arg vars, not bash JQ_FILTER='.inbounds[0].streamSettings.realitySettings.privateKey = $pk | .inbounds[0].streamSettings.realitySettings.shortIds[0] = $sid' - if [ -n "$fragment" ] && [ "$transport" = "tcp" ]; then - JQ_ARGS+=(--arg f_packets "$fragment" --arg f_length "$fragment_length" --arg f_interval "$fragment_interval") - # shellcheck disable=SC2016 # $f_* are jq --arg vars - JQ_FILTER+=' | .inbounds[0].streamSettings.fragmentSettings = { "packets": $f_packets, "length": $f_length, "interval": $f_interval }' - fi if [ "$transport" = "xhttp" ] && [ "$xmux_concurrency" -gt 0 ] 2>/dev/null; then - JQ_ARGS+=(--argjson xmux_cc "$xmux_concurrency") - # shellcheck disable=SC2016 # $xmux_cc is a jq --argjson var - JQ_FILTER+=' | .inbounds[0].streamSettings.xhttpSettings.xmux = { "concurrency": $xmux_cc, "connIdleTime": 60 }' + JQ_ARGS+=(--argjson xmux_cc "$xmux_concurrency" --argjson xmux_idle "$xmux_conn_idle") + # shellcheck disable=SC2016 # $xmux_cc, $xmux_idle are jq --argjson vars + JQ_FILTER+=' | .inbounds[0].streamSettings.xhttpSettings.xmux = { "concurrency": $xmux_cc, "connIdleTime": $xmux_idle }' fi jq "${JQ_ARGS[@]}" "$JQ_FILTER" "$XRAY_DIR/config.json" > "${XRAY_DIR}/config.json.tmp" && \ mv "${XRAY_DIR}/config.json.tmp" "$XRAY_DIR/config.json" log_info "配置文件已生成 (transport=$transport)" - if [ -n "$fragment" ]; then - if [ "$transport" = "tcp" ]; then - log_info "Fragment 混淆已启用: packets=$fragment length=$fragment_length interval=$fragment_interval" - else - log_info "注意: Fragment 不适用于 $transport 传输方式,已跳过(仅支持 tcp)" - fi - fi if [ "$transport" = "xhttp" ] && [ "$xmux_concurrency" -gt 0 ] 2>/dev/null; then log_info "XMUX 多路复用已启用: concurrency=$xmux_concurrency" fi @@ -291,8 +259,6 @@ show_config() { public_ip=$(get_public_ip) transport=$(jq -r '.inbounds[0].streamSettings.network // "tcp"' "$config_file") xhttp_mode=$(jq -r '.inbounds[0].streamSettings.xhttpSettings.mode // "auto"' "$config_file") - fragment_packets=$(jq -r '.inbounds[0].streamSettings.fragmentSettings.packets // empty' "$config_file") - fragment_length=$(jq -r '.inbounds[0].streamSettings.fragmentSettings.length // empty' "$config_file") echo "" echo "========================================" @@ -307,14 +273,12 @@ show_config() { echo "传输方式: $transport" if [ "$transport" = "xhttp" ]; then echo "XHTTP 模式: $xhttp_mode" - fi - if [ -n "$fragment_packets" ]; then - echo "Fragment 混淆: $fragment_packets / $fragment_length" + echo "流控: xtls-rprx-vision" fi echo "" if [ "$transport" = "xhttp" ]; then - config_url="vless://$uuid@$public_ip:$PORT?encryption=none&security=reality&sni=$server_names&fp=chrome&pbk=$public_key&sid=$short_id&type=xhttp&mode=$xhttp_mode#EasyNet-Reality" + config_url="vless://$uuid@$public_ip:$PORT?encryption=none&security=reality&sni=$server_names&fp=chrome&pbk=$public_key&sid=$short_id&type=xhttp&flow=xtls-rprx-vision&mode=$xhttp_mode#EasyNet-Reality" else echo "流控: xtls-rprx-vision" config_url="vless://$uuid@$public_ip:$PORT?encryption=none&security=reality&sni=$server_names&fp=chrome&pbk=$public_key&sid=$short_id&type=tcp&flow=xtls-rprx-vision#EasyNet-Reality" diff --git a/scripts/protocols/xray-reality/export.sh b/scripts/protocols/xray-reality/export.sh old mode 100755 new mode 100644 index 0bf507e..db57994 --- a/scripts/protocols/xray-reality/export.sh +++ b/scripts/protocols/xray-reality/export.sh @@ -43,7 +43,7 @@ export_xray_reality_metadata() { # Build URI if [ "$transport" = "xhttp" ]; then - uri="vless://$uuid@$public_ip:$port?encryption=none&security=reality&sni=$sni&fp=chrome&pbk=$public_key&sid=$short_id&type=xhttp&mode=$xhttp_mode#EasyNet-Reality" + uri="vless://$uuid@$public_ip:$port?encryption=none&security=reality&sni=$sni&fp=chrome&pbk=$public_key&sid=$short_id&type=xhttp&flow=xtls-rprx-vision&mode=$xhttp_mode#EasyNet-Reality" else uri="vless://$uuid@$public_ip:$port?encryption=none&security=reality&sni=$sni&fp=chrome&pbk=$public_key&sid=$short_id&type=tcp&flow=xtls-rprx-vision#EasyNet-Reality" fi @@ -93,8 +93,6 @@ export_xray_reality_metadata() { '.["xhttp-opts"].xmux = { concurrency: $xmux_cc }') fi clash_json=$(echo "$clash_json" | jq -s '.[0] * .[1]' - <(echo "$xhttp_opts")) - # flow: xtls-rprx-vision only works with tcp transport — remove from xhttp - clash_json=$(echo "$clash_json" | jq 'del(.flow)') fi metadata_json=$(jq -n \ diff --git a/tests/test_config_generation.bats b/tests/test_config_generation.bats index 240a37c..53efb34 100644 --- a/tests/test_config_generation.bats +++ b/tests/test_config_generation.bats @@ -205,9 +205,6 @@ source_protocol() { [ "$output" = "xtls-rprx-vision" ] run jq -r '.inbounds[0].port' "$config" [ "$output" = "8443" ] - # Verify default TCP transport also includes fragmentSettings - run jq -r '.inbounds[0].streamSettings.fragmentSettings.packets // empty' "$config" - [ "$output" = "tlshello" ] # Verify xhttpSettings not present in tcp mode run jq '.inbounds[0].streamSettings.xhttpSettings // empty' "$config" [ -z "$output" ] @@ -236,7 +233,7 @@ source_protocol() { [ "$output" = "deadbeefdeadbeef" ] } -@test "Xray: xhttp transport generates config with xhttpSettings and no fragment" { +@test "Xray: xhttp transport generates config with xhttpSettings" { export EASYNET_REALITY_TRANSPORT=xhttp source_protocol "$PROJECT_ROOT/scripts/protocols/xray-reality/deploy.sh" @@ -257,43 +254,10 @@ source_protocol() { run jq -r '.inbounds[0].streamSettings.network' "$config" [ "$output" = "xhttp" ] run jq -r '.inbounds[0].streamSettings.xhttpSettings.mode // empty' "$config" - [ "$output" = "auto" ] - # fragmentSettings must NOT exist in xhttp mode - run jq '.inbounds[0].streamSettings.fragmentSettings // empty' "$config" - [ -z "$output" ] - # flow must NOT exist (xtls-rprx-vision only works with tcp) - run jq '.inbounds[0].settings.clients[0].flow // empty' "$config" - [ -z "$output" ] -} - -@test "Xray: xhttp transport with fragment env var set skips fragmentSettings" { - export EASYNET_REALITY_TRANSPORT=xhttp - export EASYNET_REALITY_FRAGMENT=tlshello - export EASYNET_REALITY_FRAGMENT_LENGTH=40-120 - export EASYNET_REALITY_FRAGMENT_INTERVAL=5-15 - source_protocol "$PROJECT_ROOT/scripts/protocols/xray-reality/deploy.sh" - - eval 'openssl() { - if [ "$1" = "rand" ] && [ "$2" = "-hex" ]; then echo "aabbccddeeff0011" - else command openssl "$@"; fi - }' - export -f openssl - export XRAY_BIN="xray" - - run configure_reality - [ "$status" -eq 0 ] || { echo "# configure_reality failed: $output" >&3; return 1; } - - local config="${XRAY_DIR:-$TMP_DIR/xray}/config.json" - [ -f "$config" ] || { echo "# config.json not found" >&3; return 1; } - - run jq -r '.inbounds[0].streamSettings.network' "$config" - [ "$output" = "xhttp" ] - # fragmentSettings must be absent even though var is set - run jq '.inbounds[0].streamSettings.fragmentSettings // empty' "$config" - [ -z "$output" ] - # flow must NOT exist (xtls-rprx-vision only works with tcp) - run jq '.inbounds[0].settings.clients[0].flow // empty' "$config" - [ -z "$output" ] + [ "$output" = "stream-one" ] + # flow must exist for xhttp+vision + run jq -r '.inbounds[0].settings.clients[0].flow // empty' "$config" + [ "$output" = "xtls-rprx-vision" ] } @test "Xray: transport switch from tcp to xhttp preserves UUID and keys" { @@ -356,16 +320,12 @@ source_protocol() { run cat "$XRAY_DIR/public.key" [[ "$output" == *"PublicKeyBase64"* ]] - # fragmentSettings must not exist after switching to xhttp - run jq '.inbounds[0].streamSettings.fragmentSettings // empty' "$XRAY_DIR/config.json" - [ -z "$output" ] - # xhttpSettings should exist run jq -r '.inbounds[0].streamSettings.xhttpSettings.mode // empty' "$XRAY_DIR/config.json" - [ "$output" = "auto" ] - # flow must NOT exist after switch (xtls-rprx-vision only works with tcp) - run jq '.inbounds[0].settings.clients[0].flow // empty' "$XRAY_DIR/config.json" - [ -z "$output" ] + [ "$output" = "stream-one" ] + # flow must persist after switch to xhttp + run jq -r '.inbounds[0].settings.clients[0].flow // empty' "$XRAY_DIR/config.json" + [ "$output" = "xtls-rprx-vision" ] } # ------------------------------------------------------------------------- diff --git a/tests/test_protocol_metadata.bats b/tests/test_protocol_metadata.bats index cf776fc..e05634f 100644 --- a/tests/test_protocol_metadata.bats +++ b/tests/test_protocol_metadata.bats @@ -126,12 +126,12 @@ JSON [[ "$output" == *"type=xhttp"* ]] run jq -r '.client.clash.network' "$meta" [ "$output" = "xhttp" ] - # xhttp URI must NOT contain flow=xtls-rprx-vision + # xhttp URI must contain flow=xtls-rprx-vision run jq -r '.client.uri' "$meta" - [[ "$output" != *"flow="* ]] - # xhttp Clash config must NOT have flow field + [[ "$output" == *"flow=xtls-rprx-vision"* ]] + # xhttp Clash config must have flow field run jq -r '.client.clash.flow // empty' "$meta" - [ -z "$output" ] + [ "$output" = "xtls-rprx-vision" ] } # --- Shadowsocks ---