1.1 背景介绍

暴力破解是公网服务器面临的最常见攻击之一。SSH、Web 登录页面、API 接口是主要目标,攻击者使用 Hydra、Medusa、Burp Suite 等自动化工具,以每秒数百次的频率尝试用户名密码组合。一台暴露 22 端口的服务器,上线几小时内就会收到大量暴力破解请求,这在 /var/log/auth.log 中随处可见。

Fail2ban 是 Linux 环境下成熟的入侵防御工具,工作原理直观:监控日志文件 → 正则匹配失败记录 → 达到阈值触发封禁动作(通过 iptables/nftables 封锁 IP)→ 到期自动解封。整个过程无需人工干预,资源消耗极低。

核心架构由三大组件构成:

  • Jail(监狱):定义监控目标,包括日志路径、匹配规则、封禁参数

  • Filter(过滤器):定义正则表达式,从日志中提取失败的 IP 地址

  • Action(动作):定义触发封禁后的具体操作,如 iptables 规则、邮件通知、Webhook 调用

三者关系:Jail 引用 Filter 和 Action,Filter 负责"发现",Action 负责"执行",Jail 负责"编排"。

同类工具对比

特性

Fail2ban

DenyHosts

CrowdSec

支持服务

SSH/Web/邮件/自定义

仅 SSH

SSH/Web/自定义

封禁方式

iptables/nftables/firewalld

hosts.deny

iptables/nftables/云 API

分布式能力

无(单机)

有限(同步黑名单)

原生分布式,社区情报共享

社区威胁情报

有(Community Blocklists)

自定义 Filter

强,正则灵活

中等,YAML 场景定义

资源消耗

极低

中等(Go 编写,需 Agent)

成熟度

非常成熟,生态丰富

停止维护

较新,快速发展中

Fail2ban 在单机防护场景下仍然是最佳选择:配置灵活、生态成熟、资源消耗低。如果需要分布式情报共享,可以考虑 CrowdSec,但运维复杂度更高。

1.2 技术特点

  • 日志驱动:基于日志分析而非流量嗅探,不影响服务性能,不依赖内核模块

  • 正则灵活:Filter 使用 Python 正则表达式,可匹配任意日志格式

  • 多后端支持:支持 iptables、nftables、firewalld、tcpwrapper 等多种封禁后端

  • 渐进式封禁:通过 bantime.increment 实现累犯加重,首次短封、多次永封

  • 低资源消耗:Python 编写,常驻内存约 30-50MB,CPU 占用可忽略

  • 热加载:修改配置后 fail2ban-client reload 即可生效,无需重启服务

1.3 适用场景

  • SSH 暴力破解防护:公网服务器的基本安全底线

  • Nginx/Apache 登录页面防护:Web 应用的认证接口保护

  • 邮件服务防护:Postfix、Dovecot 等邮件服务的暴力破解防御

  • 自定义应用防护:任何能输出日志的应用都可以接入 Fail2ban

  • CC 攻击缓解:通过限制单 IP 高频请求,缓解应用层 DDoS

1.4 环境要求

组件

版本要求

说明

操作系统

Ubuntu 22.04+ / Debian 12+ / CentOS 8+

推荐 Ubuntu 22.04 LTS

Fail2ban

1.1.x

2025 最新稳定版,支持 nftables 和 bantime.increment

Python

3.8+

Fail2ban 1.1.x 已移除 Python 2 支持

防火墙

iptables / nftables / firewalld

Ubuntu 22.04 默认 nftables,CentOS 8+ 默认 firewalld

systemd

必须

Fail2ban 1.1.x 依赖 systemd 管理服务


二、详细步骤

2.1 安装与基础配置

2.1.1 安装 Fail2ban
# Ubuntu / Debian
sudo apt update && sudo apt install -y fail2ban

# CentOS 8+ / RHEL(EPEL 源)
sudo dnf install -y epel-release && sudo dnf install -y fail2ban

# 验证版本
fail2ban-client version
# 预期输出:Fail2Ban v1.1.x
2.1.2 配置文件层级

Fail2ban 的配置文件有明确的优先级层级,理解这一点非常重要:

/etc/fail2ban/
├── jail.conf            # 默认配置,升级时会被覆盖,永远不要直接修改
├── jail.local           # 本地覆盖配置,优先级高于 jail.conf
├── jail.d/              # 分文件配置目录,优先级最高
│   ├── sshd.conf
│   └── nginx.conf
├── filter.d/            # 过滤器定义目录
│   ├── sshd.conf
│   └── nginx-http-auth.conf
└── action.d/            # 动作定义目录
    ├── iptables-multiport.conf
    └── nftables-multiport.conf

