原文地址:https://blog.cloudflare.com/meet-gatebot-a-bot-that-allows-us-to-sleep/
翻译水平有限,有不通顺的语句,请见谅。
原作者:Marek Majkowski 写于 25 Sep 2017

译者:驱蚊器喵#ΦωΦ


在过去,我们已经谈过Cloudflare是如何架构来支撑最大的DDoS攻击. 在流量突增(surges?)的时候,我们将流量分布在大量的边缘服务器上.这样的架构使我们避免于单个服务器的阻塞,因为流量在多个数据中心的多台服务器上广泛分布开来. 我们通过采用 Anycast和 ECMP技术来实现.

gatebot
gatebot

我们没有使用单独的清洗设备或者是专用硬件 - 如果有需要,我们的每台边缘服务器都可以执行高级流量过滤. 这样可以随着我们的业务发展,来扩大DDoS防御流量. 每个新添加到我们的数据中心的服务器,都会增加我们的理论最大DDoS “清洗” 力量. 它也可以很好的缩小 - 在较小的数据中心,我们就不必过多的投资独立服务器.

在通常的操作中,我们对于处理攻击的态度非常务实. 由于入站流量分布在数百台服务器上,所以我们不做任何操作都可以抗住周期性的突发攻击和小型攻击。Vanilla(译者注:这是一个内核) Linux 能够弹性的处理意外的网络事件. 自从4.4版本后的内核后,SYN cookies的性能得到了显著的提升.

但是在某些时候, 恶意的流量体积可能会变得很大,以至于我们必须从网络堆栈中分离负载. 我们必须尽量减少处理攻击流量包的CPU消耗. Cloudflare运营的是一个用户庞大的服务,我们必须始终有足够的处理能力来给正常的网络提供服务。不能让我们的 HTTP 代理服务 (nginx)或者自定义的 DNS 服务器 (名叫 RRDNS,用GO编写) 的CPU空缺. 当攻击的流量超过预先设置的阀值时 (这根据具体的攻击类型所变化),我们必须进行干预.

缓解处理

Screen-Shot-2017-09-25-at-11.21.11-AM
Screen-Shot-2017-09-25-at-11.21.11-AM

在大量攻击时,我们部署缓解程序来减少CPU被恶意流量消耗的占用. 我们有多层防御, 每层都针对特定的攻击向量.

首先是 “scattering(散播)”. 因为我们掌控DNS的解析,我们能够将服务的域名解析到我们自己的服务器的ip地址上(我们称之为”scattering(散播)”). 只要攻击不是跟踪最新的DNS解析,这就是个有效的技术方案.这通常发生在L3(译者注:TCP/IP协议簇的第三层是网络层)攻击中,攻击者通常是目标的IP地址硬编码到攻击程序中.

接下来,是各种各样的缓解技术,使用的是Linux内核中内置的防火墙,iptables. 但是我们不是使用静态规则这样的传统防火墙的用法. 我们会基于特定的攻击特征来不断的添加、调整、删除规则. 多年来,我们已经掌握了最有效的iptables拓展:

  • xt_bpf
  • ipsets
  • hashlimits
  • connlimit

为了充分利用 iptables,我们建立了一个用于管理整个机组的iptables 配置的系统,使我们能够在各个地方迅速部署规则. 这很适合我们的架构: 因为有Anycast技术, 一个针对单个IP地址的攻击会被传送到多个地区. 在那个IP的所有服务器上运行 iptables 规则就有了意义.

使用公式化的 iptables 给予我们充分的信心. 如果可能,我们更愿意使用现成的(off-the-shelf)工具来处理攻击.

但有时候,即使这样都还不够. 一般情况下,iptables 运行很快的, 但是有他的局限性. 在非常大的攻击中, 每台服务器每秒超过 1M 数据包, 我们将攻击流量转移从内核iptables转移到一个内核分流的用户空间程序中 (我们叫它 floodgate). 我们使用一个叫局部内核分流(partial kernel bypass) 的方案,调用 Solarflare EF_VI 的接口. 这样后,我们的每台服务器每秒可以处理超过 5M 攻击数据包,但同时只消耗一个CPU核心. 有了 floodgate后,即使在最大的网络事件中,我们也可以为正常的应用程序留出足够的CPU.

最后,我们在HTTP层做了一些调整. 对于特定的攻击,我们禁用了HTTP Keep-Alives,强制攻击者为每个请求重新建立 TCP 会话.这也为正常流量牺牲了一点性能,但却是一个令人惊讶的强大工具,可以限制许多攻击。 对于其他攻击模式,我们打开“我受到攻击(I’m under attack)”模式,强制攻击进入我们的JavaScript挑战页面。

手动处理攻击

cloudflare_outage
cloudflare_outage

