12 Commits

Author SHA1 Message Date
eli ca5d8ee84a refactor: CDN 清单生成器移交 gc-toolkit/cdn-list
这份清单存在的唯一原因是 GCP 把 CDN 出站单独计价(Carrier Peering,$0.08/GiB 从
第一字节起算、无 200 GiB 免费额度),而消费它的 VPC 拒绝规则 cdn-egress-deny 本身
也是 GCP 资源。所以生成器归 gc-toolkit 更合理。

- 删掉本仓库的 tools/refresh-cdn-lists.sh(功能已合并进 gc-toolkit,避免两份分叉)
- 脚本头部与 docs/cdn-list-audit.md 改成指路 gc-toolkit
- 清单内容本次无变化:v4 271 条 / v6 193 条,与 gc-toolkit 生成的 canonical 文件一致

迁移后 gc-toolkit 那边多了两件事:生成 canonical 的 cdn-cidrs-v4/v6.txt,
以及 --vpsetup 参数(一次运行同时同步本仓库的内嵌清单与 v6 文件)。
2026-11-08 13:12:01 +08:00
eli 37db75413d fix(cdn-list): 换用上游仓库自带的合并版,并补上一直缺失的 Akamai IPv6
两件事,都是之前搞错的。

一、v4 清单的源头找错了处理方式
实际数据源是 SecOps-Institute/Akamai-ASN-and-IPs-List(按 40 个 ASN 从 RADb 拉 BGP
宣告,每小时更新)。内嵌的那份是拿它的 akamai_ip_list.lst 手工合并、且是一两年前的
快照。复核发现:
  - 该仓库本来就提供合并好的 akamai_ip_cidr_blocks.lst(235 条,与原始 9462 行
    collapse 后完全等价),根本不用自己合;
  - 旧快照已漂移:265 段 / 209,106 地址 Akamai 已不再宣告(白绕),
    49 段 / 13,504 地址是新宣告的(没绕 → 一直在按 $0.08/GiB 计费)。
换成 235 条后,v4 总数 402 → 269。

二、Akamai 的 IPv6 一直是空的
之前写"没有官方来源所以故意不收"——前提就错了:同一个仓库有 akamai_ipv6_list.lst
(3849 行,未合并,需要自己 collapse 成 183 条)。也就是说在此之前所有走 v6 的
Akamai 边缘流量都没被绕行,一直在按 CDN 计费。现已补上:v6 从 10 条 → 193 条
(CF 7 + Fastly 3 + Akamai 183)。

配套改动:
  - 脚本头部写清【怎么更新这份清单】+ CDN_LIST_AUDIT_DATE,超过 180 天打告警;
  - 测试断言从"单一区间"改成三层:总数 269±10%、CF 15 条逐条核对、Fastly 抽样;
    v6 侧补 Akamai 主段与条数下限,防止再次被删空;
  - docs/cdn-list-audit.md 重写,记录依据(GCP 按目的地网络自动判定、Akamai 无官方
    公开清单的穷举证据)、20 万地址漂移、以及 5 万条集合的实测无差异。

线上已同步:两台主机渲染结果 md5 一致(c8f16ac8…);两个 GCP 项目的 egress 拒绝规则
都改成同一份 269 段(原来是 gcoregon 旧 368 段、gcoregon-2 中间版 402 段,两边还不一致)。
真实 Akamai 边缘地址抽验命中:www.akamai.com / www.microsoft.com 的 AAAA 都落在
2600:1400::/24 内。
2026-11-08 11:17:55 +08:00
eli bb27fb24dc feat(cdn-list): v4 清单 188 → 402 条,覆盖面上补到 GCP 那份的规模
现网 188 条(CF 15 + Fastly 19 + Akamai/Linode 154)是 2026-09 审计的产物;
GCP 项目里的 egress 拒绝规则用的是更完整的一份(Akamai 368 条)。这次把主机侧
对齐到同一份,两边从此一致——不一致会导致"漏绕(按 CDN 计费)"或"误丢包"。

