Files
tamabox/outputs/edgeone-cloudflare-dual-cdn-guide.md
T
tamakyi e3e59e7c69 chore: baseline snapshot before moderation-strictness feature
Repo .git was damaged (refs/ missing, index truncated, object store near-empty).
Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak,
then full worktree committed as a rollback baseline.
2026-09-10 10:56:27 +08:00

156 lines
11 KiB
Markdown
Raw 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.
# TamaBox 双 CDN 接入指南:境内腾讯 EdgeOne + 境外 Cloudflare
> 目标:中国大陆访客走腾讯 EdgeOne 节点加速,境外访客走 Cloudflare 边缘网络加速,共用同一域名与源站。
> 代码前置:`internal/shared/ip.go` 自 **2026-09-03** 起在 `trust_proxy = true` 时信任 EdgeOne 权威客户端头 `EO-Client-IP`(等效 `CF-Connecting-IP`)。单 EdgeOne 或双 CDN 部署均可直接用本文件配套。
---
## 0. 总体架构
```
┌──────────────── DNS(DNSPod 智能解析)─────────────────┐
│ 线路「境内」 → CNAME → EdgeOne 分配的 CNAME │
│ 线路「境外」 → CNAME → Cloudflare for SaaS CNAME │
│ 线路「默认」 → CNAME → Cloudflare(未匹配线路兜底) │
└───────────────┬───────────────────┬────────────────────┘
▼ ▼
EdgeOne(腾讯,大陆节点) Cloudflare(全球边缘)
│ │
└─────► 源站(仅放行两家回源网段)◄──────┘
TamaBox (Flamego) + PG + Redis
```
核心思路:**CDN 各自接入,DNS 按线路分发**。两家 CDN 都不需要托管你的主域名 NS,主域名 NS 全程留在 DNSPod(腾讯云解析),靠「境内 / 境外 / 默认」三条线路把用户分给不同的 CNAME 目标。
## 1. 前置条件(逐项确认)
| # | 条件 | 说明 |
|---|------|------|
| 1 | 域名已 **ICP 备案** | EdgeOne 加速区域含「中国大陆 / 全球可用区」时的硬性要求,不可绕开 |
| 2 | 腾讯云账号已实名 | 开通 EdgeOne 需要 |
| 3 | Cloudflare 账号 | Free 套餐即可(见步骤 3 的 SaaS 说明) |
| 4 | **一个闲置域名(或其子域)用于 Cloudflare 承载 zone** | Cloudflare for SaaS 需要一个已托管在 CF(NS 迁过去)的域名作为“承载 zone”,主域名不需要迁 NS。没有就考虑末尾的备选方案 C |
| 5 | 源站公网可达 | 回源协议 / 端口按你的部署调整 |
## 2. 步骤 1:DNS 迁到 DNSPod 并建立分线路骨架
1. 在 DNSPod(腾讯云解析)添加你的域名,按提示把注册商处的 NS 改成 DNSPod 提供的 `xxx.dnspod.net` 两条。
2. 等解析生效后,先**建立保底记录**,避免切换期中断:
- 线路「默认」:`www` → CNAME → EdgeOne(或先保持源站 A 记录,逐步切);
- 建议把各记录 **TTL 临时调小(如 60 秒)**,切换完成后再调回。
3. ⚠️ 若根域(`@`)承载邮箱(存在 MX 记录),**不能**给 `@` 配 CNAME(与 MX 冲突)。此时只对 `www` / 业务子域做 CDN 加速,`@` 保持 A 记录直连或做 301 跳转。
## 3. 步骤 2:EdgeOne 接入(CNAME 模式)
1. 控制台 → 添加站点 → 输入主域名 → 选购套餐 → **加速区域:中国大陆可用区**(境外交给 Cloudflare)。
2. **接入模式选 CNAME 接入**(主域名 NS 在 DNSPod,不能用 NS 接入)。
3. 归属权验证:按控制台提示在 DNSPod 添加 TXT 或做文件验证。
4. 添加加速域名:在「域名服务 → 域名管理 → 添加域名」里填业务主机名(`www` 或 `@`)+ 源站 IP/域名。
5. 平台分配一个 CNAME 地址(形如 `xxx.edgeone.net`),**先记下来**,步骤 5 统一填到 DNSPod。
6. 证书:开启「站点加速 → HTTPS」自动证书(免费)。
## 4. 步骤 3:Cloudflare for SaaS(主域名不迁 NS 的关键)
Cloudflare Free/Pro/Business 套餐的普通接入要求把整个 zone 迁到 CF 的 NS,与分线路解析冲突。**Cloudflare for SaaS**(Free 套餐含 100 个自定义主机名免费)允许主域名不迁 NS、仅用 CNAME 挂到 CF:
1. 用闲置域名(如 `cf-host.example`)在 Cloudflare 添加 zone,并把该域名的 NS 迁到 Cloudflare(承载 zone,仅此域名迁)。
2. 承载 zone 内:`SSL/TLS → Custom Hostnames(SSL for SaaS)→ Add Custom Hostname`,填入你的**主域名**(如 `www.example.com`)。
3. 按提示完成归属验证:CF 会给出一条验证 TXT(加到 DNSPod)+ 一个 **CNAME 目标值**(形如 `xxx.1234.cdn.cloudflare.net`),记下来用于步骤 5 的境外线路。
4. 等待证书签发至状态 `Active`(通常数分钟到数小时)。之后 CF 自动管理该域名的证书与续期。
> 备选:若不想多养一个域名做承载 zone,可退回**方案 C**——EdgeOne 加速区域直接选「全球可用区」,一家覆盖全球,不做 Cloudflare(代价:境外节点质量与覆盖面不如 CF)。或评估 Cloudflare Business(约 $200/月)的官方 CNAME 接入。
## 5. 步骤 4:DNSPod 分线路记录(最终形态)
同一主机名、不同线路分别建行(全部 CNAME,避免 A/CNAME 冲突提示):
| 主机记录 | 线路 | 类型 | 记录值 | 说明 |
|---|---|---|---|---|
| `www` | 境内 | CNAME | `xxx.edgeone.net`(EO 分配) | 大陆走 EdgeOne |
| `www` | 境外 | CNAME | `xxx.…cdn.cloudflare.net`(SaaS 分配) | 境外走 Cloudflare |
| `www` | 默认 | CNAME | `xxx.…cdn.cloudflare.net` | 未匹配线路(如部分境外 DNS)兜底 |
> 验证:境内 `dig www.example.com`(或 `nslookup`)应解析出 EdgeOne CNAME;用境外 DNS(如 `dig @8.8.8.8`)应解析出 Cloudflare CNAME。不同递归节点可能有缓存,等待 TTL 过期后复测。
## 6. 步骤 5:源站锁定(安全前提,不可跳过)
`trust_proxy = true` 意味着应用信任 CDN 写来的客户端头,**前提是请求只能来自这两家 CDN**:
1. 从官方渠道取回源网段并配置防火墙 / 安全组,仅放行:
- Cloudflare 官方 IP 段:https://www.cloudflare.com/ips/ (`cloudflare.com/ips-v4`、`ips-v6`)
- EdgeOne 回源 IP 段:腾讯云 EdgeOne 文档「回源 IP 网段」页(会动态更新,建议做成定期同步或至少配置时核对)
2. 放行你的运维入口(SSH 管理 IP)与回源端口(80/443),其余一律拒绝直连。
3. 回归验证:跑一次仓库自带的 `dev/security-selfcheck.sh`,确认带伪造 `CF-Connecting-IP` 的直连请求已被防火墙拦下(应无法到达应用)。
## 7. 步骤 6:真实 IP 传递(EdgeOne 侧关键配置!)
EdgeOne **默认不携带** `EO-Client-IP`,必须在控制台开启,否则经 EdgeOne 的请求到源站后拿不到客户端真实 IP(自动封禁会误伤所有大陆访客):
- 站点级:`站点加速(全局)→ 网络优化 → 客户端 IP 头部配置 → 开启`,**头部名称务必填 `EO-Client-IP`**(代码固定读这个名字,大小写敏感)。
- 或域名级:`规则引擎 → 创建规则(Host 匹配该域名)→ 操作:客户端 IP 头部 → 开启 → 名称填 `EO-Client-IP``。
Cloudflare 侧无需额外配置:`CF-Connecting-IP` 默认就有且恒被 CF 覆盖为真实客户端 IP。
## 8. 步骤 7:防“借道伪造”剥离规则(双 CDN 特有安全约束)
两家并存时出现新的伪造面:**攻击者借道 CDN-A 时携带 CDN-B 的权威头**。例如客户端经 Cloudflare 访问时自行塞一个 `EO-Client-IP: 任意IP`,若 CF 透传该头到源站,代码会信任它 → 可绕过限流 / 自动封禁 / 单 IP 投票限制,或嫁祸他人。因此必须在两家 CDN 的**回源请求**上剥离对方权威头:
| CDN | 剥离动作 | 位置 |
|---|---|---|
| Cloudflare | 回源前删除请求头 `EO-Client-IP` | `Rules → Transform Rules → Modify Request Header`(Free 套餐可用,规则数有限但只建一条即可) |
| EdgeOne | 回源前删除请求头 `CF-Connecting-IP` | `规则引擎 → 操作:修改回源请求头 → 删除 CF-Connecting-IP`(或站点全局回源请求头设置) |
> `X-Forwarded-For` / `X-Real-IP` 不需要在此剥离:代码只在 TCP 对端落入 `trusted_proxies` 时才解析它们,两家 CDN 边缘 IP 不在清单内,天然不可信。
>
> 验证方法:分别经两家 CDN 发起带伪造头的请求(如 `curl -H 'EO-Client-IP: 203.0.113.66' https://www.example.com/`),在源站访问日志里确认最终到达的请求**不包含**伪造的对方头,且应用记录的客户端 IP 为你的真实出口 IP。
## 9. 步骤 8:应用配置(conf/app.ini)
```ini
[security]
trust_proxy = true
; trusted_proxies 仅用于“你自己的”反代/LB(如本机 nginx 127.0.0.1),
; 双 CDN 场景不需要也不应把两家 CDN 的全量网段填进来(代码直接信任 CF-Connecting-IP / EO-Client-IP)。
trusted_proxies =
[server]
; 若用防火墙按网段放行回源,可继续监听 :443;若希望更严,可只监听本机 + 本机回源代理。
listen =
```
行为(2026-09-03 起):`trust_proxy = true` 时 `ResolveClientIP` 依次信任 `CF-Connecting-IP` → `EO-Client-IP` → (仅当 TCP 对端在 `trusted_proxies` 内)解析 XFF/X-Real-IP → 最后回退 RemoteAddr。
## 10. 步骤 9:缓存与动态内容
TamaBox 是动态应用:
- 两侧都**不要缓存 HTML / 页面 / API 响应**;只缓存静态资源。
- EdgeOne:缓存规则命中 `/static/*` 与带版本号资源即可;
- Cloudflare:`Caching → Cache Rules` 对 `/static/*` 开缓存,其余 `Bypass`。
- 若将来启用 WebSocket 长连接:Cloudflare Free 支持;EdgeOne 需在站点配置里显式开启 WebSocket。
- 注意两侧的 TLS 版本 / HTTP/3 行为差异属正常现象,与应用无关。
## 11. 上线切换与回滚
1. 先切「默认」线路 → 观察 15~30 分钟(错误率、日志中的真实 IP 是否正确)。
2. 再切「境内」线路(EdgeOne CNAME 生效)→ 用大陆网络(或大陆 DNS 递归查询)验证。
3. 最后切「境外」线路到 Cloudflare SaaS CNAME → 境外访问验证 + 证书状态 `Active`。
4. 回滚:把对应线路的 CNAME 改回原记录即可,秒级生效(DNS TTL 决定传播速度)。
## 12. 故障排查速查
| 现象 | 检查 |
|---|---|
| 大陆访问慢 / 未走 EO | DNSPod「境内」线路是否已配 EO CNAME;`nslookup` 从大陆递归查询结果 |
| 境外访问失败 / 证书错误 | SaaS Custom Hostname 状态是否 `Active`;境外线路 CNAME 是否拼错 |
| 自动封禁误封 / 限流全走同一个 IP | EO「客户端 IP 头部」是否开启且名称是否为 `EO-Client-IP`;`trust_proxy` 是否 true |
| 应用日志里出现伪造 IP | 两家剥离规则是否生效;防火墙是否真的挡住了直连 |
| 根域无法配 CNAME | 根域有 MX → 只加速 `www`/业务子域,`@` 保持 A 记录 |
## 13. 相关代码与文档
- 代码改动:`internal/shared/ip.go`(`EO-Client-IP` 信任分支)、`internal/shared/ip_test.go`(新增 4 条用例)、`internal/conf/static.go` 与 `conf/app.sample.ini`(注释同步)。
- 单 CDN Cloudflare 加固参考:`outputs/cloudflare-origin-lockdown-guide.md`。
- EdgeOne 客户端 IP 头部(腾讯官方文档,会更新):「携带客户端 IP 头部回源」。
- Cloudflare for SaaS 官方文档:developers.cloudflare.com → SSL/TLS → SSL for SaaS。