# 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 万地址。