nginx 防火墙模块开发总结

为什么要写这篇文章?

我起初并没有写这篇文章的意思,因为无论是从难度还是代码复杂度来说这个项目在我写过的项目里只能算中等,但是它却有一个特点,就是我的第一个专门开发出来供他人使用并且希望被他人使用的项目。先前的项目要不然不能公开,要不然个人属性非常强,不适合他人使用。所以这篇文章主要是以纪念为目的的。

为什么要开启这个项目?

那段时间我的站点有时会打不开,起初以为是网络问题,后来越来越严重,进入后台才发现数据库 IO 拉满了。看了看 nginx 的日志才发现站点被疯扫,于是打算做点什么。

我先从网上搜索到了一些基础的拦截扫站的规则,直接 copy 到 nginx.conf 里就能用,但是这些规则都特别多,而且杂,直接丢到配置文件里会大大降低配置的可读性,所以当初就被否了。

后来我拉黑了一些 IP,不过这也就一时起作用,过段时间换了 IP 继续来扫。补救措施是用 whois 找到 IP 地址快直接全部拉黑。但是这导致了一些误伤,于是这个方案也被否了。

然后我找到了 ngx_lua_waf,用了一段觉得还行,但是也发现了一些缺陷,最明显的就是拉黑 IP 的时候只能拉黑单个 IP,而不能拉黑一个地址块。但是这个模块大概已经停止维护了,不能指望作者更新了。

然后在 Github 上看了几个防火墙模块,要不然功能不全,要不然使用复杂,于是萌生了自己写模块的想法。

为什么用 C 语言开发?

主要是因为这样可以不用安装 lua_nginx_module,其次是因为 C 的执行效率理论上要高于 lua,因为 lua 需要启动一个 VM。

不过我主要考虑的是安装是否方便,性能倒不是那么看重,因为 lua_nginx_module 身经百战,性能完全可以保证。

不过用 C 语言开发必然增加开发难度,最重要的是要自己控制内存,这类 Web 服务器上的模块一旦内存泄露、段错误啥的那麻烦就大了,不过我还是有信心不出大问题的。

设计原则

只实现基础的防护,即 IP 检测、Url 检测、Get 参数检测、Cookie 检测、Post 检测、Referer 检测和 CC 防御。不会引入更复杂的功能,比如根据某个 IP 的行为进行综合判断是否拦截。

在保证代码良好可读性的情况下提高性能。

语义化版本

说实话我之前还真没把这个当回事,但是当我打算把这个项目给别人用的时候我发现版本号似乎也能传递一些比较重要的信息,所以开发这个项目的时候我是一直遵守这个标准的。

简单来说语义化版本将版本号分为 X.Y.Z。

  • 当你做了不向下兼容的更改之后 X 要递增。
  • 当你做了向下兼容的功能性新增的时候 Y 要递增。
  • 当你做了向下兼容的修正时 Z 要递增。

这样版本号就可以向用户传递对应的信息,这无疑是十分有用的,最明显的就是当用户看到 X 递增的时候就会十分谨慎,因为贸然更新可能会导致现有的程序出现不兼容的情况。

Change Log

作为一个打算给他人使用的项目,一个清晰的 Change Log 就是很必要的了,它可以将每次更新的内容展示出来,方便他人了解也方便自己记录。

格式上基本遵循:https://keepachangelog.com/zh-CN/1.0.0/

效果图

从效果上来看应该还算是清晰准确的 Change Log。

开发文档

项目写到后期就不得不写开发文档了,首先是方便了自己,其次也是方便别人。可以从头开始写效率太低了,强烈推荐 doxygen。这个工具可以通过代码内的注释自动生成文档,这样为源代码写注释的同时还能生成开发文档,十分方便。

下面是对应的注释以及生成的开发文档。

注释内容
开发文档

开发文档简单,清晰,还能自动生成函数调用图和被调用图等一系列图。如果觉得丑还能自定义 CSS,不过对于我这个前端困难户来说就不搞了。

性能优化

性能优化的主要工作就是降低请求检查花费的时间。

Url 检查、Get 参数检查、Post 检查、Cookie 检查和 Referer 检查都需要执行正则匹配,暂时没发现什么合适的方法去优化,只能一个一个地去检查。时间复杂度为 $\text{O}(nm)$,$n$ 为需要测试的正则条数,$m$ 为执行正则匹配的时间。

前缀树优化

IP 检查很有搞头。本模块使用了一种经过修改的前缀树来改进 IP 检查的性能,见下图。

本模块所使用的前缀树

从图示就可以看出此结构可以方便地处理单个 IP 的检查和 IP 地址块的检查。

前缀树的查找时间为 $\text{O}(h)$,其中 $h$ 为树的高度。对于 IPV4 来说一共有 32 位,那么树高最多 32 位,IPV6 对应的树高最多为 128 位。那么查找时间复杂度为 $\text{O}(1)$,也就是说执行 IP 检查的时间基本上不会受到 IP 黑白名单规模的影响。

测试

仅开启 IP 检查,其中 IPV4 黑名单中由 21w 个 IPV4 地址快,且互不重叠。使用 ab 命令,100 并发,每轮测试请求 10w 次,重复三轮,取平均值。

  • 平均每 10w 次请求所用时间(未开启防火墙):23.482 s。
  • 平均吞吐率(未开启防火墙):4,292.98 req/s。
  • 平均每 10w 次请求所用时间(开启防火墙):23.116 s。
  • 平均吞吐率(开启防火墙):4,326.173 req/s。

可以看出开启防火墙前后性能没有数量级上的变化。

后续计划

本项目近期完成了 IPV6 支持和基于前缀树的性能优化。基本的功能已经完成,后续应该只是修修补补和宣传了,如果有合适的内容还会继续更新本文。

本文作者:ADD-SP
本文链接:https://www.addesp.com/archives/2876
版权声明:本博客所有文章除特别声明外,均默认采用 CC-BY-NC-SA 4.0 许可协议。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