技术解析

基于 Nginx 的 WAF 实现
0
2021-05-21 03:55:21
idczone
作者: Sophos
链接: https://zhuanlan.zhihu.com/p/23447143
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

一直以来,陆陆续续有不少同学跟我咨询是否方便公开 Nginx 的 WAF 模块代码。对于这个问题,我个人认为是可以开源的。当然,开源的部分只应包含框架代码, WAF 的规则必须保密。只是从公司的角度来看,这并不属于个人财产,因此我也就不好把这套 WAF 源码分享出来。但又考虑到大家的需求,于是就有了写这篇文章的想法。

先说明一下整个 WAF 系统的结构:

  1. 规则处理模块( nginx )
  2. 日志收集模块( gofluent+httpmq )
  3. 日志分析模块( mysql )
  4. 数据展示模块( web )

规则处理模块

考虑框架的可扩展性,便没有花费过多气力在具体功能的实现上,更多的思考如何使得该框架更为通用。因为后来发现,即便我做再多功夫,一旦某个需求变更,便不得不更改 C 源码,而这又会牵扯到一系列如编译、测试及部署,热更新等问题。

老实说,这套 WAF 模块的代码质量并不算高,毕竟那会儿对于 Nginx 的理解有限。由于这纯属一个人闭门造车,想通过 C 语言实现一个通用的模块框架来 Hack Nginx ,从而提供各种定制 Nginx 功能的方式。但是后来又发现工作量及难度都超出预期,于是只实现了如下功能的框架(或许这就是理想与现实的差距):

  • 实现了简单的语法解析引擎,包括 Variable 、 Operator 及 Action 三个小模块
  • 提供 Get 和 Set Nginx 内置变量的操作
  • 基于 pcre 正则库,实现串行匹配指定变量的功能
  • 提供一些常用的转换操作,如 urldecode 等
  • 在 handler 及 filter 内置了四个 hook 点,分别对应 request_header 、 request_body 、 response_header 及 response_body

日志收集模块

最初我们用的是 fluentd ,期间出了各种问题,最后就用 golang 重写了一套收集与存储的服务。

具体细节可以参考我的这个回答:为什么要使用 Go 语言? Go 语言的优势在哪里? - 知乎用户的回答

日志分析模块及数据展示模块

这个两个部分并不是我负责的,所以技术选型方面我也没有参与。只是做出来的界面不仅难看还难用 (大哭

总结

抗投诉服务器

其实, Nginx 的配置命令就是一种编程语言,没有过多的复杂运算,只是一些简单的赋值。事实上,本身 Nginx 配置是不带有任何语言性质的。类似的正则匹配和逻辑操作都是由其他模块提供的,如 PCRE 库等。

而我所想做的事情,也正是 lua-nginx-module 所实现的。随着之后更加深入的了解,我才知道 Cloudflare 的 WAF 就是基于 lua-nginx-module 实现的。原来他们早就考虑到类似的问题并提出了相应的解决之道。

利用这个 lua-nginx-module 模块,介入到 Nginx 处理 HTTP 请求或相应的各个阶段(如 access , rewrite , content 及 log 等 phase ),首先在请求到对应 location 时,将 Nginx 相关信息记录到 Lua 虚拟机中(或以库的形式提供 Api 给 Lua 调用),再通过 Lua 虚拟机来执行 Nginx 配置中的 Lua 脚本。

后来看了一下 lua-nginx-module 的官方文档,得出以下几点:

  • 一个 worker 进程对应一个 Lua 虚拟机
  • 利用 Lua 的 coroutine 实现用户态协程调度
  • 利用 Nginx 提供 HTTP RESTful Api Services
  • 提供 GET 和 SET Nginx 变量的操作
  • 能够高效构建完整复杂的业务逻辑
  • 可通过 LuaJit 大幅提升性能,接近 C++

若 Lua 仅作为一种独立语言,支持协程可能并不算麻烦。可困难在于 Lua 生来以一门嵌入式语言存在,天生需要大量与宿主系统 C 语言做交互。典型的应用环境是由 C 语言开发的系统,嵌入 Lua 解析器,加载 Lua 脚本运行。同时注入一些 C 函数供 Lua 脚本调用。 Lua 作为控制脚本,并不直接控制外界的模块,做此桥梁的正是那些注入的 C 接口。在较为复杂的应用环境中,这些注入的 C 函数还需要有一些回调方法。当我们企图用 Lua 脚本去定制这些回调行为时,就出现了 C 函数调用 Lua 函数, Lua 函数再调用 C 函数,这个 C 函数又调用 Lua 函数的层层嵌套的过程。

从云风的《 Lua 源码赏析》中看到上面这段文字,使我更为明确了之前的那些猜想。确实, Lua 天生就是与 Nginx 绝配啊。

本来有计划在开发 WAF 2.0 版本的时候,完全通过 lua-nginx-module 来重构的。只是后来跑路了……

(逃

Anyway ,如果现在让我来选型,我这样设计:

  1. 规则处理模块基于 lua-nginx-module
  2. 日志收集模块基于 logstash/rsyslog+kafka
  3. 日志分析模块基于 elasticsearch
  4. 数据展示模块基于 kibana/grafana

仅供参考,欢迎交流。


“ Go 语言的优势在哪里? - 知乎用户的回答 ”没有连接......

看不懂,求解释

漏掉了,原文链接里面有。。。

看不懂

https://github.com/alexazhou/VeryNginx

期待 op 出官方 waf,在春哥微博问了,他说当然会有

同期待,这个正是 nginx-lua 的一个很好应用场景

春哥团队正在新做一个正则引擎,然后利用这个效率最高的正则引擎做 waf

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服