技术解析
作者: Sophos
链接: https://zhuanlan.zhihu.com/p/23447143
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一直以来,陆陆续续有不少同学跟我咨询是否方便公开 Nginx 的 WAF 模块代码。对于这个问题,我个人认为是可以开源的。当然,开源的部分只应包含框架代码, WAF 的规则必须保密。只是从公司的角度来看,这并不属于个人财产,因此我也就不好把这套 WAF 源码分享出来。但又考虑到大家的需求,于是就有了写这篇文章的想法。
先说明一下整个 WAF 系统的结构:
考虑框架的可扩展性,便没有花费过多气力在具体功能的实现上,更多的思考如何使得该框架更为通用。因为后来发现,即便我做再多功夫,一旦某个需求变更,便不得不更改 C 源码,而这又会牵扯到一系列如编译、测试及部署,热更新等问题。
老实说,这套 WAF 模块的代码质量并不算高,毕竟那会儿对于 Nginx 的理解有限。由于这纯属一个人闭门造车,想通过 C 语言实现一个通用的模块框架来 Hack Nginx ,从而提供各种定制 Nginx 功能的方式。但是后来又发现工作量及难度都超出预期,于是只实现了如下功能的框架(或许这就是理想与现实的差距):
最初我们用的是 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 的官方文档,得出以下几点:
若 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 ,如果现在让我来选型,我这样设计:
仅供参考,欢迎交流。