V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NGINX
NGINX Trac
3rd Party Modules
Security Advisories
CHANGES
OpenResty
ngx_lua
Tengine
在线学习资源
NGINX 开发从入门到精通
NGINX Modules
ngx_echo
sophos
V2EX  ›  NGINX

基于 Nginx 的 WAF 实现

  •  1
     
  •   sophos ·
    sysulq · 2016-11-08 16:58:15 +08:00 · 8423 次点击
    这是一个创建于 2944 天前的主题,其中的信息可能已经有所发展或是发生改变。
    作者: 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

    仅供参考,欢迎交流。

    8 条回复    2016-11-22 08:47:11 +08:00
    Marser
        1
    Marser  
       2016-11-08 17:06:49 +08:00
    “ Go 语言的优势在哪里? - 知乎用户的回答 ”没有连接......
    daya
        2
    daya  
       2016-11-08 17:13:41 +08:00 via Android
    @Marser 看不懂,求解释
    sophos
        3
    sophos  
    OP
       2016-11-08 17:18:00 +08:00
    @Marser 漏掉了,原文链接里面有。。。
    zhangfanfan
        4
    zhangfanfan  
       2016-11-08 17:18:37 +08:00
    看不懂
    hcymk2
        5
    hcymk2  
       2016-11-08 17:39:34 +08:00
    wwek
        6
    wwek  
       2016-11-08 19:01:38 +08:00
    期待 op 出官方 waf,在春哥微博问了,他说当然会有
    anyforever
        7
    anyforever  
       2016-11-09 09:29:51 +08:00
    @wwek 同期待,这个正是 nginx-lua 的一个很好应用场景
    wwek
        8
    wwek  
       2016-11-22 08:47:11 +08:00
    @anyforever 春哥团队正在新做一个正则引擎,然后利用这个效率最高的正则引擎做 waf
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2602 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 04:09 · PVG 12:09 · LAX 20:09 · JFK 23:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.