基于 Nginx 的 WAF 实现 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sophos
V2EX    NGINX

基于 Nginx 的 WAF 实现

  •  1
     
  •   sophos 2016-11-08 16:58:15 +08:00 8820 次点击
    这是一个创建于 3330 天前的主题,其中的信息可能已经有所发展或是发生改变。
    作者: 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     Solana     2598 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 00:29 PVG 08:29 LAX 16:29 JFK 19:29
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86