Cloudflare 2025年11月18日中断事件剖析:一次数据库权限变更引发的蝴蝶效应

2025年11月18日,Cloudflare网络发生严重中断,核心流量交付失败。根本原因是数据库权限变更导致Bot管理系统使用的特征文件意外加倍,触发代理软件内存限制,引发连锁反应。本文深度复盘了故障时间线、技术根因及修复措施。

Cloudflare 2025年11月18日中断事件

2025年11月18日11:20 UTC,Cloudflare的网络开始出现核心网络流量交付的重大故障。对于试图访问我们客户网站的互联网用户而言,这表现为一个显示Cloudflare网络内部故障的错误页面。

该问题并非直接或间接由网络攻击或任何恶意活动引起。相反,它是由对我们一个数据库系统的权限更改触发的。该更改导致数据库向我们的Bot管理系统使用的"特征文件"中输出多个条目。该特征文件的大小因此翻倍。

这个超出预期的较大特征文件随后被传播到构成我们网络的所有机器上。在这些机器上运行以在网络中路由流量的软件会读取此特征文件,以使我们的Bot管理系统能跟上不断变化的威胁。该软件对特征文件的大小设有一个限制,该限制低于文件翻倍后的大小。这导致了软件故障。

起初我们错误地怀疑所见的症状是由超大规模DDoS攻击引起的,之后我们正确地识别了核心问题,并成功阻止了超出预期的特征文件的传播,用该文件的早期版本进行了替换。到14:30,核心流量已基本恢复正常流动。在接下来的几个小时内,随着流量快速恢复上线,我们努力缓解网络各部分的负载增加。截至17:06,Cloudflare的所有系统均已恢复正常运行。

我们对此次事件给客户及整个互联网带来的影响深表歉意。考虑到Cloudflare在互联网生态系统中的重要性,我们任何系统的任何中断都是不可接受的。我们的网络曾有一段时间无法路由流量,这对我们团队的每个成员来说都是极其痛苦的。我们知道今天我们让大家失望了。

这篇文章详细描述了事件经过以及出现故障的系统和流程。它也是我们为确保此类中断不再发生而计划采取的措施的开端(尽管并非终点)。

中断情况

下图显示了Cloudflare网络提供的5xx错误HTTP状态码数量。通常这个数值应该非常低,并且在中断开始之前也确实如此。

11:20之前的数值是我们网络中观察到的5xx错误的预期基线。峰值及随后的波动显示了我们的系统因加载错误的特征文件而出现故障。值得注意的是,此后系统会恢复一段时间。这对于内部错误来说是非常不寻常的行为。

解释是,该文件是由运行在ClickHouse数据库集群上的查询每五分钟生成一次,该集群当时正在逐步更新以改进权限管理。只有当查询在集群已完成更新的部分上运行时,才会生成坏数据。因此,每五分钟就有机会生成一套好的或坏的配置文件,并在网络中快速传播。

这种波动使得情况变得不明确,因为整个系统会恢复然后再次故障,因为有时好的、有时坏的配置文件被分发到我们的网络。最初,这使我们相信这可能是一次攻击造成的。最终,每个ClickHouse节点都开始生成坏的配置文件,波动在故障状态下稳定下来。

错误一直持续到14:30开始识别并解决根本问题。我们通过停止生成和传播坏的特征文件,并手动将已知良好的文件插入特征文件分发队列,然后强制重启我们的核心代理,从而解决了问题。

图表中剩余的长尾部分是我们的团队重启了进入不良状态的剩余服务,5xx错误码数量在17:06恢复到正常水平。

以下服务受到影响:

受影响服务及影响描述

核心CDN和安全服务 HTTP 5xx状态码。本文开头的截图显示了展示给最终用户的典型错误页面。

Turnstile Turnstile无法加载。

Workers KV Workers KV返回了显著升高的HTTP 5xx错误水平,因为对KV"前端"网关的请求由于核心代理故障而失败。

仪表板 虽然仪表板大部分情况下可运行,但由于登录页面上的Turnstile不可用,大多数用户无法登录。

邮件安全 虽然邮件处理和投递未受影响,但我们观察到暂时无法访问一个IP信誉来源,这降低了垃圾邮件检测的准确性,并阻止了一些新域名检测触发,未观察到对客户的关键影响。我们还观察到一些Auto Move操作失败;所有受影响的邮件均已审查并修复。

Access 大多数用户的身份验证普遍失败,从事件开始时开始,一直持续到13:05开始回滚。任何现有的Access会话未受影响。

所有失败的身份验证尝试都导致错误页面,这意味着在身份验证失败期间,这些用户均未到达目标应用程序。在此期间成功的登录在此次事件中被正确记录。

当时尝试的任何Access配置更新要么会完全失败,要么传播非常缓慢。所有配置更新现已恢复。

