Nginx日志触发iptables封禁需统一时间格式(用%b匹配Aug)、过滤200/301/302状态码、防重复封禁、持久化规则并记录封禁日志,建议每1–2分钟cron执行分析脚本。

直接用 Nginx 日志触发 iptables 封禁,核心是“日志分析 + 规则写入”,不依赖额外服务,轻量、可控、适合中小规模运维场景。关键不在脚本多复杂,而在时间匹配、阈值合理、避免误封。
确保日志格式与时间解析对得上
Nginx 默认日志时间格式为 [07/Aug/2025:10:00:00 +0800],而多数 shell 脚本用 date +"%d/%m/%Y:%H:%M:%S" 生成的是 07/08/2025:10:00:00 —— 月份缩写(Aug)和数字(08)不一致,grep 或 awk 就筛不到数据。必须统一:
- 要么改脚本,用
date +"%d/%b/%Y:%H:%M:%S"匹配%b(如 Aug) - 要么改 Nginx 日志格式,在
log_format中用$time_local并自定义为数字月,例如:log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';配合time_format %d/%m/%Y:%H:%M:%S(需 Nginx ≥ 1.19.10) - 更稳妥的做法:用
tac access.log | awk '{t=substr($4,2,20)} t ~ /^'"$(date -d '1 minute ago' +'%d\/%b\/%Y:%H:%M')"'/{print $1}'提取时间字段并截取比对
封禁逻辑要防重复、防漏判
单纯统计 IP 出现次数容易误伤(比如 CDN 回源、合法爬虫),建议加两层过滤:
- 只统计返回状态码为 200、301、302 的请求(排除大量 404 扫描干扰):在 awk 中加条件
$9 ~ /^(200|301|302)$/ - 检查 IP 是否已存在 iptables 规则,避免重复插入:用
iptables -C INPUT -s $ip -j DROP > /dev/null || iptables -I INPUT -s $ip -j DROP - 封禁前记录原始日志片段(如 top 5 请求 URL),方便事后人工复核
封禁后必须持久化且可回溯
iptables 规则重启即失效,必须保存;同时封禁行为本身也要留痕:
- 执行
iptables-save > /etc/sysconfig/iptables(CentOS 7)或netfilter-persistent save(Ubuntu) - 把每次封禁的 IP、时间、触发次数、日志采样写入独立文件,例如:
echo "$(date '+%F %T') $ip ($count req in 60s) $(tail -n10 $LOG_FILE | grep $ip | head -n3)" >> /var/log/nginx/ip_block_history.log - 配合 logrotate 每日切割封禁日志,防止无限增长
建议搭配 cron 定时运行,而非实时监听
用 inotifywait 实时监听日志变动看似高效,但高并发下易丢事件、增加系统负载。更稳的做法是每 1–2 分钟 cron 执行一次分析脚本:
- 编辑 crontab:
*/2 * * * * /usr/local/bin/ip_block.sh > /dev/null 2>&1 - 脚本内限定只分析最近 90 秒日志(覆盖 cron 延迟),避免重复处理旧数据
- 首次运行前手动执行一次,确认规则能正常写入、日志路径无权限问题


















