abuse-guard:按错误响应率自动封禁滥用客户端
安装
您可以在任何基于 RHEL 的发行版上安装此模块,包括但不限于:
- RedHat Enterprise Linux 7、8、9 和 10
- CentOS 7、8、9
- AlmaLinux 8、9
- Rocky Linux 8、9
- Amazon Linux 2 和 Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-abuse-guard
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 nginx-module-abuse-guard
通过在 /etc/nginx/nginx.conf 顶部添加以下内容来启用模块:
load_module modules/ngx_http_abuse_guard_module.so;
本文档描述的是 nginx-module-abuse-guard v2.1.0 发布于 2026 年 9 月 3 日。
您的错误日志就是一份自白书。Abuse Guard 实时读取它并将滥用者拒之门外。
每个扫描器、模糊测试器和撞库机器人都会留下相同的指纹:
一连串寻找隐藏路径的 404、敲击锁死房门的 403、
一次又一次失败的请求。Abuse Guard 监视您的服务器实际返回的状态码,
识别出流量中大部分是失败的客户端,并将它们封锁——这一切都在 NGINX worker 内部,
针对请求本身,在几微秒内完成决策。无需 sidecar。无需日志传输器。无需脚本层。
只有编译后的 C 语言在出色地完成一项工作。
规格说明
| 触发器 | 您选择的每个客户端的错误响应率(默认为 403/404) |
| 动作 | 定时封锁——在固定时间窗口内的硬性封禁,而非限速 |
| 决策点 | NGINX preaccess 阶段,在任何 handler 或 upstream 运行之前 |
| 内存模型 | 每个客户端固定字节数,与阈值无关 → 可应对僵尸网络规模 |
| 集群模式 | 可选:通过 Redis / Valkey 在节点间复制封禁信息 |
| 持久性 | 可选:退出快照在重启后保留活动封禁 |
| 资源占用 | 一个自包含模块;默认零运行时依赖 |
| 平台 | RHEL / AlmaLinux / Rocky / CentOS Stream / Oracle / Amazon Linux |
它解决的问题
合法访客几乎从不会产生一连串错误。而滥用者除了错误几乎不产生其他东西——
这种不对称性就是全部关键。一个扫描您目录树的漏洞扫描器就是一堵 404 的墙。
一个试探 admin 端点的机器人就是一堵 403 的墙。一次暴力破解就是一面失败的墙。
限流器将这些流量视为普通流量:它们按请求量减缓所有人的速度, 并在攻击者稍一放松时就立刻放行。Abuse Guard 则相反。 它完全忽略行为良好的流量,并将其唯一的响应——一个真正的、有时间限制的封禁—— 保留给那些以错误为特征的客户端。
使用限流器来塑造负载。使用 Abuse Guard 来驱逐滥用。
封禁是如何决定的
三个活动部件,全部在 worker 内部:
1 · 每个客户端的泄漏分数。 每个客户端身份在共享内存中携带一个小的数字。
每个匹配的错误默认加一分,或加上您为该状态码分配的权重;分数会以
阈值 ÷ 间隔 每秒的速度持续流失。短暂而猛烈的爆发会将其推过线;
缓慢的涓涓细流永远不会。关键在于,无论您将阈值设置得多高,
该分数都是一个固定大小的记录,因此单个 zone 可以轻松跟踪
僵尸网络抛给您的数万个不同源地址。
2 · 一个硬性截止时间。 当分数超过您的阈值的那一刻,
该客户端就获得了一个 blocked_until 时间戳。在此之前它就这样消失了——
每个请求都在 preaccess 阶段被拒绝,NGINX 不会花费任何周期在路由、
文件或 upstream 上。这种拒绝是最便宜的可能的处理结果。
3 · 一个隐私正确的拒绝。 被封禁的客户端会收到 429 Too Many Requests
(您可以选择状态码),并带有标记,使得任何共享缓存都无法存储它,
也就不会将一个客户端的惩罚提供给另一个客户端,同时带有 Retry-After
告诉诚实的客户端何时可以返回。
身份被折叠成固定大小的摘要,因此使用像 $request_uri 或某个 header
这样臃肿的内容作为键,与使用 IP 作为键所消耗的内存完全相同。
一分钟内上线
Abuse Guard 以预编译、签名的模块形式从 GetPageSpeed 仓库提供—— 直接安装即可,无需构建工具链。
sudo yum -y install https://extras.getpagespeed.com/release-latest.rpm
sudo yum -y install nginx-module-abuse-guard
配置它:
load_module modules/ngx_http_abuse_guard_module.so;
http {
abuse_guard_zone zone=clients:10m; # 一个共享内存 zone
server {
location / {
abuse_guard zone=clients; # 在此处执行
}
}
}
sudo nginx -t && sudo systemctl reload nginx
这些默认值会封禁任何在 5 分钟窗口内返回 100 个 403/404 响应的 IP,
封禁一小时。 您可以收紧或放宽下面的每个数字。
配置
Abuse Guard 有四个指令。第一个声明策略;其余的用于应用策略、 豁免某些人,以及(可选地)在多台机器之间共享策略。
声明策略 — abuse_guard_zone
一个 http 级别的指令。 它划分出一个共享内存 zone 并设置管理该 zone 的策略。
您可以根据需要设置尽可能多或尽可能少的参数——zone 的名称和大小是唯一必须提供的;
合理的默认值会填补其余部分(下面显示的值正是这些默认值)。
abuse_guard_zone zone=clients:10m ← 名称 + 大小(唯一必须项)
key=$binary_remote_addr ← 谁是“一个客户端”
statuses=403,404 ← 哪些响应会增加分数
interval=300s ← 计分窗口
threshold=100 ← 该窗口内的分数 → 触发封禁
block=60m; ← 封禁持续多长时间
zone=clients:10m 是策略的身份和预算:一个您从 abuse_guard
引用的名称,以及共享内存大小。大约 10 MB 可以跟踪大约十万个活动客户端。
其他一切都是可选的调优:
key— 定义单个客户端的表达式。可以是任何 NGINX 变量; 默认的$binary_remote_addr以源 IP 为键。如果某个请求的 key 解析为空,则该请求会被完全跳过(在下面的map场景中很方便)。statuses— 会增加分数的响应。单独的状态码或范围权重为 1, 保持原始语法:statuses=403,404,500-599。添加:weight可以使某个响应消耗更多的同一预算,例如statuses=404,401:5,500-599:2。 权重是 1 到 1024 之间的整数,范围会将其权重应用于其覆盖的每个状态码。 使用相同权重重复或重叠某个状态码是无害的;权重冲突会导致nginx -t失败。 默认值仍为403,404。interval— 分数衰减的窗口(默认300s)。窗口内的突发会触发封禁; 分散在更长时间内的缓慢涓流永远不会累积。threshold— 在该窗口内多少分才会越线,最高 1024(默认100)。 一个高权重的响应可以消耗剩余预算并立即触发封禁。block— 被触发的客户端保持封锁的时间(默认60m)。inactive— 一个不活跃的客户端在内存中停留多长时间后才被回收 (默认max(1h, interval, block);任何显式值必须至少与interval和block一样大)。redis—on表示将此 zone 的封禁信息复制到整个集群 (见下文);默认为off。persist— 一个文件路径,在 worker 干净退出时将活动封禁转储到该文件, 并在启动时加载。persist_secret— 在支持签名快照的构建中,一个十六进制密钥, 用于添加 HMAC-SHA256 认证,以便拒绝被篡改的文件。
为什么默认不包含
5xx: 服务器错误通常是您这边的问题, 将其计入会让一个有问题的后端导致无辜访客被封禁。 仅当您确实想要针对触发服务器错误的客户端采取行动时, 才添加statuses=403,404,500-599。
应用策略 — abuse_guard
在 http、server 和 location 块中均有效,因此您可以保护整个站点
或仅保护那些容易招致滥用的端点。指定 zone 名称即可启用;
在嵌套作用域中写入 abuse_guard off; 可将其关闭。
location /wp-login.php {
abuse_guard zone=clients status=429 log_level=warn;
}
zone— 此处应用的 zone(在上面声明)的策略。status— 被封禁客户端收到的状态码,范围在400–599之间 (默认429)。dry_run—on表示仅观察而不执行:判定会被记录但不会写入封禁。 默认为 off。log_level— 记录每个决策的详细程度:info、notice(默认)、warn或error。
使用 dry_run=on 放心地逐步上线。 它会记录每一个将要发出的封禁,
而不改变任何状态,因此您可以针对实时流量校准阈值——甚至可以与同一 zone 上
执行模式的 location 并行——然后切换为实时执行。
豁免好人 — abuse_guard_allow
上下文: http · server · location · 可重复,向下继承。
abuse_guard_allow 127.0.0.0/8;
abuse_guard_allow 10.0.0.0/8 192.168.0.0/16;
列出的客户端永远不会被计数,也永远不会被封禁。匹配基于真实的连接地址,
因此它与 realip 配合良好。这也是保护经过验证的搜索引擎爬虫的方法:
允许已发布的 Googlebot / Bingbot 地址范围,这样那些爬取过期 URL
(并积累 404)的机器人永远不会被捕获。
在集群中共享封禁 — abuse_guard_redis
上下文: http
abuse_guard_redis host=10.0.0.5 password=… ; # tls://host 用于 TLS
abuse_guard_zone zone=clients:10m redis=on;
将每个节点指向同一个 Redis 或 Valkey,打开 redis=on,
任何一台机器上产生的封禁都会传播到所有机器。默认值:port=6379、
db=0、prefix=ag_、timeout=100ms。它如何保持快速将在下一节说明。
SELinux: 在强制模式下(RHEL、Rocky、AlmaLinux),内核会阻止 NGINX
打开到 Redis 的连接,直到您允许一次——
setsebool -P httpd_can_network_connect 1。跳过此步骤,
复制将静默地不执行任何操作,而本地执行则照常进行。
一个封禁,所有节点——且不拖慢任何单个请求
在负载均衡器后面,单服务器的封禁只是做做样子:攻击者只需落在不同的节点上即可。 Abuse Guard 弥补了这一差距,而绝不会将 Redis 置于请求路径中。
每个节点在本地决策并在本地计数。一旦发出封禁,它就向集群广播这一个事实 并记录一个持久副本。其他每个节点都会在毫秒内导入它, 任何离线的节点在重新连接的那一刻就会进行同步。由于执行始终由每个节点 自己的内存状态提供,访客的请求永远不会等待网络往返—— 集群化的唯一代价是,一个新被封禁的攻击者会在一个心跳之后 才在整个集群范围内被拒之门外,而不是立即生效。
这里的 Redis 是一个单向的警铃,而不是每个请求都要查询的共享账本—— 因此,一个缓慢或不可用的 Redis 永远不会给您的流量增加延迟。 在私有网络上运行它,并将其视为特权服务:任何能写入它的人都能发出封禁。
比重启更持久的封禁
将 zone 指向一个文件,活动封禁会在 worker 干净退出时被转储, 然后在启动时恢复。重新加载和有序重启会保留当前的封禁, 而无需运行周期性的全 zone 写入器。进程或机器突然故障可能会丢失 自上次干净退出以来发出的封禁;执行仍然会以失败开放的方式继续。
abuse_guard_zone zone=clients:10m
persist=/var/lib/nginx/abuse_guard/clients.state
persist_secret=00112233445566778899aabbccddeeff;
紧凑的快照仅包含身份摘要和封禁截止时间。CRC32 检测损坏,
原子重命名防止部分写入进入活动路径。支持签名快照的构建
还可以使用 persist_secret 对其进行认证。保持该目录仅对 worker 用户可读。
查看它做出的所有决策
三个变量将 Abuse Guard 的判定暴露给您的日志和配置:
| 变量 | 值 |
|---|---|
$abuse_guard_status |
BYPASSED · PASSED · COUNTED · BLOCKED · DRY_RUN |
$abuse_guard_count |
当前加权分数,四舍五入为整数。 |
$abuse_guard_blocked_until |
封禁解除的 Unix 时间,或 0。 |
log_format guard '$remote_addr "$request" $status '
'guard=$abuse_guard_status count=$abuse_guard_count';
在 CDN 或代理后面进行键控? 永远不要信任原始的 X-Forwarded-For。
先让 realip 解析出真实客户端,然后以 $binary_remote_addr 为键:
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
需要按请求的豁免逻辑?任何 key 解析为空字符串的请求都会被忽略——
因此 map 可以让您,例如,按 IP 跟踪匿名访客,同时让已认证用户不受影响。
为生产环境的信任而设计
Abuse Guard 的标准远高于“它能编译”。每次更改都要经过 AddressSanitizer、 UndefinedBehaviorSanitizer、Valgrind、静态分析以及对其解析器和磁盘格式的 持续模糊测试。其可选依赖——集群和签名快照——在设计上就是尽力而为: 如果 Redis 或磁盘出现异常,执行会安静地从本地内存继续。 您的流量永远不会被某个依赖所挟持。
获取 Abuse Guard
Abuse Guard 是来自 GetPageSpeed LLC 的商业 NGINX 模块, 通过 GetPageSpeed 订阅提供持续更新和支持。
- 浏览完整的 NGINX 模块目录 → https://nginx-extras.getpagespeed.com/modules/
- 许可、大规模部署或需要帮助进行设置 → getpagespeed.com/contact-us
© GetPageSpeed LLC. 保留所有权利。