除了返回HTTP 5xx错误,我们还在影响期间观察到来自我们CDN的响应延迟显著增加。这是由于我们的调试和可观测性系统消耗了大量CPU,这些系统会自动为未捕获的错误添加额外的调试信息。

Cloudflare如何处理请求,以及今天出了什么问题

每个到达Cloudflare的请求都会在我们的网络中沿着一条明确定义的路径行进。它可能来自加载网页的浏览器、调用API的移动应用或来自其他服务的自动化流量。这些请求首先在我们的HTTP和TLS层终止,然后流入我们的核心代理系统(我们称之为FL,代表"Frontline"),最后通过Pingora,根据需要执行缓存查找或从源站获取数据。

我们之前在此分享了关于核心代理工作原理的更多细节。

当请求经过核心代理时,我们会运行网络中可用的各种安全和性能产品。代理应用每个客户的独特配置和设置,从执行WAF规则和DDoS防护,到将流量路由到开发平台和R2。它是通过一组特定领域的模块来实现的,这些模块将配置和策略规则应用于流经我们代理的流量。

其中一个模块——Bot Management(机器人管理)——是今天中断的源头。Cloudflare的Bot Management包含(除其他系统外)一个机器学习模型,我们使用它来为经过我们网络的每个请求生成机器人评分。我们的客户使用机器人评分来控制哪些机器人被允许访问他们的网站。

该模型以一个"特征"配置文件作为输入。在此上下文中,特征是机器学习模型用来预测请求是否为自动化的单个特征。特征配置文件是各个特征的集合。

该特征文件每几分钟刷新一次,并发布到我们的整个网络,使我们能够应对整个互联网流量模式的变化。它使我们能够应对新型机器人和新的机器人攻击。因此,随着不良行为者迅速改变策略,频繁且快速地推出该文件至关重要。

我们底层ClickHouse查询行为(下文解释)的更改导致生成此文件时包含大量重复的"特征"行。这改变了先前固定大小的特征配置文件的大小,导致机器人模块触发错误。

其结果是,为我们客户处理流量的核心代理系统为任何依赖机器人模块的流量返回了HTTP 5xx错误码。这也影响了依赖核心代理的Workers KV和Access。

与本次事件无关,我们当时并且目前正在将客户流量迁移到我们代理服务的新版本,内部称为FL2。两个版本都受到了此问题的影响,尽管观察到的影响不同。

部署在新FL2代理引擎上的客户观察到了HTTP 5xx错误。使用我们旧代理引擎(称为FL)的客户没有看到错误,但机器人评分未能正确生成,导致所有流量收到的机器人评分为零。部署了规则来阻止机器人的客户会看到大量误报。未在其规则中使用我们机器人评分的客户未受到任何影响。

另一个我们观察到的明显症状使我们误入歧途,认为这可能是一次攻击:Cloudflare的状态页面宕机了。该状态页面完全托管在Cloudflare基础设施之外,不依赖Cloudflare。虽然结果证明这只是巧合,但它导致一些诊断问题的团队成员认为攻击者可能同时针对我们的系统和我们的状态页面。当时访问状态页面的访客会看到一条错误信息:

在我们的内部事件聊天室中,我们担心这可能是最近一系列高容量Aisuru DDoS攻击的延续:

查询行为变更

我上面提到,底层查询行为的变更导致特征文件包含大量重复行。相关的数据库系统使用ClickHouse软件。

为了理解背景,了解ClickHouse分布式查询的工作原理很有帮助。一个ClickHouse集群由许多分片组成。为了查询所有分片的数据,我们在一个名为default的数据库中有所谓的分布式表(由表引擎Distributed支持)。Distributed引擎查询一个名为r0的数据库中的底层表。底层表是每个ClickHouse集群分片上存储数据的地方。

对分布式表的查询通过一个共享系统账户运行。作为改进分布式查询安全性和可靠性工作的一部分,正在努力让它们在初始用户账户下运行。

在今天之前,用户从ClickHouse系统表(如system.tables或system.columns)查询表元数据时,只会看到default数据库中的表。

由于用户已经隐式访问r0中的底层表,我们在11:05进行了一次更改,使这种访问显式化,以便用户也能看到这些表的元数据。通过确保所有分布式子查询可以在初始用户下运行,可以以更细粒度的方式评估查询限制和访问授权,避免一个用户的错误子查询影响他人。

上述更改导致所有用户都能访问他们有权访问的表的准确元数据。不幸的是,过去存在一些假设,认为像下面这样的查询返回的列列表将只包含"default"数据库:

1
2
3
4
5
6
7
SELECT
  name,
  type
FROM system.columns
WHERE
  table = 'http_requests_features'
order by name;

请注意查询中未筛选数据库名称。随着我们逐步向特定ClickHouse集群的用户推出显式授权,在11:05的更改之后,上述查询开始返回列的"重复项",因为这些列对应于存储在r0数据库中的底层表。

