本节摘要:Nginx 通过编译期或动态加载方式接入第三方模块。本节用 GeoIP 做地理封禁、用 ModSecurity 拼一层基础 WAF,并给出"要不要上第三方模块"的决策框架——扩展能力的代价是升级与维护责任转移到自己手上。
1.9.11 起 Nginx 支持动态模块:编译为 .so 文件,主配置一行 load_module 挂载,不必重编整个 Nginx。官方仓库的包自带 modules 目录,常用模块(含 geoip、image-filter)可以直接装:
sudo yum install nginx-module-geoip # 发行版/官方仓库提供
# 主配置最外层,events 之前 load_module modules/ngx_http_geoip2_module.so;
⚠️ 动态模块对 Nginx 版本与编译参数敏感,升级 Nginx 时模块必须同步重编——这正是不敢随便上第三方模块的头号原因。评估时先问:这个能力官方模块或配置技巧能不能凑出来?
业务只面向国内,海外流量全是扫描器——这是上 GeoIP 最典型的理由:
http { geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb { $geoip2_country_code country iso_code; } # 国内放行地图 map $geoip2_country_code $is_blocked { CN 0; HK 0; default 1; } server { if ($is_blocked) { return 403; } # ... } }
数据库文件需要定期更新(MaxMind 免费库每月一版),过期后未知地区的流量会走 default——把 default 设为拒绝还是放行要想清楚,我倾向放行并告警,封禁过狠的代价是真实用户被地图错误连累。
OWASP 核心规则集 提供了开箱即用的常见攻击特征:SQL 注入、XSS、路径穿越、扫描器 UA。接入后以"只检测不拦截"模式跑两周,是所有 WAF 上线的标准动作:
http { modsecurity on; modsecurity_rules_file /etc/nginx/modsecurity/main.conf; } # main.conf 里关键设置 # SecRuleEngine DetectionOnly ← 先观察 # include crs-setup.conf # include rules/*.conf
日志里观察误报(富文本编辑器的内容极容易被 XSS 规则命中),逐条加例外,误报率降到可接受后再切 SecRuleEngine On。跳过观察期直接拦截,等于把生产环境当规则测试场。

能不上就不上。官方模块加配置技巧(map、限流、allow/deny)能覆盖八成需求;剩下两成里,GeoIP 和 WAF 属于"生态成熟、收益明确"值得上的,Lua 系(OpenResty 生态)属于"能力天花板高但等于换技术栈"要单独立项评估。每多一个第三方模块,你的升级路径就多一个需要同步重编的依赖。
免费地理库每月更新,手工替换必然被遗忘。用系统定时任务加失败告警做成无人值守:
# 每月 1 号下载数据库,校验后原子替换,失败则保留旧库 curl -sS -o /tmp/geo.mmdb.new "下载地址(占位,填实际授权链接)" \ && mmdbinspect --file /tmp/geo.mmdb.new 1.1.1.1 > /dev/null \ && mv /tmp/geo.mmdb.new /etc/nginx/geoip/GeoLite2-Country.mmdb \ && nginx -s reload || echo "geo db update FAILED" | mail -s "geoip" ops@example.com
设计要点有两个:替换前先校验文件有效性,坏库绝不上线;reload 放在替换之后,Nginx 会在下次用到时重新 mmap 数据库文件。缺少校验环节的自动更新,等于把一个静默故障源挂在了入口上——某次上游返回了 HTML 错误页被当成数据库写入,所有请求的地理判定全部落空,这种事故在采用 GeoIP 的团队里反复发生。
DetectionOnly 模式下,规则命中写进专门的审计日志。两周观察期要回答三个问题:总命中量与误报量的比例;误报集中在哪些规则与哪些业务接口;放行策略应该是逐条加例外还是整条关规则。评估模板:
规则 942100(SQL 注入检测) 命中 1.2 万次 / 日,其中误报 340 次 误报集中在商品搜索接口(用户搜索词含单引号与 or) 处置:对该接口的搜索参数加例外,规则本体保留
经验规律:误报八成集中在两三条规则、一两个接口;把这些点位的例外配好,拦截开启后的投诉量就能压到接近零。反过来"误报多就整个关掉规则"的做法,等于因为两扇窗漏风拆掉了整面墙。
升级演练因此成为用模块团队的必修课:每次 Nginx 安全版本发布,流水线自动触发"新版编译加全模块构建、全量回归用例",绿灯才允许生产升级。没有这条流水线,一次高危漏洞的修复可能因为"模块没编过"而拖延数日——安全补丁被自己的扩展能力锁死,是引入第三方模块最讽刺的代价。评估一个模块值不值得上,看的不是它的功能列表,而是团队是否有能力为它维护这条构建验证链。
模块清单本身也该是一份受版本控制的文档:每个模块记录用途、来源、编译参数、上次验证过的 Nginx 版本与负责人。没有这份清单的团队,两年后的升级就是一场考古。清单上没有的模块出现在机器上,按违规处理——第三方模块的治理纪律,说到底是"入口上跑的每一行代码都要有出处"。