waf: 基于 nginx-module-lua 堆栈的高性能 WAF
安装
如果您尚未设置 RPM 仓库订阅,请 注册。然后您可以继续以下步骤。
CentOS/RHEL 7 或 Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-waf
CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-waf
要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua。
本文档描述了 lua-resty-waf v0.11.1,于 2017 年 5 月 09 日发布。
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 为其在西部州长大学的硕士论文开发。
./configure --with-pcre=/path/to/pcre/source --with-pcre-jit
您可以从 [PCRE 网站](http://www.pcre.org/) 下载 PCRE 源代码。有关使用启用 JIT 的 PCRE 库构建 OpenResty 的逐步指南,请参见此 [博客文章](https://www.cryptobells.com/building-openresty-with-pcre-jit/)。
## 性能
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。
## make && sudo make install
或者,通过 Luarocks 安装:
### process_multipart_body
*默认* true
启用对 multipart/form-data 请求体的处理(如果存在),使用 `lua-resty-upload` 模块。将来,lua-resty-waf 可能会利用此处理对上传体进行更严格的检查;目前,此模块仅对请求体执行最小的合理性检查,如果请求体无效,则不会记录事件。如果您不需要此检查,或者上游模块中的错误导致 HTTP 上传出现问题,请禁用此选项。
*示例*:
```lua
location / {
access_by_lua_block {
-- 禁用 multipart/form-data 请求的处理
-- 请注意,请求体仍将发送到上游
waf:set_option("process_multipart_body", false)
}
}
req_tid_header
默认: false
在上游请求中设置 HTTP 头 X-Lua-Resty-WAF-ID,其值为事务 ID。此 ID 将与调试日志中存在的事务 ID 相关联(如果设置)。这对于请求跟踪或调试目的非常有用。
示例:
location / {
access_by_lua_block {
waf:set_option("req_tid_header", true)
}
}
res_body_max_size
默认: 1048576 (1 MB)
定义响应体不被处理的内容长度阈值。响应体的大小由 Content-Length 响应头决定。如果响应中不存在此头,则响应体将永远不会被处理。
示例:
location / {
access_by_lua_block {
-- 将最大响应大小增加到 2 MB
waf:set_option("res_body_max_size", 1024 * 1024 * 2)
}
}
res_body_mime_types
默认: "text/plain", "text/html"
定义 lua-resty-waf 将处理的响应体 MIME 类型。此值由 Content-Type 头决定。如果此头不存在,或者响应类型不在此列表中,则响应体将不会被处理。设置此选项将把给定的 MIME 类型添加到现有的 text/plain 和 text/html 默认值中。
示例:
location / {
access_by_lua_block {
-- 现在将处理的 MIME 类型为 text/plain、text/html 和 text/json
waf:set_option("res_body_mime_types", "text/json")
}
}
可以通过将类型表传递给 set_option 来添加多个 MIME 类型。
res_tid_header
默认: false
在下游响应中设置 HTTP 头 X-Lua-Resty-WAF-ID,其值为事务 ID。此 ID 将与调试日志中存在的事务 ID 相关联(如果设置)。这对于请求跟踪或调试目的非常有用。
示例:
location / {
access_by_lua_block {
waf:set_option("res_tid_header", true)
}
}
score_threshold
默认: 5
设置异常评分的阈值。当达到阈值时,lua-resty-waf 将拒绝请求。
示例:
location / {
access_by_lua_block {
waf:set_option("score_threshold", 10)
}
}
storage_backend
默认: dict
定义用于持久变量存储的引擎。当前可用的选项有 dict(ngx_lua 共享内存区域)、memcached 和 redis。
示例:
location / {
acccess_by_lua_block {
waf:set_option("storage_backend", "memcached")
}
}
storage_keepalive
默认: true
启用或禁用与远程持久存储主机的 TCP keepalive。
示例:
location / {
acccess_by_lua_block {
waf:set_option("storage_keepalive", false)
}
}
storage_keepalive_timeout
默认: 10000
配置(以毫秒为单位)远程持久存储主机的 cosocket keepalive 池的超时。
示例:
location / {
acccess_by_lua_block {
waf:set_option("storage_keepalive_timeout", 30000)
}
}
storage_keepalive_pool_size
默认: 100
配置远程持久存储主机的 cosocket keepalive 池的池大小。
示例:
location / {
acccess_by_lua_block {
waf:set_option("storage_keepalive_pool_size", 50)
}
}
storage_memcached_host
默认: 127.0.0.1
定义在使用 memcached 作为持久变量存储引擎时使用的主机。
示例:
location / {
acccess_by_lua_block {
waf:set_option("storage_memcached_host", "10.10.10.10")
}
}
storage_memcached_port
默认: 11211
定义在使用 memcached 作为持久变量存储引擎时使用的端口。
示例:
location / {
acccess_by_lua_block {
waf:set_option("storage_memcached_port", 11221)
}
}
storage_redis_host
默认: 127.0.0.1
定义在使用 redis 作为持久变量存储引擎时使用的主机。
示例:
location / {
acccess_by_lua_block {
waf:set_option("storage_redis_host", "10.10.10.10")
}
}
storage_redis_port
默认: 6379
定义在使用 redis 作为持久变量存储引擎时使用的端口。
示例:
location / {
acccess_by_lua_block {
waf:set_option("storage_redis_port", 6397)
}
}
storage_zone
默认: none
定义将用于保存持久存储数据的 lua_shared_dict。此区域必须在配置的 http{} 块中定义。
示例:
http {
-- 定义一个 64M 的共享内存区域以保存持久存储数据
lua_shared_dict persistent_storage 64m;
}
location / {
access_by_lua_block {
waf:set_option("storage_zone", "persistent_storage")
}
}
可以定义和使用多个共享区域,但每个配置位置只能定义一个区域。如果某个区域变满且共享字典接口无法添加额外的键,则以下内容将被记录到错误日志中:
Error adding key to persistent storage, increase the size of the lua_shared_dict
阶段处理
lua-resty-waf 设计为在请求生命周期的多个阶段运行。规则可以在以下阶段处理:
- access: 在此阶段,可以获取请求信息,例如 URI、请求头、URI 参数和请求体。
- header_filter: 在此阶段,可以获取响应头和 HTTP 状态。
- body_filter: 在此阶段,可以获取响应体。
- log: 在此阶段,事件日志会在完成时自动写入。
这些阶段对应于其适当的 Nginx lua 处理程序(access_by_lua、header_filter_by_lua、body_filter_by_lua 和 log_by_lua)。请注意,在此列表之外的 lua 阶段处理程序中运行 lua-resty-waf 将导致行为不正常。早期阶段中可用的所有数据在后期阶段中也可用。也就是说,在 access 阶段中可用的数据在 header_filter 和 body_filter 阶段中也可用,但反之则不然。
包含的规则集
lua-resty-waf 附带了一些旨在模仿 ModSecurity CRS 功能的规则集。作为参考,这些规则集在此列出:
- 11000_whitelist: 本地策略白名单
- 20000_http_violation: HTTP 协议违规
- 21000_http_anomaly: HTTP 协议异常
- 35000_user_agent: 恶意/可疑用户代理
- 40000_generic_attack: 通用攻击
- 41000_sqli: SQLi
- 42000_xss: XSS
- 90000_custom: 自定义规则/虚拟补丁
- 99000_scoring: 异常评分处理
规则定义
lua-resty-waf 从存储在磁盘上的 JSON 数据块中解析规则定义。规则根据目的和严重性分组,定义为规则集。包含的规则集旨在模仿 ModSecurity CRS 的某些功能,特别是 base_rules 定义。此外,包含的 modsec2lua-resty-waf.pl 脚本可用于将其他或自定义规则集转换为与 lua-resty-waf 兼容的 JSON 数据块。
请注意,翻译脚本在不支持的操作、集合和运算符方面存在几个限制。有关已知不兼容的最新列表,请参见 此维基页面。
注意事项
拉取请求
请将所有拉取请求目标指向开发分支,或者如果 PR 是重大更改,则指向功能分支。对主分支的提交应仅以文档更新或对模块本身没有影响的其他更改的形式出现(并且可以干净地合并到开发中)。
路线图
- 扩展虚拟补丁规则集:增加对新兴威胁的覆盖。
- 扩展集成/验收测试:增加对常见威胁和使用场景的覆盖。
- 扩展 ModSecurity 语法翻译:支持更多运算符、变量和操作。
- 常见应用程序配置文件:针对常见 CMS/应用程序的优化规则集。
- 支持多个套接字/文件记录器目标:可能需要分叉 lua-resty-logger-socket 项目。
限制
lua-resty-waf 正在持续开发和改进,因此可能在功能和性能上受到限制。当前已知的限制可以在此仓库的 GitHub 问题跟踪器中找到。
另见
- OpenResty 项目:http://openresty.org/
- 我个人的博客,提供有关 lua-resty-waf 开发的更新和笔记:http://www.cryptobells.com/tag/lua-resty-waf/
GitHub
您可以在 nginx-module-waf 的 GitHub 仓库 中找到有关此模块的其他配置提示和文档。