不幸的是,这正是Bot Management特征文件生成逻辑所执行的查询类型,用于构建本节开头提到的文件中每个输入"特征"。

上述查询将返回一个类似下图的列表(简化示例):

但是,作为授予用户的附加权限的一部分,响应现在包含了r0模式的所有元数据,实际上使响应中的行数增加了一倍多,最终影响了最终文件输出中的行数(即特征数量)。

内存预分配

在我们代理服务上运行的每个模块都有许多限制,以避免无限制的内存消耗,并进行内存预分配作为性能优化。在这个具体实例中,Bot Management系统对运行时可使用的机器学习特征数量有一个限制。目前该限制设置为200,远高于我们当前使用的约60个特征。同样,存在此限制是出于性能原因,我们为特征预分配了内存。

当包含超过200个特征的坏文件传播到我们的服务器时,触发了此限制——导致系统崩溃。进行此检查并成为未处理错误来源的FL2 Rust代码如下:

1
// (原文中未提供具体代码,此处示意。)

这导致了以下崩溃,进而导致了5xx错误:

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

事件期间的其他影响

依赖我们核心代理的其他系统在事件期间也受到影响。这包括Workers KV和Cloudflare Access。团队在13:04减少了对这些系统的影响,当时对Workers KV进行了补丁以绕过核心代理。随后,所有依赖Workers KV的下游系统(如Access本身)观察到的错误率有所降低。

Cloudflare仪表板也受到影响,原因是内部使用了Workers KV,并且Cloudflare Turnstile作为我们登录流程的一部分部署。

Turnstile受到此次中断的影响,导致没有活动仪表板会话的客户无法登录。如下方图表所示,这在两个时间段内表现为可用性降低:从11:30到13:10,以及14:40到15:30之间。

第一个时期,从11:30到13:10,是由于Workers KV受到影响,一些控制平面和仪表板功能依赖它。这在13:10得到恢复,当时Workers KV绕过了核心代理系统。

对仪表板的第二个影响时期发生在恢复特征配置文件数据之后。积压的登录尝试开始使仪表板不堪重负。这种积压,加上重试尝试,导致了延迟升高,降低了仪表板的可用性。扩展控制平面并发性后,大约在15:30恢复了可用性。

补救和后续步骤

既然我们的系统已恢复在线并正常运行,我们已经开始着手研究未来如何强化系统以抵御此类故障。具体来说,我们正在:

  • 以与处理用户生成输入相同的方式,强化Cloudflare生成的配置文件的摄取过程。
  • 为功能启用更多的全局关闭开关。
  • 消除核心转储或其他错误报告耗尽系统资源的可能性。
  • 审查所有核心代理模块的错误条件的故障模式。

今天是Cloudflare自2019年以来最严重的一次中断。我们曾出现过导致仪表板不可用的中断。有些曾导致新功能在一段时间内不可用。但在过去6年多里,我们没有遇到过导致大部分核心流量停止流经我们网络的另一次中断。

像今天这样的中断是不可接受的。我们设计了我们的系统具有高度的弹性,以确保流量始终能够继续流动。过去当我们出现中断时,总是促使我们构建新的、更具弹性的系统。

我谨代表Cloudflare的整个团队,为我们今天给互联网造成的痛苦表示歉意。

时间(UTC) 状态 描述
11:05 正常。 数据库访问控制变更部署。
11:28 影响开始。 部署到达客户环境,首次在客户HTTP流量上观察到错误。
11:32 - 13:05 团队调查了Workers KV服务的流量水平和错误升高情况。最初的症状似乎是Workers KV响应率下降,导致对其它Cloudflare服务的下游影响。尝试了流量操作和账户限制等缓解措施,以使Workers KV服务恢复正常运行水平。首次自动化测试在11:31检测到问题,手动调查在11:32开始。事件电话会议在11:35创建。
13:05 实现Workers KV和Cloudflare Access绕过 — 影响减少。 在调查期间,我们对Workers KV和Cloudflare Access使用了内部系统绕过,使它们回退到我们核心代理的先前版本。虽然该问题也存在于我们代理的先前版本中,但影响较小,如下所述。
13:37 工作重点转向将Bot Management配置文件回滚到最后一个已知良好版本。 我们确信Bot Management配置文件是事件的触发因素。团队通过多个工作流研究修复服务的方法,最快的工作流是恢复文件的先前版本。
14:24 停止创建和传播新的Bot Management配置文件。 我们确定Bot Management模块是500错误的来源,并且这是由坏的配置文件引起的。我们停止了Bot Management配置文件的自动部署。
14:24 新文件测试完成。 我们观察到使用旧版本配置文件恢复成功,然后专注于加速全球范围的修复。
14:30 主要影响已解决。下游受影响的服务开始观察到错误减少。 正确的Bot Management配置文件在全球部署,大多数服务开始正确运行。
17:06 所有服务已解决。影响结束。 所有下游服务已重启,所有操作完全恢复。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计