启用Fail2ban后系统负载升高,如何从封禁日志、连接数与进程状态定位
本文介绍如何对齐封禁日志、TCP连接、CPU、内存、磁盘I/O及进程状态,判断负载升高源于外部攻击、日志匹配还是防火墙动作,并给出配置调整与恢复验证方法,适合Linux服务器运维人员参考。

启用 Fail2ban 后,服务器可能同时出现 load average 上升、登录变慢、磁盘等待增加等现象,但时间上先后发生,并不代表负载一定由封禁服务本身造成。更常见的链路是:外部失败连接增加,业务或认证日志随之增长,过滤器持续匹配并触发封禁,防火墙规则被频繁更新,最终由其中一个或多个环节形成瓶颈。
排查的关键是把同一时间窗口内的五类数据对齐:CPU、内存、磁盘 I/O、TCP 连接状态和进程状态,再与 Found、Ban、Unban 事件增量比较。只有资源占用与匹配或封禁事件同步增长,并能进一步落到日志扫描、正则匹配或防火墙动作上,才应将根因定位到 Fail2ban 处理链路;如果连接数和应用负载先升高,它通常只是攻击压力的后续处理者。
保留现场并确定观察窗口
不要一开始就执行 systemctl restart fail2ban。重启虽然可能暂时降低负载,却会改变进程状态;部分 action 还会在服务停止时撤销已有封禁规则,导致关键线索消失。
先记录故障开始时间,并检查版本、服务状态和活跃 jail:
cat /etc/os-release
fail2ban-client -V
sudo systemctl status fail2ban --no-pager -l
sudo fail2ban-client ping
sudo fail2ban-client status
以上命令适用于使用 systemd 的常见 Linux 发行版。旧版系统、容器环境或自行编译安装的实例,服务名和路径可能不同,应先核对实际进程:
systemctl list-unit-files | grep -i fail2ban
ps -ef | grep '[f]ail2ban'
后续检查最好使用相同长度、相近时间的窗口。例如封禁日志统计最近 10 分钟,连接数和资源数据也应在该时段采集,不能用当前连接快照解释数小时前的封禁记录。
先区分是哪一种资源在升高
load average 不是 CPU 使用率,它同时反映可运行任务和不可中断任务。负载升高后,应先判断任务是在等待 CPU、内存还是 I/O:
uptime
free -h
vmstat 1 10
ps -eo pid,ppid,stat,pcpu,pmem,rss,etime,cmd --sort=-pcpu | head -n 20
可以按以下条件判断:
| 观察结果 | 更可能的方向 | 下一步 |
|---|---|---|
fail2ban-server CPU 持续较高 |
日志量大、过滤范围过宽或正则匹配成本高 | 检查 Found 增量、活跃 jail 和过滤器 |
CPU 不高,但 vmstat 的 b、wa 上升,进程出现 D 状态 |
日志写入或其他磁盘 I/O 阻塞 | 检查磁盘队列及日志写入进程 |
si、so 持续变化 |
整机内存不足并频繁交换 | 对比各进程 RSS、swap 和 OOM 记录 |
大量短时 iptables、nft 或 firewall-cmd 进程 |
Ban/Unban 频繁触发防火墙动作 | 检查封禁事件和规则规模 |
| 连接数、认证日志与应用进程负载先上升 | 外部扫描或攻击是压力源 | 先检查入口连接,再评估封禁链路 |
进程状态中,R 表示正在运行,S 通常是正常休眠,D 表示不可中断 I/O 等待,Z 表示僵尸进程。若 Fail2ban 自身 CPU 不高,而大量业务进程处于 D 状态,就不能仅凭负载均值归因于它。
安装了 sysstat 的系统可以继续检查进程和磁盘:
PID=$(pgrep -xo fail2ban-server)
printf 'fail2ban PID: %s\n' "$PID"
sudo pidstat -p "$PID" 1 10
sudo pidstat -d -p "$PID" 1 10
iostat -xz 1 10
如果 PID 为空,应先执行 pgrep -af fail2ban 确认真实进程名。pidstat、iostat 不存在时,需要通过当前发行版的软件包管理器安装 sysstat,生产环境应遵守变更流程。
内存问题还应检查是否发生过 OOM:
sudo journalctl -k --since "30 minutes ago" --no-pager |
grep -Ei 'out of memory|oom|killed process'
单独看到系统使用了 swap,不足以证明封禁服务存在内存泄漏。只有其 RSS 持续增长,并与 jail 状态、事件量变化相符,才值得继续沿该进程排查。
将封禁事件与连接状态对齐
日志中的三个关键事件可以区分“匹配成本”和“动作成本”:
Found:过滤器发现符合条件的失败记录;Ban:失败次数达到条件后执行封禁;Unban:封禁到期或经管理员操作后解除封禁。
使用 systemd journal 的环境可以查看指定时间段:
sudo journalctl -u fail2ban --since "10 minutes ago" --no-pager
如果配置写入独立文件,常见位置是 /var/log/fail2ban.log,但必须以实际 logtarget 为准:
sudo tail -n 200 /var/log/fail2ban.log
统计固定窗口内的事件增量:
for event in Found Ban Unban; do
printf '%-7s ' "$event"
sudo journalctl -u fail2ban --since "5 minutes ago" --no-pager |
grep -c " ${event} "
done
如果日志不在 journal 中,应将输入替换为实际文件,并根据真实日志格式调整匹配条件。日志可能包含 IP、用户名等信息,对外提供前应先脱敏。
不同事件组合代表不同排查方向:
Found很多而Ban较少:大量记录进入匹配流程,但只有少数来源达到封禁条件。应检查日志量是否异常、logpath是否过宽、多个 jail 是否重复读取同一日志,以及过滤器是否匹配了无关记录。这类情况更容易使主进程 CPU 升高。Ban和Unban都很频繁:封禁时间可能较短,而攻击来源持续重试,系统不断添加和删除规则。此时开销可能主要来自防火墙命令,而不是fail2ban-server。- 事件量不高但负载仍高:继续检查应用、数据库、日志服务和磁盘状态,不应强行归因于封禁处理。
查看具体 jail 时,先取得真实名称,再检查状态:
sudo fail2ban-client status
JAIL=sshd
sudo fail2ban-client status "$JAIL"
sshd 只是示例,需要替换为实际 jail。重点比较 Currently failed、Total failed、Currently banned 和 Total banned。累计值不能代表当前压力,应间隔一段时间重复执行,观察增量。
与此同时检查 TCP 状态:
ss -s
ss -Htan | awk '{state[$1]++} END {for (s in state) print s, state[s]}' | sort
printf 'SYN_RECV: '
ss -Htan state syn-recv | wc -l
printf 'ESTABLISHED: '
ss -Htan state established | wc -l
printf 'TIME_WAIT: '
ss -Htan state time-wait | wc -l
sudo ss -lntp
判断时不要孤立使用某一种连接状态:
SYN_RECV、认证失败日志和Found同时上升,说明外部连接正在推动日志匹配;- 连接数平稳,但
Found突然增加,应检查应用是否重复写日志、日志格式是否变化或过滤器是否误匹配; - 连接数和
Found都不高,但 Ban/Unban 密集,应检查封禁时间和 action 执行情况; TIME_WAIT较多本身既不能证明遭受攻击,也不能证明配置异常,必须结合端口、增长趋势和应用日志判断。
如果服务位于反向代理之后,还要确认日志记录的是实际客户端地址还是代理地址。来源地址识别错误会造成封禁无效,也可能误封代理节点。
检查进程与防火墙动作是否形成瓶颈
当 Ban 日志密集而主进程 CPU 不高时,检查是否持续创建防火墙相关进程:
ps -eo pid,ppid,stat,pcpu,pmem,etime,cmd |
grep -E '[f]ail2ban|[i]ptables|[n]ft|[f]irewall-cmd'
再根据服务器实际使用的防火墙框架选择一种检查方式,不要把 iptables 与 nftables 的结果混为一谈:
sudo iptables-save | grep -c 'f2b-'
或:
sudo nft list ruleset | grep -c 'f2b-'
规则集很大时,完整输出本身可能产生短时开销,并暴露地址信息。应尽量在低峰期执行,或先检查当前 jail、backend、filter 和 action:
sudo fail2ban-client -d | less
不同版本、发行版和防火墙后端的 action 名称可能不同,不应直接复制其他服务器的 banaction。
若怀疑日志轮转或重复读取,可检查对应业务日志的 inode、大小和打开状态:
sudo ls -li /实际/业务日志路径
sudo stat /实际/业务日志路径
sudo lsof /实际/业务日志路径
文件被频繁截断、重建,或应用与轮转工具处理方式不一致时,可能产生额外读取和写入。检查路径必须替换为目标 jail 实际监听的日志,不能假设所有 jail 都读取 /var/log/auth.log。
按根因调整配置并保留回滚能力
如果定位到日志扫描或正则匹配,应优先缩小 logpath 或 journal 匹配范围,停用没有保护对象的 jail,避免多个 jail 重复扫描高流量日志,并检查自定义正则是否过度宽泛或存在高回溯成本。
可以复制少量日志离线测试。临时文件可能包含敏感信息,应限制权限并在测试后删除:
TMPFILE=$(mktemp)
chmod 600 "$TMPFILE"
sudo tail -n 10000 /实际/业务日志路径 > "$TMPFILE"
sudo fail2ban-regex "$TMPFILE" /etc/fail2ban/filter.d/实际过滤器.conf
rm -f "$TMPFILE"
匹配数量高不等于规则正确,还要抽查被匹配、忽略和未匹配的记录,确认正常请求没有被识别为失败。
如果问题来自频繁 Ban/Unban,则应结合正常用户行为和攻击频率调整:
findtime:累计失败次数的时间窗口;maxretry:窗口内触发封禁的失败次数;bantime:封禁持续时间。
bantime 过短且来源持续重试,会反复执行 Ban 和 Unban;设置过长则会扩大误封影响和活动封禁集合。不要为了降低负载随意增大参数,也不要将大网段直接加入 ignoreip。后者会让对应来源完全绕过检查,只适用于明确可信且不受代理、NAT 或地址复用影响的来源。
修改配置时,应优先使用 /etc/fail2ban/jail.local 或 /etc/fail2ban/jail.d/*.local,不要直接覆盖软件包提供的 jail.conf。修改前确认实际配置文件并创建备份;如果 jail.local 存在,可执行:
sudo cp -a /etc/fail2ban/jail.local \
"/etc/fail2ban/jail.local.bak.$(date +%F-%H%M%S)"
完成修改后先测试语法,再重新加载:
sudo fail2ban-client -t
sudo fail2ban-client reload
sudo fail2ban-client ping
sudo fail2ban-client status
测试或加载失败时,应恢复对应备份,再次执行配置测试。部分 action 或底层防火墙变更可能需要重启才能完全生效,应根据当前版本输出并在维护窗口处理,不能把重启作为首选排障手段。
如果连接数、应用进程负载和日志写入量先于匹配事件上升,仅优化 jail 无法消除入口压力。此时应核对是否暴露了不需要公开访问的端口、登录入口能否限制来源、代理是否正确传递客户端地址,以及应用是否在认证查询、密码校验或日志写入环节消耗了主要资源。
用同一窗口验证恢复并监控复发
调整后应使用与故障期相同长度的窗口重复采集:
uptime
vmstat 1 10
ss -s
sudo fail2ban-client status
ps -eo pid,ppid,stat,pcpu,pmem,rss,etime,cmd --sort=-pcpu | head -n 20
不要只看负载均值是否立即下降。已有磁盘队列、不可中断任务和 TIME_WAIT 连接都需要时间消退。恢复是否成立,应同时满足以下可验证条件:
Found、Ban、Unban的增长速度回到可解释范围;- 主进程不再持续占用高 CPU,RSS 也没有无依据地增长;
vmstat中运行队列、I/O 等待和交换活动下降;- 防火墙相关命令不再被高频创建;
- 正常登录仍然可用,恶意失败请求仍能被目标 jail 正确封禁;
- 服务日志没有持续出现配置、正则或 action 执行错误。
后续监控应分别记录 TCP 状态、业务失败日志量、各 jail 的匹配与封禁增量、进程 CPU/RSS、磁盘等待和防火墙规则规模。只有将这些指标按时间关联起来,负载再次升高时才能迅速区分:是外部连接推动了整个处理链路,还是日志匹配或封禁动作本身成为了性能瓶颈。