在早期,这些缓解措施是由我们不知疲倦的系统可靠性工程师(SRE’s)去手动应用的。不幸的是,这会让人处于压力只下… 然后,犯错误. 我们从中吸取到了教训 - 一次有名的事故发生在 2013年的3月,一个简单的拼写错误 导致了整个网络的宕机.

Screen-Shot-2017-09-25-at-11.19.11-AM
Screen-Shot-2017-09-25-at-11.19.11-AM

人类对于应用精确的规则总是不那么擅长的. 随着我们系统的扩大,缓解程序变得更加复杂,有很多特定的开关, 我们的 SREs 被这些细节击溃了. 向操作者提供有关攻击的所有信息是一项麻烦的事情. 我们常常应用过于宽松的缓解措施, 这会对正常的流量造成一些不必要的影响. 随着 Gatebot的引入,所有的这些问题都改善了.

迎接 Gatebot

Screen-Shot-2017-09-25-at-11.19.31-AM
Screen-Shot-2017-09-25-at-11.19.31-AM


为了援助我们的our SREs ,我们开发了一个全自动化的缓解系统. 我们叫它Gatebot1.

Gatebot 的主要目的是为了尽可能多的自动化进行缓解工作的流程. 这就意味着: 要观察网络注意其中的异常情况,了解攻击目标和他们的基本信息 (比如所涉及的客户类型), 并执行适当的缓解措施.

Screen-Shot-2017-09-25-at-11.20.22-AM
Screen-Shot-2017-09-25-at-11.20.22-AM

现在我们有多个 Gatebot的实例 - 我们称之为 “缓解工作流(mitigation pipelines)”. 每个工作流有三个部分:

  1. “攻击检测” 或者 “信号” - 一个专用系统用于检测网络流量中的异常.一般是对接入我们网络中的一小部分流量进行抽样, 并使用流式算法分析他们. 这样,我们就能实时的查看当前网络的状态. 这部分代码(stack)是由 Golang 编写的, 因为要使用密集型CPU,所以只检测采样的数据. 令人安慰的是,这这种非常时期,我们有两台大型 Xeon(志强) 服务器,将所有的48个 Skylake CPU 核心组合在一起,辛苦的处理这些数据包,并执行复杂的分析来寻找攻击.

  2. “自动化反应” 或者 “商业逻辑”. 对于每个异常 (攻击) ,我们会看到攻击者的目标是谁, 我们能否缓解它, 以及使用什么参数. 根据特定的工作流, 业务逻辑可以是简单的程序到需要查询数据库的几个步骤程序,以及人类操作者的确认. 这些代码不是性能的关键,所以我们使用python编写. 为了让公司的其他人更加容易阅读代码, 我们部署了一个简易的功能性、反应式编程引擎. 这帮助我们让代码简洁且易懂, 即使我们添加了更多的步骤,更多的工作流和复杂的逻辑. 给你感受一下复杂性: 想象如果客户在遭受攻击的时候,升级了订阅套餐,系统应该怎么做.

  3. “缓解”. 上一步将特定的缓解指令提供给统一的缓解管理系统. 缓解措施会部署到我们在全球范围内的服务器上的应用程序、客户设置, 有些情况下还会部署到网络硬件上。

在夜晚安心睡觉

Gatebot 会不断的运行, 不会因为吃饭而中断. 仅对于iptables方式的缓解工作流, Gatebot一天会执行 30 到 1500 次. 下面的图表是有关最近6个月内每天的iptables缓解操作数目:

attacks-iptables
attacks-iptables

Gatebot 比我们最有经验的SREs更快更精确. 如果没有 Gatebot,我们也无法以适当的信心运营这些服务. 除此之外, Gatebot已被证明有显著的适应性 - 我们从自动化处理Layer 3的攻击中, 很快证明了这套通用模型为其他的自动化事务工作的很好。现在,我们运行有十多个单独的 Gatebot, 负责处理缓解Layer 7 攻击,然后向我们客户支持团队通知行为不当的客户端服务器。

因为Gatebot开始运作,我们从”检测 / 逻辑 / 缓解”的工作流学习到了很多.我们在自动化的网络系统重用了这套模型,用于缓解网络的拥塞2.

无论你订阅的套餐是哪种, 免费版、专业版、商业版还是企业版, Gatebot 都会为你服务,保护你的网站.. 这就是为什么我们能为所有客户提供相同级别DDoS保护的原因3.

1.有趣的事情: 在这个环节中,我们所有的组件都叫 “gate-什么什么”, 比如: gatekeeper, gatesetter, floodgate, gatewatcher, gateman… 谁说取个名字很难? ↩︎

2.团队中的一些人议论说,这个系统应该叫Netbot. ↩︎

3.注意: 如果有警告,请咨询你的成功工程师(Success Engineer?) ↩︎

备忘: 因为网页的标题栏占了两行位置,所以markdown的两组对应锚地往上提前了一行,有点乱