不能靠 cron 每分钟跑一次“全量统计 + 封禁”实现高频 IP 防御,因 iptables 无时间戳、日志延迟至少 60 秒;应采用 hashlimit 内核级限速 + LOG 日志提取 + 脚本持久化封禁的双层方案。

如何用iptables + shell脚本实时封禁每分钟请求超限的IP
直接说结论:不能靠 cron 每分钟跑一次“全量统计 + 封禁”来实现真正有效的高频 IP 防御。原因很简单——iptables 本身不记录连接时间戳,纯靠 last、auth.log 或 access.log 统计会有至少 60 秒延迟,且无法区分是单次大流量还是持续扫描。真正可行的做法是用 iptables 的 hashlimit 模块做内核级限速,再辅以轻量脚本做“超标后持久化封禁”。
核心思路是分两层:第一层用 iptables -m hashlimit 实时拦截(毫秒级),第二层用脚本定期扫描 iptables -L -n --line-numbers 输出中被 hashlimit 触发的 DROP 记录,提取源 IP 并追加到永久封禁链(如 INPUT 链顶部的 REJECT 规则)。
为什么不能只用 iptables -m hashlimit 做最终封禁
hashlimit 是状态型限速模块,它只在匹配窗口内临时丢包,不改变规则本身,也不记录“谁被限过”。一旦连接间隔拉长或连接数回落,IP 就自动解封——这对防暴力破解或爬虫不够用。
-
hashlimit的--hashlimit-burst和--hashlimit-upto控制的是“速率窗口”,不是“封禁动作” - 它没有日志开关(
--log-prefix必须配合-j LOG才生效),单纯-j DROP不留痕迹 - 重启
iptables或服务器后,所有hashlimit状态清空,无持久性
怎么写一个可靠的每分钟检测脚本
关键不是“每分钟执行”,而是“每次执行时精准识别出刚被hashlimit 拦截的 IP”。推荐做法是:
- 给
hashlimit规则加上-j LOG --log-prefix "HASHLIMIT_DROP: ",并确保sysctl net.netfilter.nf_log.2 = 1(启用 ipt_LOG) - 用
journalctl -u systemd-journald --since "1 minute ago" | grep "HASHLIMIT_DROP"或读/var/log/kern.log(取决于 rsyslog 配置) - 用
awk '{print $NF}' | grep -E 'SRC=[0-9.]+' | cut -d= -f2 | sort -u提取唯一 IP - 对每个 IP,执行
iptables -C INPUT -s <code>$ip-j REJECT 检查是否已存在;不存在则插入:iptables -I INPUT 1 -s <code>$ip-j REJECT - 为避免规则爆炸,脚本开头可先清理 24 小时前的日志匹配 IP(需额外维护时间戳文件或用
iptables -S解析注释)
#!/bin/bash
LOG_PREFIX="HASHLIMIT_DROP:"
IPS=$(dmesg -t | awk -v prefix="$LOG_PREFIX" '$0 ~ prefix {for(i=1;i<=NF;i++) if($i ~ /^SRC=/) print $i}' | \
cut -d= -f2 | sort -u | head -50)
for ip in $IPS; do
[[ $(iptables -C INPUT -s "$ip" -j REJECT 2>/dev/null; echo $?) == 0 ]] && continue
iptables -I INPUT 1 -s "$ip" -j REJECT -m comment --comment "auto-ban-$(date +%s)"
done
容易忽略的三个实际问题
第一,hashlimit 默认使用“每连接”哈希,如果攻击者复用 TCP 连接(比如 HTTP/1.1 keep-alive),会误判为低频;应加 --hashlimit-mode srcip,dstport 或至少 srcip。
第二,脚本里用 dmesg 读内核日志虽快,但默认只缓存最近几万行,高频攻击下可能丢记录;生产环境建议改用 journalctl -o json --since "1 min ago" 并解析 _HOSTNAME 和 MESSAGE 字段。
第三,iptables -I INPUT 1 插入规则虽保证优先级,但频繁增删会导致规则链变长、匹配变慢;更稳的做法是维护一个独立链(如 ATTACKERS),主链首条跳转过去,脚本只更新该链内容。
ab -n 1000 -c 100 http://test/ 模拟压测,观察 iptables -L -nv 中各链包计数变化是否符合预期。


















