暗色模式

Fail2ban:从 SSH 暴力破解封禁到通用服务防护

技术教程
2026-08-23
6
0
本文要点
  • fail2ban 通过分析服务日志,把失败次数超过阈值的来源 IP 自动拉入防火墙黑名单,到期自动解封。
  • Ubuntu 24.04 上 apt install fail2ban 后,sshd 守护已默认启用,后台直接读 journald,不需要额外的 auth.log 配置。
  • 核心参数三个:maxretry(几次算攻击)、findtime(统计时间窗)、bantime(封多久),修改建议写进 /etc/fail2ban/jail.local,不碰默认文件。
  • 实测:连续 5 次错误密码登录即触发封禁,被封后的 SSH 连接直接 Connection refused,一条 unbanip 命令即可解除。
  • 排障用 fail2ban-client -t 测配置、fail2ban-regex 验证过滤规则,日志看 /var/log/fail2ban.log

为什么需要 fail2ban

只要你的服务器有公网 IP 并且开着 SSH,就几乎每天都有人来试密码。我在一台刚装好系统的演示机上查看 SSH 日志,几分钟内就能翻到来自各个 IP 的「无效格式 banner」、未知用户、错误密码尝试——这些大多是扫描器在批量探测。

密码复杂、改用密钥登录能挡住大部分攻击,但总有一些主机出于兼容性仍开着密码认证。fail2ban 的思路很直接:让日志说话——哪个 IP 在短时间内反复登录失败,就把它拉进防火墙黑名单封一段时间。它不依赖你是 SSH、FTP 还是 Web 服务,凡是「能在日志里看到失败记录」的服务都能管,所以是 Linux 运维里通用的防暴力破解方案。

fail2ban 是什么

fail2ban 是一个用 Python 写的守护进程。它读服务的日志(支持文件日志和 systemd journal),用正则表达式从日志行中提取需要判断的 IP,统计在 findtime 时间窗内失败的次数,超过 maxretry 阈值就执行封禁动作(默认是调用系统防火墙加入黑名单);到了 bantime 设定的时长后自动解封。它只做一件事:把连续失败的可疑来源挡在外面

工作流程可以用四步概括:

  1. 监听:读取 /var/log/auth.log 或 journald 里的服务日志;
  2. 匹配:用过滤规则(filter)里的正则提取「谁、何时、失败了几次」;
  3. 判定findtime 窗口内同一来源失败次数 ≥ maxretry,判定为攻击;
  4. 执行:触发 action 封禁 IP(默认写防火墙规则),bantime 到期后自动删除规则。

安装与启停

Ubuntu 24.04(以及 Debian、CentOS/Rocky)的软件源里都有 fail2ban,一条命令装完:

sudo apt update && sudo apt install -y fail2ban

安装后服务会自动启动并设为开机自启(我实测安装完 systemctl is-active fail2ban 已返回 active)。常用的启停命令:

sudo systemctl enable --now fail2ban   # 开机自启 + 立即启动
sudo systemctl status fail2ban          # 查看运行状态
sudo systemctl reload fail2ban          # 重载配置(不中断,推荐)
sudo systemctl restart fail2ban         # 完全重启(会清空内存计数)

Ubuntu/Debian 有一个贴心的默认:装完 sshd 这一条 jail 已经是启用状态,且后端默认走 journald,即使服务器没有生成 /var/log/auth.log 也能工作。装好后直接看状态就能确认:

fail2ban 安装后默认状态

图中 fail2ban-client status 显示已经有一个名为 sshd 的 jail;fail2ban-client status sshd 则给出 Filter(当前失败数、累计失败数、日志匹配条件)和 Actions(当前封禁数、累计封禁数、封禁 IP 列表)两组信息。注意 Total failed: 5——那是此前模拟的攻击已经留下的痕迹,说明统计是真实、连续的。

理解默认配置

fail2ban 的主配置文件是 /etc/fail2ban/jail.conf,安装包自带的默认值足够先跑起来。几个最关键的参数:

参数默认值含义
bantime10m封禁时长,支持 1m/10m/1h/1d 等写法
findtime10m统计时间窗:在这个时间段内累计失败
maxretry5达到几次失败就封禁
ignoreip127.0.0.1/8 ::1永不封禁的 IP/网段

它们配合的含义是:在 10 分钟内同一个来源累计 5 次登录失败,就把它封禁 10 分钟.local 后缀的配置文件优先级更高——自定义配置应该写入 /etc/fail2ban/jail.local,而不要直接改 jail.conf,这样软件升级时不会被覆盖。

第一次使用的人最常踩的坑是把自己封了:你用办公网络、家里宽带或云服务器管理台的 IP 都可能变化,一旦你的当前出口 IP 被误封,SSH 就连不上了。务必要在 ignoreip 里加上你常用的管理来源网段(比如公司出口 IP),并给管理员保留一个带外通道(云控制台 VNC/救援模式)。

模拟一次暴力破解:看 fail2ban 自动封禁

纸上谈兵不如实测。我在演示机上真实触发了 6 次错误密码登录(模拟攻击者用字典跑 SSH),来观察 fail2ban 的行为。

第 1~5 次失败时,sshd jail 的 Currently failed 计数会不断增长;当同一个来源在第 5 次失败时达到 maxretry = 5,fail2ban 立刻执行封禁。之后第 6 次及以后的连接直接失败,客户端报错:

ssh: connect to host 47.238.230.124 port 22: Connection refused

注意这个报错不是「密码错」,而是 TCP 层面直接拒绝连接——防火墙规则已经在本次连接建立之前把来源 IP 丢掉了。这时再回看服务端:

