fail2ban 是一个开源的入侵防御工具,用于保护 Linux 服务器免受暴力破解攻击。它通过监控系统日志文件(如 /var/log/auth.log)来检测恶意行为,如多次失败的 SSH 登录尝试,然后自动更新防火墙规则来阻止这些攻击者的 IP 地址。
fail2ban 核心功能
- 实时监控日志: fail2ban 持续监控指定的日志文件,寻找预定义模式的恶意行为。
- 自动封禁 IP: 当检测到来自同一 IP 的多次失败尝试(可配置阈值),fail2ban 会自动将该 IP 添加到防火墙阻止列表中。
- 可配置的封禁时间: 管理员可以设置初始封禁时间和多次违规后的递增封禁时间。
- 多服务支持: 不仅支持 SSH,还支持 Apache、Nginx、FTP、邮件服务等多种服务的防护。
- 邮件通知: 可配置在封禁 IP 时发送邮件通知给管理员。
如何查看爆力请求的日志
sshd
方法一:使用 journalctl 命令(推荐)
sudo journalctl -u ssh # 查看所有 SSH 相关日志,-u 参数的作用
sudo journalctl -fu ssh # 持续输出新产生的 SSH 日志,方便你实时观察登录尝试
sudo journalctl -u ssh -n 50 --no-pager # 不分页查看最新50条,SSH相关日志
# 查看从特定时间点开始的日志,可以组合 `--until` 来限定结束时间
sudo journalctl -u ssh --since "2026-07-11 00:00:00"
sudo journalctl -u ssh --since "1 hour ago" # 查看最近1小时的日志
sudo journalctl -u ssh --since "1 day ago" # 查看最近1天的日志
sudo journalctl -u ssh --since "1 week ago" # 查看最近1周的日志
密码登录:查看失败记录
# 从 SSH 日志中筛选出所有包含“Failed password”的记录
sudo journalctl -u ssh | grep "Failed password"
sudo journalctl -u ssh --since "1 day ago" | grep "Failed password"
示例记录
sshd[15959]: Failed password for root from 222.186.180.142 port 27860 ssh2[reference:12]
证书登录:查看失败记录
# 查看所有 SSH 相关日志,并过滤出与证书/公钥/签名验证有关的条目
FailedKey="error|Certificate|publickey|signature|authenticated|invalid|expired"
sudo journalctl -u ssh | grep -E "$FailedKey"
sudo journalctl -u ssh --since "1 day ago" | grep -E "$FailedKey"
示例记录
# 含义:客户端尝试使用一个不存在的系统用户(`controll`)进行登录,SSH 在预认证阶段(`preauth`)就拒绝了连接并关闭了会话。
# 是否危险:只要你的真实用户有强密码或仅使用密钥,这种尝试不会成功,属于“噪音”。但如果这种记录多,表示正在做**暴力破解**
Jul 11 03:09:36 vps260315 sshd[315981]: Connection closed by invalid user controll 102.203.197.6 port 42912 [preauth]
登录成功的记录
# 含义:用户 ecs 使用 RSA 公钥成功登录,来源 IP a.b.c.d。
Jul 11 11:45:48 vps260315 sshd[317102]: Accepted publickey for ecs from a.b.c.d port 2253 ssh2: RSA SHA256:Y5u
以下是无害,或低水平攻击者的记录
# 含义:在密钥交换(KEX)阶段,对方主动重置了 TCP 连接,导致服务器读到 read 错误。
Jul 11 07:25:29 vps260315 sshd[316534]: error: kex_exchange_identification: read: Connection reset by peer
# 含义:客户端发送的 SSH 协议版本标识(banner)格式不符合 RFC 标准(比如缺少 SSH- 开头)。
Jul 10 14:29:31 vps260315 sshd[314265]: banner exchange: Connection from 45.227.254.156 port 65235: invalid format
# 含义:客户端尝试使用 SSH 协议版本 1(已废弃),而你的服务器只支持版本 2。
Jul 10 15:11:19 vps260315 sshd[314344]: error: Protocol major versions differ: 2 vs. 1
# 含义:客户端尝试用 SSH-DSS 算法 的公钥进行身份验证,但你的服务器配置中 (PubkeyAcceptedAlgorithms 或老版本的 PubkeyAcceptedKeyTypes)不包含 ssh-dss,因此拒绝。
Jul 10 10:06:22 vps260315 sshd[313705]: userauth_pubkey: signature algorithm ssh-dss not in PubkeyAcceptedAlgorithms [preauth]
方法二:查看传统日志文件 /var/log/auth.log
这个方法在 Ubuntu 上也很常用。需要注意的是,/var/log/auth.log 文件依赖于 rsyslog 服务,在极简安装的系统中可能不存在。
sudo less /var/log/auth.log # 查看所有认证相关日志
sudo grep "Failed password" /var/log/auth.log # 过滤出 SSH 登录失败记录
sudo tail -f /var/log/auth.log | grep "sshd" # 实时监控
一些实用技巧
lastb 命令
lastb 命令:这是一个专门用来显示最近登录失败记录的命令,它从 /var/log/btmp 文件中读取数据,输出结果清晰易读。每一行对应一次失败的登录尝试,各列含义如下:
| 列位置 | 示例值 | 含义 |
|---|---|---|
| 第1列 | ubuntu, dell, jenkins | 尝试登录的用户名。攻击者正在用字典批量猜解常见用户名(如果为空,表示攻击者尝试了空用户名或特殊字符)。 |
| 第2列 | ssh:notty | 连接方式。ssh 表示协议,notty 表示客户端没有申请虚拟终端。这几乎可以断定是脚本或工具(如 Hydra、Medusa)在自动化爆破,而不是真人手动输入。 |
| 第3列 | 64.62.197.188 | 攻击来源的 IP 地址。 |
| 第4-5列 | Sat Jul 11 11:31 – 11:31 | 尝试发生的时间段。 |
| 第6列 | (00:00) | 会话持续时间。全部是 00:00,说明每次尝试瞬间就因认证失败断开了。 |
示例记录:sudo lastb|head -n 30
ecs@vps260315:~$ sudo lastb
ssh:notty 64.62.197.188 Sat Jul 11 11:31 - 11:31 (00:00)
ssh:notty 44.220.188.24 Sat Jul 11 11:06 - 11:06 (00:00)
node ssh:notty 102.203.197.6 Sat Jul 11 10:53 - 10:53 (00:00)
ubuntu ssh:notty 117.89.254.46 Sat Jul 11 07:38 - 07:38 (00:00)
controll ssh:notty 102.203.197.6 Sat Jul 11 03:09 - 03:09 (00:00)
ssh:notty 47.242.105.178 Fri Jul 10 19:56 - 19:56 (00:00)
dell ssh:notty 102.203.197.6 Fri Jul 10 16:02 - 16:02 (00:00)
middlewa ssh:notty 102.203.197.6 Fri Jul 10 12:30 - 12:30 (00:00)
jenkins ssh:notty 102.203.197.6 Fri Jul 10 11:53 - 11:53 (00:00)
ssh:notty 64.62.197.92 Fri Jul 10 09:06 - 09:06 (00:00)
jacob ssh:notty 102.203.197.6 Fri Jul 10 08:55 - 08:55 (00:00)
ssh:notty 104.248.158.38 Fri Jul 10 06:48 - 06:48 (00:00)
ssh:notty 43.139.194.150 Fri Jul 10 02:02 - 02:02 (00:00)
last 命令
last 命令:与 lastb 相对,last 命令用于查看最近登录成功的记录,它读取的是 /var/log/wtmp 文件。每一行代表一次登录会话或系统事件,日志按时间倒序排列(最近的在最上面)。
格式为:
用户名 终端 来源IP 登录时间 结束时间 / 状态 (持续时间)
示例记录 sudo last|head -n 30
- 用户名:登录使用的账号(
ecs、ecs-user、root)。 - 终端:
pts/0表示伪终端(通常为 SSH 连接),system boot表示系统启动。 - 来源IP:客户端公网 IP(若从内网或控制台登录可能显示为
localhost或:,此处均为公网 IP)。 - 登录时间:月 日 时:分。
- 结束时间:
-后接实际登出时间;对于reboot行,则显示系统启动完成时间。still logged in表示会话当前活跃;still running表示一直运行至今(未再重启)
- 持续时间:括号内为会话持续时长,如
(00:03)表示 3 分钟。
ecs@vps260315:~$ last
ecs pts/0 183.194.145.144 Sat Jul 11 11:45 still logged in
ecs pts/0 183.194.145.144 Wed Jul 8 22:06 - 22:09 (00:03)
ecs pts/0 220.196.57.244 Fri Jun 26 15:03 - 15:03 (00:00)
reboot system boot 6.8.0-100-generi Thu Apr 9 22:09 still running
ecs pts/0 183.194.145.176 Sat Mar 28 13:30 - 13:52 (00:22)
reboot system boot 6.8.0-100-generi Sat Mar 28 13:29 - 22:09 (12+08:39)
ecs pts/0 183.194.145.176 Sat Mar 28 13:18 - 13:29 (00:10)
ecs pts/0 183.194.145.176 Sat Mar 28 09:03 - 13:15 (04:11)
reboot system boot 6.8.0-100-generi Sat Mar 28 07:08 - 13:29 (06:20)
fail2ban 安装与配置
安装
apt 源
sudo apt update
sudo apt install fail2ban -y # 是1.0.2,最新是1.1.0
下载安装
cd /tmp
wget -O fail2ban.deb https://github.com/fail2ban/fail2ban/releases/download/1.1.0/fail2ban_1.1.0-1.upstream1_all.deb
sudo apt install ./fail2ban.deb # 安装
sudo apt -f install # 如果提示依赖问题
sudo fail2ban-client --version # 查看版本
此时启动没有防护作用,因没有配置
sudo systemctl enable fail2ban # 设置开机自启
sudo systemctl start|stop|restart fail2ban # 启动服务
sudo systemctl status fail2ban # 查看服务状态
ps -aux|grep fail2ban # 是以 root 用户运行
主要配置文件
fail2ban 的主要配置文件位于:
/etc/fail2ban/jail.conf– 主配置文件(不建议直接修改)/etc/fail2ban/jail.local– 用户自定义配置(推荐在此修改),安装后默认没有,需要手工创建。
$ l /etc/fail2ban
/etc/fail2ban:
action.d/ fail2ban.conf fail2ban.d/ filter.d/ jail.conf jail.d/ paths-arch.conf paths-common.conf paths-debian.conf paths-opensuse.conf
filter.d/(过滤器目录):存放正则表达式规则文件,用于识别日志中的恶意行为。例如sshd.conf定义如何匹配 SSH 登录失败,nginx-http-auth.conf匹配 Nginx 认证失败。系统通过这里定义的“过滤器”从日志中抓取可疑行。action.d/(动作目录):存放触发封禁时的具体执行动作。例如iptables-multiport.conf(封禁 IP)、mail-whois.conf(发邮件通知)、firewallcmd-ipset(Firewalld 封禁)。当过滤器匹配成功时,Fail2ban 会调用这里的动作来执行封禁。jail.d/(监狱配置片段目录):存放自定义的服务监控配置(片段)。您在这里新建.conf或.local文件来开启对特定服务的防护(例如ssh.local),系统会自动读取并覆盖主配置。fail2ban.d/(主配置片段目录):存放fail2ban.conf的补充或覆盖配置。通常用来调整 Fail2ban 自身的行为(如日志级别、Socket 路径),而不直接修改主文件。jail.conf(核心配置文件):最主要的配置文件,定义了“启用哪些服务”(如 SSH、Apache)、“封禁时长”、“重试次数”等全局参数和具体服务的封禁策略。注意:系统升级时会覆盖此文件,不建议直接修改。fail2ban.conf(主服务配置文件):配置 Fail2ban 守护进程本身的运行参数(如日志记录位置logtarget、后台运行模式等)。同样,不建议直接修改。paths-common.conf/paths-debian.conf/paths-arch.conf/paths-opensuse.conf(路径定义文件):用于定义不同系统发行版下各类日志文件(如/var/log/auth.log)和服务二进制文件的默认路径。paths-common.conf是通用基础,paths-debian.conf是 Debian/Ubuntu 的专属适配。系统会根据发行版自动加载对应的路径文件,确保 Fail2ban 能正确找到日志。
最佳实践(Debian/Ubuntu 规则):
永远不要直接修改 .conf 文件,因为软件包更新时会直接覆盖它们。如果需要自定义配置,请新建同名的 .local 文件(例如 jail.local)或在 jail.d/ 中创建新文件。.local 和 .d/ 目录中的配置拥有更高优先级,且不会被系统升级覆盖。
- 如果您想自定义封禁动作,可以在
action.d/中创建iptables-multiport.local进行覆盖。
fail2ban.local
处理提示 WARNING 'allowipv6' not defined in 'Definition'. Using default one: 'auto'
先看是否有ipv6公网:ip addr show | grep inet6
inet6 ::1/128 scope host→ 这是 IPv6 本地回环(localhost),相当于 IPv4 的127.0.0.1,不代表公网 IPv6。inet6 fe80::.../64 scope link→ 这是 IPv6 链路本地地址(自动生成,仅内网有效),不可用于公网访问。- 如果看到以
2、3等数字开头的全球单播地址(如240e:...、2001:...、2a01:...),并且scope global,说明你的服务器拥有公网 IPv6 地址,这就是 Fail2Ban 应当封禁的对象。
如果没有ipv6公网地址,可以配置 allowipv6 = false。如果有,可以配置为 allowipv6 = true
在 sudo vim /etc/fail2ban/fail2ban.local 添加以下内容
[DEFAULT]
allowipv6 = false
filter.d/*.conf
# 测试 SSH 过滤器是否能匹配日志样本:
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
输出示例
Running tests
=============
Use filter file : sshd, basedir: /etc/fail2ban
Use maxlines : 1
Use datepattern : {^LN-BEG} : Default Detectors
Use log file : /var/log/auth.log
Use encoding : UTF-8
Results
=======
Prefregex: 3664 total
xxx
Failregex: 674 total
|- #) [# of hits] regular expression
| 6) [170] xxx
| 19) [63] xxx
| 20) [439] xxx
| 21) [2] xxx
`-
Ignoreregex: 0 total
Date template hits:
|- [# of hits] date format
| [3664] xxx
`-
Lines: 3664 lines, 504 ignored, 170 matched, 2990 missed
[processed in 0.17 sec]
Ignored line(s): too many to print. Use --print-all-ignored to print all 504 lines
Missed line(s): too many to print. Use --print-all-missed to print all 2990 lines
| 类别 | 行数 | 含义 |
|---|---|---|
| 总日志行数 | 3664 | /var/log/auth.log 中最近被扫描的日志总量。 |
| ✅ 匹配(封禁候选) | 170 | 被规则明确判定为攻击,会触发封禁的 IP。 |
| ⛔ 忽略(排除项) | 504=63+439+2 | 匹配了规则但被标记为忽略(如断开连接、成功登录),不会封禁。 |
| ❌ 未匹配(Missed) | 2990 | 规则“看不懂”(未匹配上)的日志行,被放过了。 |
jail.local
sudo vim /etc/fail2ban/jail.local
# 配置检查
sudo fail2ban-client --test # 测启动
sudo fail2ban-client -d # 看配置
# 修改后,需要重启,再确认是否已监控
sudo systemctl restart fail2ban && sudo fail2ban-client status
sudo fail2ban-client status sshd
sudo ufw status
sudo tail -n /var/log/fail2ban.log
jail.local 内容
# Fail2Ban 本地配置文件 (Ubuntu 24.04 + UFW), 此文件会覆盖 jail.conf 中的默认设置,且不会被软件包升级覆盖
[DEFAULT]
# 1. 防火墙后端:使用 UFW
banaction = ufw / iptables-multiport
# 2. 白名单(务必添加你的管理 IP,防止自己被误封)
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24
# 3. 全局封禁时长(秒),1小时
bantime = 3600
# 3. 全局查找失败记录的时间窗口(秒),10分钟
findtime = 600
# 3. 全局最大失败次数,超过则封禁
maxretry = 3
# 4. 日志后端自动检测,但指定文件路径也没问题
backend = auto
# ========================================
# 监狱(Jails):启用你需要防护的服务
# filter 文件需要存在 /etc/fail2ban/filter.d/{filter}.conf
# 默认的日志文件位置 /var/log/fail2ban.log
# ========================================
# ---------- SSH 防护 ----------
[sshd]
enabled = true
port = ssh
filter = sshd
# 通常为 /var/log/auth.log
logpath = %(sshd_log)s
# ---------- (可选)累犯监狱:对反复被封的IP延长封禁时间 ----------
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
bantime = 604800
findtime = 86400
maxretry = 3
周边与使用
logrotate.d
编辑:sudo vim /etc/logrotate.d/fail2ban
验证: sudo logrotate -d /etc/logrotate.d/fail2ban
/var/log/fail2ban.log {
monthly
rotate 12
nocompress
dateext
dateformat .%Y%m
# Do not rotate if empty
notifempty
missingok
su root adm
postrotate
fail2ban-client flushlogs 1>/dev/null
endscript
# If fail2ban runs as non-root it still needs to have write access
# to logfiles.
# create 640 fail2ban adm
create 640 root adm
}
查看
sudo tail -n 30 /var/log/fail2ban.log
# 查看所有 Ban 记录
sudo grep " Ban " /var/log/fail2ban.log | tail -n 30
# 查看某个 jail 的封禁历史
sudo grep " Ban " /var/log/fail2ban.log | grep nginx-http-auth | tail -n 30
# 示例记录
2026-07-11 16:53:11,024 fail2ban.actions [923101]: NOTICE [sshd] Ban 2.57.122.238
sudo fail2ban-client status sshd # 查看sshd状态,含被封禁的 IP
sudo fail2ban-client set sshd unbanip 192.168.1.100 # 解封特定 IP
sudo fail2ban-client set sshd banip 192.168.1.100 # 手动封禁 IP
.bashrc_f2b
f2b-status-all() {
local jails
# 取出 "Jail list:" 那一行,截取冒号后内容,去掉逗号,拆成词
jails=$(sudo fail2ban-client status | awk -F: '/Jail list/ {print $2}' | sed 's/,//g')
for jail in $jails; do
sudo fail2ban-client status "$jail"
done
}
alias f2b='sudo fail2ban-client'
alias f2bStatus='f2b-status-all'
发表回复
要发表评论,您必须先登录。