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.
156 lines
11 KiB
Markdown
156 lines
11 KiB
Markdown
# 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。
|