正则的灾难性回溯?没想到我们真的踩到了……

前言

今年7月28日,我发布了一篇文章

nginx的正则回溯和灾难性回溯​​​​​​​

万万没想到,不到3个月之后,我们尽然真的差点踩到了我当时描述的那个坑里……

起因

15:51 开始,我们陆续收到的线上nginx集群的CPU idle 过低报警。我们赶紧看了下服务,整个nginx集群的CPU idle 从80%开始逐渐降低,降到了60%左右,最低的机器已经到了40%多。然而整个集群的qps 流量并没有大幅上涨,反而有些许下降,这就麻烦了,非异常流量导致的CPU异常往往不好排查,且不可控,目前虽然CPU的负载尚能正常承担业务,但是不能保证后面会不会恶化

排查

我们开始紧急对线上进行了排查,登到机器后top看了一下,发现nginx worker 的负载很不均匀,个别worker cpu使用已经达到了100%,且不是固定的worker。当时的现场没来的及截图,但是大约是下面这个情况

这个现象很像是nginx在处理什么很重的请求,跟之前我们遇到的dyups瓶颈有点像。于是就perf top看一下

看到这我立刻就明白了,八成是遇到了之前那篇文章遇到的正则灾难性回溯的问题。

原因找到了,但是异常配置和异常请求还没定位。我先问了下值班同学最近有没有变更,得知没有后便开始尝试从error log里找有没有pcre的报错。在敲命令时内心还是有些忐忑的,虽然现在cpu负载尚能承载业务,但是万一异常流量再次增长,不需要很高的qps就可以拖垮集群

如果异常请求恰好没有触发pcre的回溯上线,抓不到异常日志,那就只能去全量分析nginx 配置,排查异常请求,这样定位时间周期会大大拉长, 增加了影响业务的风险。幸运的是我很快就抓到的异常的日志

异常请求,造成灾难性回溯的正则很顺利的找到了,但这还不能100%确定。我紧急对线上的一台机器修改了一下正则,删掉了前面的(/.*)* 。新的正则虽然跟原来的匹配范围有些区别,但我思考了一下影响不是很大,而且现在事态比较紧急,我就先灰度了一台机器观察了一下

上线后cpu立刻恢复了,这说明就是这条正则导致的cpu负载飙升,到这心里悬着的剑放下了。不过现在事情还没完,由于正则是紧急修改的,存在一定影响业务的风险,并且nginx集群的整体响应时间还算可以,于是我只灰度了部分nginx并继续关注状态。异常的请求同步给其他同学,并定位到了来源,原来是安全团队的扫描导致的。这样异常配置和异常请求来源都找到了,影响处于可控的状态,之后不久扫描流量停止后,我回滚了之前的临时配置,重写了那段正则上线后,事情才暂时告一段落。

异常分析

异常正则的所在配置是这样的

if ( $uri ~ "(/.*)*(/[^\.]+)[^/]$" ) {
    rewrite "^(.+)$" https://$host$uri/ permanent;
}

他想实现的功能是当请求的url中最后没有斜杠,且最后一级路径中没有 “.” 也就是请求的不是文件的时候,就 301 跳转在最后补一个斜杠。这是一个非常古老的配置,比我们组大部分同学来公司的时间都长,已经找不到添加人和添加的目的了。

根据我总结的易出现的灾难性正则特征,它符合这下面这条

if ( $uri ~ "(/.*)*(/[^\.]+)[^/]$" ) {
    rewrite "^(.+)$" https://$host$uri/ permanent;
}

这条正则要命的是他是一个nginx server 块的正则配置,该域名下所有的请求都会经过他处理。一般用户请求的url中不会有太多斜杠,所以平时影响还好。但是这次的异常流量是安全的扫描,URL中含有大量斜杠,导致回溯次数指数级增长。更更要命的是,根据上图中灾难性回溯的特征 3 中提到的,只有在中间发生匹配失败时才会灾难性回溯,也就是结尾不带 / 的url不会有影响,然后这条if中的补全斜杠的逻辑又会让本不会出现异常的URL 在301跳转后100%触发异常回溯……

后续处理:

后面我把这条配置改成了

if ( $uri ~ "/[^/\.]+[^/]$" ) {
    rewrite "^(.+)$" https://$host$uri/ permanent;
}

通过 非/ 这种方式来定位到最后一级URL,可以大大减少回溯的次数,上线后nginx的性能还有了10%左右的提升,可以说是因祸得福了

这次异常可以说是非常精准的验证了之前文章提到的影响

1. 少量请求占用了大量cpu资源

这次的扫描请求大约是 1400 qps左右,就把我们几十个节点的nginx集群的整体cpu idle从 80%降到了60%+

2. 故障需要特定流量才会触发,流量的增加和配置上线的时间节点不一定一致。

上面提到了,这个异常正则是多年前就上线了,但最近才出现大量的灾难性回溯。还好我经验丰富,不然可能要查很久(doge)

反思

看到这可能有同学会有疑问,既然之前已经知道了影响,为什么不扫描配置文件,找到这个正则呢?

实际上这条配置我之前扫出来过,但是之前我写文章的时候对灾难性回溯的总结不是很到位,我错误的认为这个正则不会造成灾难性回溯。下面是我之前总结的

最新的文章我已经改正了

这个事情发生后,我的感觉还是很奇妙的。写上一篇正则灾难性回溯那篇文章的原因是在网上偶然看到一个大佬分享的一个文章里简单提了一下灾难性回溯。然后我顺着深入了解后发现这个对我们nginx服务的影响很大,我们可能会踩到这个坑便写了文章简单总结了一下。本以为灾难性回溯是特定流量对特定配置下才会发生的故障,对于我们线上来说会是一个小概率事件,然而就是这么一个偶然发现的坑,不到3个月后就踩到了……

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值