Linux 下的防爆破 fail2ban

从日志中分析失败的IP,自动封禁IP,也自动解封IP。

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

  • 用户名:登录使用的账号(ecsecs-userroot)。
  • 终端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 链路本地地址(自动生成,仅内网有效),不可用于公网访问
  • 如果看到以 23 等数字开头的全球单播地址(如 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'

发表回复