一、概述
1.1 背景介紹
暴力破解(Brute Force Attack)是最原始也是最有效的攻擊手段之一。攻擊者通過(guò)自動(dòng)化工具對(duì) SSH、Web 登錄、數(shù)據(jù)庫(kù)等服務(wù)進(jìn)行大量密碼嘗試,直到命中正確的憑據(jù)。根據(jù)公網(wǎng)蜜罐數(shù)據(jù)統(tǒng)計(jì),一臺(tái)新上線的 Linux 服務(wù)器暴露 SSH 端口后,平均 5 分鐘內(nèi)就會(huì)收到第一次暴力破解嘗試,每天被掃描數(shù)千次是常態(tài)。
暴力破解攻擊的危害不僅在于密碼被破解本身:
| 危害類型 | 具體說(shuō)明 |
|---|---|
| 憑據(jù)泄露 | 弱密碼被破解后攻擊者獲取系統(tǒng)權(quán)限 |
| 資源消耗 | 大量認(rèn)證請(qǐng)求消耗 CPU 和內(nèi)存 |
| 日志膨脹 | auth.log/secure 日志文件快速增長(zhǎng) |
| 影響正常訪問(wèn) | 連接數(shù)被惡意請(qǐng)求占滿 |
| 合規(guī)風(fēng)險(xiǎn) | 未防護(hù)暴力破解不符合等保要求 |
fail2ban 的工作原理
fail2ban 是一個(gè)入侵防御框架,核心機(jī)制是日志監(jiān)控 + 自動(dòng)封禁:
日志文件 fail2ban 防火墻
(auth.log) 引擎
|
新日志行 ------> Filter(正則匹配)
|
匹配失敗特征?
| |
是 否(忽略)
|
計(jì)數(shù)器 +1
|
超過(guò)閾值?
| |
是 否(繼續(xù)計(jì)數(shù))
|
Action(執(zhí)行封禁) ------> iptables/nftables
| 添加 DROP 規(guī)則
記錄封禁日志
|
啟動(dòng)解封定時(shí)器 ------> 到期自動(dòng)解封
核心概念:
Filter:定義匹配規(guī)則的正則表達(dá)式,從日志中識(shí)別失敗的認(rèn)證嘗試
Jail:一個(gè)監(jiān)控單元,包含 filter + action + 參數(shù)(閾值/時(shí)間窗/封禁時(shí)間)
Action:匹配后執(zhí)行的操作,通常是防火墻封禁
Ban/Unban:封禁和解封操作
1.2 與其他防御手段對(duì)比
| 工具 | 原理 | 優(yōu)勢(shì) | 劣勢(shì) |
|---|---|---|---|
| fail2ban | 日志正則匹配 | 通用性強(qiáng),支持任意日志格式的服務(wù) | 依賴日志實(shí)時(shí)寫(xiě)入 |
| DenyHosts | 解析 /etc/hosts.deny | 簡(jiǎn)單輕量 | 僅支持 SSH,項(xiàng)目已不活躍 |
| SSHGuard | 日志解析 | 多協(xié)議支持,低資源占用 | 自定義規(guī)則不如 fail2ban 靈活 |
| CrowdSec | 日志分析 + 社區(qū)威脅情報(bào) | 共享封禁列表,現(xiàn)代架構(gòu) | 部署復(fù)雜,需要聯(lián)網(wǎng)同步 |
| 防火墻限速 | 連接頻率限制 | 不依賴日志 | 無(wú)法區(qū)分正常和惡意請(qǐng)求 |
fail2ban 在單機(jī)防護(hù)場(chǎng)景下是最成熟的選擇:配置靈活、文檔豐富、社區(qū)活躍、資源消耗低。對(duì)于大規(guī)模集群,可以考慮 CrowdSec 或 fail2ban + 集中式日志的組合方案。
1.3 適用場(chǎng)景
SSH 服務(wù)暴力破解防護(hù)
Nginx/Apache 的 HTTP Basic Auth 暴力破解防護(hù)
Web 應(yīng)用登錄接口的暴力破解防護(hù)
惡意掃描(路徑遍歷、漏洞探測(cè))過(guò)濾
MySQL/PostgreSQL 遠(yuǎn)程登錄保護(hù)
郵件服務(wù)(Postfix/Dovecot)暴力破解防護(hù)
自定義應(yīng)用日志的異常行為檢測(cè)
1.4 環(huán)境要求
| 組件 | 版本要求 | 說(shuō)明 |
|---|---|---|
| 操作系統(tǒng) | Ubuntu 24.04 LTS / Rocky Linux 9.5 | 內(nèi)核 6.12+ |
| fail2ban | 1.1.x | 當(dāng)前穩(wěn)定版 |
| Python | 3.12+ | fail2ban 運(yùn)行依賴 |
| iptables/nftables | 系統(tǒng)自帶 | 封禁后端 |
| firewalld | 2.x(可選) | Rocky Linux 默認(rèn) |
| rsyslog/systemd-journald | 系統(tǒng)自帶 | 日志來(lái)源 |
# 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 # 檢查服務(wù)狀態(tài) sudo systemctl status fail2ban
二、詳細(xì)步驟
2.1 配置文件結(jié)構(gòu)
fail2ban 的配置文件層級(jí):
/etc/fail2ban/ ├── fail2ban.conf # 全局配置(日志級(jí)別、socket 路徑等) ├── fail2ban.local # 全局配置覆蓋(自定義項(xiàng)寫(xiě)在這里) ├── jail.conf # 默認(rèn) jail 定義(不要直接修改) ├── jail.local # jail 自定義配置(所有自定義都寫(xiě)在這里) ├── jail.d/ # jail 片段配置目錄 │ └── defaults-debian.conf ├── filter.d/ # filter 正則定義 │ ├── sshd.conf │ ├── nginx-http-auth.conf │ └── ... ├── action.d/ # action 動(dòng)作定義 │ ├── iptables-multiport.conf │ ├── nftables-multiport.conf │ ├── firewallcmd-rich-rules.conf │ └── ... └── paths-*.conf # 不同發(fā)行版的路徑定義
核心原則:永遠(yuǎn)不要修改.conf文件,所有自定義配置寫(xiě)在.local文件中。fail2ban 會(huì)先讀取.conf,再用.local覆蓋。
2.2 基礎(chǔ)配置
創(chuàng)建 jail.local
sudo cat > /etc/fail2ban/jail.local <'EOF' [DEFAULT] # 封禁時(shí)間(秒),默認(rèn) 10 分鐘 bantime = 3600 # 檢測(cè)時(shí)間窗口(秒),在這個(gè)時(shí)間窗口內(nèi)達(dá)到閾值就封禁 findtime = 600 # 失敗次數(shù)閾值 maxretry = 5 # 封禁動(dòng)作(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(不會(huì)被封禁) ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 # 封禁時(shí)間遞增(重復(fù)違規(guī)者封禁時(shí)間翻倍) bantime.increment =?true bantime.factor = 2 bantime.maxtime = 604800 # 通知郵箱(可選) # destemail = admin@example.com # sender = fail2ban@example.com # mta = sendmail # action = %(action_mwl)s # 后端(自動(dòng)檢測(cè),推薦 systemd) backend = systemd [sshd] enabled =?true port = ssh filter = sshd maxretry = 3 findtime = 300 bantime = 3600 EOF
啟動(dòng)服務(wù)
# 檢查配置語(yǔ)法 sudo fail2ban-client -t # OK: configuration test is successful # 啟動(dòng)并設(shè)置開(kāi)機(jī)自啟 sudo systemctlenable--now fail2ban # 查看運(yùn)行狀態(tài) sudo fail2ban-client status # Status # |- Number of jail: 1 # `- Jail list: sshd
2.3 SSH 防護(hù)配置
SSH 暴力破解是最常見(jiàn)的攻擊類型。fail2ban 內(nèi)置的 sshd filter 覆蓋了大部分場(chǎng)景。
# 查看 sshd filter 內(nèi)置的匹配規(guī)則 cat /etc/fail2ban/filter.d/sshd.conf
sshd filter 能匹配的日志模式包括:
| 日志模式 | 含義 |
|---|---|
|
Failed password for |
密碼認(rèn)證失敗 |
|
Failed publickey for |
公鑰認(rèn)證失敗 |
|
Invalid user |
不存在的用戶名 |
| Connection closed by authenticating user | 認(rèn)證過(guò)程中斷開(kāi) |
| maximum authentication attempts exceeded | 超過(guò)最大認(rèn)證次數(shù) |
增強(qiáng)的 SSH 防護(hù)配置
# /etc/fail2ban/jail.local 中的 [sshd] 部分 [sshd] enabled = true port = ssh filter = sshd[mode=aggressive] # aggressive 模式包含更多匹配規(guī)則,會(huì)匹配 "Invalid user" 等 maxretry = 3 findtime = 300 bantime = 3600 # 如果 SSH 使用非標(biāo)準(zhǔn)端口 # port = 2222
驗(yàn)證 SSH 防護(hù)是否生效
# 查看 sshd jail 狀態(tài) 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 中的封禁規(guī)則 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 防護(hù)配置
HTTP Basic Auth 暴力破解防護(hù)
# /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 惡意掃描防護(hù)
fail2ban 默認(rèn)不包含通用的 Nginx 惡意掃描 filter,需要自定義:
# 創(chuàng)建自定義 filter sudo cat > /etc/fail2ban/filter.d/nginx-badbots.conf <'EOF' [Definition] # 匹配常見(jiàn)惡意掃描特征 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 攻擊防護(hù)
# 創(chuàng)建 CC 攻擊檢測(cè) filter sudo cat > /etc/fail2ban/filter.d/nginx-cc.conf <'EOF' [Definition] # 匹配短時(shí)間內(nèi)同一 IP 的高頻請(qǐng)求(需要在 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 正則編寫(xiě)
編寫(xiě)自定義 filter 是 fail2ban 最核心的技能。
正則編寫(xiě)規(guī)則
# fail2ban filter 正則語(yǔ)法 #- 特殊標(biāo)記,匹配 IP 地址并作為封禁目標(biāo) # ^ - 行首(fail2ban 自動(dòng)處理時(shí)間戳前綴) # .* - 任意字符 # 其他 - 標(biāo)準(zhǔn) Python 正則語(yǔ)法
測(cè)試 filter 是否生效
# 測(cè)試 filter 規(guī)則是否匹配日志 sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf # 測(cè)試自定義 filter sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-badbots.conf # 測(cè)試單行日志 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 暴力破解防護(hù)
假設(shè)應(yīng)用日志格式如下:
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
編寫(xiě) filter:
sudo cat > /etc/fail2ban/filter.d/myapp-login.conf <'EOF' [Definition] failregex = ^s*[WARN]s+Login failed?for?user .* froms*$ ignoreregex = datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S EOF
配置 jail:
# /etc/fail2ban/jail.local 追加 [myapp-login] enabled = true port = 8080 filter = myapp-login logpath = /var/log/myapp/application.log maxretry = 5 findtime = 300 bantime = 1800
2.6 多服務(wù)防護(hù)配置
MySQL 遠(yuǎn)程登錄防護(hù)
[mysqld-auth] enabled = true port = 3306 filter = mysqld-auth logpath = /var/log/mysql/error.log maxretry = 5 findtime = 600 bantime = 3600
FTP 防護(hù)(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 防護(hù)
[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 聯(lián)動(dòng)
firewalld 聯(lián)動(dòng)(Rocky Linux)
# /etc/fail2ban/jail.local 的 [DEFAULT] 部分 [DEFAULT] banaction = firewallcmd-rich-rules banaction_allports = firewallcmd-rich-rules
驗(yàn)證:
# 查看 firewalld 中的 fail2ban 規(guī)則 sudo firewall-cmd --list-rich-rules # rule family="ipv4" source address="103.xx.xx.92" reject type="icmp-port-unreachable"
nftables 聯(lián)動(dòng)
# /etc/fail2ban/jail.local 的 [DEFAULT] 部分 [DEFAULT] banaction = nftables-multiport banaction_allports = nftables-allports
驗(yàn)證:
# 查看 nftables 中的 fail2ban 規(guī)則 sudo nft listsetinet f2b-table addr-set-sshd
2.8 啟動(dòng)和驗(yàn)證
# 重新加載配置 sudo fail2ban-client reload # 查看所有活躍的 jail sudo fail2ban-client status # 查看特定 jail 的詳細(xì)狀態(tài) sudo fail2ban-client status sshd # 手動(dòng)封禁測(cè)試 sudo fail2ban-clientsetsshd banip 1.2.3.4 # 手動(dòng)解封 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]
# ========================
# 全局默認(rèn)參數(shù)
# ========================
# 封禁時(shí)間 1 小時(shí)
bantime = 3600
# 檢測(cè)窗口 10 分鐘
findtime = 600
# 失敗閾值 5 次
maxretry = 5
# 白名單(內(nèi)網(wǎng) + 跳板機(jī) 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 防護(hù)
# ========================
[sshd]
enabled = true
port = ssh
filter = sshd[mode=aggressive]
maxretry = 3
findtime = 300
bantime = 3600
# ========================
# Nginx 防護(hù)
# ========================
[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
# ========================
# 其他服務(wù)
# ========================
[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 暴力破解防御(封禁統(tǒng)計(jì))
場(chǎng)景:一臺(tái)公網(wǎng)服務(wù)器遭受持續(xù)的 SSH 暴力破解,需要配置 fail2ban 并統(tǒng)計(jì)封禁效果。
# 1. 先看看攻擊有多嚴(yán)重
sudo journalctl -u sshd --since"1 hour ago"| grep"Failed password"| wc -l
# 2847
# 統(tǒng)計(jì)攻擊來(lái)源 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. 確認(rèn) fail2ban 已配置并運(yùn)行
sudo fail2ban-client status sshd
# 3. 查看封禁效果(運(yùn)行 24 小時(shí)后)
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. 統(tǒng)計(jì)每日封禁 IP 數(shù)量
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 惡意掃描過(guò)濾
場(chǎng)景:Nginx 日志中發(fā)現(xiàn)大量 404/403 請(qǐng)求,都是漏洞掃描器的探測(cè)行為。
# 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. 確認(rèn) nginx-badbots filter 匹配效果
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-badbots.conf
# Results
# =======
# Failregex: 3247 total
# ...
# 3. 啟用后驗(yàn)證
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:自定義規(guī)則防御 API 暴力調(diào)用
場(chǎng)景:API 網(wǎng)關(guān)日志發(fā)現(xiàn)有 IP 在暴力嘗試登錄接口。
應(yīng)用日志格式:
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. 創(chuàng)建自定義 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. 測(cè)試 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 服務(wù)狀態(tài) ====="
sudo fail2ban-client status
echo""
echo"===== 各 Jail 封禁數(shù) ====="
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}: 當(dāng)前封禁${RED}${banned}${NC}, 累計(jì)封禁${total}"
done
;;
banned)
echo"===== 當(dāng)前封禁 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"===== 封禁統(tǒng)計(jì)(最近 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
四、最佳實(shí)踐和注意事項(xiàng)
4.1 最佳實(shí)踐
白名單管理
# /etc/fail2ban/jail.local 的 [DEFAULT] 部分
[DEFAULT]
# 白名單:絕不封禁的 IP
# 包含:本機(jī)、內(nèi)網(wǎng)、跳板機(jī)、監(jiān)控服務(wù)器
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 # 跳板機(jī) IP
203.0.113.51 # 監(jiān)控服務(wù)器 IP
注意事項(xiàng):
白名單一定要包含所有合法的運(yùn)維入口 IP,否則運(yùn)維人員輸錯(cuò)密碼也會(huì)被封
如果使用跳板機(jī),跳板機(jī) IP 必須在白名單中
監(jiān)控系統(tǒng)的探測(cè) IP 也要加入白名單
白名單支持 CIDR 表示法
封禁策略梯度設(shè)計(jì)
[DEFAULT] # 遞增封禁:每次被封,時(shí)間翻倍 bantime.increment = true # 倍增因子 bantime.factor = 2 # 最大封禁時(shí)間 7 天 bantime.maxtime = 604800 # 跨 jail 累計(jì)(同一 IP 在不同 jail 的違規(guī)都計(jì)入) bantime.overalljails = true
遞增封禁效果示例:
| 被封次數(shù) | 封禁時(shí)間 | 累計(jì) |
|---|---|---|
| 第 1 次 | 1 小時(shí) | 1 小時(shí) |
| 第 2 次 | 2 小時(shí) | 3 小時(shí) |
| 第 3 次 | 4 小時(shí) | 7 小時(shí) |
| 第 4 次 | 8 小時(shí) | 15 小時(shí) |
| 第 5 次 | 16 小時(shí) | 31 小時(shí) |
| 第 6 次+ | 7 天(上限) | - |
與 CrowdSec 混合部署
fail2ban 負(fù)責(zé)本地日志檢測(cè),CrowdSec 提供社區(qū)威脅情報(bào)補(bǔ)充:
# 安裝 CrowdSec curl -s https://install.crowdsec.net | sudo bash sudo apt install -y crowdsec crowdsec-firewall-bouncer-iptables # CrowdSec 會(huì)自動(dòng)共享封禁列表,補(bǔ)充 fail2ban 的本地檢測(cè) # 兩者并存不沖突,分別使用不同的 iptables 鏈
4.2 注意事項(xiàng)
常見(jiàn)錯(cuò)誤
| 錯(cuò)誤現(xiàn)象 | 原因分析 | 解決方案 |
|---|---|---|
| fail2ban 啟動(dòng)失敗 | jail.local 語(yǔ)法錯(cuò)誤 | fail2ban-client -t 檢查語(yǔ)法 |
| filter 不匹配日志 | 正則表達(dá)式錯(cuò)誤 | fail2ban-regex 測(cè)試 |
| 封禁后仍可訪問(wèn) | iptables 規(guī)則順序問(wèn)題 | 檢查規(guī)則鏈優(yōu)先級(jí) |
| 誤封合法 IP | 白名單配置不完整 | 完善 ignoreip |
| 日志時(shí)間不匹配 | 時(shí)區(qū)或日期格式問(wèn)題 | 檢查 datepattern |
| jail 顯示 0 failed | backend 配置不對(duì) | 切換 backend 為 systemd 或 polling |
| Docker 容器內(nèi)日志 | fail2ban 無(wú)法讀取容器日志 | 將日志掛載到宿主機(jī) |
| 重啟后封禁丟失 | 默認(rèn)不持久化封禁 | 啟用 dbpurgeage 或增大 bantime |
性能注意事項(xiàng)
# fail2ban 的資源消耗主要在日志匹配 # 每個(gè) jail 都會(huì)啟動(dòng)一個(gè)線程監(jiān)控日志文件 # 查看 fail2ban 內(nèi)存使用 ps aux | grep fail2ban | grep -v grep # 如果日志文件非常大,建議: # 1. 使用 logrotate 定期切割日志 # 2. 減少 findtime 縮小掃描范圍 # 3. 優(yōu)化正則表達(dá)式(避免過(guò)于寬泛的 .*) # 查看 fail2ban 數(shù)據(jù)庫(kù)大小 ls -lh /var/lib/fail2ban/fail2ban.sqlite3
兼容性問(wèn)題
fail2ban 1.1.x 需要 Python 3.8+,Ubuntu 24.04 和 Rocky Linux 9.5 均滿足
如果系統(tǒng)同時(shí)運(yùn)行 firewalld 和 iptables,注意 banaction 的選擇
Docker 環(huán)境中,fail2ban 需要在宿主機(jī)上運(yùn)行,監(jiān)控宿主機(jī)上的日志文件
使用 systemd journal 作為 backend 時(shí),確保日志持久化(Storage=persistent)
五、故障排查和監(jiān)控
5.1 故障排查
fail2ban 日志
# 查看 fail2ban 運(yùn)行日志 sudo tail -f /var/log/fail2ban.log # 過(guò)濾封禁事件 sudo grep"Ban"/var/log/fail2ban.log | tail -20 # 過(guò)濾解封事件 sudo grep"Unban"/var/log/fail2ban.log | tail -20 # 過(guò)濾錯(cuò)誤 sudo grep"ERROR|WARNING"/var/log/fail2ban.log | tail -20 # 查看 fail2ban 服務(wù)日志 sudo journalctl -u fail2ban -n 50 --no-pager
常見(jiàn)問(wèn)題排查
問(wèn)題 1:fail2ban 無(wú)法匹配日志
# 檢查日志文件權(quán)限 ls -la /var/log/auth.log # fail2ban 需要讀取權(quán)限 # 檢查日志格式是否匹配 filter sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf --print-all-matched # 檢查 backend 設(shè)置 # 如果使用 systemd,確認(rèn) journald 正在記錄 sudo journalctl -u sshd -n 5
問(wèn)題 2:封禁后 IP 仍可訪問(wèn)
# 檢查 iptables 規(guī)則是否存在 sudo iptables -L f2b-sshd -n -v # 檢查規(guī)則順序(fail2ban 的規(guī)則是否在 ACCEPT 之前) sudo iptables -L INPUT -n -v --line-numbers # 如果使用 Docker,檢查 DOCKER 鏈?zhǔn)欠窭@過(guò)了 INPUT 鏈 sudo iptables -L FORWARD -n -v
問(wèn)題 3:重啟后封禁記錄丟失
# fail2ban 使用 SQLite 數(shù)據(jù)庫(kù)存儲(chǔ)封禁記錄 # 檢查數(shù)據(jù)庫(kù)是否正常 sudo fail2ban-client get sshd bantime # 確保 dbpurgeage 設(shè)置足夠長(zhǎng) # /etc/fail2ban/fail2ban.local # [Definition] # dbpurgeage = 86400
調(diào)試模式
# 前臺(tái)運(yùn)行 fail2ban,查看詳細(xì)輸出 sudo fail2ban-client stop sudo fail2ban-server -xf --loglevel DEBUG # 查看某個(gè) filter 的實(shí)時(shí)匹配 sudo fail2ban-clientsetsshd loglevel DEBUG sudo tail -f /var/log/fail2ban.log | grep sshd
5.2 性能監(jiān)控
fail2ban 指標(biāo)導(dǎo)出到 Prometheus
使用 fail2ban-prometheus-exporter 或自定義腳本:
#!/bin/bash # f2b_exporter.sh - fail2ban 指標(biāo)導(dǎo)出腳本 # 配合 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 的指標(biāo) 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 告警規(guī)則
# fail2ban_alerts.yml groups: -name:fail2ban rules: -alert:Fail2banJailDown expr:absent(fail2ban_currently_banned) for:5m labels: severity:critical annotations: summary:"fail2ban 指標(biāo)消失,服務(wù)可能已停止" -alert:Fail2banHighBanRate expr:rate(fail2ban_total_banned{jail="sshd"}[1h])>50 for:10m labels: severity:warning annotations: summary:"SSH 封禁頻率異常高:{{ $value }}/小時(shí)" description:"jail{{ $labels.jail }}的封禁速率超過(guò) 50/小時(shí),可能遭受大規(guī)模攻擊" -alert:Fail2banManyCurrentlyBanned expr:fail2ban_currently_banned>200 for:5m labels: severity:warning annotations: summary:"當(dāng)前封禁 IP 數(shù)量過(guò)多:{{ $value }}"
Grafana 面板關(guān)鍵查詢
# 當(dāng)前封禁數(shù) fail2ban_currently_banned # 每小時(shí)封禁速率 rate(fail2ban_total_banned[1h]) * 3600 # 每小時(shí)失敗嘗試速率 rate(fail2ban_total_failed[1h]) * 3600 # 各 jail 封禁占比 fail2ban_currently_banned / ignoring(jail) group_left sum(fail2ban_currently_banned)
5.3 備份與恢復(fù)
配置備份
#!/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
# 備份數(shù)據(jù)庫(kù)
cp /var/lib/fail2ban/fail2ban.sqlite3"${BACKUP_DIR}/fail2ban_db_${DATE}.sqlite3"
# 保留 30 天
find"$BACKUP_DIR"-mtime +30 -delete
echo"fail2ban 配置已備份到${BACKUP_DIR}"
配置恢復(fù)
# 恢復(fù)配置 sudo tar xzf /opt/backup/fail2ban/fail2ban_config_20260313.tar.gz -C / # 恢復(fù)數(shù)據(jù)庫(kù)(保留封禁記錄) sudo systemctl stop fail2ban sudo cp /opt/backup/fail2ban/fail2ban_db_20260313.sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 sudo systemctl start fail2ban # 驗(yàn)證 sudo fail2ban-client status
六、總結(jié)
6.1 技術(shù)要點(diǎn)回顧
fail2ban 通過(guò)日志正則匹配 + 自動(dòng)防火墻封禁實(shí)現(xiàn)入侵防御,核心是 Filter → Jail → Action 鏈路
所有自定義配置寫(xiě)在.local文件中,不要修改.conf原始文件
SSH 防護(hù)使用內(nèi)置 sshd filter 的 aggressive 模式,覆蓋面最廣
Nginx 防護(hù)需要自定義 filter(惡意掃描、CC 攻擊),內(nèi)置的 nginx-http-auth 只覆蓋 Basic Auth
白名單(ignoreip)必須包含所有合法運(yùn)維入口,避免誤封
遞增封禁策略(bantime.increment)對(duì)重復(fù)違規(guī)者逐步加重處罰
使用fail2ban-regex工具測(cè)試 filter 匹配效果,避免上線后才發(fā)現(xiàn)規(guī)則不生效
6.2 進(jìn)階學(xué)習(xí)方向
CrowdSec 社區(qū)防御:基于社區(qū)威脅情報(bào)的協(xié)同防御,適合大規(guī)模部署
自定義 Action:封禁后自動(dòng)發(fā)送通知(郵件/企微/釘釘),或?qū)懭?SIEM 系統(tǒng)
集中式日志 + fail2ban:使用 Filebeat/Fluentd 收集多臺(tái)服務(wù)器日志,在中心節(jié)點(diǎn)運(yùn)行 fail2ban
WAF 集成:將 fail2ban 與 ModSecurity/Coraza WAF 結(jié)合,實(shí)現(xiàn)多層防御
6.3 參考資料
fail2ban 官方文檔: https://github.com/fail2ban/fail2ban/wiki
fail2ban filter 編寫(xiě)指南: https://github.com/fail2ban/fail2ban/wiki/Developing-Filters
CrowdSec 官方文檔: https://docs.crowdsec.net/
Linux iptables 手冊(cè): man iptables(8)
nftables wiki: https://wiki.nftables.org/
附錄
A. 命令速查表
# 服務(wù)管理 sudo systemctl start fail2ban # 啟動(dòng) sudo systemctl stop fail2ban # 停止 sudo systemctl restart fail2ban # 重啟 sudo systemctl status fail2ban # 狀態(tài) # 配置管理 sudo fail2ban-client -t # 語(yǔ)法檢查 sudo fail2ban-client reload # 重新加載配置 sudo fail2ban-client reload sshd # 重新加載單個(gè) jail # 狀態(tài)查看 sudo fail2ban-client status # 總體狀態(tài) sudo fail2ban-client status sshd # 單個(gè) jail 狀態(tài) sudo fail2ban-client banned # 所有封禁列表 # 封禁/解封 sudo fail2ban-clientsetsshd banip 1.2.3.4 # 手動(dòng)封禁 sudo fail2ban-clientsetsshd unbanip 1.2.3.4 # 手動(dòng)解封 sudo fail2ban-client unban --all # 解封所有 # 調(diào)試 sudo fail2ban-regex# 測(cè)試 filter sudo fail2ban-clientsetsshd loglevel DEBUG # 開(kāi)啟調(diào)試
B. 配置參數(shù)詳解
| 參數(shù) | 默認(rèn)值 | 說(shuō)明 |
|---|---|---|
| bantime | 600 | 封禁持續(xù)時(shí)間(秒) |
| findtime | 600 | 檢測(cè)時(shí)間窗口(秒) |
| maxretry | 5 | 觸發(fā)封禁的失敗次數(shù) |
| ignoreip | 127.0.0.1/8 | 白名單 IP/CIDR |
| backend | auto | 日志后端(systemd/polling/inotify) |
| banaction | iptables-multiport | 封禁動(dòng)作 |
| usedns | warn | DNS 解析策略(yes/no/warn/raw) |
| logencoding | auto | 日志文件編碼 |
| enabled | false | 是否啟用 jail |
| port | - | 要封禁的端口 |
| filter | - | 使用的 filter 名稱 |
| logpath | - | 監(jiān)控的日志文件路徑 |
| bantime.increment | false | 是否啟用遞增封禁 |
| bantime.factor | 1 | 遞增倍數(shù) |
| bantime.maxtime | - | 最大封禁時(shí)間 |
C. 術(shù)語(yǔ)表
| 術(shù)語(yǔ) | 英文 | 解釋 |
|---|---|---|
| 暴力破解 | Brute Force Attack | 通過(guò)窮舉方式嘗試大量密碼組合的攻擊方法 |
| 字典攻擊 | Dictionary Attack | 使用預(yù)定義密碼列表進(jìn)行暴力破解 |
| 撞庫(kù) | Credential Stuffing | 使用泄露的賬號(hào)密碼在其他平臺(tái)嘗試登錄 |
| 封禁 | Ban | 將攻擊 IP 添加到防火墻黑名單,阻止其訪問(wèn) |
| 監(jiān)獄 | Jail | fail2ban 的監(jiān)控單元,包含 filter + action + 參數(shù) |
| 過(guò)濾器 | Filter | 定義日志匹配規(guī)則的正則表達(dá)式模塊 |
| 動(dòng)作 | Action | 匹配后執(zhí)行的操作(如添加防火墻規(guī)則) |
| 遞增封禁 | Incremental Ban | 對(duì)重復(fù)違規(guī)者逐次增加封禁時(shí)間 |
| 蜜罐 | Honeypot | 故意暴露的假目標(biāo),用于誘捕和分析攻擊者 |
| CC 攻擊 | Challenge Collapsar | 通過(guò)大量合法 HTTP 請(qǐng)求耗盡服務(wù)器資源的攻擊 |
-
Linux
+關(guān)注
關(guān)注
88文章
11781瀏覽量
219229 -
服務(wù)器
+關(guān)注
關(guān)注
14文章
10299瀏覽量
91587
原文標(biāo)題:用 fail2ban 防御暴力破解的落地實(shí)踐
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
labview密碼破解
labview如何寫(xiě)暴力破解密碼 密碼4位數(shù) 由1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ 如何每次讀取一個(gè)數(shù)進(jìn)行測(cè)試完成后進(jìn)行下一個(gè)測(cè)試
何為暴力破解呢
labview的VI加密暴力破解工具
為什么不能隨便暴力破解比特幣私鑰
暴力破解比特幣私鑰可能會(huì)實(shí)現(xiàn)嗎
字符串硬核暴力破解法講解
IP知識(shí)百科之暴力破解
如何通過(guò)Python腳本實(shí)現(xiàn)WIFI密碼的暴力破解
通過(guò)Python腳本實(shí)現(xiàn)WIFI密碼的暴力破解
會(huì)用kali破解wifi嗎?
R5300 G4服務(wù)器的BMC進(jìn)行滲透測(cè)試案例
如何在服務(wù)器端自動(dòng)ban掉掃描ssh的IP
使用fail2ban防御暴力破解的落地實(shí)踐
評(píng)論