一行权限代码,如何让全球互联网“短暂熄火”?
Cloudflare 11·18 史上最严重故障全解析(深度技术长文 / 5000 字)
本文包含对 Cloudflare 技术架构、风险管理与工程流程的批判性解读,这些判断均基于可验证的文件内容,不代表绝对结论,仅作为技术视角下的专业分析。
目录
-
引言:一次“不是攻击”的互联网崩溃 -
全局事件时间线:从 11:05 到 17:06 -
故障缘起:一项权限变更如何引发系统级连锁反应 -
Cloudflare 架构解剖:为什么一个文件能拖垮全球? -
波动现象:为什么故障“周而复始”像遭遇 DDoS? -
多系统雪崩:Workers KV、Access、Turnstile、Dashboard 全部倒下 -
技术深度解析:特征文件是如何被“撕裂”的? -
更深层的问题:系统假设破碎与架构级隐患 -
行业对比:为什么 AWS/GCP 不会被一个特征文件拖死? -
前瞻推演:Cloudflare 下一步会强化哪些机制? -
这次事故对行业的真正意义 -
总结:最应该被记住的 80/20 关键洞察
01|引言:一次“不是攻击”的全球性互联网崩溃
2025 年 11 月 18 日,对于全球互联网而言是一段尴尬的时刻:
-
大量网站返回 HTTP 5xx 错误 -
Cloudflare 加速、CDN、安全、访问控制等核心功能全面异常 -
Cloudflare Dashboard 无法登录 -
Turnstile 验证组件失效 -
Workers KV 出现大面积故障 -
一些企业服务直接瘫痪数小时
当全球用户怀疑这是一次大型 DDoS 或供应链攻击时,Cloudflare 的官方解释却让所有工程师愣住了:
这不是攻击,而是一次权限变更引发的配置文件生成异常,导致核心代理集体崩溃。
一句话总结:
一次数据库权限优化 → 特征文件体积暴涨 → 系统崩溃 → 全球流量中断。
这次事件不仅是技术事故,更是一个绝佳的案例:
现代云基础设施越来越强大,同时也更脆弱——尤其是当它们高度集中自动化时。
02|从 11:05 到 17:06:这场事故如何一步步扩大?
下面的时间轴以 Cloudflare 官方公布为准,展示了整个事故链路:
timeline
title Cloudflare 11·18 大故障时间线
11:05 : ClickHouse 权限变更发布
11:20 : 异常特征文件开始生成
11:28 : 客户侧首次出现大规模 HTTP 5xx
11:32-13:05 : Workers KV 与 Access 大量失败
13:05 : KV 与 Access 启用应急绕过
13:37 : 决定回滚 Bot 管理特征文件
14:24 : 停止创建错误特征文件
14:30 : 核心网络恢复正常
17:06 : 全平台完全恢复
从这个过程可以清晰看到:
-
11:05 的改动是根源 -
11:20 出现异常,但症状并不明显 -
11:28 全球出现 HTTP 500 级错误 -
13:05 第一轮真正有效的应急操作 -
14:30 故障实质性解决 -
17:06 完整收尾
整个事故持续约 6 小时,其影响范围之广程度,是 Cloudflare 自 2019 年以来最严重的一次。
03|故障源头:ClickHouse 权限变更 × 无保护的内部生成文件
这次事故的起点,是 ClickHouse 的一个权限优化:
让内部查询可以看到 r0(底层表)的元数据,而不仅仅是 default 数据库下的表。
表面上是一次无害的小改动,甚至还提高了安全性和可审计性。
但问题在于:
Cloudflare 有一段代码默认假设查询结果只有 default 库中的列。
当权限扩大后:
-
system.columns查询结果突然包含了来自 default 和 r0 的重复列 -
这些重复列被当成“新特征”写入了 Bot Management 的 ML 特征文件 -
特征数量从 60+ 暴涨到 200+ -
想当然触发了 ML 模块的特征数量上限 -
代理软件(FL/FL2)加载文件时直接 panic
也就是说:
数据库权限变更 → 查询结果翻倍 → 文件体积翻倍 → 代理服务内存限制触发 → 全线崩溃。
一条链条一个环节事故,全球云服务就跟着倒下。
04|架构层面:为什么 Cloudflare 会被一个文件拖垮?
Cloudflare 的代理体系是高度统一的,大致如下:
flowchart TD
A[用户请求:浏览器、APP、API] --> B[TLS / HTTP 终止层]
B --> C[核心代理 Front Line (FL/FL2)]
C --> D[安全模块:WAF、Bot、DDoS、防火墙]
D --> E[缓存系统 / 源站请求 / Workers / R2]
其中,Bot 管理模块的特征文件位于:
-
核心代理的安全产品链条内部 -
每几分钟更新一次 -
同步推送到全球所有节点
它不是可选项,而是 Cloudflare 核心代理路径上的必经模块。
因此:
当特征文件导致模块崩溃时,代理直接中断,不存在兜底机制。
这是一种典型的“耦合型架构风险”:
-
没有 graceful degrade(优雅降级) -
没有模块隔离 -
没有 feature cap 防线 -
没有灰度部署(直接全网推送)
在这种架构下:
一旦共享模块出现错误,影响就是“全球级扩散”,而不是局部故障。
05|为什么故障会“周期性波动”?看起来像遭受 DDoS?
这次的一个“迷惑性特征”是:
-
故障并非一次性爆发 -
而是每 5 分钟一次,忽好忽坏
原因如下:
-
特征文件是由多个 ClickHouse 节点独立生成的 -
权限变更是逐步 rollout 的 -
所以一部分节点生成正确文件,一部分节点生成错误文件 -
文件通过内部管道在全球同步 -
正常/错误文件交替覆盖,系统就出现“周期性死亡”
这让故障排查团队一度误以为:
是攻击者在进行周期性、大规模、复杂的 DDoS 掩护。
同时,Cloudflare 自己的状态页也同时宕机,让大家更加确信“这是攻击”。
但实际原因只是一个巧合。
06|多系统雪崩:KV、Access、Turnstile、控制台接连倒下
从表象看,这次事故最先出问题的不是 Bot 模块,而是 Workers KV 和 Access。
原因很简单:
-
二者强依赖核心代理 -
核心代理崩溃 → KV 前端网关失败 -
KV 不可用 → Access 无法验证会话 -
Access 鉴权失败 → Turnstile 控制台登陆受阻 -
turnstile 无法加载 → Dashboard 用户无法登录
这是一个典型的链式依赖效应:
flowchart TD
A[Bot 特征文件异常] --> B[核心代理崩溃]
B --> C[KV 网关失败]
C --> D[Access 认证失败]
D --> E[Turnstile 加载失败]
E --> F[Cloudflare 控制台无法登录]
Cloudflare 表示:
Access 所有身份验证失败请求均正确记录,无未授权访问发生。
但对于用户端来说:
-
无法登录 Dashboard -
无法部署规则 -
无法修改配置 -
无法查看统计 -
无法排查自身业务错误
这类故障的破坏力远超一个“页面 500”。
07|深入核心:特征文件究竟被“膨胀”成了什么样?
特征文件的构建方式很简单:
-
遍历机器学习模型需要的所有字段 -
从 system.columns查询对应列 -
根据列属性生成特征配置
在正常情况下,查询结果大概长这样(示例):
| name | type |
|---|---|
| ip | String |
| user_agent | String |
| uri | String |
| … | … |
但在权限变更后:
-
default 表的字段出现一次 -
r0 底层表出现一次 -
一些字段甚至出现超过两次(Cloudflare 数据库结构较复杂)
于是,ML 特征生成逻辑误以为:
“你给了我这么多新字段,那我全都加入特征文件!”
结果爆炸:
-
60+ 特征 → 200+ -
远超代理为特征预分配的内存(limit = 200) -
Rust 代码触发 .unwrap()panic -
整个代理线程全部崩溃
Cloudflare 原始代码类似如下(伪示例):
if features.len() > MAX_FEATURES {
panic!("too many features");
}
这里缺乏:
-
防御式编程 -
自动降级策略 -
意外输入保护 -
文件体积检测
换句话说:
这是一个典型的“内部供应链文件”缺乏安全校验导致的系统性风险。
08|根本问题:这是一个“假设崩溃”事故,而不是 Bug
Cloudflare 的所有工程师都相信:
查询元数据时,只会返回 default 库的内容。
但权限变更让这个假设失效。
系统没有因为条件改变而进化,最终导致整个链条被击穿。
这不是 Bug,而是:
系统假设长期未更新 → 权限调整 → 假设崩溃 → 全局故障
这种事故极具代表性:
-
权限改变:看似安全 -
查询结果变更:没有监控 -
特征文件变大:无人感知 -
代理崩溃:缺乏保护 -
系统恢复:依赖手动回滚
本质上,这是一个 技术债 × 架构耦合 × 自动化扩散 共同作用的结果。
09|行业对比:为什么 AWS/GCP 不会被一个文件拖死?
这不是 Cloudflare 技术不好,而是架构逻辑不同。
| 平台 | 架构特性 | 结果 |
|---|---|---|
| Cloudflare | 单代理路径 + 全网同步配置 | 一个特征文件全网崩溃 |
| AWS/GCP | 多区域隔离 + 服务解耦 | 故障通常局部,不会全球传播 |
AWS/GCP 的大规模系统通常具有:
-
区域级隔离(Region Boundary) -
AZ 级隔离 -
Service Mesh 隔离 -
配置灰度 -
Feature Gate -
Canary 执行 -
自愈退化逻辑
而 Cloudflare 的全球代理网络因为需要极致性能和一致性,采用的是 统一执行框架,因此:
优点:快到无法比拟
缺点:错误传播速度也“光速”
这次特征文件事故就是典型例子。
10|前瞻推演:Cloudflare 下一步会做什么?(推测性分析)
以下内容属于逻辑推演(已标注)。
推测 1:特征文件沙盒化
-
先验证文件结构 -
再验证字段数量 -
再验证模型兼容性 -
最后才允许进入部署管道
预计未来会采用 Canary + Shadow Test 机制。
推测 2:Bot 特征模块从代理中解耦
当前的设计过于危险:
-
Bot 模块几乎是“核心链路单点” -
一崩即全崩 -
没有 fallback 机制
未来可能变成:
-
Bot scoring 放到独立进程 -
代理失败时自动降级 -
对模型特征数量采用硬限制 + 自动修正 -
不再依赖 .unwrap()这种高风险调用
推测 3:内部生成文件 =“潜在恶意文件”
Cloudflare 可能会把所有内部生成的文件也当做:
-
不可信输入 -
需要强校验 -
需要版本验证 -
强制 schema 约束 -
体积限制强制生效
这代表 Cloudflare 内部将迈向另一种“零信任”:
零信任配置文件。
11|这次事故对行业真正意味着什么
这起事故不仅属于 Cloudflare,也属于整个互联网行业。
它暴露出一个普遍趋势:
现代互联网的崩溃风险更多来自内部错误,而不是外部攻击。
表现为:
-
自动化系统扩大错误的速度 > 工程师修复错误的速度 -
系统耦合度过高会让任何错误变成“全局放大器” -
内部生成的数据文件是新的“供应链风险源” -
权限调整、性能优化、微小改动都能造成全球级问题
这对所有工程团队都是强调:
-
要对变化保持敬畏 -
要对“假设”保持怀疑 -
要对自动化保持谨慎 -
要在系统中构建更多“免疫力”
12|总结:5000 字的内容,用 80/20 方式归纳为 10 句话
-
Cloudflare 11·18 故障不是攻击,而是一次权限变更导致的内部错误。 -
ClickHouse 查询结果变大 → 特征文件膨胀 → 代理 panic。 -
Bot 模块是 Cloudflare 核心代理链路上的关键单点。 -
特征文件无保护、无灰度、无隔离,是事故放大的核心原因。 -
故障呈现周期性,是因为 ClickHouse 节点滚动更新。 -
Workers KV、Access、Turnstile、Dashboard 均因依赖链路而被拖死。 -
故障根因不是 Bug,而是假设崩溃。 -
Cloudflare 的统一架构 = 性能极强 + 风险集中。 -
AWS/GCP 因多区域隔离,不会出现类似全局崩溃。 -
现代互联网真正的风险,是内部自动化链路的脆弱点。