fail2ban-client status sshd

输出里 Actions 部分变成了 Currently banned: 1,封禁列表里正是刚才那个来源 IP;而 /var/log/fail2ban.log 里会留下一条明确记录:

2026-08-23 08:04:30 fail2ban.actions [sshd] Ban 117.182.133.208
2026-08-23 08:04:46 fail2ban.actions [sshd] Unban 117.182.133.208

Ban 表示封禁动作触发,Unban 表示到期(或手动)解封。确认封禁无误后,管理员一条命令即可解除整个封锁:

fail2ban-client set sshd unbanip <你的IP>

为了演示完整闭环而不影响演示机的对外连接,下面用 set sshd banip 手动封禁一个保留测试网段(203.0.113.10 属于 TEST-NET-3,不会是真的攻击来源),再立即解封,完整展示封禁→验证→解封的流程:

封禁与解封 IP

图中第一次 fail2ban-client status sshdBanned IP list: 203.0.113.10,执行 unbanip 后再查就清空了。set <jail> banip / unbanip 适合临时手动干预;日常的自动封禁与解封由日志驱动完成。

配置自己的封禁策略

默认 5 次/10 分钟/封 10 分钟太温和,生产上通常更严格。创建 /etc/fail2ban/jail.local 覆盖参数即可:

[sshd]
enabled = true
maxretry = 3
bantime = 1h
findtime = 10m
ignoreip = 127.0.0.1/8 ::1

含义改成:10 分钟内失败 3 次就封 1 小时。改完后先测配置再热重载:

sudo fail2ban-client -t        # 语法自检,输出 OK 才放行
sudo systemctl reload fail2ban # 热重载,不需要重启进程
fail2ban-client status sshd    # 确认重载后仍正常

实测效果如下:

自定义封禁策略

图中 fail2ban-client -t 返回 OK: configuration test is successfulreloadstatus sshd 依然正常(已统计的 Total failed 计数会被保留,不用大惊小怪)。

用 filter 规则实现匹配

jail.local 里的 [sshd] 只是 OOTB 最常用的一例。fail2ban 的模块化结构是三层:jail 定义「管哪个服务 + 策略」、filter 定义「认哪几行日志」、action 定义「怎么封」。每个 jail 可用 filteraction 字段指认:

[myapp]                 # 自定义服务名
enabled = true
port = 8080
logpath = /var/log/myapp/error.log   # file 后端
filter  = myapp                      # 用 filter.d/myapp.conf
maxretry = 3
bantime  = 1h

对应的过滤器写在 /etc/fail2ban/filter.d/myapp.conf,核心是正则 failregex。例如匹配「密码错误」:

[Definition]
failregex = ^.*authentication failed from <HOST>
ignoreregex =

<HOST> 是 fail2ban 预留占位符,会自动换成正则去提取 IP。写成 failure from 1.2.3.4 这样的日志就能被命中。

用 fail2ban-regex 预先验证正则

正则写错会导致「该封的没封」。动手写日志场景做一次离线验证,不依赖真实流量:

echo 'Aug 23 08:04:30 demo sshd: Failed password for root from 203.0.113.9 port 50000 ssh2' > /tmp/sample.log
fail2ban-regex /tmp/sample.log /etc/fail2ban/filter.d/sshd.conf

输出关键行 Lines: 1 lines, 0 ignored, 1 matched, 0 missed 表示这条失败日志被成功识别。新增自定义 filter 前先用 fail2ban-regex 跑样本,能省去大量排障时间。

加大攻击者代价的几个技巧

  1. 结合密钥认证:配合 PermitRootLogin prohibit-password 与仅密钥登录,就算密码认证关闭前被爆破,也没有密码可爆。ssh 的失败日志本身仍会被 fail2ban 计数。
  2. 缩短窗口、加长封禁:SSH 字典攻击会在几分钟内打几千次,maxretry = 3, bantime = 1h 只是入门;对重要主机可以 bantime = 1d 或配合 recidive jail(封禁过的 IP 再次出现直接拉黑更长)。
  3. 多服务共管:nginx、postfix、dovecot、vsftpd 等有日志的认证服务,都可以仿照 sshd 写自己的 jail;GitHub 上各发行版 filter.d/ 里一般自带现成模板,改参数即可。
  4. 关注 fail2ban.log/var/log/fail2ban.log 记录了每次 Ban/Unban 及其原因,是判断「到底有没有被攻击、有没有误封」的第一手证据。

常见坑与提醒

  • 别把 ignoreip 写成全局开放ignoreip 如果你图省事写上 0.0.0.0/0 等于关闭了所有封禁。
  • reload 与 restart 语义不同reload 保留内存中的失败统计,restart 全部清零;排障时二者都可以用,别再误删统计贡献排查难度。
  • journald 后端不需要 auth.log:Ubuntu 24.04 默认 backend = systemd,没有 /var/log/auth.log 也照样工作;若你手动改回 file 后端,务必确认 logpath 路径真实存在且 fail2ban 用户可读。
  • 时间与抗绕过:攻击者会随机化端口/间隔以绕过 findtime 窗口,误报与漏报之间要权衡;建议从先「防自己、信日志」起步,再逐步加严。

fail2ban 是 Linux 运维里投入产出比极高的一个工具:安装成本十几秒,换来的是公网 SSH 端口的持续降噪。装好之后,把上面几条自检命令跑一遍(status-tfail2ban-regex),再配上 jail.local 里的严格策略,服务器就可以安心挂到公网了。

发表评论

暂无评论,快来抢沙发吧!