Ubuntu系统下Nginx集成ModSecurity WAF的完整编译与配置指南

Ubuntu系统下Nginx集成ModSecurity WAF的完整编译与配置指南 1. 项目概述为什么要在Nginx上集成ModSecurity如果你负责过线上Web服务器的运维或安全加固大概率听过WAFWeb应用防火墙这个词。它像一道安检门站在你的应用服务器前面过滤掉那些恶意的SQL注入、跨站脚本XSS、路径遍历等攻击请求。ModSecurity就是一个开源的、功能强大的WAF引擎它有一整套规则比如著名的OWASP Core Rule Set来识别和阻断攻击。那么问题来了Nginx本身性能强悍、配置灵活是很多高并发场景的首选但它原生并不像Apache那样直接支持ModSecurity作为模块加载。在Ubuntu上为Nginx配置ModSecurity本质上是一个“打补丁”的过程我们需要获取ModSecurity的源码将其编译成一个Nginx能识别的动态模块ngx_http_modsecurity_module.so然后在Nginx的配置中加载并启用它。我之所以花时间折腾这个是因为之前遇到过一次针对后台管理页面的撞库攻击日志里全是各种奇怪的登录尝试。当时临时用fail2ban分析Nginx日志来封IP虽然有效但总觉得是“事后诸葛亮”而且规则维护起来麻烦。ModSecurity能提供实时的、基于规则的内容检测把防御动作前置这正是我需要的。整个过程涉及源码编译、依赖处理、规则配置和调优踩的坑不少但最终搭建起来的防护层心里踏实多了。接下来我会把从环境准备、编译安装、规则配置到优化排错的完整过程拆开揉碎了讲清楚。无论你是想为公司的业务加一道安全锁还是纯粹出于学习目的这篇记录都能给你一个清晰的路线图。2. 环境准备与依赖梳理工欲善其事必先利其器。在开始编译之前确保你的Ubuntu系统是干净且网络通畅的。我使用的是Ubuntu 22.04 LTS这个版本比较稳定软件源也新。理论上20.04或24.04的步骤也大同小异。2.1 系统更新与基础工具安装第一步永远是先更新系统并安装编译所需的工具链。这能避免很多因缺失基础组件导致的编译错误。sudo apt update sudo apt upgrade -y sudo apt install -y build-essential autoconf automake libtool pkg-configbuild-essential包含了gcc,g,make等核心编译工具。autoconf,automake,libtool,pkg-config这些是用于生成和运行编译配置脚本的工具很多开源项目包括ModSecurity的构建系统依赖于它们。2.2 安装Nginx的编译依赖和ModSecurity的运行时依赖Nginx模块的编译需要Nginx本身的源码和其依赖库。同时ModSecurity作为一个复杂的C项目也有自己的一堆依赖。我们需要一次性装好。sudo apt install -y libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev libxml2-dev libyajl-dev liblmdb-dev libcurl4-openssl-dev libfuzzy-dev这里解释几个关键包libpcre3-devPerl兼容正则表达式库Nginx用于location匹配等ModSecurity的规则引擎也重度依赖它。zlib1g-dev压缩库用于Nginx的Gzip压缩功能。libssl-dev提供SSL/TLS支持如果你需要HTTPS这个必不可少。libxml2-devXML解析库。ModSecurity的规则和部分解析功能需要处理XML格式数据。libyajl-devYet Another JSON Library。现代Web应用传输JSON很多ModSecurity需要它来解析JSON请求体。liblmdb-dev一个轻量级的内存映射数据库。ModSecurity v3用它来持久化一些运行时数据如IP地址的计数器比纯内存更可靠。libcurl4-openssl-devcURL库的开发文件。ModSecurity的remote规则功能如从远程加载黑名单可能需要它。libfuzzy-dev提供模糊哈希如SSDeep功能可用于恶意文件检测。注意liblmdb-dev在有些较旧的Ubuntu版本如18.04的默认源里可能没有可能需要添加第三方PPA或从源码编译。在22.04及以后直接安装即可。2.3 获取Nginx源码与ModSecurity源码我们不通过apt安装Nginx因为需要获取其源码来编译模块。同时我们需要ModSecurity的源码。选择一个合适的目录比如/usr/local/src来操作。cd /usr/local/src # 1. 下载Nginx稳定版源码 (以nginx-1.24.0为例请检查官网获取最新稳定版) sudo wget http://nginx.org/download/nginx-1.24.0.tar.gz sudo tar -xzvf nginx-1.24.0.tar.gz # 2. 下载ModSecurity v3源码 (libmodsecurity) sudo git clone --depth 1 https://github.com/SpiderLabs/ModSecurity cd ModSecurity # 初始化并更新子模块非常重要 sudo git submodule init sudo git submodule update这里有个关键点一定要使用ModSecurity v3。v2版本是为Apache设计的虽然有个nginx连接器但维护状态不佳且复杂。v3版本被重构为独立的库libmodsecurity然后通过一个独立的Nginx连接模块modsecurity-nginx来工作这种架构更清晰也是官方主推的方向。3. 编译与安装ModSecurity库 (libmodsecurity)现在我们先编译核心的WAF引擎——libmodsecurity。cd /usr/local/src/ModSecurity # 运行构建配置脚本这里我们选择安装到/usr/local/modsecurity目录方便管理 sudo ./build.sh sudo ./configure --prefix/usr/local/modsecurity --with-lmdb sudo make sudo make install./build.sh这个脚本会调用autoconf、automake等工具生成configure脚本。如果这步报错通常是因为前面“基础工具”没装全。--prefix/usr/local/modsecurity指定安装目录。将所有相关文件库、头文件集中放在这里后续链接时路径清晰。--with-lmdb显式启用LMDB支持用于持久化存储。make和make install编译并安装。编译过程可能需要几分钟。完成后你可以在/usr/local/modsecurity/lib/下找到libmodsecurity.so这个核心库文件。实操心得编译时如果遇到关于C11标准的错误可能需要检查你的g版本。Ubuntu 22.04默认的g-11是没问题的。如果系统较老可以尝试安装g-9或更高版本并通过sudo update-alternatives --config g来切换默认版本。4. 编译Nginx并集成ModSecurity模块这是最关键的一步我们需要将ModSecurity的Nginx连接器编译成动态模块并集成到Nginx中。4.1 下载Nginx连接器模块这个模块是Nginx和libmodsecurity之间的桥梁。cd /usr/local/src sudo git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git4.2 编译Nginx并添加ModSecurity模块现在进入Nginx源码目录执行配置和编译。这里我们采用“动态模块”的方式这样以后升级Nginx或模块会更灵活。cd /usr/local/src/nginx-1.24.0 # 查看当前系统已安装的Nginx配置如果你之前通过apt安装过这步很重要 nginx -V 21 | grep arguments # 假设输出包含类似 --prefix/etc/nginx --sbin-path/usr/sbin/nginx ... 的信息 # 我们需要在编译新Nginx时复用大部分这些参数尤其是路径以避免配置混乱。 # 执行configure命令这里是一个通用示例你需要根据上一步的输出调整 sudo ./configure \ --prefix/etc/nginx \ --sbin-path/usr/sbin/nginx \ --modules-path/usr/lib/nginx/modules \ --conf-path/etc/nginx/nginx.conf \ --error-log-path/var/log/nginx/error.log \ --http-log-path/var/log/nginx/access.log \ --pid-path/var/run/nginx.pid \ --lock-path/var/run/nginx.lock \ --http-client-body-temp-path/var/cache/nginx/client_temp \ --http-proxy-temp-path/var/cache/nginx/proxy_temp \ --http-fastcgi-temp-path/var/cache/nginx/fastcgi_temp \ --http-uwsgi-temp-path/var/cache/nginx/uwsgi_temp \ --http-scgi-temp-path/var/cache/nginx/scgi_temp \ --usernginx \ --groupnginx \ --with-compat \ --with-file-aio \ --with-threads \ --with-http_addition_module \ --with-http_auth_request_module \ --with-http_dav_module \ --with-http_flv_module \ --with-http_gunzip_module \ --with-http_gzip_static_module \ --with-http_mp4_module \ --with-http_random_index_module \ --with-http_realip_module \ --with-http_secure_link_module \ --with-http_slice_module \ --with-http_ssl_module \ --with-http_stub_status_module \ --with-http_sub_module \ --with-http_v2_module \ --with-mail \ --with-mail_ssl_module \ --with-stream \ --with-stream_realip_module \ --with-stream_ssl_module \ --with-stream_ssl_preread_module \ --with-cc-opt-g -O2 -fstack-protector-strong -Wformat -Werrorformat-security -Wp,-D_FORTIFY_SOURCE2 -fPIC \ --with-ld-opt-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie \ --add-dynamic-module/usr/local/src/ModSecurity-nginx参数解析与注意事项路径参数--prefix、--sbin-path等尽量与你系统现有Nginx的配置一致通过nginx -V查看。这样编译出来的新Nginx可以直接替换旧的配置文件无需改动。如果这是全新安装可以按你的喜好设置。--with-compat这个选项对于动态模块的兼容性非常重要务必加上。--add-dynamic-module/usr/local/src/ModSecurity-nginx这是关键它告诉Nginx的构建系统将ModSecurity连接器编译为一个动态模块.so文件而不是静态链接进去。--with-cc-opt和--with-ld-opt这些是编译器和链接器的优化与安全选项直接复制使用即可能增强Nginx的安全性。配置完成后开始编译sudo make编译成功后不要运行make install。因为我们要的只是新编译出的模块文件而不是覆盖安装整个Nginx除非你确定要升级。编译出的模块文件位于objs/目录下。# 查看编译出的模块 ls -la objs/*.so # 你应该能看到一个名为 ngx_http_modsecurity_module.so 的文件4.3 安装动态模块并更新Nginx现在将模块文件复制到Nginx的标准模块目录并替换Nginx主程序。# 1. 复制动态模块 sudo cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules/ # 2. 备份旧的Nginx可执行文件如果存在 sudo cp /usr/sbin/nginx /usr/sbin/nginx.backup.$(date %Y%m%d) # 3. 复制新的Nginx可执行文件 sudo cp objs/nginx /usr/sbin/nginx # 4. 测试新Nginx配置是否正确 sudo nginx -t如果nginx -t显示“configuration file /etc/nginx/nginx.conf test is successful”恭喜你Nginx与ModSecurity模块的集成编译成功了。踩坑记录直接make install会覆盖所有文件包括默认的html目录和配置文件可能导致你的站点文件被覆盖。所以采用只复制主程序和模块的方式更安全。如果nginx -t报错找不到模块可能是因为/usr/lib/nginx/modules/不在默认的模块搜索路径。可以在nginx.conf最顶部用load_module指令显式指定路径我们下一步就会做。5. 配置Nginx启用ModSecurity模块有了接下来就是告诉Nginx如何使用它。5.1 加载模块与基础配置编辑Nginx的主配置文件/etc/nginx/nginx.conf在events块之前添加加载模块的指令。# /etc/nginx/nginx.conf load_module modules/ngx_http_modsecurity_module.so; user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } ...然后在http块内添加ModSecurity的全局配置。我习惯在/etc/nginx/下创建一个单独的目录来管理ModSecurity的配置。sudo mkdir -p /etc/nginx/modsec创建ModSecurity的主配置文件sudo vim /etc/nginx/modsec/modsecurity.conf写入以下基础内容# /etc/nginx/modsec/modsecurity.conf # 启用ModSecurity引擎 SecRuleEngine On # 请求体处理 SecRequestBodyAccess On # 限制请求体大小为128MB可根据业务调整 SecRequestBodyLimit 131072000 SecRequestBodyNoFilesLimit 131072 # 响应体处理通常建议关闭以提升性能除非需要检测响应内容 SecResponseBodyAccess Off # SecResponseBodyLimit 524288 # 调试日志级别生产环境建议设为0调试时可设为9 SecDebugLog /var/log/nginx/modsec_debug.log SecDebugLogLevel 0 # 审计日志配置 SecAuditEngine RelevantOnly SecAuditLogRelevantStatus ^(?:5|4(?!04)) SecAuditLogParts ABIJDEFHZ SecAuditLogType Serial SecAuditLog /var/log/nginx/modsec_audit.log # 规则文件路径 Include /etc/nginx/modsec/crs-setup.conf Include /etc/nginx/modsec/rules/*.confSecRuleEngine On开启规则引擎。DetectionOnly模式只记录不阻断适合初期测试。SecRequestBodyAccess On允许检查请求体POST数据等。SecRequestBodyLimit单个请求体的最大大小超过会返回413错误。SecAuditEngine RelevantOnly审计引擎仅在规则匹配或发生错误时记录日志节省资源。SecAuditLogRelevantStatus ^(?:5|4(?!04))一个正则表达式表示只记录服务器错误5xx和客户端错误4xx但不包括404的请求。这可以过滤掉大量正常的日志。Include ...引入规则文件。这里预设了OWASP CRS的配置和规则目录。5.2 配置虚拟主机启用WAF现在在具体的站点配置server块中启用ModSecurity。假设你有一个站点配置文件/etc/nginx/conf.d/mysite.conf。server { listen 80; server_name your_domain.com; # 启用ModSecurity并指定主配置文件 modsecurity on; modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf; location / { # 你的常规代理或root配置 proxy_pass http://backend_server; # 或 root /var/www/html; } # 为审计日志和调试日志设置正确的权限如果Nginx以非root用户运行 location /logs/ { internal; alias /var/log/nginx/; } }关键指令modsecurity on;在该server或location作用域开启WAF。modsecurity_rules_file指向我们刚才创建的主配置文件。5.3 部署OWASP核心规则集CRS没有规则的WAF就像没有子弹的枪。OWASP Core Rule Set (CRS) 是ModSecurity最著名、最常用的免费规则集。cd /etc/nginx/modsec # 下载最新的OWASP CRS稳定版 sudo wget https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.0.0-rc1.tar.gz sudo tar -xzvf v4.0.0-rc1.tar.gz sudo mv coreruleset-4.0.0-rc1 crs # 复制配置文件模板 sudo cp crs/crs-setup.conf.example crs-setup.conf # 复制规则文件 sudo mkdir -p rules sudo cp crs/rules/*.conf rules/现在编辑/etc/nginx/modsec/crs-setup.conf。这个文件是CRS的“总控开关”你可以在这里调整规则的敏感度、禁用某些规则组等。对于初次使用我建议先做以下调整将SecDefaultAction改为只记录不阻断便于观察SecDefaultAction phase:1,log,auditlog,pass SecDefaultAction phase:2,log,auditlog,pass设置规则检测级别tx.crs_setup_version等参数在文件里已有主要是确认版本# 设置偏执级别Paranoia Level, PL。PL越高规则越严格误报也可能越多。从PL1开始。 SecAction \ id:900000,\ phase:1,\ nolog,\ pass,\ t:none,\ setvar:tx.paranoia_level1根据你的应用类型可能需要在crs-setup.conf中排除一些误报。例如如果你的应用有大量的JSON API可能需要调整REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example复制并重命名为.conf来排除对某些路径的检查。5.4 创建日志目录并设置权限ModSecurity需要写入日志文件。sudo touch /var/log/nginx/modsec_audit.log /var/log/nginx/modsec_debug.log sudo chown nginx:nginx /var/log/nginx/modsec_*.log sudo chmod 644 /var/log/nginx/modsec_*.log确保Nginx进程用户这里用的是nginx对这些日志文件有写权限。6. 测试、验证与性能调优配置完成后重载Nginx使配置生效。sudo nginx -t # 再次测试配置 sudo systemctl reload nginx # 或 sudo nginx -s reload6.1 功能测试发送一个简单的测试请求触发一条规则。例如经典的SQL注入探测curl -X GET http://your_server_ip/?id1 OR 11然后检查审计日志sudo tail -f /var/log/nginx/modsec_audit.log你应该能看到一条日志记录其中包含Message: Warning. detected SQLi using libinjection之类的信息并注明匹配的规则ID如942100。这证明ModSecurity已经成功检测到了攻击。6.2 性能影响评估与基础调优开启WAF必然带来性能开销主要来自正则表达式匹配和请求体解析。以下是一些调优思路规则集裁剪CRS非常全面但你的应用可能只用到了其中一部分协议和功能。可以仔细分析rules/目录下的文件禁用与你的应用完全无关的规则组如REQUEST-921-PROTOCOL-ATTACK.conf如果你没有文件上传功能。调整偏执级别Paranoia Level在crs-setup.conf中tx.paranoia_level从1到4。PL1是基础防护误报少。生产环境可以从PL1开始稳定后再考虑是否提高到PL2。禁用响应体检查除非你有检测响应内容泄露的特定需求否则务必保持SecResponseBodyAccess Off。检查响应体对性能影响巨大。合理设置请求体限制SecRequestBodyLimit不要设置得过大根据你实际的业务需求如文件上传大小来设定。使用SecRuleRemoveById或SecRuleUpdateActionById在站点配置中针对特定路径如/api/排除已知的、会产生大量误报的规则。这需要结合审计日志进行分析。启用连接池和缓存确保Nginx本身的worker_processes、worker_connections设置合理并考虑启用代理缓存等从整体上缓解后端压力。6.3 监控与维护日志轮转modsec_audit.log可能会快速增长需要配置日志轮转。编辑/etc/logrotate.d/nginx添加对ModSecurity日志的处理。规则更新安全规则需要定期更新。可以编写一个定时任务cron job定期从GitHub拉取最新的CRS规则并重载Nginx。但务必在测试环境先验证。误报分析定期查看审计日志分析误报false positive。将合法的请求路径或参数添加到排除规则exclusion rule中这是一个持续的过程。7. 常见问题与排查技巧实录在实际部署和运行中你肯定会遇到各种问题。这里记录几个我踩过的坑和解决方法。7.1 编译阶段问题问题1configure时报错提示找不到libmodsecurity。checking for ModSecurity ... not found configure: error: ngx_http_modsecurity_module requires the ModSecurity library.解决这是因为configure脚本找不到libmodsecurity的库文件和头文件。你需要通过环境变量告诉它路径。# 在运行Nginx的configure命令之前设置环境变量 export MODSECURITY_INC/usr/local/modsecurity/include export MODSECURITY_LIB/usr/local/modsecurity/lib # 然后重新运行./configure命令并在最后加上 # --add-dynamic-module/usr/local/src/ModSecurity-nginx更稳妥的做法是将库路径添加到系统配置中# 创建modsecurity库配置文件 sudo sh -c echo /usr/local/modsecurity/lib /etc/ld.so.conf.d/modsecurity.conf # 更新动态链接库缓存 sudo ldconfig之后再重新运行Nginx的configure和make。问题2Nginx启动失败错误日志显示module “/usr/lib/nginx/modules/ngx_http_modsecurity_module.so” is not binary compatible解决这通常是因为动态模块与当前运行的Nginx版本不兼容。可能的原因你编译模块时使用的Nginx源码版本与系统当前安装的Nginx二进制版本不一致。编译时缺少--with-compat选项。确保编译模块的Nginx源码版本必须与你最终要运行的Nginx二进制版本完全一致。最安全的方法就是像我前面写的用同一份源码编译出新的Nginx主程序来替换旧的。7.2 运行阶段问题问题3Nginx启动或重载成功但审计日志modsec_audit.log里没有任何记录即使发送攻击payload。排查步骤检查配置语法sudo nginx -t确保无误。检查modsecurity指令确认在server或location块中设置了modsecurity on;。检查规则引擎状态确认modsecurity.conf中SecRuleEngine是On或DetectionOnly。检查日志路径和权限确保/var/log/nginx/modsec_audit.log文件存在且Nginx进程用户如nginx对其有写权限。可以尝试sudo -u nginx touch /var/log/nginx/test.log来测试。降低日志级别临时将SecDebugLogLevel设为1或3查看modsec_debug.log里面会有更详细的加载和执行信息。检查规则文件路径确认modsecurity.conf中的Include指令路径正确且规则文件如crs-setup.conf没有语法错误。一个快速测试方法是在modsecurity.conf最前面加一条简单的规则SecRule ARGS contains test id:1000,phase:2,log,deny,status:403然后访问http://yourserver/?argtest看是否触发403和日志记录。问题4大量误报阻塞了正常用户请求。解决这是使用WAF最常见的问题。从DetectionOnly开始初期一定要将SecRuleEngine设为DetectionOnly并设置SecAuditEngine RelevantOnly只观察不阻断。运行你的业务一段时间收集日志。分析审计日志使用工具如modsec-clamfi或自己写脚本分析modsec_audit.log找出触发规则的正常请求。重点关注规则ID如942100和触发的参数。添加排除规则Exclusion Rules这是最精准的方法。在modsecurity.conf中或专门的自定义规则文件里并在主配置中Include在CRS规则加载之前使用SecRuleRemoveById或SecRuleUpdateActionById。按路径排除如果/api/v1/upload这个路径总是误报可以SecRule REQUEST_URI beginsWith /api/v1/upload \ id:10000,\ phase:1,\ nolog,\ pass,\ ctl:ruleRemoveById942100按参数排除如果参数search_term容易触发SQLi规则可以SecRule REQUEST_FILENAME rx ^/search$ \ id:10001,\ phase:2,\ nolog,\ pass,\ ctl:ruleRemoveTargetById942100;ARGS:search_term调整CRS配置仔细阅读crs-setup.conf中的注释调整tx.paranoia_level、tx.anomaly_score_threshold等参数或启用/禁用特定的规则文件。问题5性能明显下降服务器负载升高。解决定位瓶颈使用top或htop观察是CPU高还是I/O高ModSecurity主要是CPU密集型。检查规则数量grep -r SecRule /etc/nginx/modsec/rules/ | wc -l。规则数过多会直接影响性能。实施调优措施确保SecResponseBodyAccess Off。**降低SecRequestBodyLimit和SecRequestBodyNoFilesLimit**到合理值。在location块中精细控制只为需要防护的路径如登录、表单提交开启modsecurity on;对静态文件如图片、CSS、JS可以关闭。考虑硬件或架构升级如果经过优化后性能仍不满足可能需要更强大的CPU或者考虑将WAF功能卸载到专门的硬件设备或云WAF服务。整个过程从准备到调优确实需要一些耐心。但一旦搭建完成并稳定运行它为你Web应用带来的主动防护能力是单纯依赖系统防火墙或网络ACL无法比拟的。尤其是在应对自动化扫描和常见Web漏洞攻击时ModSecurity能帮你过滤掉绝大部分噪音让你更专注于真正的业务逻辑和安全威胁。