[linux][shell]通过分析 Nginx 的访问日志,检测异常 IP 地址并使用iptables 将其封禁

这段脚本的作用是通过分析 Nginx 的访问日志,检测异常的 IP 地址,并使用 iptables 封禁这些 IP。

bash 复制代码
#!/bin/bash

# 配置变量
LOG_FILE="/usr/local/nginx/logs/access.log"
THRESHOLD=10
DROP_LOG_FILE="/tmp/drop_ip.log"
DATE=$(date +"%d/%b/%Y:%H:%M")

# 检测异常 IP
ABNORMAL_IP=$(tail -n5000 "$LOG_FILE" | grep "$DATE" | awk '{a[$1]++} END {for(i in a) if(a[i]>'$THRESHOLD') print i}')

# 封禁异常 IP
for IP in $ABNORMAL_IP; do
    # 检查 IP 是否已经被封禁
    if ! iptables -C INPUT -s "$IP" -j DROP &> /dev/null; then
        # 封禁 IP
        iptables -I INPUT -s "$IP" -j DROP
        echo "$(date +'%F_%T') $IP - Access count exceeded threshold ($THRESHOLD)" >> "$DROP_LOG_FILE"
    fi
done

以下是脚本的详细解析和一些需要注意的地方:

脚本功能

  1. 配置变量

    • LOG_FILE:指定 Nginx 的访问日志文件路径。

    • THRESHOLD:设置访问次数的阈值,超过此值的 IP 将被认定为异常。

    • DROP_LOG_FILE:记录被封禁 IP 的日志文件路径。

    • DATE:获取当前时间,格式为 日/月/年:时:分,用于筛选日志中当前时间的记录。

  2. 检测异常 IP

    • 使用 tail -n5000 "$LOG_FILE" 获取日志文件的最后 5000 行。

    • 使用 grep "$DATE" 筛选出当前时间的记录。

    • 使用 awk 统计每个 IP 的访问次数,并筛选出访问次数超过阈值的 IP。

  3. 封禁异常 IP

    • 遍历检测到的异常 IP。

    • 使用 iptables -C INPUT -s "$IP" -j DROP 检查该 IP 是否已经被封禁。

    • 如果未被封禁,则使用 iptables -I INPUT -s "$IP" -j DROP 将其封禁,并将封禁信息记录到 DROP_LOG_FILE 中。

注意事项

  1. 日志格式依赖

    • 脚本假设 Nginx 的日志格式是标准的 Combined Log Format,其中第一个字段是 IP 地址。如果日志格式不同,需要调整 awk 的字段索引。
  2. 时间格式匹配

    • DATE 的格式需要与 Nginx 日志中的时间格式完全匹配。如果日志中的时间格式不同(例如包含毫秒或时区信息),脚本可能无法正确筛选记录。
  3. 权限问题

    • 脚本需要以 root 用户运行,因为 iptables 命令需要管理员权限。
  4. 性能问题

    • 如果日志文件非常大,tail -n5000grep 操作可能会消耗较多资源。可以考虑优化日志处理方式,例如定期清理日志或使用更高效的数据处理工具。
  5. 误封风险

    • 脚本简单地根据访问次数封禁 IP,可能会误封正常用户(例如爬虫或高频访问的合法用户)。建议结合其他检测机制(如访问频率、请求类型等)来提高准确性。
  6. 日志记录

    • 封禁日志记录到 /tmp/drop_ip.log,但 /tmp 目录可能会被定期清理。建议将日志存储到更安全的路径,例如 /var/log

改进建议

  • 日志格式检查:在脚本开头检查日志文件是否存在以及是否符合预期格式。

  • 动态阈值:根据流量情况动态调整阈值,而不是固定值。

  • 白名单机制:维护一个白名单,避免误封重要 IP。

  • 持久化封禁规则 :使用 iptables-saveiptables-restore 保存和恢复规则,避免系统重启后规则丢失。

  • 日志清理:定期清理封禁日志文件,避免文件过大。