Files
vpsetup/docs/superpowers/specs/2026-08-23-caddy-xray-hysteria-design.md
eli 15d91b2b7a fix: drop XHTTP extra/xPadding block — breaks every link-imported client (400)
End-to-end matrix with real xray v26.3.27 + caddy:
- server with extra padding + plain client: 400 (direct AND via caddy)
- server without extra + plain client: 204 (direct AND via caddy, TLS h2)

Padding placement/key names must match on both ends, but share links cannot
carry these params and mainstream clients can't configure them — so a
server-side-only extra block rejects every real client. Removed from the
template along with XPADDING_HEADER/XPADDING_KEY.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 23:55:38 +08:00

214 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 设计文档:setup_caddy_xray_hysteria.sh
日期:2026-08-23
状态:已获用户批准(含 XHTTP 增量)
## 目标
新增单一自包含 bash 脚本 `setup_caddy_xray_hysteria.sh`,在一台 Debian/Ubuntu VPS 上自动部署:
- **Caddy**cloudsmith apt 源):监听 `127.0.0.1:8003`PROXY protocol wrapper,接收 Xray REALITY 回落流量),提供伪装站点(反代 engineersblog.net)、WebSocket 路径 `/scilad` → Xray:54442、XHTTP 路径 → Xray:8080、portainer 路径 → localhost:9000`http://` 站跳转 https:443
- **Xray**(官方 install-release.sh):443 端口 VLESS+REALITYxtls-rprx-visiondest 8003 回落 Caddyxver 1 发 PROXY protocol);54442 端口 VLESS+WS(仅 127.0.0.1);8080 端口 VLESS+XHTTP(仅 127.0.0.1
- **Hysteria2**get.hy2.sh):8443/UDPCloudflare DNS-01 申请 `*.BASE_DOMAIN` 泛域名证书,密码认证,伪装代理
- **nftables**UDP 20000-30000 端口跳跃 redirect 到 8443
## 架构与执行流程
分阶段函数,按序执行:
```
preflight → collect_params → install_caddy → install_xray →
install_hysteria → setup_nftables → validate_configs →
enable_services → print_summary
```
- `set -euo pipefail`;脚本注释用中文(仓库约定)
- 参数:**环境变量优先,`/dev/tty` 交互兜底**(支持 `wget|bash`,同 setup_warp_zerotrust.sh 约定);无 tty 且无 env 时,无默认值的必填项报错退出
- 模板以 **quoted heredoc + 独立占位符 token**`__DOMAIN__` 等)内嵌,写出时 `sed``|` 分隔符)统一替换;随机值限定 hex/base64url 字符集,无 sed 特殊字符风险
- `ACTION=install`(默认)/ `ACTION=uninstall`
- 组件开关:`SKIP_CADDY` / `SKIP_XRAY` / `SKIP_HYSTERIA` / `SKIP_NFTABLES`;跳过有依赖关系的组件时打印警告(如 REALITY 回落依赖 Caddy
- 供应链说明:本脚本按用户手工命令添加 cloudsmith 第三方 apt 源并管道执行 Xray/hy2 官方安装脚本——与 setup_warp_zerotrust.sh 的"仅发行版签名源"约束不同,头部注释明确说明
## 变量清单
| 变量 | 来源 | 默认值 | 去向 |
|---|---|---|---|
| `DOMAIN` | 用户输入(必填,正则校验) | 无 | Caddy 站点、REALITY serverNames |
| `BASE_DOMAIN` | 推导 `${DOMAIN#*.}` | — | Hysteria 泛域名 `*.BASE_DOMAIN` |
| `EMAIL` | 输入可覆盖 | `admin@BASE_DOMAIN` | Caddy 全局 email + hy2 ACME email |
| `CF_API_TOKEN` | 输入(hy2 必填,无默认) | 无 | hy2 DNS-01 |
| `XRAY_UUID` | 自动生成可覆盖 | `xray uuid`(回退 `/proc/sys/kernel/random/uuid` | REALITY inbound |
| `XRAY_WS_UUID` | 同上 | 同上 | WS inbound |
| `XRAY_XHTTP_UUID` | 同上 | 同上 | XHTTP inbound |
| `XRAY_PRIVATE_KEY` | 自动生成可覆盖 | `xray x25519`,解析兼容新旧输出(`grep -iE 'private'` 取私钥;公钥取 `public` 或新版 `password` 行) | REALITY |
| `XRAY_SHORT_ID` | 自动生成可覆盖 | 随机 8 位 hex(`/dev/urandom` | REALITY shortIds |
| `XHTTP_PATH` | 自动生成可覆盖 | `/` + 8 位随机 hex | XHTTP inbound path + Caddy 路径匹配 |
| `XHTTP_PORT` | env | `8080` | XHTTP inbound(仅 127.0.0.1 |
| `XPADDING_HEADER` / `XPADDING_KEY` | 自动生成可覆盖 | 随机稀有字符串(不用 X-Cache 等常见名) | XHTTP `extra` padding 混淆 |
| `HY2_PASSWORD` | 自动生成可覆盖 | `openssl rand -hex 16` | hy2 auth |
| `HY2_MASQUERADE_URL` | 输入可覆盖 | `https://${DOMAIN}/` | hy2 masquerade |
| `CDN_DOMAIN` | env/输入(可选,默认空=不启用) | 空 | Caddy 第二个站点块(橙云 CDN 回源专用) |
| `DIST_UPGRADE` | env | 默认跳过;设 1 执行 `apt-get dist-upgrade -y` | — |
**凭据持久化**:最终参数写入 `/etc/caddy-xray-hy2/setup.env`mode 600),重跑时自动 source 作为默认值,避免重跑重新生成 UUID 导致已分发客户端失效。
## 组件细节
### Caddy
- cloudsmith gpg key → `/usr/share/keyrings/caddy-stable-archive-keyring.gpg`;源 → `/etc/apt/sources.list.d/caddy-stable.list`(覆盖写同路径,天然幂等)→ `apt update && apt install -y caddy`
-`/etc/caddy/Caddyfile`:替换 `__DOMAIN__``__EMAIL__``__XHTTP_PATH__``__XHTTP_PORT__`;模板原样保留 portainer 路径、`/scilad` WS 路径、php_fastcgi、伪装反代 engineersblog.net
- XHTTP 路由新增:`@xhttppaths path __XHTTP_PATH__ __XHTTP_PATH__/*` + `reverse_proxy @xhttppaths 127.0.0.1:__XHTTP_PORT__`(不 strip path
- **可选 CDN 站点块**`CDN_DOMAIN` 非空时,Caddyfile 追加第二个站点块——只含 XHTTP 路径、`/scilad` WS 路径与同样的伪装反代。该域名应配为橙云:CF 边缘 → 回源 443 → Xray REALITYCF 不是 REALITY 客户端)→ 无差别回落 Caddy → 按 SNI 进入该站点块。证书用 ACME **HTTP-01**CF 透传 challenge 请求;TLS-ALPN 不可过 CFCaddy 自动回退)。Xray 侧零改动。REALITY 与 CDN 在同一主机名上互斥(REALITY 须灰云直连,CDN 须橙云),所以 CDN 域名必须独立;注意 CDN 不隐藏源站 IP(REALITY 域名本就直连暴露),其价值是 VPS IP 被封时的备用通道
### Xray
- `bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install`
-`/usr/local/etc/xray/config.json`,三个 inbound
1. 443 VLESS+REALITY+visiondest `8003` 原样保留,由 `xray run -test` 校验兜底;xver 1serverNames=`__DOMAIN__`privateKey/shortIds/uuid 替换)——修正模板第 15 行 `""uuid-uuid` 多引号笔误
2. 54442 VLESS+WS,仅 127.0.0.1(保留兼容旧客户端)
3. `__XHTTP_PORT__`(默认 8080VLESS+XHTTP,仅 127.0.0.1`mode: auto`path=`__XHTTP_PATH__`CDN 时代 padding 混淆(XTLS/BBS #25 调研结论,必须放 `extra` 才生效):
```json
"extra": {
"xPaddingObfsMode": true,
"xPaddingMethod": "tokenish",
"xPaddingPlacement": "queryInHeader",
"xPaddingHeader": "__XPADDING_HEADER__",
"xPaddingKey": "__XPADDING_KEY__"
}
```
`__XPADDING_HEADER__`/`__XPADDING_KEY__` 自动生成(随机稀有字符串,不用常见的 X-Cache),可覆盖
- outbounds/routing/dns 保留模板原样(含 geoip:cn 阻断、私有网段阻断、bittorrent 阻断等)
### Hysteria2
- `bash <(curl -fsSL https://get.hy2.sh/)` 安装(自带 hysteria 用户与 hysteria-server.service
- 写 `/etc/hysteria/config.yaml``acme.domains: ["*.BASE_DOMAIN"]`、email、cloudflare_api_token、`dir: /etc/hysteria/acme_certs`、password、masquerade proxy url
- `chown -R hysteria:hysteria /etc/hysteria/`ACME 证书目录需可写)
### nftables
- `mkdir -p /etc/nftables` → 覆盖写 `/etc/nftables/hysteria.nft``table inet hysteria_nat`udp dport 20000-30000 redirect to :8443
- `/etc/nftables.conf`:先 grep 检查再追加 `include "/etc/nftables/hysteria.nft"`(幂等);文件不存在则新建
- `systemctl enable --now nftables`
## 校验与错误处理
- 启动前校验:`caddy validate --config /etc/caddy/Caddyfile`、`xray run -test -config /usr/local/etc/xray/config.json`;任一失败即中止,不 enable 带病服务;hy2 无离线校验命令,靠启动后 `systemctl is-active` 检查
- 启动顺序无关依赖:Caddy127.0.0.1:8003)与 Xray443)互相独立,回落连接由 Xray 发起
- 本脚本不改 resolv.conf,无需 cleanup trap
- 自检(结尾):`curl -skI https://${DOMAIN}` 验证 REALITY→Caddy 回落伪装、`ss -lun` 查 UDP 8443、`nft list table inet hysteria_nat`、三服务 `is-active`
## 卸载(ACTION=uninstall
1. `systemctl disable --now` caddy / xray / hysteria-serverbest-effort
2. 删 `/etc/caddy/Caddyfile`、`/usr/local/etc/xray/config.json`、`/etc/hysteria/config.yaml`
3. 删 `/etc/nftables.conf` 中的 include 行(sed 精确删除)、删 `hysteria.nft`、`nft delete table inet hysteria_nat`best-effort
4. `PURGE=1` 时:`apt purge caddy`、删 cloudsmith 源与 keyring、Xray 官方脚本 `remove --purge`、hy2 官方脚本 `--remove`
5. 默认保留 `/etc/caddy-xray-hy2/setup.env``KEEP_ENV` 未设且 PURGE=1 时删除
## 输出
结尾打印客户端参数汇总表 + 分享链接:
- REALITY`vless://XRAY_UUID@DOMAIN:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=DOMAIN&fp=chrome&pbk=公钥&sid=shortId&type=tcp#...`
- WS(旧):`vless://WS_UUID@DOMAIN:443?...&type=ws&path=/scilad#...`
- XHTTP`vless://XHTTP_UUID@DOMAIN:443?...&type=xhttp&path=XHTTP_PATH&mode=auto#...`(备注:如需套 CF CDN 把该域名改橙云;REALITY 必须灰云)
- XHTTPCDN 版,仅 CDN_DOMAIN 非空时):同上但地址/SNI 换为 `CDN_DOMAIN`
- Hysteria2`hysteria2://HY2_PASSWORD@IP:8443/?mport=20000-30000&sni=BASE_DOMAIN#...`(公网 IP 用 curl best-effort 探测)
## 调研结论(2026-08,影响设计的部分)
1. **REALITY 不能走 CDN 是原理性限制**(客户端须直连校验被偷的目标证书;CDN 边缘终止 TLS 后证书是 CF 的),REALITY 域名须保持灰云。参照 chika0801/Xray-examples#49。同一主机名上 REALITY 与 CDN 前置互斥 → CDN 通道必须用独立的第二个子域名(可选 `CDN_DOMAIN`
2. **WS 是上一代方案**XHTTP 是 XTLS 官方继任者("XHTTP: Beyond REALITY"Xray-core discussion #4113);WS inbound 保留仅为兼容
3. **2026 年 CF 开始自动检测 XHTTP 的 x_padding 特征并发滥用警告**XTLS/BBS #25、Xray-core #5967/#5414):缓解=最新 Xray(修 UA+ padding 混淆放 `extra` + 稀有自定义 padding header/key + 非根路径;可选 VLESS-Enc`mlkem768x25519plus.random`)与客户端 ECH,本设计默认不开 Enc(客户端兼容性),在输出中提示
## 测试
- `bash -n setup_caddy_xray_hysteria.sh` + `shellcheck`
- 真机验证只能在目标 VPS:检查三服务 active、`curl -skI https://DOMAIN` 拿到伪装站响应、hy2 UDP 8443 监听、nft 表生效、ACME 证书签出(caddy HTTP-01 走 80 端口,hy2 走 CF DNS-01
## 明确不做(YAGNI
- 不装 php-fpm / portainer(模板路径保留,未使用时 Caddy 仅在该路径被访问时才报 502)
- 不做 hy2 官方安装脚本之外的安装渠道
- 不做多用户/多 UUID 管理
- 不自动改 CF DNS 记录(域名解析与灰/橙云状态由用户自行配置,脚本输出提示)
## 修订(2026-08-24):hy2 证书默认改 HTTP-01
原设计 hy2 只有 Cloudflare DNS-01 通配符。修订为 `HY2_CERT_MODE` 二选一:
- `http`**默认**):hy2 内置 certmagic 走 HTTP-01`listenHost: 127.0.0.1` +
`http.altPort: 9180`(仅回环,不开公网端口),Caddy 80 端口块把
`/.well-known/acme-challenge/` 反代给它(Caddy 只拦截自己正在签的 token,
互不影响)。签 DOMAIN 非通配证书;certmagic 自动续期且新证书对新握手热生效。
不再需要 CF_API_TOKEN。
- `dns`(备选):维持原 Cloudflare DNS-01 通配符,需 CF_API_TOKEN。
被否决的方案:
- ACME TLS-ALPNCA 只连 TCP 443(改 altPort 无效,需 SNI proxy),443 是 Xray
REALITY 的 → 不可行。
- `tls:` 复用 Caddy 证书:hy2 LocalCertificateLoader 按 mtime 热加载(新连接生效,
无需重启),技术上可行;但 /var/lib/caddy 为 0700 caddy:caddy,需 systemd.path
监听+复制+chownCaddy 无内置续期 hookevents exec 是第三方插件),存储路径还随
CA 目录名变化。部件最多,不选。
## 调研结论补充(2026-08-24,近半年动态评估)
**Xray-core v26.2→v26.7**
- v26.3.27 Xray 原生支持完整 Hysteria2 入站/传输层——本架构跑独立 hy2 进程,不受影响
- REALITY:非 443 端口与"偷苹果"目标会被官方警告(易封 IP);本架构 443 + 自有 Caddy 回落,合规。新版服务端自动探测 target maxUselessRecords,无需配置
- TLS `allowInsecure` 已移除并于 2026-06-01 硬禁用(GFW MITM 能力背景);本脚本分享链接本就不含该参数
- XHTTP CDN 检测绕过选项(PR #5414)即 extra 块 xPadding 混淆,已实现;UA 改动态 Chrome,服务端零配置受益
- VLESS 后量子加密(mlkem768x25519plus)与 TLS ECH 已可用,但要求客户端同步升级;REALITY 本身不暴露真实 SNI,默认不开,属可选增强
**Hysteria2 2.8→2.12**
- 2.8.0 内置端口范围监听(listen :20000-50000,自动配防火墙)——但主端口会变成范围首端口、需 root/CAP_NET_ADMIN;维持现有手动 nftables(文档认可的方式,主端口 8443 保留)
- 2.8.0 拥塞控制可配(bbr/reno + 三档 profile),默认 bbr standard 已合理
- 2.9.2 Gecko 混淆(实验性)与 2.10.0 ECH:均需客户端同开/额外密钥分发,默认不开
- 2.11.0 客户端 Chrome QUIC 指纹拟态默认开启;2.12.1 服务端 stateless reset 加速移动端重连——均为零配置受益
- 2.8.2/2.9.2 重要安全修复:安装脚本始终拉最新版,天然覆盖
- 2.12.0 mimicXDP 伪装 TCP):UDP 全封场景专用,需内核模块+root,默认不加
**AnyTLS**2025 年新协议(sing-box/mihomo 系),主打低开销与 TLS-in-TLS 缓解,
与 REALITY/hy2 是并列选项而非替代;Xray-core 不支持其服务端,引入需新增
sing-box 组件与供应链。现有 REALITY(隐蔽)+ hy2(速度)+ XHTTPCDN 备用)
覆盖已完整,不加入。
**总评**:服务端配置无需功能性更新;保持安装最新版即可获得全部安全修复。
---
## 修订(2026-08-24):auto_https disable_redirects —— 第三层 challenge 拦截
上线后 hy2 HTTP-01 仍失败,LE 报 `Invalid response from https://DOMAIN/...: 404`
https + 无端口 + 404 = 经 443 落到伪装反代)。根因:Caddy 为**它自己管理证书的域名**
DOMAIN/CDN_DOMAIN)在运行时向 80 端口服务器注入自动 HTTP→HTTPS 重定向路由,
该路由带 host 匹配器、排在 catch-all `http://` 站点块的路由之前,且**不排除**
ACME challenge 路径——Caddy 自己的 token 由更靠前的拦截器应答,hy2 的 token
被 308 到 443。此路由在 `caddy adapt` 静态输出中不可见,只抽取 `http://` 块的
活测试无法复现;必须带一个被管理证书的站点块才触发。
修复:全局块加 `auto_https disable_redirects`(证书管理不受影响;80 端口重定向
本就由 `http://` 块手动接管)。已用真实 caddy v2.11.4 双向验证:无此行 → challenge
308(精确复现线上);有此行 → challenge 200 来自 hy2、其他路径 301。
活测试已改为抽取全局块+http 块并附加 `tls internal` 受管站点块,永久防回归。
## 修订(2026-08-24 之二):handle_path 剥离 /scilad 导致 WS 全挂;ALPN 去掉 h3
线上实测 WS 握手(完整 Upgrade 头)直连/CDN 均 404:原模板的
`handle_path /scilad{...}` 会剥离路径前缀,xray wsSettings.path=/scilad 收到 `/`
直接 404。改为站点级 `@websockets` 匹配器(path + Upgrade 头)+ reverse_proxy
保路径转发,CDN 块删除冗余有害的 `handle_path /scilad*`(它还把 `@wspaths`
变成死代码)。已用真实 caddy + mock upstream 验证:101 且上游收到 `/scilad`。
同批:Caddy tls 块 `alpn h3 h2 http/1.1` 是从直Face模板继承的隐患——本监听器只收
Xray 回落的 TCP 流量,实测客户端 offer h3 时 Caddy 在 TCP 上协商出 h3 直接挂死,
改为 `alpn h2 http/1.1`。所有 TLS 客户端链接显式带 fp=chrome。
## 修订(2026-08-24 之三):移除 XHTTP extra/xPadding 块(客户端全部 400
线上 XHTTP 直连+CDN 全挂。本机用真实 xray v26.3.27 服务端+客户端+caddy 做
端到端对照实验:服务端带 extraxPaddingObfsMode/tokenish/queryInHeader
时,无 padding 参数的客户端(=所有从分享链接导入的客户端,链接无法携带这些
参数)直连 xray 也收到 **400**;去掉 extra 后直连与经 caddyTLS h2)全链路
均 204 通过。结论:padding 的位置/键名必须两端一致,服务端单方面开启即破坏
互操作,故从模板删除(XPADDING_HEADER/XPADDING_KEY 一并移除)。