cloudflare事故报告硬核详解

概览

昨晚,500成了全球网站上最醒目的数字。小编在访问atcoder时发现无法正常登录,随后发现推特也报出500错误,航空公司无法在线订票,许多使用Cloudflare的个人网站也跟着受害,并且我的一些朋友已经申请赔偿,Cloudflare这波可谓损失惨重。

团队起初认为这又一次是由攻击导致的,随后意识到是因为数据库的权限设置出了问题,导致配置文件被多次写入,错误的配置文件下放到服务端导致服务端瘫痪,团队在几小时内通过替换配置文件迅速挽救这次灾难。

为什么cloudflare如此重要

1. 加速网站访问

Cloudflare 在全球有 300+ 数据中心(节点),能把静态资源缓存到离用户更近的地方

2. 保护网站不被攻击

Cloudflare 提供业界最强的 DDoS 防御

当黑客发起恶意访问时,它会拦截请求,确保你的服务器不会挂掉。

包括:

  • DDoS 清洗
  • 防爬虫
  • 防火墙规则
  • 机器人识别

3. 隐藏服务器 IP

因为 Cloudflare 是反向代理,用户只看到 Cloudflare 的 IP,看不到你真实服务器地址

,达到防止被人直接攻击你服务器的效果。

4. 提供 HTTPS、SSL 证书

无需自己配置证书,可以自动为网站启用 HTTPS。

5. 更多高级功能

包括但不限于:

  • Workers(无服务器函数)
  • R2 对象存储(S3 替代)
  • Zero Trust 访问控制
  • WAF Web 应用防火墙
  • Turnstile(无验证码人机验证)
  • Pages(静态网站托管)

所以,为避免个人网站受攻击,使用Cloudflare的安全服务是许多开发者的首选,下面这张图形象说明了cloudflare在全球互联网中的重要地位:

事故过程

数据库向配置文件写入过量条目,之后将这些配置文件下放到服务端后,配置文件大小超过了服务端规定的上限,因而引发错误。(这些配置文件描述了cloudflare最新的威胁数据)

从11:30开始,检测报告显示收到大量的5xx响应结果,起初结果呈现波动,这是由于一开始数据库集群只有部分节点会放出错误配置文件,每过5分钟,数据库都会重新生成新的配置文件并下方,当请求被分配到有故障的节点,才会下放错误的配置文件。

一段时间后,13:00后,所有节点均出现了这个错误,因而导致连续的大面积5xx结果,之后,开发人员手动将旧版本的配置文件插入队列,并强制重启配置文件发放服务,在14:30左右令错误情况得到显著缓解,而15:00之后的"长尾巴",是在逐个重启被此配置文件错误影响到的其他服务。

被影响的服务如下:

Service / Product Impact description
Core CDN and security services HTTP 5xx status codes. The screenshot at the top of this post shows a typical error page delivered to end users.
Turnstile Turnstile failed to load.
Workers KV Workers KV returned a significantly elevated level of HTTP 5xx errors as requests to KV's "front end" gateway failed due to the core proxy failing.
Dashboard While the dashboard was mostly operational, most users were unable to log in due to Turnstile being unavailable on the login page.
Email Security While email processing and delivery were unaffected, we observed a temporary loss of access to an IP reputation source which reduced spam-detection accuracy and prevented some new-domain-age detections from triggering, with no critical customer impact observed. We also saw failures in some Auto Move actions; all affected messages have been reviewed and remediated.
Access Authentication failures were widespread for most users, beginning at the start of the incident and continuing until the rollback was initiated at 13:05. Any existing Access sessions were unaffected. All failed authentication attempts resulted in an error page, meaning none of these users ever reached the target application while authentication was failing. Successful logins during this period were correctly logged during this incident. Any Access configuration updates attempted at that time would have either failed outright or propagated very slowly. All configuration updates are now recovered.

原理

cloudflare的服务由三层架构组成,当客户端向配置了Cloudflare服务的服务器端发送请求时,请求依次通过: HTTP & TLS Termination、FL(核心代理模块)、缓存\数据库模块。

这次的问题出在了FL,核心代理模块(Core Proxy Module),其中有一个工具,用于检测操作是否由机器人\自动化工具完成------Bot Management,这个反机器人工具,使用一个ML(机器学习)方法,读入配置文件,根据配置文件中定义的近期用户行为,来对此次用户的请求是否由机器人完成进行预测。