实测结论(写进 docs/cdn-list-audit.md):
- nft 是"一个集合一次查找",netlink 字节码里只有一条 lookup,不是几百条 cmp;
- 集合 188 条 vs 40 条端到端无差异,扩到 50,188 条仍然测不出差异;
- 所以"精简到几十条"没有任何性能收益,只会把无辜地址丢给上游 Oracle。

顺带证伪了两个之前的口头推测:
- GCP 单条防火墙规则没有 256 个 destinationRanges 上限(实测 3000 个也能建);
- 精确挖掉 Linode 反而使条数从 368 涨到 381(减法切碎片),故不挖。

测试断言从单一区间 [180,200] 换成三层保护:总数 402±10%、Cloudflare 15 条逐条核对、
Fastly 抽样核对——CF/Fastly 是官方权威值,不该漂移;Akamai 是 BGP 派生的浮动值。

两台机器已重跑并验证:CDN 出口走 Oracle,非 CDN 仍走 GCP 自己。
2026-11-08 10:38:45 +08:00
eli 48e6bd4313 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
eli 4138484128 fix: WS route stripped /scilad prefix (handle_path) — xray 404'd every handshake
Root-caused with live probes: WS upgrade to /scilad returned 404 on both
direct and CDN paths. handle_path strips the prefix before proxying, but
xray wsSettings.path=/scilad expects it intact. Replaced with a site-level
@websockets matcher (path + Upgrade headers) + path-preserving reverse_proxy
in the main block; deleted the redundant handle_path /scilad* in the CDN
block (it also shadowed reverse_proxy @wspaths into dead code).

Verified with real caddy + mock upstream: WS handshake gets 101 and the
upstream receives /scilad unstripped.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 23:27:59 +08:00
eli f4616814b8 fix: disable Caddy auto_https redirects shadowing hy2 ACME challenge route
Caddy injects runtime auto-redirect routes (host-matched, ahead of the
catch-all http:// site, not excluding ACME challenge paths) for domains
it manages certs for — invisible in static 'caddy adapt' output, so the
previous live test could not reproduce it. hy2's tokens got 308'd to 443
(LE: 'Invalid response from https://DOMAIN/...: 404' via masquerade).

Verified bidirectionally with real caddy v2.11.4: without the option the
challenge path gets 308 (exact production repro), with it 200 from hy2.
Live test now extracts the global block + http block + a tls-internal
managed site block to reproduce production conditions permanently.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 20:27:05 +08:00
eli fbda86236a docs: add 2026-08 protocol landscape review to design doc
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 13:36:19 +08:00
eli 1b500d5a69 feat: default hysteria2 cert to ACME HTTP-01 via Caddy port-80 proxy
hy2 certmagic HTTP-01 listens only on 127.0.0.1:9180 (altPort); Caddy's
port-80 block reverse-proxies /.well-known/acme-challenge/ to it. Non-wildcard
DOMAIN cert, auto-renewed and hot-applied, no DNS API token needed, no new
public ports. TLS-ALPN is unusable (CA only connects to TCP 443 = Xray) and
reusing Caddy's cert files needs permission/copy hooks, so HTTP-01 is the
minimal stable default. HY2_CERT_MODE=dns keeps Cloudflare DNS-01 wildcard
as fallback (CF_API_TOKEN required only in dns mode).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 12:57:49 +08:00
eli b8ffb98277 docs: usage docs for caddy+xray+hysteria script, archive original templates
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 00:37:04 +08:00
eli 0f14089e6d docs: condense implementation plan (keep tasks + key info)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 00:15:49 +08:00
eli 4dbf477e8b Add optional CDN_DOMAIN second site block to design
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-23 23:47:29 +08:00
eli 5721c6f85b Add design doc for caddy+xray+hysteria2 setup script
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-23 23:44:25 +08:00