ca5d8ee84a
这份清单存在的唯一原因是 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 文件)。
229 lines
9.8 KiB
Markdown
229 lines
9.8 KiB
Markdown
# CDN 网段清单:来源、依据与实测结论
|
||
|
||
状态:已采纳并上线(2026-11-08)
|
||
影响:`setup_cdn_bypass_tproxy.sh` 内嵌 v4 清单 + `cdn-cidrs-v6.txt`,
|
||
以及 GCP 项目里的 egress 拒绝规则(`cf-no-cdn` / `fastly-no-cdn` / `no-akamai-0` /
|
||
`no-akamai-1`;新项目 `vps-free-xu11` 里是单条 `cdn-egress-deny`)
|
||
|
||
---
|
||
|
||
## 0. 这份清单干什么用
|
||
|
||
GCP 的 CDN 出站走 `Network Data Transfer Out via Carrier Peering Network` 科目:
|
||
**$0.08/GiB、从第一字节起算、没有 200 GiB 免费额度**(官方原话,见
|
||
[Network Connectivity pricing](https://cloud.google.com/network-connectivity/pricing):
|
||
"Egress charges for CDN Interconnect appear on the invoice as Network Data Transfer Out via
|
||
Carrier Peering Network")。普通互联网出站则有 200 GiB 免费。
|
||
|
||
CDN 绕行就是把"目的地属于 CDN 网络"的出站流量 TPROXY 到上游 Oracle 节点,
|
||
让 GCP 看到的目的是 Oracle 而不是 CDN,从而落回有免费额度的科目。
|
||
|
||
---
|
||
|
||
## 1. 依据是什么(本文档最重要的部分)
|
||
|
||
### 1.1 GCP 怎么判定"这是 CDN 流量"
|
||
|
||
按**目的地属于哪个网络**自动判定,不看你配了什么、也没有 IP 清单可查:
|
||
|
||
> If your CDN provider is already part of the program, you don't have to do anything.
|
||
> Traffic from supported Google Cloud locations to your CDN provider **automatically** takes
|
||
> advantage of the direct connection.
|
||
> —— [CDN Interconnect overview](https://docs.cloud.google.com/network-connectivity/docs/cdn-interconnect)
|
||
|
||
所以**真正的 ground truth(GCP 内部的路由分类表)我们看不到**,只能靠账单科目反推。
|
||
|
||
### 1.2 Cloudflare / Fastly:官方公开,权威
|
||
|
||
| 来源 | 条数(v4 / v6) |
|
||
|---|---|
|
||
| `https://www.cloudflare.com/ips-v4`、`/ips-v6` | 15 / 7 |
|
||
| `https://api.fastly.com/public-ip-list`(`addresses` / `ipv6_addresses`) | 19 / 3 |
|
||
|
||
这两家没有争议,直接用。
|
||
|
||
### 1.3 Akamai:没有官方公开清单,只能用 ASN/BGP 派生
|
||
|
||
**已穷举确认**(不是关键词搜索):
|
||
|
||
- 官方 TechDocs 里的 CIDR 列表全是**客户作用域**的:Site Shield、Origin IP ACL、
|
||
Client Access Control —— 给的是"你这个合同的回源入口",不是"Akamai 全部边缘",要登录。
|
||
- 官方 GitHub 组织 `github.com/akamai` **207 个仓库**全量枚举,没有任何网段仓库;
|
||
`github.com/linode` **118 个**同样没有。
|
||
- 第三方研究文档结论一致:
|
||
[firehol/update-ipsets 调研](https://github.com/firehol/update-ipsets/blob/master/.agents/sow/research/sow-0017/soft-cdn-edges.md)
|
||
—— "Akamai CDN: **No official public unauthenticated bulk feed**;Authenticated API exists
|
||
for customers;建议 BGP enumeration of AS20940 + AS16625"。
|
||
|
||
**实际采用的数据源**:[`SecOps-Institute/Akamai-ASN-and-IPs-List`](https://github.com/SecOps-Institute/Akamai-ASN-and-IPs-List)
|
||
|
||
- 做法:按 40 个 Akamai ASN(`AS20940` CDN 边缘、`AS16625`、…、`AS63949` Linode 云主机)
|
||
从 RADb 拉 BGP 宣告记录,拼成 IP 列表。
|
||
- 更新频率:**每小时**(`Last-Checked-At` 文件,2026-11-08 08:03 IST)。
|
||
- 提供的文件:
|
||
|
||
| 文件 | 说明 |
|
||
|---|---|
|
||
| `akamai_ip_list.lst` | v4,9462 行,**按注册原样、互相重叠**(有 `2.16.0.0/13` 又有 `2.16.0.0/24`) |
|
||
| `akamai_ip_cidr_blocks.lst` | v4,**已合并好**,235 条 —— 与上面那个 collapse 后完全等价 |
|
||
| `akamai_ipv6_list.lst` | v6,3849 行,**同样未合并**(仓库没提供 v6 合并版,必须自己合) |
|
||
| `akamai_asn_list.lst` | 收录的 40 个 ASN |
|
||
|
||
**关键教训**:之前内嵌的那份是拿 `akamai_ip_list.lst` **手工合并**、且是**一两年前的快照**。
|
||
2026-11-08 复核发现:
|
||
|
||
- 已过时:265 段 / **209,106 个地址** Akamai 已不再宣告(还在绕,纯浪费);
|
||
另有 49 段 / **13,504 个地址**是新宣告的(当时没绕 → 在按 $0.08/GiB 计费)。
|
||
- 合并方式不必要:手动合并出 368 条,而仓库自带的合并版只要 **235 条**,覆盖完全相同。
|
||
|
||
⇒ **不要自己合并 v4**,直接用 `akamai_ip_cidr_blocks.lst`;v6 仓库没给合并版,自己 collapse。
|
||
|
||
### 1.4 Linode 在里面,怎么办
|
||
|
||
`akamai_asn_list.lst` 含 `AS63949`(Akamai Connected Cloud / Linode)。那是**客户虚拟机**,
|
||
不是 CDN 边缘,严格说不该算 CDN 目的地。
|
||
|
||
2026-11-08 评估过"精确挖掉":
|
||
|
||
| | 条数 |
|
||
|---|---|
|
||
| 挖之前 | 368 |
|
||
| 精确挖掉后 | **381**(减法会把整块切成碎片) |
|
||
|
||
而且挖完必须同步改 GCP 拒绝规则,否则那些站点会被 VPC **直接丢包**(不是回退直连)。
|
||
净负收益 ⇒ **保留**。若将来"Linode 站点被绕行"这件事本身不可接受,再单独处理。
|
||
|
||
---
|
||
|
||
## 2. 条数要精简吗:不需要
|
||
|
||
### 2.1 这不是 iptables 规则链,是一个集合查表
|
||
|
||
`ip daddr { … 269 个 … } accept` 编译成**一条** lookup 指令。用
|
||
`nft --debug=netlink list chain ip cdn_bypass_mark output` 直接看到:
|
||
|
||
```
|
||
[ payload load 4b @ network header + 16 => reg 1 ]
|
||
[ lookup reg 1 set __set0 0x0 ]
|
||
```
|
||
|
||
不是 269 条 `cmp`。集合元素在 nft 用哈希表/红黑树存放
|
||
([官方 wiki](https://wiki.nftables.org/wiki-nftables/index.php/Sets)),区间集是 rbtree,
|
||
查找 O(log n)。
|
||
|
||
### 2.2 实测
|
||
|
||
在 `gcoregon-2` 上做 A/B,目标选**不在集合里**的站点(不经过代理,隔离查找本身),
|
||
每组 120 次,并**人为把集合灌到 5 万条**做灵敏度对照:
|
||
|
||
| 集合元素数 | 均值 | 中位 | P90 |
|
||
|---|---|---|---|
|
||
| 188 | 0.2108s | 0.2099s | 0.2164s |
|
||
| 488 | 0.2101s | 0.2094s | 0.2157s |
|
||
| 5,188 | 0.2112s | 0.2095s | 0.2154s |
|
||
| **50,188** | 0.2095s | 0.2085s | 0.2158s |
|
||
| 188(还原) | 0.2107s | 0.2101s | 0.2176s |
|
||
|
||
**5 万条都测不出差异** ⇒ 条数在性能上不是问题,不构成精简理由。
|
||
|
||
### 2.3 而且 GCP 也没有 256 条上限(之前的推测是错的)
|
||
|
||
曾推测 `no-akamai-0`(256) + `no-akamai-1`(112) 的拆分是因为单条规则上限 256。
|
||
实测:300 / 1000 / **3000** 个 `destinationRanges` 全部创建成功。
|
||
那个拆分是**人为的**,不是平台限制。
|
||
|
||
### 2.4 强行合并到"几十条"的代价
|
||
|
||
对 Akamai 段做 trie 上的精确 DP,得到"条数 ↔ 多绕地址"前沿:
|
||
|
||
| 条数 | 多绕地址 | 占全部公网 IPv4 |
|
||
|---|---|---|
|
||
| 368 | 0 | 0% |
|
||
| 300 | 186,048 | 0.004% |
|
||
| 256 | 1,624,863 | 0.038% |
|
||
| 210 | 14,755,006 | 0.34% |
|
||
| 150 | 124,964,718 | 2.9% |
|
||
| 50 | 1,157,258,014 | **27%** |
|
||
| 20 | 2,385,523,230 | **56%** |
|
||
|
||
"几十条"意味着把公网 27%~56% 的地址丢给 3 台 Oracle 转发 —— 不可接受。
|
||
|
||
---
|
||
|
||
## 3. 最终清单
|
||
|
||
| | 内容 | 条数 |
|
||
|---|---|---|
|
||
| v4 | CF 15 + Fastly 19 + Akamai 235(仓库自带合并版) | **269** |
|
||
| v6 | CF 7 + Fastly 3 + Akamai 183(自己 collapse) | **193** |
|
||
|
||
审计日期记录在脚本里的 `CDN_LIST_AUDIT_DATE`;超过 180 天未刷新会打印告警。
|
||
|
||
验证记录(2026-11-08):
|
||
|
||
- 两台机器渲染出的 `/etc/nftables/cdn-bypass.nft` **md5 一致**(`c8f16ac8…`)。
|
||
- 真实 Akamai 边缘地址能命中:`www.akamai.com` → `2600:1417:4400:26::17c9:673c` 命中
|
||
`2600:1400::/24`;`www.microsoft.com` → `2600:1417:4400:1188::356e` 同样命中
|
||
(微软是 Akamai 客户)。`www.apple.com` 的 AAAA 走移动线路,不在清单里,符合预期。
|
||
|
||
---
|
||
|
||
## 4. 怎么刷新
|
||
|
||
生成器已经挪到 **gc-toolkit** 的 `cdn-list` 子工具(这份清单存在的唯一原因是 GCP 计费,
|
||
VPC 拒绝规则本身也是 GCP 资源):
|
||
|
||
```bash
|
||
cd ../gc-toolkit
|
||
./cdn-list/refresh.sh # 只报告差异
|
||
./cdn-list/refresh.sh --apply --vpsetup ../vpsetup # 落盘并同步本仓库的内嵌清单
|
||
```
|
||
|
||
`.github/workflows/refresh-cdn-list.yml` 每天 04:00 UTC 自动跑一次(GitHub 托管 runner,
|
||
无需自备机器)。
|
||
|
||
历史:原先本仓库有过 `tools/refresh-cdn-lists.sh`,功能等价但只服务本仓库;已合并进
|
||
gc-toolkit,避免两份生成器分叉。
|
||
|
||
### 手工流程(脚本不可用时的兜底)
|
||
|
||
|
||
见 `setup_cdn_bypass_tproxy.sh` 头部注释的【怎么更新这份清单】。要点:
|
||
|
||
1. **先看仓库有没有在更新**:`curl -s $B/Last-Checked-At`
|
||
2. v4 用 `akamai_ip_cidr_blocks.lst`(已合并),**不要**拿 `akamai_ip_list.lst` 自己合
|
||
3. v6 用 `akamai_ipv6_list.lst`,**必须**自己 collapse(仓库没给合并版)
|
||
4. CF / Fastly 用官方两个 URL
|
||
5. 三个来源 cat 到一起 collapse,替换清单体,更新 `CDN_LIST_AUDIT_DATE`
|
||
|
||
---
|
||
|
||
## 5. 两侧必须一致
|
||
|
||
主机侧清单和 GCP 拒绝规则的网段集合必须是同一份。不一致会导致:
|
||
|
||
- 主机侧少了 → 那些 CDN 目的地直连出去,按 $0.08/GiB 计费(**漏绕**)
|
||
- GCP 侧多了 → 拒绝规则把本应经代理绕出去的流量挡住(**误丢包**)
|
||
|
||
---
|
||
|
||
## 6. 未完成
|
||
|
||
- `gcoregon` 所在项目(`western-watch-268811`)的四条拒绝规则仍是旧的 368 段快照,
|
||
未与新的 269 段对齐(主机的绕行名单已更新,所以现在是"主机新、GCP 旧")。
|
||
影响:老的 209k 段仍被 VPC 拒绝但主机已不再绕它们 → 那些目的地会丢包。
|
||
**需要尽快对齐**(改成单条 269 段即可,3000 条实测可行)。
|
||
- 清单是**内嵌**的(被封锁的机器在代理起来之前访问不了 cloudflare.com),
|
||
不会自动更新,只能靠 §4 的手工流程 + 180 天告警。
|
||
|
||
---
|
||
|
||
## 7. 本次纠正的错误认知
|
||
|
||
1. ❌ "GCP 单条规则上限 256 个网段" → 实测 3000 个也能建。
|
||
2. ❌ "Akamai v6 没有可用来源,所以故意不收" → 前提错了。该仓库有
|
||
`akamai_ipv6_list.lst`。之前 v6 只有 CF+Fastly 10 条,
|
||
**所有走 v6 的 Akamai 边缘流量都没被绕行**,一直在按 CDN 计费。
|
||
3. ❌ "精确挖掉 Linode 会更简洁" → 368 → 381,反而更多。
|
||
4. ❌ "清单 188 条够用" → 那是 1~2 年前的快照,已漂移约 20 万地址。
|