优先级:jail.d/*.conf > jail.local > jail.conf。所有自定义配置写在 jail.local 或 jail.d/ 下,确保系统升级不会覆盖。

2.1.3 全局基础配置
# /etc/fail2ban/jail.local - 全局配置
[DEFAULT]
# 封禁时间,单位秒(默认 10 分钟)
bantime = 3600

# 检测时间窗口:在 findtime 秒内达到 maxretry 次失败则封禁
findtime = 600

# 最大失败次数
maxretry = 5

# 白名单 IP,多个用空格分隔,支持 CIDR
# 务必加入运维跳板机和监控服务器 IP
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 192.168.1.0/24

# 封禁后端选择:
# Ubuntu 22.04+ 推荐 nftables-multiport
# CentOS 8+ 使用 firewallcmd-ipset
# 传统环境使用 iptables-multiport
banaction = nftables-multiport
banaction_allports = nftables-allports

# 渐进式封禁(Fail2ban 1.1.x 特性)
# 累犯每次封禁时间翻倍,上限 4 周
bantime.increment = true
bantime.factor = 2
bantime.formula = ban.Time * math.exp(float(ban.Count) * banFactor) / math.exp(1 * banFactor)
bantime.maxtime = 4w

# 邮件通知(可选)
# destemail = admin@example.com
# sender = fail2ban@example.com
# mta = sendmail

# 后端选择:systemd 环境用 systemd,否则用 auto
backend = systemd
2.1.4 banaction 选择指南

banaction

适用环境

特点

iptables-multiport

传统 iptables 环境

兼容性最好,性能一般

nftables-multiport

Ubuntu 22.04+、Debian 12+

性能更好,规则管理更清晰

firewallcmd-ipset

CentOS 8+ / RHEL 8+

通过 firewalld 管理,使用 ipset 提升大量 IP 封禁性能

2.2 SSH 暴力破解防护

2.2.1 SSH Jail 配置
# /etc/fail2ban/jail.d/sshd.conf
[sshd]
enabled = true
port = ssh,22222                # 如果修改了 SSH 端口,这里一并添加
filter = sshd
# 使用 systemd 后端直接读取 journal,无需指定 logpath
backend = systemd
# 如果不用 systemd 后端,指定日志路径:
# logpath = /var/log/auth.log

# SSH 防护策略:比全局更严格
maxretry = 3                    # 3 次失败即封禁
findtime = 300                  # 5 分钟内
bantime = 600                   # 首次封禁 10 分钟(渐进式封禁会自动加重)

# SSH 专用白名单(追加到全局 ignoreip)
ignoreip = 127.0.0.1/8 ::1 10.0.0.100
2.2.2 Filter 正则解读

Fail2ban 1.1.x 内置的 sshd filter 已经覆盖了绝大多数场景,位于 /etc/fail2ban/filter.d/sshd.conf。核心匹配规则:

# /etc/fail2ban/filter.d/sshd.conf 关键部分(内置,通常不需要修改)
[Definition]
# 匹配模式:aggressive 模式覆盖更多攻击特征
mode = aggressive

# 常见匹配的日志行:
# "Failed password for root from 192.168.1.100 port 52312 ssh2"
# "Invalid user admin from 192.168.1.100 port 52312"
# "Connection closed by authenticating user root 192.168.1.100 port 52312 [preauth]"
# "Unable to negotiate with 192.168.1.100 port 52312: no matching key exchange method found"

设置 mode = aggressive 可以匹配更多攻击特征,包括连接重置、协议探测等行为。在公网服务器上推荐开启:

# /etc/fail2ban/jail.d/sshd.conf 中追加
[sshd]
filter = sshd[mode=aggressive]
2.2.3 渐进式封禁策略

渐进式封禁通过 bantime.increment 实现,已在全局 [DEFAULT] 中配置。实际效果:

封禁次数

封禁时长

说明

第 1 次

10 分钟

基础 bantime

第 2 次

~30 分钟

指数增长

第 3 次

~2 小时

持续加重

第 4 次

~8 小时

接近永封

第 5 次+

4 周(上限)

bantime.maxtime 封顶

封禁记录存储在 Fail2ban 的数据库中(/var/lib/fail2ban/fail2ban.sqlite3),重启服务不会丢失。

2.3 Nginx/Apache Web 服务防护

2.3.1 HTTP 基础认证防护

Nginx 的 HTTP Basic Auth 失败会记录在 error.log 中,Fail2ban 内置了对应的 filter:

# /etc/fail2ban/jail.d/nginx.conf
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 300
bantime = 600
2.3.2 自定义 Nginx 登录页面防护

对于应用自身的登录接口(如返回 401/403 的 REST API),需要编写自定义 filter。假设登录失败时 Nginx access.log 记录如下:

192.168.1.100 - - [15/Mar/2025:10:23:45 +0800] "POST /api/login HTTP/1.1" 401 52

编写自定义 filter:

# /etc/fail2ban/filter.d/nginx-login.conf
[Definition]
# 匹配 POST 登录接口返回 401 或 403 的请求
failregex = ^<HOST> -.*"POST /api/login.*" (401|403)
# 忽略正常请求和健康检查
ignoreregex = ^<HOST> -.*"GET /health"
              ^<HOST> -.*"HEAD /"

对应的 jail 配置:

# /etc/fail2ban/jail.d/nginx-login.conf
[nginx-login]
enabled = true
port = http,https
filter = nginx-login
logpath = /var/log/nginx/access.log
maxretry = 10                   # 登录接口允许更多重试
findtime = 300
bantime = 1800                  # 首次封禁 30 分钟
2.3.3 WordPress 登录防护

WordPress 的 wp-login.php 是暴力破解的重灾区:

# /etc/fail2ban/filter.d/wordpress-login.conf
[Definition]
# 匹配 wp-login.php 的 POST 请求返回 200(WP 登录失败也返回 200)
# 因此需要结合频率判断,而非状态码
failregex = ^<HOST> -.*"POST /wp-login\.php
# 排除 wp-cron 和正常的 GET 请求
ignoreregex = ^<HOST> -.*"GET /wp-login\.php
              ^<HOST> -.*wp-cron\.php
# /etc/fail2ban/jail.d/wordpress.conf
[wordpress-login]
enabled = true
port = http,https
filter = wordpress-login
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 120                  # 2 分钟内 5 次 POST 即封禁
bantime = 3600
2.3.4 CC 攻击防护(高频请求限制)

针对单 IP 高频访问的 CC 攻击:

# /etc/fail2ban/filter.d/nginx-cc.conf
[Definition]
# 匹配所有请求(通过 maxretry 控制频率阈值)
failregex = ^<HOST> -.*"(GET|POST|PUT|DELETE) /
ignoreregex = ^<HOST> -.*"GET /static/
              ^<HOST> -.*"GET /favicon\.ico
# /etc/fail2ban/jail.d/nginx-cc.conf
[nginx-cc]
enabled = true
port = http,https
filter = nginx-cc
logpath = /var/log/nginx/access.log
maxretry = 300                  # 5 分钟内 300 次请求(根据业务调整)
findtime = 300
bantime = 600

2.4 自定义 Jail 开发

2.4.1 Filter 编写规范

自定义 filter 的核心是 failregex,使用 Python 正则表达式语法。关键规则:

  • <HOST> 是 Fail2ban 的特殊标记,匹配 IPv4/IPv6 地址,提取为封禁目标

  • 每行一个正则,多个正则之间是 OR 关系

  • ignoreregex 用于排除误报,格式与 failregex 相同

2.4.2 使用 fail2ban-regex 测试

编写 filter 后,务必用 fail2ban-regex 工具验证,避免上线后才发现正则不匹配:

# 测试 filter 是否能匹配日志
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-login.conf

# 输出示例:
# Results
# =======
# Failregex: 42 total
# Ignoreregex: 3 total
# Date template hits: ...

# 用单条日志测试(调试用)
echo '192.168.1.100 - - [15/Mar/2025:10:23:45 +0800] "POST /api/login HTTP/1.1" 401 52' | \
  fail2ban-regex - /etc/fail2ban/filter.d/nginx-login.conf
2.4.3 自定义 Action 开发

除了封禁 IP,还可以在触发时执行自定义动作。Action 定义文件位于 /etc/fail2ban/action.d/,支持以下钩子:

  • actionstart:Fail2ban 启动时执行

  • actionstop:Fail2ban 停止时执行

  • actionban:封禁 IP 时执行

  • actionunban:解封 IP 时执行

# /etc/fail2ban/action.d/webhook-notify.conf
[Definition]
# 封禁时发送 Webhook 通知
actionban = /etc/fail2ban/scripts/webhook-notify.sh ban <ip> <name> <bantime>
# 解封时也通知(可选)
actionunban = /etc/fail2ban/scripts/webhook-notify.sh unban <ip> <name>

在 jail 中引用多个 action(封禁 + 通知):

[sshd]
action = nftables-multiport[name=sshd, port="ssh", protocol=tcp]
         webhook-notify
2.4.4 多日志源监控

一个 jail 可以监控多个日志文件,用换行分隔:

[nginx-login]
logpath = /var/log/nginx/site-a-access.log
          /var/log/nginx/site-b-access.log
          /var/log/nginx/site-c-access.log

也支持通配符:

logpath = /var/log/nginx/*-access.log

三、示例代码和配置

3.1 企业级 jail.local 完整配置

# /etc/fail2ban/jail.local
# 企业级 Fail2ban 配置 - 覆盖 SSH / Nginx / 邮件 / 自定义应用
# 适用 Fail2ban 1.1.x + Ubuntu 22.04

[DEFAULT]
# === 全局封禁参数 ===
bantime = 3600                  # 默认封禁 1 小时
findtime = 600                  # 检测窗口 10 分钟
maxretry = 5                    # 默认 5 次失败触发封禁

# === 白名单 ===
# 运维跳板机、监控服务器、办公网出口 IP
ignoreip = 127.0.0.1/8 ::1
           10.0.0.0/8
           172.16.0.0/12
           192.168.1.100/32

# === 封禁后端 ===
banaction = nftables-multiport
banaction_allports = nftables-allports
backend = systemd

# === 渐进式封禁 ===
bantime.increment = true
bantime.factor = 2
bantime.formula = ban.Time * math.exp(float(ban.Count) * banFactor) / math.exp(1 * banFactor)
bantime.maxtime = 4w
bantime.overalljails = true     # 跨 jail 累计封禁次数

# === 邮件通知(可选) ===
# destemail = sre-team@example.com
# sender = fail2ban@example.com
# mta = sendmail
# action = %(action_mwl)s

# ============================================
# SSH 防护
# ============================================
[sshd]
enabled = true
port = ssh
filter = sshd[mode=aggressive]
maxretry = 3
findtime = 300
bantime = 600

# ============================================
# Nginx HTTP 基础认证防护
# ============================================
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 300
bantime = 600

# ============================================
# Nginx 自定义登录接口防护
# ============================================
[nginx-login]
enabled = true
port = http,https
filter = nginx-login
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 300
bantime = 1800

# ============================================
# Nginx CC 攻击防护
# ============================================
[nginx-cc]
enabled = true
port = http,https
filter = nginx-cc
logpath = /var/log/nginx/access.log
maxretry = 300
findtime = 300
bantime = 600

# ============================================
# Postfix 邮件服务防护
# ============================================
[postfix]
enabled = true
port = smtp,465,submission
filter = postfix[mode=aggressive]
logpath = /var/log/mail.log
maxretry = 5
findtime = 300
bantime = 3600

# ============================================
# Dovecot IMAP/POP3 防护
# ============================================
[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps
filter = dovecot[mode=aggressive]
logpath = /var/log/mail.log
maxretry = 5
findtime = 300
bantime = 3600

# ============================================
# 自定义应用防护(示例:内部管理后台)
# ============================================
[app-admin]
enabled = true
port = http,https
filter = app-admin
logpath = /var/log/myapp/access.log
maxretry = 5
findtime = 120
bantime = 3600

3.2 自定义 Nginx 登录防护完整示例

3.2.1 Filter 定义
# /etc/fail2ban/filter.d/nginx-login.conf
# 自定义 Nginx 登录接口防护过滤器
# 匹配登录接口返回 401/403 的请求

[Definition]
# 匹配 POST 到登录相关接口,返回 401 或 403
failregex = ^<HOST> -.*"POST /api/v[0-9]+/auth/login.*" (401|403)
            ^<HOST> -.*"POST /api/login.*" (401|403)
            ^<HOST> -.*"POST /admin/login.*" (401|403)
            ^<HOST> -.*"POST /user/signin.*" (401|403)

# 排除健康检查、静态资源、内部监控
ignoreregex = ^<HOST> -.*"GET /health"
              ^<HOST> -.*"GET /api/ping"
              ^<HOST> -.*Prometheus.*
              ^<HOST> -.*UptimeRobot.*

[Init]
# 日期格式(Nginx 默认格式)
datepattern = %%d/%%b/%%Y:%%H:%%M:%%S %%z
3.2.2 Jail 配置
# /etc/fail2ban/jail.d/nginx-login.conf
[nginx-login]
enabled = true
port = http,https
filter = nginx-login
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 300
bantime = 1800
# 同时封禁 + Webhook 通知
action = nftables-multiport[name=nginx-login, port="http,https", protocol=tcp]
         webhook-notify
3.2.3 验证 Filter
# 用实际日志测试匹配效果
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-login.conf --print-all-matched

# 用模拟日志测试
echo '10.20.30.40 - - [15/Mar/2025:14:22:01 +0800] "POST /api/login HTTP/1.1" 401 45 "-" "curl/7.88"' | \
  fail2ban-regex - /etc/fail2ban/filter.d/nginx-login.conf

3.3 Webhook 通知脚本

封禁事件推送到企业微信/钉钉/Slack,便于安全团队实时感知攻击态势。

#!/bin/bash
# /etc/fail2ban/scripts/webhook-notify.sh
# Fail2ban 封禁事件 Webhook 通知脚本
# 支持企业微信、钉钉、Slack
# 用法:webhook-notify.sh <ban|unban> <ip> <jail_name> [bantime]

set -euo pipefail

# === 配置区域 ===
# 企业微信 Webhook(按需启用)
WECHAT_WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY_HERE"
# 钉钉 Webhook(按需启用)
# DINGTALK_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"
# Slack Webhook(按需启用)
# SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"

# 通知开关:wechat / dingtalk / slack
NOTIFY_CHANNEL="wechat"

# === 参数解析 ===
ACTION="${1:-}"
IP="${2:-}"
JAIL="${3:-}"
BANTIME="${4:-N/A}"
HOSTNAME=$(hostname -f)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

if [[ -z "${ACTION}" || -z "${IP}" || -z "${JAIL}" ]]; then
    echo "用法: $0 <ban|unban> <ip> <jail_name> [bantime]" >&2
    exit 1
fi

# === IP 归属地查询(可选,依赖 curl + 公共 API) ===
get_ip_location() {
    local ip="$1"
    # 使用免费 API 查询,超时 3 秒
    local location
    location=$(curl -s --max-time 3 "https://ipinfo.io/${ip}/json" 2>/dev/null | \
        python3 -c "import sys,json; d=json.load(sys.stdin); print(f\"{d.get('country','未知')}/{d.get('region','未知')}/{d.get('org','未知')}\")" 2>/dev/null) || true
    echo "${location:-未知}"
}

IP_LOCATION=$(get_ip_location "${IP}")

# === 构造消息 ===
if [[ "${ACTION}" == "ban" ]]; then
    TITLE="🚨 Fail2ban 封禁告警"
    COLOR="warning"
    MSG_BODY="主机: ${HOSTNAME}\n监狱: ${JAIL}\n封禁IP: ${IP}\n归属地: ${IP_LOCATION}\n封禁时长: ${BANTIME}s\n时间: ${TIMESTAMP}"
else
    TITLE="✅ Fail2ban 解封通知"
    COLOR="info"
    MSG_BODY="主机: ${HOSTNAME}\n监狱: ${JAIL}\n解封IP: ${IP}\n归属地: ${IP_LOCATION}\n时间: ${TIMESTAMP}"
fi

# === 发送通知 ===
send_wechat() {
    local payload
    payload=$(cat <<EOJSON
{
    "msgtype": "markdown",
    "markdown": {
        "content": "### ${TITLE}\n${MSG_BODY}"
    }
}
EOJSON
)
    curl -s --max-time 5 -H "Content-Type: application/json" \
        -d "${payload}" "${WECHAT_WEBHOOK}" > /dev/null 2>&1 || true
}

send_dingtalk() {
    local payload
    payload=$(cat <<EOJSON
{
    "msgtype": "markdown",
    "markdown": {
        "title": "${TITLE}",
        "text": "### ${TITLE}\n${MSG_BODY}"
    }
}
EOJSON
)
    curl -s --max-time 5 -H "Content-Type: application/json" \
        -d "${payload}" "${DINGTALK_WEBHOOK}" > /dev/null 2>&1 || true
}

send_slack() {
    local payload
    payload=$(cat <<EOJSON
{
    "text": "*${TITLE}*\n\`\`\`${MSG_BODY}\`\`\`"
}
EOJSON
)
    curl -s --max-time 5 -H "Content-Type: application/json" \
        -d "${payload}" "${SLACK_WEBHOOK}" > /dev/null 2>&1 || true
}

# 根据配置发送
case "${NOTIFY_CHANNEL}" in
    wechat)   send_wechat ;;
    dingtalk) send_dingtalk ;;
    slack)    send_slack ;;
    *)        echo "未知通知渠道: ${NOTIFY_CHANNEL}" >&2; exit 1 ;;
esac

echo "[${TIMESTAMP}] ${ACTION} ${IP} (${JAIL}) 通知已发送至 ${NOTIFY_CHANNEL}"

设置脚本权限:

sudo chmod +x /etc/fail2ban/scripts/webhook-notify.sh

3.4 启动与验证

# 检查配置语法
fail2ban-client -t

# 启动服务
sudo systemctl start fail2ban
sudo systemctl enable fail2ban

# 查看所有 jail 状态
fail2ban-client status

# 查看指定 jail 详情
fail2ban-client status sshd

# 手动封禁/解封测试
fail2ban-client set sshd banip 203.0.113.100
fail2ban-client set sshd unbanip 203.0.113.100

# 查看当前 nftables 封禁规则
nft list ruleset | grep -A 5 f2b

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 封禁策略设计

渐进式封禁(递增 bantime)

一刀切的封禁时长并不合理——偶尔输错密码的合法用户和持续扫描的攻击者,不应该受到相同的惩罚。Fail2ban 0.11+ 提供了 bantime.increment 机制:

# /etc/fail2ban/jail.local - 渐进式封禁配置
[DEFAULT]
# 启用递增封禁
bantime.increment = true
# 递增因子:每次封禁时长 = bantime * (1 * factor^(banCount))
bantime.factor = 24
# 封禁次数记忆窗口(7天内的封禁历史都会计入)
bantime.maxtime = 4w
# 首次封禁基础时长
bantime = 10m

实际效果:首次封禁 10 分钟 → 第二次 4 小时 → 第三次 4 天 → 最终触顶 4 周。对于偶发误触的用户影响极小,对持续攻击者则自动升级为长期封禁。

永久封禁黑名单

对于已确认的恶意 IP,直接写入持久化黑名单比依赖 Fail2ban 的动态封禁更可靠:

# /etc/fail2ban/action.d/blacklist.local
# 自定义 action:将累计封禁超过 5 次的 IP 写入永久黑名单
[Definition]
actionban = if [ $(grep -c '<ip>' /etc/fail2ban/ip.blacklist 2>/dev/null) -eq 0 ]; then
             echo '<ip>' >> /etc/fail2ban/ip.blacklist
            fi
            ipset add f2b-blacklist <ip> -exist

配合 ipset 的 timeout 0(永不过期),即使 Fail2ban 服务重启,黑名单依然生效。

GeoIP 国家级封禁

如果业务明确只面向国内用户,可以在 Fail2ban 之前用 GeoIP 做第一层过滤:

# 安装 GeoIP 模块(以 nftables 为例)
sudo apt install -y geoip-bin geoip-database

# 在 nftables 中按国家代码放行/拒绝
# /etc/nftables.d/geoip-filter.nft
table inet geoip_filter {
    set cn_allowed {
        type ipv4_addr
        flags interval
        # 通过 geoip 数据库生成中国 IP 段
    }
    chain input {
        type filter hook input priority -10; policy accept;
        tcp dport { 22, 80, 443 } ip saddr != @cn_allowed drop
    }
}

封禁数量上限控制

当封禁列表膨胀到数万条时,iptables 的逐条匹配会显著拖慢网络性能。建议设置上限:

# jail.local
[DEFAULT]
# 单个 jail 最大封禁数量
bantime.maxcount = 50000
4.1.2 性能优化

banaction 选择对性能的影响

封禁动作的选择直接决定了高并发攻击下的系统开销:

banaction

封禁方式

万级 IP 性能

适用场景

iptables-multiport

逐条 -A 规则

差,线性匹配 O(n)

封禁量 < 500 的小规模场景

nftables-multiport

nft 规则集

中等,集合优化

中等规模,已迁移 nftables 的系统

firewallcmd-ipset

ipset 哈希集合

优秀,O(1) 查找

CentOS/RHEL firewalld 环境

nftables-allports

 + set

nft set 原生集合

优秀,内核态匹配

推荐,现代 Linux 首选方案

生产环境强烈建议使用 ipset 或 nftables set,当封禁列表达到 5000+ 条时,iptables 逐条匹配的 CPU 开销会变得不可忽视。

ipset 批量封禁配置

# /etc/fail2ban/jail.local
[DEFAULT]
banaction = iptables-ipset-proto6
# ipset 使用 hash:ip 类型,内核态 O(1) 匹配
# 相比 iptables 逐条规则,万级封禁下 CPU 开销降低 95%+

日志轮转与 Fail2ban 的配合

日志轮转后如果 Fail2ban 仍然持有旧文件描述符,会导致新日志无法被监控。正确的 logrotate 配置:

# /etc/logrotate.d/auth-fail2ban
/var/log/auth.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    # 关键:轮转后通知 Fail2ban 重新打开日志文件
    postrotate
        /usr/bin/fail2ban-client flushlogs >/dev/null 2>&1 || true
    endscript
}

flushlogs 比 reload 更轻量——它只重新打开文件句柄,不会重新加载配置和重置计数器。

4.1.3 高可用部署

单机 Fail2ban 的封禁列表无法跨节点共享,攻击者换一台目标服务器就能绕过封禁。多台服务器间同步封禁状态有以下方案:

方案

实现方式

优点

缺点

数据库后端

dbpurgeage

 + SQLite/MySQL 共享

原生支持,配置简单

SQLite 不支持并发写入,MySQL 需额外维护

Redis 共享

自定义 action 写入 Redis Set

高性能,天然支持 TTL 过期

需要自行编写 action 脚本

CrowdSec

分布式安全引擎,API 共享决策

社区威胁情报、自动同步

架构较重,学习成本高

自定义同步脚本

rsync/scp 定时同步封禁列表

实现简单

存在同步延迟,不适合实时场景

对于 10 台以内的服务器集群,Redis 方案性价比最高:

#!/bin/bash
set -euo pipefail
# /etc/fail2ban/action.d/redis-ban.sh
# 将封禁 IP 写入 Redis,所有节点共享

REDIS_HOST="10.0.0.100"
REDIS_PORT="6379"
REDIS_KEY="fail2ban:banned"
IP="$1"
BANTIME="$2"

# 写入 Redis 并设置过期时间
redis-cli -h "${REDIS_HOST}" -p "${REDIS_PORT}" SET "${REDIS_KEY}:${IP}" 1 EX "${BANTIME}"
4.1.4 与其他安全工具联动

Fail2ban + CrowdSec 社区情报

CrowdSec 维护了一个全球共享的恶意 IP 情报库。将 Fail2ban 的封禁数据上报到 CrowdSec,同时拉取社区情报做预防性封禁:

# 安装 CrowdSec 并注册
sudo apt install -y crowdsec
sudo cscli hub update
sudo cscli collections install crowdsecurity/linux

# 将 Fail2ban 日志接入 CrowdSec 作为数据源
# /etc/crowdsec/acquis.yaml 追加:
# ---
# filename: /var/log/fail2ban.log
# labels:
#   type: fail2ban

与 WAF 联动

Fail2ban 检测到的攻击 IP 可以通过 API 推送到 Nginx ModSecurity 或云 WAF 的黑名单:

# /etc/fail2ban/action.d/waf-notify.conf
[Definition]
actionban = curl -s -X POST https://waf-api.internal/blocklist \
            -H "Authorization: Bearer <token>" \
            -d '{"ip":"<ip>","duration":<bantime>,"source":"fail2ban"}'
actionunban = curl -s -X DELETE https://waf-api.internal/blocklist/<ip> \
              -H "Authorization: Bearer <token>"

与 SIEM 集成

通过 syslog 或 JSON 格式日志将 Fail2ban 事件推送到 ELK/Splunk/Graylog:

# /etc/fail2ban/fail2ban.local
[Definition]
logtarget = SYSLOG
syslogsocket = /dev/log

4.2 注意事项

4.2.1 配置注意事项

⚠️ 警告:ignoreip 配置不当会导致管理员自锁

这是 Fail2ban 运维中最常见的事故——管理员忘记将自己的 IP 加入白名单,调试时触发封禁规则把自己锁在门外。

# /etc/fail2ban/jail.local - 白名单配置(必须优先配置)
[DEFAULT]
# 本机回环 + 内网段 + 管理员固定 IP + VPN 出口
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 <管理员公网IP>
# 白名单支持 DNS 域名(会在启动时解析)
# ignoreip = office.example.com

关键注意事项:

  • bantime 设置过长(如 bantime = -1 永久封禁)时,一旦误封将无法通过等待自动解封来恢复,必须通过带外管理(IPMI/KVM/云控制台)登录处理

  • IPv6 支持需要确认 banaction 兼容性:iptables-multiport 不支持 IPv6,需使用 iptables-multiport 配合 ip6tables-multiport,或直接使用 nftables(原生双栈支持)

  • findtime 窗口过大会消耗更多内存,因为 Fail2ban 需要在内存中维护该时间窗口内的所有失败记录

4.2.2 常见错误

错误现象

原因分析

解决方案

Jail 启动后不封禁任何 IP

filter 正则与实际日志格式不匹配

fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

 测试匹配

fail2ban-client status

 显示 jail 未运行

jail.local 中 enabled = true 未设置或语法错误

检查配置语法:fail2ban-client -d 2>&1 | grep -i error

封禁后攻击者仍可访问

banaction 规则插入位置在 ACCEPT 规则之后

iptables -L INPUT --line-numbers

 检查规则顺序,确保 f2b 链在前

日志轮转后 Fail2ban 停止检测

文件句柄指向已归档的旧日志

logrotate postrotate 中添加 fail2ban-client flushlogs

服务启动报 NOK: ("no section",)

jail.local 文件编码异常或缺少 section header

确认文件为 UTF-8 无 BOM 编码,首行必须是 [DEFAULT] 或 [sshd] 等 section

数据库锁定 database is locked

SQLite 并发写入冲突(多 jail 同时触发)

设置 dbpurgeage = 1d 减小数据库体积,或迁移到 MySQL 后端

4.2.3 兼容性问题

systemd journal 后端 vs 传统日志文件

现代发行版(Ubuntu 22.04+、RHEL 8+)默认使用 systemd journal,部分服务不再写入传统日志文件:

# 使用 systemd journal 作为日志源
[sshd]
backend = systemd
# journalmatch 过滤条件(替代 logpath)
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd

注意:backend = systemd 模式下 logpath 参数会被忽略,两者不能混用。

容器环境中的 Fail2ban

Docker/Kubernetes 环境下,Fail2ban 面临两个核心问题:容器网络的 NAT 导致源 IP 丢失,以及 iptables 规则与容器运行时冲突。

# Docker 环境:Fail2ban 需要操作 DOCKER-USER 链而非 INPUT 链
# /etc/fail2ban/action.d/docker-action.conf
[Definition]
actionban = iptables -I DOCKER-USER -s <ip> -j DROP
actionunban = iptables -D DOCKER-USER -s <ip> -j DROP

Cloud 环境 NAT 后的真实 IP 获取

在 AWS ALB/NLB、Cloudflare 等反向代理后面,日志中记录的是代理 IP 而非真实客户端 IP。需要确保上游服务正确传递 X-Forwarded-For 或使用 PROXY Protocol,并在应用日志中记录真实 IP 后再交给 Fail2ban 分析。


五、故障排查和监控

5.1 故障排查

5.1.1 日志查看
# 实时跟踪 Fail2ban 运行日志
sudo tail -f /var/log/fail2ban.log

# 查看所有 jail 的汇总状态
sudo fail2ban-client status

# 查看指定 jail 的详细信息(当前封禁列表、累计封禁/检测次数)
sudo fail2ban-client status sshd
# 输出示例:
# Status for the jail: sshd
# |- Filter
# |  |- Currently failed: 3
# |  |- Total failed:     1247
# |  `- File list:        /var/log/auth.log
# `- Actions
#    |- Currently banned: 12
#    |- Total banned:     389
#    `- Banned IP list:   203.0.113.5 198.51.100.23 ...

# 临时调高日志级别用于调试(无需重启服务)
sudo fail2ban-client set loglevel DEBUG
# 调试完成后恢复
sudo fail2ban-client set loglevel INFO
5.1.2 常见问题排查

问题一:Jail 已启动但 filter 不匹配任何日志行

这是最高频的问题,通常是日志格式变化导致正则失效。

# 使用 fail2ban-regex 离线测试 filter 匹配情况
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

# 输出关键信息解读:
# Lines: 15234 lines, 0 ignored, 847 matched, 14387 missed
# ↑ matched 为 0 说明正则完全不匹配,需要检查日志格式

# 用单行日志测试特定正则
fail2ban-regex \
  "Jun 15 08:23:01 server sshd[12345]: Failed password for root from 203.0.113.5 port 22 ssh2" \
  /etc/fail2ban/filter.d/sshd.conf

# 常见原因:系统升级后 sshd 日志格式变化(如 OpenSSH 9.x 调整了日志前缀)
# 解决:更新 filter.d/sshd.conf 或升级 Fail2ban 到最新版本

问题二:封禁规则未生效(IP 已在封禁列表但仍可访问)

# 检查 iptables 规则链顺序
sudo iptables -L INPUT --line-numbers -n
# 确认 f2b-sshd 链的跳转规则在 ACCEPT 规则之前
# 如果使用 nftables:
sudo nft list ruleset | grep -B2 -A5 f2b

# 检查封禁 IP 是否确实在防火墙规则中
sudo iptables -L f2b-sshd -n
# 或
sudo ipset list f2b-sshd 2>/dev/null

# 常见原因:Docker 的 FORWARD 链或 firewalld 的 zone 优先级覆盖了 Fail2ban 规则
# 解决:调整 banaction 使用 DOCKER-USER 链或 firewallcmd-rich-rules

问题三:误封合法用户的紧急处理

# 紧急解封指定 IP
sudo fail2ban-client set sshd unbanip 10.20.30.40

# 批量解封某个网段(需要逐个处理)
for ip in $(sudo fail2ban-client status sshd | grep "Banned IP" | sed 's/.*://'); do
    if echo "$ip" | grep -q "^10\.20\."; then
        sudo fail2ban-client set sshd unbanip "$ip"
    fi
done

# 解封后立即加入白名单防止再次封禁
sudo fail2ban-client set sshd addignoreip 10.20.30.40

问题四:Fail2ban 服务崩溃或无响应

# 检查服务状态和最近的错误日志
sudo systemctl status fail2ban
sudo journalctl -u fail2ban --since "1 hour ago" --no-pager

# 常见原因1:SQLite 数据库损坏
# 症状:日志中出现 "database disk image is malformed"
sudo systemctl stop fail2ban
sudo mv /var/lib/fail2ban/fail2ban.sqlite3 /var/lib/fail2ban/fail2ban.sqlite3.bak
sudo systemctl start fail2ban
# Fail2ban 会自动创建新数据库,但历史封禁记录会丢失

# 常见原因2:内存泄漏(长期运行 + 大量 jail)
# 检查 Fail2ban 进程内存占用
ps aux | grep fail2ban-server | grep -v grep
# 如果 RSS 持续增长超过 500MB,考虑设置定期重启
5.1.3 fail2ban-client 调试命令集
# 检查配置文件语法(不启动服务)
fail2ban-client -d 2>&1 | head -50

# 查看当前生效的所有配置(含默认值)
fail2ban-client -d 2>&1 | grep -E "^\['set'"

# 重新加载单个 jail(不影响其他 jail 的封禁状态)
fail2ban-client reload sshd

# 重新加载全部配置
fail2ban-client reload

# 查看指定 jail 的 filter 正则表达式
fail2ban-client get sshd failregex

# 查看指定 jail 的当前配置值
fail2ban-client get sshd bantime
fail2ban-client get sshd findtime
fail2ban-client get sshd maxretry

# 运行时动态修改参数(立即生效,重启后失效)
fail2ban-client set sshd bantime 7200
fail2ban-client set sshd maxretry 10

# 获取 Fail2ban 服务器 ping 状态
fail2ban-client ping
# 正常输出:Server replied: pong

5.2 性能监控

5.2.1 关键指标

日常运维需要关注的核心指标:

# 各 jail 当前封禁 IP 数量
for jail in $(fail2ban-client status | grep "Jail list" | sed 's/.*://;s/,//g'); do
    count=$(fail2ban-client status "$jail" | grep "Currently banned" | awk '{print $NF}')
    echo "${jail}: ${count} banned"
done

# 最近 24 小时封禁/解封事件统计
grep "$(date +%Y-%m-%d)" /var/log/fail2ban.log | grep -c "Ban "
grep "$(date +%Y-%m-%d)" /var/log/fail2ban.log | grep -c "Unban "

# 被封禁次数最多的 Top 10 IP
grep "Ban " /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -10
5.2.2 监控指标说明

指标名称

正常范围

告警阈值

说明

当前封禁 IP 总数

< 500

> 2000

过高可能遭受大规模扫描或 banaction 性能下降

单小时新增封禁数

< 50

> 200

突增说明正在遭受集中攻击

filter 匹配失败率

< 5%

> 30%

过高说明日志格式可能已变化

Fail2ban 进程内存

< 200MB

> 500MB

持续增长提示内存泄漏

fail2ban-client ping 延迟

< 1s

> 5s

过高说明服务负载过重或数据库锁定

5.2.3 Prometheus + Grafana 监控

使用 fail2ban_exporter 将指标暴露给 Prometheus:

# 安装 fail2ban_exporter(Go 二进制,无额外依赖)
wget https://github.com/hectorj/fail2ban-prometheus-exporter/releases/latest/download/fail2ban_exporter_linux_amd64
chmod +x fail2ban_exporter_linux_amd64
sudo mv fail2ban_exporter_linux_amd64 /usr/local/bin/fail2ban_exporter

# 创建 systemd 服务
sudo tee /etc/systemd/system/fail2ban-exporter.service > /dev/null <<'EOF'
[Unit]
Description=Fail2ban Prometheus Exporter
After=fail2ban.service

[Service]
ExecStart=/usr/local/bin/fail2ban_exporter --web.listen-address=:9191
Restart=always
User=root

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload && sudo systemctl enable --now fail2ban-exporter

Prometheus 告警规则:

# /etc/prometheus/rules/fail2ban.yml
groups:
  - name: fail2ban
    rules:
      - alert: Fail2banHighBanRate
        expr: rate(fail2ban_banned_total[5m]) > 1
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Fail2ban 封禁速率异常 ({{ $labels.jail }})"
          description: "jail {{ $labels.jail }} 在过去 5 分钟内封禁速率超过 1 IP/s"

      - alert: Fail2banServiceDown
        expr: up{job="fail2ban"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Fail2ban exporter 不可达"

5.3 备份与恢复

5.3.1 配置备份脚本
#!/bin/bash
set -euo pipefail
# fail2ban-backup.sh - Fail2ban 配置与数据完整备份
# 建议通过 crontab 每日执行:0 2 * * * /opt/scripts/fail2ban-backup.sh

BACKUP_DIR="/backup/fail2ban"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/fail2ban-backup-${DATE}.tar.gz"
RETAIN_DAYS=30

# 创建备份目录
mkdir -p "${BACKUP_DIR}"

# 创建临时工作目录
TMPDIR=$(mktemp -d)
trap 'rm -rf "${TMPDIR}"' EXIT

# 备份核心配置文件
cp -a /etc/fail2ban/jail.local "${TMPDIR}/" 2>/dev/null || true
cp -a /etc/fail2ban/jail.d/ "${TMPDIR}/" 2>/dev/null || true
cp -a /etc/fail2ban/filter.d/*.local "${TMPDIR}/filter.d/" 2>/dev/null || true
cp -a /etc/fail2ban/action.d/*.local "${TMPDIR}/action.d/" 2>/dev/null || true
cp -a /etc/fail2ban/fail2ban.local "${TMPDIR}/" 2>/dev/null || true

# 备份数据库(包含封禁历史和递增计数)
if [ -f /var/lib/fail2ban/fail2ban.sqlite3 ]; then
    # 使用 sqlite3 的 .backup 命令确保一致性(避免直接 cp 导致损坏)
    sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 ".backup '${TMPDIR}/fail2ban.sqlite3'"
fi

# 备份自定义黑名单
cp -a /etc/fail2ban/ip.blacklist "${TMPDIR}/" 2>/dev/null || true

# 打包压缩
tar -czf "${BACKUP_FILE}" -C "${TMPDIR}" .

# 清理过期备份
find "${BACKUP_DIR}" -name "fail2ban-backup-*.tar.gz" -mtime +${RETAIN_DAYS} -delete

echo "[$(date)] 备份完成: ${BACKUP_FILE} ($(du -h "${BACKUP_FILE}" | awk '{print $1}'))"
5.3.2 恢复流程
#!/bin/bash
set -euo pipefail
# fail2ban-restore.sh - 从备份恢复 Fail2ban 配置
# 用法: ./fail2ban-restore.sh /backup/fail2ban/fail2ban-backup-20260225_020000.tar.gz

BACKUP_FILE="${1:?用法: $0 <备份文件路径>}"

if [ ! -f "${BACKUP_FILE}" ]; then
    echo "错误: 备份文件不存在: ${BACKUP_FILE}"
    exit 1
fi

# 1. 停止服务
echo "[1/4] 停止 Fail2ban 服务..."
sudo systemctl stop fail2ban

# 2. 备份当前配置(防止恢复失败时无法回退)
echo "[2/4] 备份当前配置..."
sudo cp -a /etc/fail2ban /etc/fail2ban.pre-restore.$(date +%s)

# 3. 解压恢复
echo "[3/4] 恢复配置文件..."
TMPDIR=$(mktemp -d)
trap 'rm -rf "${TMPDIR}"' EXIT
tar -xzf "${BACKUP_FILE}" -C "${TMPDIR}"

# 恢复配置文件(仅覆盖 .local 文件,不动默认配置)
sudo cp -f "${TMPDIR}"/jail.local /etc/fail2ban/ 2>/dev/null || true
sudo cp -rf "${TMPDIR}"/jail.d/ /etc/fail2ban/ 2>/dev/null || true
sudo cp -f "${TMPDIR}"/filter.d/*.local /etc/fail2ban/filter.d/ 2>/dev/null || true
sudo cp -f "${TMPDIR}"/action.d/*.local /etc/fail2ban/action.d/ 2>/dev/null || true

# 恢复数据库
if [ -f "${TMPDIR}/fail2ban.sqlite3" ]; then
    sudo cp -f "${TMPDIR}/fail2ban.sqlite3" /var/lib/fail2ban/
fi

# 4. 验证并启动
echo "[4/4] 验证配置并启动服务..."
sudo fail2ban-client -d > /dev/null 2>&1 && echo "配置语法检查通过" || { echo "配置语法错误,请检查"; exit 1; }
sudo systemctl start fail2ban
sudo fail2ban-client status

echo "恢复完成。"

六、总结

6.1 技术要点回顾

  • ✅ Fail2ban 的核心价值在于自动化响应——通过日志分析实时检测暴力破解行为,自动触发防火墙封禁,将人工巡检转化为机器实时防护

  • ✅ filter 正则的准确性是整个防护链的基础,日志格式变更(系统升级、应用更新)后必须用 fail2ban-regex 重新验证匹配率

  • ✅ banaction 的选择直接影响大规模封禁下的系统性能,生产环境应优先使用 ipset 或 nftables set 替代原生 iptables 逐条规则

  • ✅ 渐进式封禁策略(bantime.increment)比固定时长封禁更合理,既不会过度惩罚偶发误触的合法用户,又能对持续攻击者自动升级封禁力度

  • ✅ 白名单(ignoreip)是防止管理员自锁的最后一道保险,部署 Fail2ban 的第一步永远是配置白名单而非封禁规则

6.2 进阶学习方向

  1. CrowdSec 分布式安全引擎

    • 学习资源:CrowdSec 官方文档

    • 实践建议:从 Fail2ban + CrowdSec 并行部署开始,逐步将检测逻辑迁移到 CrowdSec 的 Scenario 机制,利用社区情报实现预防性封禁

  2. 基于 eBPF 的实时防护

    • 学习资源:Cilium 项目、bpfilter

    • 实践建议:eBPF 可以在内核态直接丢弃恶意流量,比 iptables/nftables 的用户态处理效率更高,适合超大规模封禁场景(10 万+ IP)

  3. 零信任架构下的认证防护

    • 学习资源:BeyondCorp 论文、Teleport

    • 实践建议:零信任架构通过证书认证替代密码认证,从根本上消除暴力破解的攻击面,Fail2ban 在这种架构下退化为兜底防护层

6.3 参考资料

  • Fail2ban 官方文档 - 权威配置参考和 FAQ

  • Fail2ban GitHub 仓库 - 源码、Issue 和最新 Release

  • CrowdSec 官方文档 - 分布式安全引擎,Fail2ban 的现代替代方案

  • nftables Wiki - nftables 规则语法和最佳实践

  • fail2ban_exporter - Prometheus 监控集成


附录

A. 命令速查表

# === 服务管理 ===
sudo systemctl start fail2ban          # 启动服务
sudo systemctl stop fail2ban           # 停止服务
sudo systemctl restart fail2ban        # 重启服务
sudo systemctl status fail2ban         # 查看服务状态

# === 状态查询 ===
fail2ban-client status                 # 查看所有 jail 汇总
fail2ban-client status sshd            # 查看指定 jail 详情
fail2ban-client ping                   # 检测服务是否响应

# === 封禁操作 ===
fail2ban-client set sshd banip 1.2.3.4       # 手动封禁 IP
fail2ban-client set sshd unbanip 1.2.3.4     # 手动解封 IP
fail2ban-client unban --all                   # 解封所有 jail 的所有 IP

# === 白名单管理 ===
fail2ban-client set sshd addignoreip 10.0.0.1    # 运行时添加白名单
fail2ban-client set sshd delignoreip 10.0.0.1    # 运行时移除白名单
fail2ban-client get sshd ignoreip                 # 查看当前白名单

# === 配置管理 ===
fail2ban-client reload                 # 重新加载全部配置
fail2ban-client reload sshd            # 重新加载单个 jail
fail2ban-client -d                     # 输出当前生效配置(调试用)
fail2ban-client set loglevel DEBUG     # 临时开启调试日志
fail2ban-client set loglevel INFO      # 恢复正常日志级别
fail2ban-client flushlogs              # 重新打开日志文件句柄

# === 运行时参数调整 ===
fail2ban-client get sshd bantime       # 查看当前 bantime
fail2ban-client set sshd bantime 3600  # 动态修改 bantime
fail2ban-client get sshd maxretry      # 查看当前 maxretry
fail2ban-client set sshd maxretry 5    # 动态修改 maxretry

# === Filter 测试 ===
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf    # 测试 filter 匹配
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf --print-all-matched  # 显示所有匹配行

B. 配置参数详解

jail.local 全局参数([DEFAULT] 段)

参数

默认值

说明

ignoreip 127.0.0.1/8 ::1

白名单 IP/CIDR/域名,空格分隔,永不封禁

bantime 10m

封禁持续时间,支持 s/m/h/d/w 后缀,-1 为永久

findtime 10m

检测时间窗口,在此窗口内累计失败次数

maxretry 5

触发封禁的失败次数阈值

backend auto

日志监控后端:auto/pyinotify/systemd/polling

banaction iptables-multiport

封禁动作,决定使用哪种防火墙机制

banaction_allports iptables-allports

全端口封禁动作(用于 recidive 等 jail)

action %(action_)s

封禁时执行的动作组合(封禁/通知/上报)

destemail root@localhost

告警邮件接收地址

sender root@<fq-hostname>

告警邮件发送地址

mta sendmail

邮件发送程序:sendmail/mail

protocol tcp

封禁协议:tcp/udp/icmp/all

chain INPUT

iptables 链名称

dbpurgeage 1d

数据库清理周期,超过此时间的记录自动删除

dbfile /var/lib/fail2ban/fail2ban.sqlite3

SQLite 数据库路径,设为 None 禁用持久化

jail.local 单 jail 参数

参数

说明

enabled

是否启用此 jail:true/false

port

监控的端口,支持数字或服务名(如 sshhttp,https

filter

使用的 filter 名称,对应 filter.d/<name>.conf

logpath

监控的日志文件路径,支持通配符(如 /var/log/nginx/access*.log

journalmatch

systemd journal 过滤条件(backend = systemd 时使用)

maxretry

覆盖全局 maxretry

findtime

覆盖全局 findtime

bantime

覆盖全局 bantime

bantime.increment

是否启用递增封禁:true/false

bantime.factor

递增因子,每次封禁时长乘以此系数

bantime.maxtime

递增封禁的最大时长上限

ignoreip

覆盖全局白名单(追加而非替换)

C. 术语表

术语

英文

解释

暴力破解

Brute Force Attack

通过穷举用户名/密码组合尝试登录的攻击方式

封禁

Ban

将恶意 IP 加入防火墙黑名单,阻断其网络访问

Jail

Jail

Fail2ban 的核心概念,一个 jail 定义了一组完整的检测+封禁规则

Filter

Filter

日志匹配规则,包含用于识别攻击行为的正则表达式

Action

Action

封禁/解封时执行的具体操作(如添加防火墙规则、发送邮件)

递增封禁

Incremental Ban

根据历史封禁次数自动延长封禁时间的机制

ipset

IP Set

Linux 内核的 IP 地址集合框架,支持 O(1) 复杂度的地址匹配

nftables

nftables

Linux 新一代包过滤框架,iptables 的替代方案

误封

False Positive

将合法用户的正常行为误判为攻击并封禁

带外管理

Out-of-Band Management

通过独立于业务网络的管理通道(IPMI/KVM/云控制台)访问服务器

CrowdSec

CrowdSec

开源分布式安全引擎,通过社区共享威胁情报实现协同防护

eBPF

Extended Berkeley Packet Filter

Linux 内核态可编程框架,可用于高性能网络过滤

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