0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用fail2ban防御暴力破解的落地实践

马哥Linux运维 来源:马哥Linux运维 2026-03-23 10:27 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

一、概述

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 from 密码认证失败
Failed publickey for from 公钥认证失败
Invalid user from 不存在的用户名
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 .* from s*$

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密码破解暴力模式小程序,得重新去下载破解密码的字典
    发表于 12-18 10:33

    labview密码破解

    求助,软件是去年改过的不知道谁设置了密码,都说不知道。暴力破解字典实在太大。速度太慢,故求帮助~~PS :网上说的MD5破解,试了下也没成。大神们,求交流~~
    发表于 01-19 23:37

    labview如何写暴力破解密码 密码4位数 由1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ 如何每次读取一个数进行测试完成后进行下一个测试

    labview如何写暴力破解密码 密码4位数 由1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ 如何每次读取一个数进行测试完成后进行下一个测试
    发表于 02-17 09:46

    何为暴力破解

    ps:代码复制黏贴即可,有什么问题下方留言,博主会及时回复 !工具:1:笔记本2:USB无线网卡(必备)3:kali系统4:靠谱字典暴力破解法:何为暴力破解呢,其实就是一个一个密码试,直到正确的密码
    发表于 07-15 09:46

    labview的VI加密暴力破解工具

    VI加密破解工具,有需要可以联系我发送wx:***,破解内容仅用于学习,勿用于商用。
    发表于 09-03 08:53

    为什么不能随便暴力破解比特币私钥

    试图用暴力破解私钥有点像试图数到无限大:越早开始,就越不可能到达那里。尽管几乎不可能做到,但对许多人来说,使用暴力破解一个比特币私钥仍然是一个耐人寻味的想法。 永不消逝的梦想
    发表于 10-30 11:23 9335次阅读

    暴力破解比特币私钥可能会实现吗

    试图用暴力破解私钥有点像试图数到无限大:越早开始,就越不可能到达那里。尽管几乎不可能做到,但对许多人来说,使用暴力破解一个比特币私钥仍然是一个耐人寻味的想法。 永不消逝的梦想
    发表于 10-31 09:35 4356次阅读

    字符串硬核暴力破解法讲解

    1 暴力破解法 在主串A中查找模式串B的出现位置,其中如果A的长度是n,B的长度是m,则n 》 m。当我们暴力匹配时,在主串A中匹配起始位置分别是 0、1、2….n-m 且长度为 m 的 n-m+1
    的头像 发表于 04-04 11:50 3892次阅读
    字符串硬核<b class='flag-5'>暴力破解</b>法讲解

    IP知识百科之暴力破解

    暴力破解 暴力破解是一种针对于密码的破译方法,将密码进行逐个推算直到找出真正的密码为止。设置长而复杂的密码、在不同的地方使用不同的密码、避免使用个人信息作为密码、定期修改密码等是防御暴力破解
    的头像 发表于 09-06 09:28 5056次阅读

    如何通过Python脚本实现WIFI密码的暴力破解

    前言 本文将记录学习下如何通过 Python 脚本实现 WIFI 密码的暴力破解,从而实现免费蹭网。 无图形界面 先来看看没有图形界面版的爆破脚本。 WIFI爆破 import pywififrom
    的头像 发表于 09-10 17:09 2.6w次阅读
    如何通过Python脚本实现WIFI密码的<b class='flag-5'>暴力破解</b>

    暴力破解压缩包密码

    可以暴力破解压缩包密码
    发表于 08-08 14:23 10次下载

    通过Python脚本实现WIFI密码的暴力破解

    本文将记录学习下如何通过 Python 脚本实现 WIFI 密码的暴力破解
    的头像 发表于 09-19 09:55 8030次阅读

    会用kali破解wifi吗?

    准备好 [kali] 系统,电脑可以链接无线 wifi!! 使用 Aircrack-ng 进行暴力破解,linux 上已经安装此工具!
    的头像 发表于 12-05 11:54 2702次阅读

    R5300 G4服务器的BMC进行渗透测试案例

    对R5300 G4服务器的BMC进行渗透测试,发现存在BMC被暴力破解密码的安全问题:攻击者通过使用字典进行暴力破解破解成功后获取密码,登录BMC Web门户对服务器进行操控,影响服务器运行安全。
    发表于 06-25 12:29 1479次阅读
    R5300 G4服务器的BMC进行渗透测试案例

    如何在服务器端自动ban掉扫描ssh的IP

    扫描,可以利用fail2ban这个框架来把频繁扫描的源IP直接丢进黑名单。让服务器在一定时间内拒绝连接。    1.安装fail2ban: # Ubuntu sudo apt update sudo
    的头像 发表于 11-06 11:53 1257次阅读
    如何在服务器端自动<b class='flag-5'>ban</b>掉扫描ssh的IP