lua-resty-waf - 基于 OpenResty 技术栈构建的高性能 WAF
注意:lua-resty-waf 基本上已被弃用。这个项目在 ModSecurity 对 Nginx 不是可行选择的时代曾有用武之地;如今情况已不再如此。曾有在 2020 年重振该项目的尝试,但我没有资源来完成这项工作;这项工作在 redux 分支中部分完成。
lua-resty-waf 是一个基于 OpenResty 技术栈构建的反向代理 WAF。它使用 Nginx Lua API 分析 HTTP 请求信息,并按照灵活的规则结构进行处理。lua-resty-waf 随附一套模拟 ModSecurity CRS 的规则集,以及一些在初始开发和测试期间构建的自定义规则,还有一个用于新兴威胁的小型虚拟补丁集。此外,lua-resty-waf 还随附自动翻译现有 ModSecurity 规则的工具,使用户无需学习新的规则语法即可扩展 lua-resty-waf 的实现。
lua-resty-waf 最初由 Robert Paprocki 为他在 Western Governor's University 的硕士论文开发。
lua-resty-waf 需要几个第三方 resty lua 模块,尽管这些模块都已随 lua-resty-waf 打包,因此无需单独安装。建议在运行 OpenResty 软件包的系统上安装 lua-resty-waf;lua-resty-waf 尚未在使用独立 Nginx 源码和 Nginx Lua 模块包构建的平台上测试过。
为获得最佳的正则表达式编译性能,建议使用支持 JIT 编译的 PCRE 版本来构建 Nginx/OpenResty。如果您的操作系统未提供此版本,您可以将支持 JIT 的 PCRE 直接构建到您的 Nginx/OpenResty 中。为此,请在 --with-pcre 配置标志中引用 PCRE 源码的路径。例如:```sh
您可以从 [PCRE 官网](http://www.pcre.org/) 下载 PCRE 源码。另请参阅这篇 [博客文章](https://www.cryptobells.com/building-openresty-with-pcre-jit/),了解如何构建带有 JIT 支持的 PCRE 库的 OpenResty 的分步指南。
## 性能
lua-resty-waf 的设计以效率和可扩展性为核心。它利用 Nginx 的异步处理模型和高效设计,尽可能快速地处理每个事务。负载测试表明,部署了所有自带规则集(这些规则集旨在模拟 ModSecurity CRS 背后的逻辑)的部署,每个请求的处理时间大约为 300-500 微秒;这相当于 [Cloudflare 的 WAF](https://www.cloudflare.com/waf) 所宣传的性能。测试在合理的硬件配置(E3-1230 CPU、32 GB RAM、2 x 840 EVO 组 RAID 0)上运行,最高达到约每秒 15,000 个请求。更多信息请参阅 [这篇博客文章](http://www.cryptobells.com/freewaf-a-high-performance-scalable-open-web-firewall)。
lua-resty-waf 的工作负载几乎完全受 CPU 限制。Lua VM 中的内存占用(不包括由 `lua-shared-dict` 支持的持久化存储)约为 2MB。
## 安装
提供了一个简单的 Makefile:```
# make && sudo make install
或者,通过 Luarocks 安装:```
lua-resty-waf 使用 [OPM](https://github.com/openresty/opm) 包管理器,该管理器可在现代 OpenResty 发行版中使用。OPM 客户端工具要求系统的 `PATH` 环境变量中存在 `resty` 命令行工具。
请注意,默认情况下 lua-resty-waf 以 SIMULATE 模式运行,以避免立即影响应用程序;希望启用规则操作的用户必须显式将运行模式设置为 ACTIVE。
## Synopsis```lua
http {
init_by_lua_block {
-- use resty.core for performance improvement, see the status note above
require "resty.core"
-- require the base module
local lua_resty_waf = require "resty.waf"
-- perform some preloading and optimization
lua_resty_waf.init()
}
server {
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- define options that will be inherited across all scopes
waf:set_option("debug", true)
waf:set_option("mode", "ACTIVE")
-- this may be desirable for low-traffic or testing sites
-- by default, event logs are not written until the buffer is full
-- for testing, flush the log buffer every 5 seconds
--
-- this is only necessary when configuring a remote TCP/UDP
-- socket server for event logs. otherwise, this is ignored
waf:set_option("event_log_periodic_flush", 5)
-- run the firewall
waf:exec()
}
header_filter_by_lua_block {
local lua_resty_waf = require "resty.waf"
-- note that options set in previous handlers (in the same scope)
-- do not need to be set again
local waf = lua_resty_waf:new()
waf:exec()
}
body_filter_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:exec()
}
log_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:exec()
}
}
}
}
从磁盘解析并初始化一个 ModSecurity SecRules 文件。请注意,这仍然需要通过 add_ruleset 添加规则集(必须使用文件的基本名称作为键)。
示例:```lua http { init_by_lua_block { local lua_resty_waf = require "resty.waf"
-- this translates and calculates a ruleset called 'ruleset_name'
local ok, errs = pcall(function()
lua_resty_waf.load_secrules("/path/to/secrules/ruleset_name")
end)
-- errs is an array-like table
if errs then
for i = 1, #errs do
ngx.log(ngx.ERR, errs[i])
end
end
}
server {
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"