基于Fail2ban的暴力破解防护:SSH与Web服务安全加固
暴力破解是公网服务器面临的最常见攻击之一。SSH、Web 登录页面、API 接口是主要目标,攻击者使用 Hydra、Medusa、Burp Suite 等自动化工具,以每秒数百次的频率尝试用户名密码组合。一台暴露 22 端口的服务器,上线几小时内就会收到大量暴力破解请求,这在中随处可见。Fail2ban 是 Linux 环境下成熟的入侵防御工具,工作原理直观:监控日志文件 → 正则匹配失败记录 →
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 |
逐条 |
差,线性匹配 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 中 |
检查配置语法: |
|
封禁后攻击者仍可访问 |
banaction 规则插入位置在 ACCEPT 规则之后 |
iptables -L INPUT --line-numbers
检查规则顺序,确保 f2b 链在前 |
|
日志轮转后 Fail2ban 停止检测 |
文件句柄指向已归档的旧日志 |
logrotate postrotate 中添加 |
|
服务启动报 |
jail.local 文件编码异常或缺少 section header |
确认文件为 UTF-8 无 BOM 编码,首行必须是 |
|
数据库锁定 |
SQLite 并发写入冲突(多 jail 同时触发) |
设置 |
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 进阶学习方向
-
CrowdSec 分布式安全引擎
-
学习资源:CrowdSec 官方文档
-
实践建议:从 Fail2ban + CrowdSec 并行部署开始,逐步将检测逻辑迁移到 CrowdSec 的 Scenario 机制,利用社区情报实现预防性封禁
-
-
基于 eBPF 的实时防护
-
学习资源:Cilium 项目、bpfilter
-
实践建议:eBPF 可以在内核态直接丢弃恶意流量,比 iptables/nftables 的用户态处理效率更高,适合超大规模封禁场景(10 万+ IP)
-
-
零信任架构下的认证防护
-
学习资源: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 |
封禁持续时间,支持 |
findtime |
10m |
检测时间窗口,在此窗口内累计失败次数 |
maxretry |
5 |
触发封禁的失败次数阈值 |
backend |
auto |
日志监控后端: |
banaction |
iptables-multiport |
封禁动作,决定使用哪种防火墙机制 |
banaction_allports |
iptables-allports |
全端口封禁动作(用于 recidive 等 jail) |
action |
%(action_)s |
封禁时执行的动作组合(封禁/通知/上报) |
destemail |
root@localhost |
告警邮件接收地址 |
sender |
root@<fq-hostname> |
告警邮件发送地址 |
mta |
sendmail |
邮件发送程序: |
protocol |
tcp |
封禁协议: |
chain |
INPUT |
iptables 链名称 |
dbpurgeage |
1d |
数据库清理周期,超过此时间的记录自动删除 |
dbfile |
/var/lib/fail2ban/fail2ban.sqlite3 |
SQLite 数据库路径,设为 |
jail.local 单 jail 参数
|
参数 |
说明 |
|---|---|
enabled |
是否启用此 jail: |
port |
监控的端口,支持数字或服务名(如 |
filter |
使用的 filter 名称,对应 |
logpath |
监控的日志文件路径,支持通配符(如 |
journalmatch |
systemd journal 过滤条件( |
maxretry |
覆盖全局 maxretry |
findtime |
覆盖全局 findtime |
bantime |
覆盖全局 bantime |
bantime.increment |
是否启用递增封禁: |
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 内核态可编程框架,可用于高性能网络过滤 |
更多推荐


所有评论(0)