由于新的自动化工具和机器人技术手段层出不穷,这个配置文件会被频繁更新给Bot Management,而由于错误的数据库Query语句,导致配置文件被大量写入重复条目,让配置文件超过了固定大小,从而让ML模块读取文件时出错。

如果Cloudflare用户在核心代理模块中启用了Bot Management,新版本FL2会直接抛出5xx错误,而旧版本FL,则会ML失效,返回100%是机器人的错误判断,从而直接认为你的一切操作都是人机!(这就是无法登陆配置了cloudflare人机验证网站的原因,被100%当机器人了呵呵)

错误的数据库请求

Cloudflare使用ClickHouse数据库,这是一种超高速的数据分析数据库,用来做报表、统计、指标查询,而不擅于做业务事务。

ClickHouse使用分布式模式,当用户需要查询数据时,会从每个分片shard查询并将结果合并返回,以提高性能,具体原理是:数据库中有一张表default,向default表提交查询语句,查询交给名为Distributed的引擎,这个引擎唤起每个shard,让shard去查其下的r0表,也就是说,每个shard实际上有一个r0表,所有的数据只在r0中储存,default是一张"代理表"。

Cloudflare团队发现,ClickHouse数据库在执行查询时,并不会用发起查询的用户身份进行查询,在Distributed引擎中,不管是谁的查询,都由一个shared account执行,而这样的方式,让权限控制和历史记录分析变得困难,因此,cloudflare团队计划调整ClickHouse的查询逻辑,将对r0的隐式访问改为显式访问,让用户直接获得对底层shard上的数据库r0的访问权限,这样,所有的操作不经default表代理,直接来到r0,让监控变得容易。

可是!问题就出在了小小的数据库查询语句上,当Cloudflare团队修改了用户对r0的权限为可访问时,此时的查询语句是:

SQL 复制代码
SELECT
  name,
  type
FROM system.columns
WHERE
  table = 'http_requests_features'
order by name;

查询没有指定表!因此,结果从r0和default两张表返回两次,因此才让配置文件的大小翻了一倍!

查询结果类似:

Rust的"设计哲学"

"不安全的情况我直接停机。"

在用于获取供Bot Management使用的配置文件的Rust代码中,有这么一段:

Rust 复制代码
/// Fetch edge features based on `input` struct into [`Features`] buffer.
pub fn fetch_features(
    &mut self,
    input: &dyn BotsInput,
    features: &mut Features,
) -> Result<(), (ErrorFlags, i32)> {
    // update features checksum (lower 32 bits) and copy edge feature names
    features.checksum &= 0xFFFF_FFFF_0000_0000;
    features.checksum |= u64::from(self.config.checksum);
    let (feature_values, _) = features
        .append_with_names(&self.config.feature_names)
        .unwrap();
}

我们看到最后的.unwrap()语句,这个语句在得到Err(失败)的表达时,不会做兜底处理或者打出日志,而是直接抛出Panic,让Rust线程宕机!

因此,当配置文件大小超过限制,引发Err结果时,cloudflare的开发人员没有在这里做兜底处理,导致抛出Panic:

thread fl2_worker_thread panicked: called Result::unwrap() on an Err value

简单总结

一条被忽略的数据库查询语句 + 一次权限调整 → 导致配置重复写入 → 配置文件过大 → Bot Management 崩溃 → FL 模块 panic → 全球范围 5xx。

这说明了一个朴素事实:

庞大的互联网是由无数极小的细节互相牵动的,一个小小的 SQL 查询也可能让半个互联网倒下。

相关推荐
消失的旧时光-19435 小时前
Kotlinx.serialization 对多态对象(sealed class )支持更好用
java·服务器·前端
少卿5 小时前
React Compiler 完全指南:自动化性能优化的未来
前端·javascript
广州华水科技5 小时前
水库变形监测推荐:2025年单北斗GNSS变形监测系统TOP5,助力基础设施安全
前端
广州华水科技5 小时前
北斗GNSS变形监测一体机在基础设施安全中的应用与优势
前端
七淮5 小时前
umi4暗黑模式设置
前端
8***B5 小时前
前端路由权限控制,动态路由生成
前端
军军3605 小时前
从图片到点阵:用JavaScript重现复古数码点阵艺术图
前端·javascript
znhy@1235 小时前
Vue基础知识(一)
前端·javascript·vue.js
terminal0075 小时前
浅谈useRef的使用和渲染机制
前端·react.js·面试
我的小月月5 小时前
🔥 手把手教你实现前端邮件预览功能
前端·vue.js