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>
16 KiB
设计文档: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+REALITY(xtls-rprx-vision,dest 8003 回落 Caddy,xver 1 发 PROXY protocol);54442 端口 VLESS+WS(仅 127.0.0.1);8080 端口 VLESS+XHTTP(仅 127.0.0.1)
- Hysteria2(get.hy2.sh):8443/UDP,Cloudflare 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 路径、/sciladWS 路径、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 路径、/sciladWS 路径与同样的伪装反代。该域名应配为橙云:CF 边缘 → 回源 443 → Xray REALITY(CF 不是 REALITY 客户端)→ 无差别回落 Caddy → 按 SNI 进入该站点块。证书用 ACME HTTP-01(CF 透传 challenge 请求;TLS-ALPN 不可过 CF,Caddy 自动回退)。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:- 443 VLESS+REALITY+vision(dest
8003原样保留,由xray run -test校验兜底;xver 1;serverNames=__DOMAIN__;privateKey/shortIds/uuid 替换)——修正模板第 15 行""uuid-uuid多引号笔误 - 54442 VLESS+WS,仅 127.0.0.1(保留兼容旧客户端)
__XHTTP_PORT__(默认 8080)VLESS+XHTTP,仅 127.0.0.1,mode: auto,path=__XHTTP_PATH__;CDN 时代 padding 混淆(XTLS/BBS #25 调研结论,必须放extra才生效):"extra": { "xPaddingObfsMode": true, "xPaddingMethod": "tokenish", "xPaddingPlacement": "queryInHeader", "xPaddingHeader": "__XPADDING_HEADER__", "xPaddingKey": "__XPADDING_KEY__" }__XPADDING_HEADER__/__XPADDING_KEY__自动生成(随机稀有字符串,不用常见的 X-Cache),可覆盖
- 443 VLESS+REALITY+vision(dest
- 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检查 - 启动顺序无关依赖:Caddy(127.0.0.1:8003)与 Xray(443)互相独立,回落连接由 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)
systemctl disable --nowcaddy / xray / hysteria-server(best-effort)- 删
/etc/caddy/Caddyfile、/usr/local/etc/xray/config.json、/etc/hysteria/config.yaml - 删
/etc/nftables.conf中的 include 行(sed 精确删除)、删hysteria.nft、nft delete table inet hysteria_nat(best-effort) PURGE=1时:apt purge caddy、删 cloudsmith 源与 keyring、Xray 官方脚本remove --purge、hy2 官方脚本--remove- 默认保留
/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 必须灰云) - XHTTP(CDN 版,仅 CDN_DOMAIN 非空时):同上但地址/SNI 换为
CDN_DOMAIN - Hysteria2:
hysteria2://HY2_PASSWORD@IP:8443/?mport=20000-30000&sni=BASE_DOMAIN#...(公网 IP 用 curl best-effort 探测)
调研结论(2026-08,影响设计的部分)
- REALITY 不能走 CDN 是原理性限制(客户端须直连校验被偷的目标证书;CDN 边缘终止 TLS 后证书是 CF 的),REALITY 域名须保持灰云。参照 chika0801/Xray-examples#49。同一主机名上 REALITY 与 CDN 前置互斥 → CDN 通道必须用独立的第二个子域名(可选
CDN_DOMAIN) - WS 是上一代方案,XHTTP 是 XTLS 官方继任者("XHTTP: Beyond REALITY",Xray-core discussion #4113);WS inbound 保留仅为兼容
- 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-ALPN:CA 只连 TCP 443(改 altPort 无效,需 SNI proxy),443 是 Xray REALITY 的 → 不可行。
tls:复用 Caddy 证书:hy2 LocalCertificateLoader 按 mtime 热加载(新连接生效, 无需重启),技术上可行;但 /var/lib/caddy 为 0700 caddy:caddy,需 systemd.path 监听+复制+chown,Caddy 无内置续期 hook(events 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 mimic(XDP 伪装 TCP):UDP 全封场景专用,需内核模块+root,默认不加
AnyTLS:2025 年新协议(sing-box/mihomo 系),主打低开销与 TLS-in-TLS 缓解, 与 REALITY/hy2 是并列选项而非替代;Xray-core 不支持其服务端,引入需新增 sing-box 组件与供应链。现有 REALITY(隐蔽)+ hy2(速度)+ XHTTP(CDN 备用) 覆盖已完整,不加入。
总评:服务端配置无需功能性更新;保持安装最新版即可获得全部安全修复。
修订(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 做 端到端对照实验:服务端带 extra(xPaddingObfsMode/tokenish/queryInHeader) 时,无 padding 参数的客户端(=所有从分享链接导入的客户端,链接无法携带这些 参数)直连 xray 也收到 400;去掉 extra 后直连与经 caddy(TLS h2)全链路 均 204 通过。结论:padding 的位置/键名必须两端一致,服务端单方面开启即破坏 互操作,故从模板删除(XPADDING_HEADER/XPADDING_KEY 一并移除)。