一、概述
1.1 背景介绍
暴力破解(Brute Force Attack)是最原始也是最有效的攻击手段之一。攻击者通过自动化工具对 SSH、Web 登录、数据库等服务进行大量密码尝试,直到命中正确的凭据。根据公网蜜罐数据统计,一台新上线的 Linux 服务器暴露 SSH 端口后,平均 5 分钟内就会收到第一次暴力破解尝试,每天被扫描数千次是常态。
暴力破解攻击的危害不仅在于密码被破解本身:
| 危害类型 | 具体说明 |
|---|---|
| 凭据泄露 | 弱密码被破解后攻击者获取系统权限 |
| 资源消耗 | 大量认证请求消耗 CPU 和内存 |
| 日志膨胀 | auth.log/secure 日志文件快速增长 |
| 影响正常访问 | 连接数被恶意请求占满 |
| 合规风险 | 未防护暴力破解不符合等保要求 |
fail2ban 的工作原理
fail2ban 是一个入侵防御框架,核心机制是日志监控 + 自动封禁:
日志文件 fail2ban 防火墙
(auth.log) 引擎
|
新日志行 ------> Filter(正则匹配)
|
匹配失败特征?
| |
是 否(忽略)
|
计数器 +1
|
超过阈值?
| |
是 否(继续计数)
|
Action(执行封禁) ------> iptables/nftables
| 添加 DROP 规则
记录封禁日志
|
启动解封定时器 ------> 到期自动解封
核心概念:
Filter:定义匹配规则的正则表达式,从日志中识别失败的认证尝试
Jail:一个监控单元,包含 filter + action + 参数(阈值/时间窗/封禁时间)
Action:匹配后执行的操作,通常是防火墙封禁
Ban/Unban:封禁和解封操作
1.2 与其他防御手段对比
| 工具 | 原理 | 优势 | 劣势 |
|---|---|---|---|
| fail2ban | 日志正则匹配 | 通用性强,支持任意日志格式的服务 | 依赖日志实时写入 |
| DenyHosts | 解析 /etc/hosts.deny | 简单轻量 | 仅支持 SSH,项目已不活跃 |
| SSHGuard | 日志解析 | 多协议支持,低资源占用 | 自定义规则不如 fail2ban 灵活 |
| CrowdSec | 日志分析 + 社区威胁情报 | 共享封禁列表,现代架构 | 部署复杂,需要联网同步 |
| 防火墙限速 | 连接频率限制 | 不依赖日志 | 无法区分正常和恶意请求 |
fail2ban 在单机防护场景下是最成熟的选择:配置灵活、文档丰富、社区活跃、资源消耗低。对于大规模集群,可以考虑 CrowdSec 或 fail2ban + 集中式日志的组合方案。
1.3 适用场景
SSH 服务暴力破解防护
Nginx/Apache 的 HTTP Basic Auth 暴力破解防护
Web 应用登录接口的暴力破解防护
恶意扫描(路径遍历、漏洞探测)过滤
MySQL/PostgreSQL 远程登录保护
邮件服务(Postfix/Dovecot)暴力破解防护
自定义应用日志的异常行为检测
1.4 环境要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 24.04 LTS / Rocky Linux 9.5 | 内核 6.12+ |
| fail2ban | 1.1.x | 当前稳定版 |
| Python | 3.12+ | fail2ban 运行依赖 |
| iptables/nftables | 系统自带 | 封禁后端 |
| firewalld | 2.x(可选) | Rocky Linux 默认 |
| rsyslog/systemd-journald | 系统自带 | 日志来源 |
# Ubuntu 24.04 安装 sudo apt update sudo apt install -y fail2ban # Rocky Linux 9.5 安装(需要 EPEL 源) sudo dnf install -y epel-release sudo dnf install -y fail2ban fail2ban-firewalld # 检查版本 fail2ban-client version # Fail2Ban v1.1.0 # 检查服务状态 sudo systemctl status fail2ban
二、详细步骤
2.1 配置文件结构
fail2ban 的配置文件层级:
/etc/fail2ban/ ├── fail2ban.conf # 全局配置(日志级别、socket 路径等) ├── fail2ban.local # 全局配置覆盖(自定义项写在这里) ├── jail.conf # 默认 jail 定义(不要直接修改) ├── jail.local # jail 自定义配置(所有自定义都写在这里) ├── jail.d/ # jail 片段配置目录 │ └── defaults-debian.conf ├── filter.d/ # filter 正则定义 │ ├── sshd.conf │ ├── nginx-http-auth.conf │ └── ... ├── action.d/ # action 动作定义 │ ├── iptables-multiport.conf │ ├── nftables-multiport.conf │ ├── firewallcmd-rich-rules.conf │ └── ... └── paths-*.conf # 不同发行版的路径定义
核心原则:永远不要修改.conf文件,所有自定义配置写在.local文件中。fail2ban 会先读取.conf,再用.local覆盖。
2.2 基础配置
创建 jail.local
sudo cat > /etc/fail2ban/jail.local << 'EOF' [DEFAULT] # 封禁时间(秒),默认 10 分钟 bantime = 3600 # 检测时间窗口(秒),在这个时间窗口内达到阈值就封禁 findtime = 600 # 失败次数阈值 maxretry = 5 # 封禁动作(Ubuntu 用 iptables,Rocky 用 firewallcmd) # Ubuntu 24.04: banaction = iptables-multiport banaction_allports = iptables-allports # Rocky Linux 9.5(取消上面两行注释,使用下面的): # banaction = firewallcmd-rich-rules # banaction_allports = firewallcmd-rich-rules # 忽略的 IP(不会被封禁) ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 # 封禁时间递增(重复违规者封禁时间翻倍) bantime.increment = true bantime.factor = 2 bantime.maxtime = 604800 # 通知邮箱(可选) # destemail = admin@example.com # sender = fail2ban@example.com # mta = sendmail # action = %(action_mwl)s # 后端(自动检测,推荐 systemd) backend = systemd [sshd] enabled = true port = ssh filter = sshd maxretry = 3 findtime = 300 bantime = 3600 EOF
启动服务
# 检查配置语法 sudo fail2ban-client -t # OK: configuration test is successful # 启动并设置开机自启 sudo systemctlenable--now fail2ban # 查看运行状态 sudo fail2ban-client status # Status # |- Number of jail: 1 # `- Jail list: sshd
2.3 SSH 防护配置
SSH 暴力破解是最常见的攻击类型。fail2ban 内置的 sshd filter 覆盖了大部分场景。
# 查看 sshd filter 内置的匹配规则 cat /etc/fail2ban/filter.d/sshd.conf
sshd filter 能匹配的日志模式包括:
| 日志模式 | 含义 |
|---|---|
|
Failed password for |
密码认证失败 |
|
Failed publickey for |
公钥认证失败 |
|
Invalid user |
不存在的用户名 |
| Connection closed by authenticating user | 认证过程中断开 |
| maximum authentication attempts exceeded | 超过最大认证次数 |
增强的 SSH 防护配置
# /etc/fail2ban/jail.local 中的 [sshd] 部分 [sshd] enabled = true port = ssh filter = sshd[mode=aggressive] # aggressive 模式包含更多匹配规则,会匹配 "Invalid user" 等 maxretry = 3 findtime = 300 bantime = 3600 # 如果 SSH 使用非标准端口 # port = 2222
验证 SSH 防护是否生效
# 查看 sshd jail 状态 sudo fail2ban-client status sshd # Status for the jail: sshd # |- Filter # | |- Currently failed: 2 # | |- Total failed: 47 # | `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd # `- Actions # |- Currently banned: 3 # |- Total banned: 15 # `- Banned IP list: 103.xx.xx.92 45.xx.xx.201 185.xx.xx.33 # 查看 iptables 中的封禁规则 sudo iptables -L f2b-sshd -n -v # Chain f2b-sshd (1 references) # pkts bytes target prot opt in out source destination # 234 14040 REJECT all -- * * 103.xx.xx.92 0.0.0.0/0 # 156 9360 REJECT all -- * * 45.xx.xx.201 0.0.0.0/0
2.4 Nginx 防护配置
HTTP Basic Auth 暴力破解防护
# /etc/fail2ban/jail.local 追加 [nginx-http-auth] enabled = true port = http,https filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 5 findtime = 300 bantime = 3600
Nginx 恶意扫描防护
fail2ban 默认不包含通用的 Nginx 恶意扫描 filter,需要自定义:
# 创建自定义 filter sudo cat > /etc/fail2ban/filter.d/nginx-badbots.conf << 'EOF' [Definition] # 匹配常见恶意扫描特征 failregex = ^.*"(GET|POST|HEAD) .*(.php|.asp|.aspx|.jsp|.cgi|.env|wp-login|wp-admin|phpmyadmin|.git|.svn|config.|.bak|.sql|shell|eval|base64).*"(400|403|404|444) ^ .*"(GET|POST) /"[0-9]+ [0-9]+"-"".*(?:masscan|zgrab|python-requests|Go-http-client|Scrapy|curl/|wget/).*" ignoreregex = EOF
# /etc/fail2ban/jail.local 追加 [nginx-badbots] enabled = true port = http,https filter = nginx-badbots logpath = /var/log/nginx/access.log maxretry = 3 findtime = 60 bantime = 86400
Nginx CC 攻击防护
# 创建 CC 攻击检测 filter sudo cat > /etc/fail2ban/filter.d/nginx-cc.conf << 'EOF' [Definition] # 匹配短时间内同一 IP 的高频请求(需要在 Nginx 中配置 limit_req 返回 429 或 503) failregex = ^.*"(GET|POST|PUT|DELETE) .*"(429|503) .*$ limiting requests, excess: .* by zone .*, client: ignoreregex = EOF
# /etc/fail2ban/jail.local 追加
[nginx-cc]
enabled = true
port = http,https
filter = nginx-cc
logpath = /var/log/nginx/error.log
/var/log/nginx/access.log
maxretry = 30
findtime = 60
bantime = 600
2.5 自定义 filter 正则编写
编写自定义 filter 是 fail2ban 最核心的技能。
正则编写规则
# fail2ban filter 正则语法 #- 特殊标记,匹配 IP 地址并作为封禁目标 # ^ - 行首(fail2ban 自动处理时间戳前缀) # .* - 任意字符 # 其他 - 标准 Python 正则语法
测试 filter 是否生效
# 测试 filter 规则是否匹配日志 sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf # 测试自定义 filter sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-badbots.conf # 测试单行日志 echo'103.1.2.3 - - [13/Mar/202600:00 +0800] "GET /wp-login.php HTTP/1.1" 404 0'| sudo fail2ban-regex - /etc/fail2ban/filter.d/nginx-badbots.conf # 输出示例 # Results # ======= # Failregex: 1 total # Ignoreregex: 0 total # Date template hits: ... # Lines: 1 lines, 0 ignored, 1 matched, 0 missed
自定义 API 暴力破解防护
假设应用日志格式如下:
2026-03-13 1045 [WARN] Login failedforuser admin from 103.1.2.3 2026-03-13 1046 [WARN] Login failedforuser admin from 103.1.2.3
编写 filter:
sudo cat > /etc/fail2ban/filter.d/myapp-login.conf << 'EOF' [Definition] failregex = ^s*[WARN]s+Login failed for user .* froms*$ ignoreregex = datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S EOF
配置 jail:
# /etc/fail2ban/jail.local 追加 [myapp-login] enabled = true port = 8080 filter = myapp-login logpath = /var/log/myapp/application.log maxretry = 5 findtime = 300 bantime = 1800
2.6 多服务防护配置
MySQL 远程登录防护
[mysqld-auth] enabled = true port = 3306 filter = mysqld-auth logpath = /var/log/mysql/error.log maxretry = 5 findtime = 600 bantime = 3600
FTP 防护(vsftpd)
[vsftpd] enabled = true port = ftp,ftp-data,ftps,ftps-data filter = vsftpd logpath = /var/log/vsftpd.log maxretry = 5 findtime = 600 bantime = 3600
Postfix SMTP 防护
[postfix] enabled = true port = smtp,465,submission filter = postfix[mode=auth] logpath = /var/log/mail.log maxretry = 5 findtime = 300 bantime = 3600
2.7 与 firewalld/nftables 联动
firewalld 联动(Rocky Linux)
# /etc/fail2ban/jail.local 的 [DEFAULT] 部分 [DEFAULT] banaction = firewallcmd-rich-rules banaction_allports = firewallcmd-rich-rules
验证:
# 查看 firewalld 中的 fail2ban 规则 sudo firewall-cmd --list-rich-rules # rule family="ipv4" source address="103.xx.xx.92" reject type="icmp-port-unreachable"
nftables 联动
# /etc/fail2ban/jail.local 的 [DEFAULT] 部分 [DEFAULT] banaction = nftables-multiport banaction_allports = nftables-allports
验证:
# 查看 nftables 中的 fail2ban 规则 sudo nft listsetinet f2b-table addr-set-sshd
2.8 启动和验证
# 重新加载配置 sudo fail2ban-client reload # 查看所有活跃的 jail sudo fail2ban-client status # 查看特定 jail 的详细状态 sudo fail2ban-client status sshd # 手动封禁测试 sudo fail2ban-clientsetsshd banip 1.2.3.4 # 手动解封 sudo fail2ban-clientsetsshd unbanip 1.2.3.4 # 查看 fail2ban 日志 sudo tail -f /var/log/fail2ban.log
三、示例代码和配置
3.1 完整的 jail.local 配置示例
# /etc/fail2ban/jail.local
# fail2ban 完整配置模板
[DEFAULT]
# ========================
# 全局默认参数
# ========================
# 封禁时间 1 小时
bantime = 3600
# 检测窗口 10 分钟
findtime = 600
# 失败阈值 5 次
maxretry = 5
# 白名单(内网 + 跳板机 IP)
ignoreip = 127.0.0.1/8 ::1
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
# 递增封禁
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 604800
bantime.overalljails = true
# 封禁后端(Ubuntu)
banaction = iptables-multiport
banaction_allports = iptables-allports
# 日志后端
backend = systemd
# ========================
# SSH 防护
# ========================
[sshd]
enabled = true
port = ssh
filter = sshd[mode=aggressive]
maxretry = 3
findtime = 300
bantime = 3600
# ========================
# Nginx 防护
# ========================
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 300
bantime = 3600
[nginx-badbots]
enabled = true
port = http,https
filter = nginx-badbots
logpath = /var/log/nginx/access.log
maxretry = 3
findtime = 60
bantime = 86400
[nginx-cc]
enabled = true
port = http,https
filter = nginx-cc
logpath = /var/log/nginx/error.log
maxretry = 30
findtime = 60
bantime = 600
# ========================
# 其他服务
# ========================
[mysqld-auth]
enabled = false
port = 3306
filter = mysqld-auth
logpath = /var/log/mysql/error.log
maxretry = 5
[postfix]
enabled = false
port = smtp,465,submission
filter = postfix[mode=auth]
logpath = /var/log/mail.log
maxretry = 5
3.2 案例 1:SSH 暴力破解防御(封禁统计)
场景:一台公网服务器遭受持续的 SSH 暴力破解,需要配置 fail2ban 并统计封禁效果。
# 1. 先看看攻击有多严重
sudo journalctl -u sshd --since"1 hour ago"| grep"Failed password"| wc -l
# 2847
# 统计攻击来源 IP TOP 10
sudo journalctl -u sshd --since"1 hour ago"|
grep"Failed password"|
grep -oP'from K[0-9.]+'|
sort | uniq -c | sort -rn | head -10
# 423 103.xx.xx.92
# 389 45.xx.xx.201
# 312 185.xx.xx.33
# 287 91.xx.xx.156
# ...
# 2. 确认 fail2ban 已配置并运行
sudo fail2ban-client status sshd
# 3. 查看封禁效果(运行 24 小时后)
sudo fail2ban-client status sshd
# Status for the jail: sshd
# |- Filter
# | |- Currently failed: 1
# | |- Total failed: 4823
# | `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
# `- Actions
# |- Currently banned: 47
# |- Total banned: 312
# `- Banned IP list: 103.xx.xx.92 45.xx.xx.201 ...
# 4. 统计每日封禁 IP 数量
sudo grep"Ban "/var/log/fail2ban.log |
awk'{print $1}'| sort | uniq -c
# 312 2026-03-13
# 287 2026-03-14
3.3 案例 2:Nginx 恶意扫描过滤
场景:Nginx 日志中发现大量 404/403 请求,都是漏洞扫描器的探测行为。
# 1. 分析恶意扫描模式
sudo grep" 404 "/var/log/nginx/access.log |
awk'{print $7}'| sort | uniq -c | sort -rn | head -20
# 847 /wp-login.php
# 623 /wp-admin/
# 412 /phpmyadmin/
# 389 /.env
# 356 /.git/config
# 298 /config.php.bak
# ...
# 2. 确认 nginx-badbots filter 匹配效果
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-badbots.conf
# Results
# =======
# Failregex: 3247 total
# ...
# 3. 启用后验证
sudo fail2ban-client status nginx-badbots
# Status for the jail: nginx-badbots
# |- Filter
# | |- Currently failed: 5
# | |- Total failed: 3247
# | `- File list: /var/log/nginx/access.log
# `- Actions
# |- Currently banned: 23
# |- Total banned: 89
3.4 案例 3:自定义规则防御 API 暴力调用
场景:API 网关日志发现有 IP 在暴力尝试登录接口。
应用日志格式:
2026-03-13 1045.123 WARN [auth-service] - Authentication failed: user=admin, ip=103.1.2.3, reason=bad_password 2026-03-13 1046.456 WARN [auth-service] - Authentication failed: user=test, ip=103.1.2.3, reason=user_not_found
# 1. 创建自定义 filter sudo cat > /etc/fail2ban/filter.d/api-auth.conf << 'EOF' [Definition] failregex = ^s*WARNs+[auth-service]s+-s+Authentication failed:.*ip=.*$ ignoreregex = datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S EOF # 2. 测试 filter sudo fail2ban-regex /var/log/myapp/auth.log /etc/fail2ban/filter.d/api-auth.conf # 3. 添加 jail 配置 sudo cat >> /etc/fail2ban/jail.local << 'EOF' [api-auth] enabled = true port = 8080,8443 filter = api-auth logpath = /var/log/myapp/auth.log maxretry = 10 findtime = 300 bantime = 1800 EOF # 4. 重新加载 sudo fail2ban-client reload sudo fail2ban-client status api-auth
3.5 fail2ban 管理脚本
#!/bin/bash
# f2b_manage.sh - fail2ban 日常管理脚本
# 用法: ./f2b_manage.sh [status|banned|unban|stats|top]
RED='�33[0;31m'
GREEN='�33[0;32m'
YELLOW='�33[1;33m'
NC='�33[0m'
case"${1:-status}"in
status)
echo"===== fail2ban 服务状态 ====="
sudo fail2ban-client status
echo""
echo"===== 各 Jail 封禁数 ====="
forjailin$(sudo fail2ban-client status | grep"Jail list"| sed's/.*://;s/,//g');do
banned=$(sudo fail2ban-client status"$jail"| grep"Currently banned"| awk'{print $NF}')
total=$(sudo fail2ban-client status"$jail"| grep"Total banned"| awk'{print $NF}')
echo-e" ${jail}: 当前封禁${RED}${banned}${NC}, 累计封禁${total}"
done
;;
banned)
echo"===== 当前封禁 IP 列表 ====="
forjailin$(sudo fail2ban-client status | grep"Jail list"| sed's/.*://;s/,//g');do
echo-e"
${YELLOW}[${jail}]${NC}"
sudo fail2ban-client status"$jail"| grep"Banned IP"| sed's/.*list:/ /'
done
;;
unban)
IP="${2:?用法: $0 unban }"
echo"解封 IP:${IP}"
forjailin$(sudo fail2ban-client status | grep"Jail list"| sed's/.*://;s/,//g');do
sudo fail2ban-clientset"$jail"unbanip"$IP"2>/dev/null &&
echo-e" ${GREEN}已从${jail}解封${NC}"||true
done
;;
stats)
echo"===== 封禁统计(最近 7 天) ====="
sudo grep"Ban "/var/log/fail2ban.log |
awk'{print $1, $6}'|
sort |
awk'{date=$1; jail=$2; count[date" "jail]++} END {for (k in count) print k, count[k]}'|
sort
;;
top)
echo"===== 被封禁最多的 IP TOP 20 ====="
sudo grep"Ban "/var/log/fail2ban.log |
awk'{print $NF}'|
sort | uniq -c | sort -rn | head -20
;;
*)
echo"用法:$0[status|banned|unban |stats|top]"
exit1
;;
esac
四、最佳实践和注意事项
4.1 最佳实践
白名单管理
# /etc/fail2ban/jail.local 的 [DEFAULT] 部分
[DEFAULT]
# 白名单:绝不封禁的 IP
# 包含:本机、内网、跳板机、监控服务器
ignoreip = 127.0.0.1/8 ::1
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
203.0.113.50 # 跳板机 IP
203.0.113.51 # 监控服务器 IP
注意事项:
白名单一定要包含所有合法的运维入口 IP,否则运维人员输错密码也会被封
如果使用跳板机,跳板机 IP 必须在白名单中
监控系统的探测 IP 也要加入白名单
白名单支持 CIDR 表示法
封禁策略梯度设计
[DEFAULT] # 递增封禁:每次被封,时间翻倍 bantime.increment = true # 倍增因子 bantime.factor = 2 # 最大封禁时间 7 天 bantime.maxtime = 604800 # 跨 jail 累计(同一 IP 在不同 jail 的违规都计入) bantime.overalljails = true
递增封禁效果示例:
| 被封次数 | 封禁时间 | 累计 |
|---|---|---|
| 第 1 次 | 1 小时 | 1 小时 |
| 第 2 次 | 2 小时 | 3 小时 |
| 第 3 次 | 4 小时 | 7 小时 |
| 第 4 次 | 8 小时 | 15 小时 |
| 第 5 次 | 16 小时 | 31 小时 |
| 第 6 次+ | 7 天(上限) | - |
与 CrowdSec 混合部署
fail2ban 负责本地日志检测,CrowdSec 提供社区威胁情报补充:
# 安装 CrowdSec curl -s https://install.crowdsec.net | sudo bash sudo apt install -y crowdsec crowdsec-firewall-bouncer-iptables # CrowdSec 会自动共享封禁列表,补充 fail2ban 的本地检测 # 两者并存不冲突,分别使用不同的 iptables 链
4.2 注意事项
常见错误
| 错误现象 | 原因分析 | 解决方案 |
|---|---|---|
| fail2ban 启动失败 | jail.local 语法错误 | fail2ban-client -t 检查语法 |
| filter 不匹配日志 | 正则表达式错误 | fail2ban-regex 测试 |
| 封禁后仍可访问 | iptables 规则顺序问题 | 检查规则链优先级 |
| 误封合法 IP | 白名单配置不完整 | 完善 ignoreip |
| 日志时间不匹配 | 时区或日期格式问题 | 检查 datepattern |
| jail 显示 0 failed | backend 配置不对 | 切换 backend 为 systemd 或 polling |
| Docker 容器内日志 | fail2ban 无法读取容器日志 | 将日志挂载到宿主机 |
| 重启后封禁丢失 | 默认不持久化封禁 | 启用 dbpurgeage 或增大 bantime |
性能注意事项
# fail2ban 的资源消耗主要在日志匹配 # 每个 jail 都会启动一个线程监控日志文件 # 查看 fail2ban 内存使用 ps aux | grep fail2ban | grep -v grep # 如果日志文件非常大,建议: # 1. 使用 logrotate 定期切割日志 # 2. 减少 findtime 缩小扫描范围 # 3. 优化正则表达式(避免过于宽泛的 .*) # 查看 fail2ban 数据库大小 ls -lh /var/lib/fail2ban/fail2ban.sqlite3
兼容性问题
fail2ban 1.1.x 需要 Python 3.8+,Ubuntu 24.04 和 Rocky Linux 9.5 均满足
如果系统同时运行 firewalld 和 iptables,注意 banaction 的选择
Docker 环境中,fail2ban 需要在宿主机上运行,监控宿主机上的日志文件
使用 systemd journal 作为 backend 时,确保日志持久化(Storage=persistent)
五、故障排查和监控
5.1 故障排查
fail2ban 日志
# 查看 fail2ban 运行日志 sudo tail -f /var/log/fail2ban.log # 过滤封禁事件 sudo grep"Ban"/var/log/fail2ban.log | tail -20 # 过滤解封事件 sudo grep"Unban"/var/log/fail2ban.log | tail -20 # 过滤错误 sudo grep"ERROR|WARNING"/var/log/fail2ban.log | tail -20 # 查看 fail2ban 服务日志 sudo journalctl -u fail2ban -n 50 --no-pager
常见问题排查
问题 1:fail2ban 无法匹配日志
# 检查日志文件权限 ls -la /var/log/auth.log # fail2ban 需要读取权限 # 检查日志格式是否匹配 filter sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf --print-all-matched # 检查 backend 设置 # 如果使用 systemd,确认 journald 正在记录 sudo journalctl -u sshd -n 5
问题 2:封禁后 IP 仍可访问
# 检查 iptables 规则是否存在 sudo iptables -L f2b-sshd -n -v # 检查规则顺序(fail2ban 的规则是否在 ACCEPT 之前) sudo iptables -L INPUT -n -v --line-numbers # 如果使用 Docker,检查 DOCKER 链是否绕过了 INPUT 链 sudo iptables -L FORWARD -n -v
问题 3:重启后封禁记录丢失
# fail2ban 使用 SQLite 数据库存储封禁记录 # 检查数据库是否正常 sudo fail2ban-client get sshd bantime # 确保 dbpurgeage 设置足够长 # /etc/fail2ban/fail2ban.local # [Definition] # dbpurgeage = 86400
调试模式
# 前台运行 fail2ban,查看详细输出 sudo fail2ban-client stop sudo fail2ban-server -xf --loglevel DEBUG # 查看某个 filter 的实时匹配 sudo fail2ban-clientsetsshd loglevel DEBUG sudo tail -f /var/log/fail2ban.log | grep sshd
5.2 性能监控
fail2ban 指标导出到 Prometheus
使用 fail2ban-prometheus-exporter 或自定义脚本:
#!/bin/bash # f2b_exporter.sh - fail2ban 指标导出脚本 # 配合 node_exporter 的 textfile collector 使用 METRICS_DIR="/var/lib/prometheus/node-exporter" METRICS_FILE="${METRICS_DIR}/fail2ban.prom" TEMP_FILE=$(mktemp) mkdir -p"$METRICS_DIR" # 获取所有 jail 的指标 forjailin$(sudo fail2ban-client status 2>/dev/null | grep"Jail list"| sed's/.*://;s/,//g');do status=$(sudo fail2ban-client status"$jail"2>/dev/null) currently_failed=$(echo"$status"| grep"Currently failed"| awk'{print $NF}') total_failed=$(echo"$status"| grep"Total failed"| awk'{print $NF}') currently_banned=$(echo"$status"| grep"Currently banned"| awk'{print $NF}') total_banned=$(echo"$status"| grep"Total banned"| awk'{print $NF}') cat >>"$TEMP_FILE"<< EOF fail2ban_currently_failed{jail="${jail}"} ${currently_failed:-0} fail2ban_total_failed{jail="${jail}"} ${total_failed:-0} fail2ban_currently_banned{jail="${jail}"} ${currently_banned:-0} fail2ban_total_banned{jail="${jail}"} ${total_banned:-0} EOF done # 原子替换 mv "$TEMP_FILE" "$METRICS_FILE" chmod 644 "$METRICS_FILE"
将脚本加入 crontab:
# 每分钟采集一次 * * * * * /opt/scripts/f2b_exporter.sh
Prometheus 告警规则
# fail2ban_alerts.yml groups: -name:fail2ban rules: -alert:Fail2banJailDown expr:absent(fail2ban_currently_banned) for:5m labels: severity:critical annotations: summary:"fail2ban 指标消失,服务可能已停止" -alert:Fail2banHighBanRate expr:rate(fail2ban_total_banned{jail="sshd"}[1h])>50 for:10m labels: severity:warning annotations: summary:"SSH 封禁频率异常高:{{ $value }}/小时" description:"jail{{ $labels.jail }}的封禁速率超过 50/小时,可能遭受大规模攻击" -alert:Fail2banManyCurrentlyBanned expr:fail2ban_currently_banned>200 for:5m labels: severity:warning annotations: summary:"当前封禁 IP 数量过多:{{ $value }}"
Grafana 面板关键查询
# 当前封禁数 fail2ban_currently_banned # 每小时封禁速率 rate(fail2ban_total_banned[1h]) * 3600 # 每小时失败尝试速率 rate(fail2ban_total_failed[1h]) * 3600 # 各 jail 封禁占比 fail2ban_currently_banned / ignoring(jail) group_left sum(fail2ban_currently_banned)
5.3 备份与恢复
配置备份
#!/bin/bash
# 备份 fail2ban 完整配置
BACKUP_DIR="/opt/backup/fail2ban"
DATE=$(date +%Y%m%d)
mkdir -p"$BACKUP_DIR"
# 备份配置文件
tar czf"${BACKUP_DIR}/fail2ban_config_${DATE}.tar.gz"
/etc/fail2ban/jail.local
/etc/fail2ban/jail.d/
/etc/fail2ban/filter.d/*local*
/etc/fail2ban/action.d/*local*
2>/dev/null
# 备份数据库
cp /var/lib/fail2ban/fail2ban.sqlite3"${BACKUP_DIR}/fail2ban_db_${DATE}.sqlite3"
# 保留 30 天
find"$BACKUP_DIR"-mtime +30 -delete
echo"fail2ban 配置已备份到${BACKUP_DIR}"
配置恢复
# 恢复配置 sudo tar xzf /opt/backup/fail2ban/fail2ban_config_20260313.tar.gz -C / # 恢复数据库(保留封禁记录) sudo systemctl stop fail2ban sudo cp /opt/backup/fail2ban/fail2ban_db_20260313.sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 sudo systemctl start fail2ban # 验证 sudo fail2ban-client status
六、总结
6.1 技术要点回顾
fail2ban 通过日志正则匹配 + 自动防火墙封禁实现入侵防御,核心是 Filter → Jail → Action 链路
所有自定义配置写在.local文件中,不要修改.conf原始文件
SSH 防护使用内置 sshd filter 的 aggressive 模式,覆盖面最广
Nginx 防护需要自定义 filter(恶意扫描、CC 攻击),内置的 nginx-http-auth 只覆盖 Basic Auth
白名单(ignoreip)必须包含所有合法运维入口,避免误封
递增封禁策略(bantime.increment)对重复违规者逐步加重处罚
使用fail2ban-regex工具测试 filter 匹配效果,避免上线后才发现规则不生效
6.2 进阶学习方向
CrowdSec 社区防御:基于社区威胁情报的协同防御,适合大规模部署
自定义 Action:封禁后自动发送通知(邮件/企微/钉钉),或写入 SIEM 系统
集中式日志 + fail2ban:使用 Filebeat/Fluentd 收集多台服务器日志,在中心节点运行 fail2ban
WAF 集成:将 fail2ban 与 ModSecurity/Coraza WAF 结合,实现多层防御
6.3 参考资料
fail2ban 官方文档: https://github.com/fail2ban/fail2ban/wiki
fail2ban filter 编写指南: https://github.com/fail2ban/fail2ban/wiki/Developing-Filters
CrowdSec 官方文档: https://docs.crowdsec.net/
Linux iptables 手册: man iptables(8)
nftables wiki: https://wiki.nftables.org/
附录
A. 命令速查表
# 服务管理 sudo systemctl start fail2ban # 启动 sudo systemctl stop fail2ban # 停止 sudo systemctl restart fail2ban # 重启 sudo systemctl status fail2ban # 状态 # 配置管理 sudo fail2ban-client -t # 语法检查 sudo fail2ban-client reload # 重新加载配置 sudo fail2ban-client reload sshd # 重新加载单个 jail # 状态查看 sudo fail2ban-client status # 总体状态 sudo fail2ban-client status sshd # 单个 jail 状态 sudo fail2ban-client banned # 所有封禁列表 # 封禁/解封 sudo fail2ban-clientsetsshd banip 1.2.3.4 # 手动封禁 sudo fail2ban-clientsetsshd unbanip 1.2.3.4 # 手动解封 sudo fail2ban-client unban --all # 解封所有 # 调试 sudo fail2ban-regex# 测试 filter sudo fail2ban-clientsetsshd loglevel DEBUG # 开启调试
B. 配置参数详解
| 参数 | 默认值 | 说明 |
|---|---|---|
| bantime | 600 | 封禁持续时间(秒) |
| findtime | 600 | 检测时间窗口(秒) |
| maxretry | 5 | 触发封禁的失败次数 |
| ignoreip | 127.0.0.1/8 | 白名单 IP/CIDR |
| backend | auto | 日志后端(systemd/polling/inotify) |
| banaction | iptables-multiport | 封禁动作 |
| usedns | warn | DNS 解析策略(yes/no/warn/raw) |
| logencoding | auto | 日志文件编码 |
| enabled | false | 是否启用 jail |
| port | - | 要封禁的端口 |
| filter | - | 使用的 filter 名称 |
| logpath | - | 监控的日志文件路径 |
| bantime.increment | false | 是否启用递增封禁 |
| bantime.factor | 1 | 递增倍数 |
| bantime.maxtime | - | 最大封禁时间 |
C. 术语表
| 术语 | 英文 | 解释 |
|---|---|---|
| 暴力破解 | Brute Force Attack | 通过穷举方式尝试大量密码组合的攻击方法 |
| 字典攻击 | Dictionary Attack | 使用预定义密码列表进行暴力破解 |
| 撞库 | Credential Stuffing | 使用泄露的账号密码在其他平台尝试登录 |
| 封禁 | Ban | 将攻击 IP 添加到防火墙黑名单,阻止其访问 |
| 监狱 | Jail | fail2ban 的监控单元,包含 filter + action + 参数 |
| 过滤器 | Filter | 定义日志匹配规则的正则表达式模块 |
| 动作 | Action | 匹配后执行的操作(如添加防火墙规则) |
| 递增封禁 | Incremental Ban | 对重复违规者逐次增加封禁时间 |
| 蜜罐 | Honeypot | 故意暴露的假目标,用于诱捕和分析攻击者 |
| CC 攻击 | Challenge Collapsar | 通过大量合法 HTTP 请求耗尽服务器资源的攻击 |
-
Linux
+关注
关注
88文章
11807浏览量
219508 -
服务器
+关注
关注
14文章
10344浏览量
91737
原文标题:用 fail2ban 防御暴力破解的落地实践
文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
labview密码破解
labview如何写暴力破解密码 密码4位数 由1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ 如何每次读取一个数进行测试完成后进行下一个测试
何为暴力破解呢
为什么不能随便暴力破解比特币私钥
暴力破解比特币私钥可能会实现吗
字符串硬核暴力破解法讲解
IP知识百科之暴力破解
如何通过Python脚本实现WIFI密码的暴力破解
会用kali破解wifi吗?
R5300 G4服务器的BMC进行渗透测试案例
如何在服务器端自动ban掉扫描ssh的IP
使用fail2ban防御暴力破解的落地实践
评论