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

16 KiB
Raw Permalink Blame History

设计文档:setup_caddy_xray_hysteria.sh

日期:2026-08-23 状态:已获用户批准(含 XHTTP 增量)

目标

新增单一自包含 bash 脚本 setup_caddy_xray_hysteria.sh,在一台 Debian/Ubuntu VPS 上自动部署:

  • Caddycloudsmith apt 源):监听 127.0.0.1:8003PROXY protocol wrapper,接收 Xray REALITY 回落流量),提供伪装站点(反代 engineersblog.net)、WebSocket 路径 /scilad → Xray:54442、XHTTP 路径 → Xray:8080、portainer 路径 → localhost:9000http:// 站跳转 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
  • Hysteria2get.hy2.sh):8443/UDPCloudflare DNS-01 申请 *.BASE_DOMAIN 泛域名证书,密码认证,伪装代理
  • nftablesUDP 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.envmode 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-01CF 透传 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.1mode: autopath=__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),可覆盖
  • outbounds/routing/dns 保留模板原样(含 geoip:cn 阻断、私有网段阻断、bittorrent 阻断等)

Hysteria2

  • bash <(curl -fsSL https://get.hy2.sh/) 安装(自带 hysteria 用户与 hysteria-server.service
  • /etc/hysteria/config.yamlacme.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.nfttable inet hysteria_natudp dport 20000-30000 redirect to :8443
  • /etc/nftables.conf:先 grep 检查再追加 include "/etc/nftables/hysteria.nft"(幂等);文件不存在则新建
  • systemctl enable --now nftables

校验与错误处理

  • 启动前校验:caddy validate --config /etc/caddy/Caddyfilexray 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.nftnft delete table inet hysteria_natbest-effort
  4. PURGE=1 时:apt purge caddy、删 cloudsmith 源与 keyring、Xray 官方脚本 remove --purge、hy2 官方脚本 --remove
  5. 默认保留 /etc/caddy-xray-hy2/setup.envKEEP_ENV 未设且 PURGE=1 时删除

输出

结尾打印客户端参数汇总表 + 分享链接:

  • REALITYvless://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#...
  • XHTTPvless://XHTTP_UUID@DOMAIN:443?...&type=xhttp&path=XHTTP_PATH&mode=auto#...(备注:如需套 CF CDN 把该域名改橙云;REALITY 必须灰云)
  • XHTTPCDN 版,仅 CDN_DOMAIN 非空时):同上但地址/SNI 换为 CDN_DOMAIN
  • Hysteria2hysteria2://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-Encmlkem768x25519plus.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-01listenHost: 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,默认不加

AnyTLS2025 年新协议(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 做 端到端对照实验:服务端带 extraxPaddingObfsMode/tokenish/queryInHeader 时,无 padding 参数的客户端(=所有从分享链接导入的客户端,链接无法携带这些 参数)直连 xray 也收到 400;去掉 extra 后直连与经 caddyTLS h2)全链路 均 204 通过。结论:padding 的位置/键名必须两端一致,服务端单方面开启即破坏 互操作,故从模板删除(XPADDING_HEADER/XPADDING_KEY 一并移除)。