返回

Fail2ban正则匹配成功却不封IP?原因与解决方法

Linux

Fail2ban 正则匹配成功,但 IP 就是不封禁?问题排查指南

搞定了 Fail2ban 的正则表达式,用 fail2ban-regex 测试工具跑了一下,完美匹配日志!心里一阵窃喜,觉得大功告成。结果呢?刷新 fail2ban-client status <jail_name>Currently failedTotal failed 计数纹丝不动,一个 IP 都没封禁。这情况,是不是有点让人摸不着头脑?

别急,这问题其实挺常见的。fail2ban-regex 能匹配成功,只能说明你的 failregex 写得没毛病,能抓住日志里的关键信息(比如那个捣乱的 IP 地址)。但这只是 Fail2ban 工作流程的第一步。从“匹配成功”到“执行封禁”,中间还有好几个环节可能出岔子。

这篇文章就带你捋一捋,当 fail2ban-regex 说“OK”但 Fail2ban 实际不干活时,该从哪些方面入手排查。

问题根源分析:fail2ban-regex 和实际运行的区别

为啥测试工具说行,实际却不行?关键在于 fail2ban-regex 和 Fail2ban 服务(fail2ban-clientfail2ban-server)的工作模式差异:

  1. 时间处理 (datepattern) : fail2ban-regex 主要关注 failregex 是否能从日志行中提取出 <HOST>(也就是 IP 地址)。它对日志行的时间戳匹配 (datepattern) 没那么严格,或者说,它测试时可能不完全模拟真实的时间检查逻辑。而 Fail2ban 服务在运行时,必须精确匹配日志行的时间戳,并结合 findtimemaxretry 参数来判断是否达到封禁条件。如果时间戳格式 (datepattern) 写错了,或者跟日志里的实际格式对不上,Fail2ban 服务就无法正确识别攻击发生的时间,自然也就无法累计失败次数。这是最常见的原因之一
  2. 日志读取 : fail2ban-regex 是直接读取你给它的静态日志文件。而 Fail2ban 服务是持续监控 (tail) 指定的 logpath 文件。这中间可能涉及到文件权限、日志轮替 (log rotation)、文件系统监控机制 (inotify) 等问题。
  3. 状态和配置 : fail2ban-regex 不关心 Fail2ban 服务的运行状态、Jail 是否启用、bantime, findtime, maxretry 这些参数的具体设置。它只是个模式匹配测试器。而实际运行中,这些配置项都至关重要。

根据你提供的配置和日志:

  • filter.d/login_eiren_studio.conf:
    • datepattern = \[%%Y-%%m-%%d\s%%H:%%M:%%S\s%%z\]: 这个格式需要匹配 [2025-01-02 12:43:58 +0100] 这样的时间戳。注意 %%z 匹配时区偏移 +0100
    • failregex = ^\s\|\sIP:\s<HOST>\s.*$: 这个正则看起来是为了匹配以空格、竖线、空格、IP:、空格开头,然后捕获 <HOST>(IP 地址)的行。从你的日志示例看,[时间戳] | IP: <IP> | ... 这样的格式,这个正则可能需要调整。日志行的开头是 [ 而不是空格。
  • jail.local:
    • 配置看起来基本合理,启用了 jail,指定了 filter、logpath,设置了尝试次数、封禁时间和查找时间窗口。
  • 日志示例:
    • [2025-01-02 12:43:58 +0100] | IP: 85.87.24.22 | ...

fail2ban-regex 匹配成功,但 fail2ban-client status 显示 0 failed,这强烈指向了 datepattern 匹配失败 或者 failregex 在实际读取时由于日志行开头的 [ 导致不匹配 的可能性。也可能是日志文件监控的问题。

解决方案:逐一排查

下面我们来一步步检查,找出症结所在。

1. 仔细检查 Fail2ban 自身日志

这是排查任何服务问题的第一步。Fail2ban 运行时会记录自己的活动、错误和警告。

操作步骤:

找到 Fail2ban 的日志文件。通常在 /var/log/fail2ban.log

sudo tail -n 50 /var/log/fail2ban.log
# 或者如果日志量大,用 less 查看
sudo less /var/log/fail2ban.log

关注点:

  • 寻找包含你的 jail 名称(login_eiren_studio)的条目。
  • 留意 WARNINGERROR 级别的日志。
  • 注意是否有关于 "Unable to find a corresponding IP address for ..."、"Found cookie"、"No file matches..."、"Giving up processing..." 或者与 datepattern 相关的错误信息。
  • 有时,如果 datepattern 完全不匹配,Fail2ban 可能不会报错,但它 просто不会处理这些日志行,导致无法触发计数。

2. 严格验证日期/时间模式 (datepattern)

这是最关键的一步。Fail2ban 需要根据 datepattern 精确解析出日志时间,才能在 findtime 时间窗口内统计 maxretry 次数的失败尝试。

原理:

Fail2ban 内部使用 Python 的时间处理库。datepattern 定义了如何将日志中的时间字符串转换成 Fail2ban 理解的时间对象。如果格式不符,转换失败,这条日志记录的时间戳就会丢失,导致 Fail2ban 无法判断它是否发生在 findtime (600秒) 内。

你的 datepattern = \[%%Y-%%m-%%d\s%%H:%%M:%%S\s%%z\] 看起来是正确的,匹配 [YYYY-MM-DD HH:MM:SS +ZZZZ] 格式。我们得用更接近实际运行的方式来测试它。

操作步骤:

使用 fail2ban-regex 并明确指定 datepattern 来测试单个日志行,并观察输出。这次不光看 failregex 匹配,更要看 Date template matches 部分。

# 取一行你的日志作为测试样本
TEST_LOG_LINE="[2025-01-02 12:43:58 +0100] | IP: 85.87.24.22 | Dispositivo: Windows 10 | Navegador: Chrome 131.0.0.0 | Usuario: intento de login: [email protected] | Mensaje: Error: El usuario existe pero la contraseña no es correcta."

# 使用你的 filter 文件和这一行日志进行测试
sudo fail2ban-regex "$TEST_LOG_LINE" /etc/fail2ban/filter.d/login_eiren_studio.conf

检查输出:

仔细看 Date template matches: 这部分。

  • 成功的样子: 会显示类似 Months: Jan Feb ..., Days: Mon Tue ..., 以及 Success, the date matched: 后面跟着成功解析出的时间。
  • 失败的样子: 可能会直接提示 Sorry, no match 或者 Ensure template and log lines correlate,或者根本不显示时间解析成功的信息。

进阶调试技巧:

使用 fail2ban-client -d 命令。这会把 Fail2ban 的调试信息直接输出到控制台,包括它如何读取日志文件、尝试匹配 failregexdatepattern 的过程。

# 先停止运行中的 fail2ban 服务
sudo systemctl stop fail2ban

# 以前台调试模式启动,观察日志处理过程
sudo fail2ban-client -d

然后在另一个终端触发几次你的应用登录失败,观察 fail2ban-client -d 的输出。重点看它是否能正确读取到 /var/www/openvscode/proyectos/login.eiren.studio/logs/badauth.log 的新行,以及尝试匹配 login_eiren_studio filter 时 failregexdatepattern 的匹配情况。按 Ctrl+C 停止调试模式,然后用 sudo systemctl start fail2ban 重启服务。

3. 修正 failregex 以匹配日志行首

你的 failregex^\s\|\sIP:\s<HOST>\s.*$,它要求日志行以空格、竖线、空格 开头。但你的日志示例 [2025-01-02 12:43:58 +0100] | IP: 85.87.24.22 | ... 实际上是以方括号 [ 开头的。这导致 failregex 在实际运行时可能根本匹配不到任何东西,即使 fail2ban-regex 工具在某种模式下看似匹配了 IP(这可能是该工具的一个宽松特性或 bug)。

解决方案:

修改 failregex,使其能匹配从行首到 IP 地址的部分。一个更健壮的模式可能像这样:

# /etc/fail2ban/filter.d/login_eiren_studio.conf

[Definition]
# datepattern 保持不变,假设它测试通过了
datepattern = \[%%Y-%%m-%%d\s%%H:%%M:%%S\s%%z\]
# 修改 failregex
failregex = ^\[.*\]\s\|\sIP:\s<HOST>\s\|.*$
ignoreregex =

这个修改后的 failregex:

  • ^: 匹配行首。
  • \[.*\]: 匹配方括号包裹的日期时间部分。\[\] 需要转义, . 匹配任意字符,* 表示匹配零次或多次。
  • \s\|\s: 匹配日期时间后的 | 分隔符(空格、竖线、空格)。
  • IP:\s: 匹配字符串 IP: 和后面的空格。
  • <HOST>: 捕获 IP 地址。Fail2ban 自动处理 IP v4 和 v6。
  • \s\|.*$: 匹配 IP 地址后的 | 分隔符以及行尾的剩余所有内容。 \| 匹配竖线,.*$ 匹配到行尾。

操作步骤:

  1. 修改 /etc/fail2ban/filter.d/login_eiren_studio.conf 文件中的 failregex
  2. 重新加载 Fail2ban 配置 (非常重要!):
    sudo fail2ban-client reload
    # 或者更保险的方式:
    sudo systemctl restart fail2ban
    
  3. 再次用 fail2ban-regex 测试修改后的 failregex
    sudo fail2ban-regex "$TEST_LOG_LINE" /etc/fail2ban/filter.d/login_eiren_studio.conf
    
    确保它仍然能匹配到 IP。
  4. 监控 fail2ban-client status login_eiren_studio,看看失败计数是否开始增加了。

4. 确认日志文件路径和权限

虽然你提到了权限没问题,但再次确认总没错。Fail2ban 服务通常以 root 用户运行(默认配置),但它读取日志文件需要相应的权限。

操作步骤:

  1. 检查路径: 确认 jail.locallogpath = /var/www/openvscode/proyectos/login.eiren.studio/logs/badauth.log 是绝对正确的,没有拼写错误。
  2. 检查文件是否存在:
    ls -l /var/www/openvscode/proyectos/login.eiren.studio/logs/badauth.log
    
    确保文件存在。
  3. 检查权限: 查看文件的权限和所有者。
    ls -l /var/www/openvscode/proyectos/login.eiren.studio/logs/badauth.log
    
    输出类似 -rw-r--r-- 1 www-data www-data 1234 Jan 2 13:00 badauth.log
    • 关键点: Fail2ban 进程(通常是 root)需要至少有读取 (r) 该文件的权限。同时,它还需要对该文件所在的目录 (/var/www/.../logs/) 具有执行 (x) 权限,才能访问到里面的文件。
    ls -ld /var/www/openvscode/proyectos/login.eiren.studio/logs/
    
    确保 root 用户或 fail2ban 运行的用户组(如果不是 root)有访问权限。通常 drwxr-xr-x 这样的权限是足够的。
  4. 使用 fail2ban-client 测试: 尝试让 fail2ban 报告它正在监控的文件。
    sudo fail2ban-client status login_eiren_studio
    
    检查输出中 "Log file list" 部分是否包含了你的 badauth.log 文件。如果没有,说明 Fail2ban 因为某种原因没有成功监控它。

安全建议:

  • 避免给日志文件或目录设置过于宽松的权限(如 777)。确保只有必要的进程(应用写入日志,Fail2ban 读取日志)有权限。
  • 如果你的 Web 服务器(如 Nginx, Apache)以特定用户(如 www-data)运行并写入日志,而 Fail2ban 以 root 运行,通常 root 都能读取。如果 Fail2ban 不是以 root 运行,或者有 AppArmor/SELinux 限制,可能需要用 ACL (Access Control Lists) 更精细地控制权限:
    # 示例:授予 root 用户读取权限 (如果文件属于 www-data)
    sudo setfacl -m u:root:r /var/www/openvscode/proyectos/login.eiren.studio/logs/badauth.log
    sudo setfacl -m u:root:rx /var/www/openvscode/proyectos/login.eiren.studio/logs/
    
    # 查看 ACL 设置
    getfacl /var/www/openvscode/proyectos/login.eiren.studio/logs/badauth.log
    getfacl /var/www/openvscode/proyectos/login.eiren.studio/logs/
    

5. 重启与重载配置

修改了 .conf.local 文件后,Fail2ban 不会自动加载新配置。必须手动触发。

操作步骤:

  • 重载配置: 尝试先用 reload。这通常能加载大部分配置更改,且不会中断正在进行的封禁。
    sudo fail2ban-client reload
    
  • 重启服务: 如果 reload 没生效,或者改动比较大(比如 filter 文件本身),建议直接重启服务。这会完全重新加载所有配置。
    sudo systemctl restart fail2ban
    

验证:

重启或重载后,再次检查 fail2ban-client status login_eiren_studio,看看 Jail 是否处于 active 状态,以及相关的配置(如 Log file list, Filter, Actions)是否是你期望的。

6. 调整 findtimemaxretry

你的设置是 maxretry = 5, findtime = 600。这意味着 Fail2ban 需要在 600 秒(10分钟)内,从日志中匹配到至少 5 次符合 failregexdatepattern 的记录,才会触发封禁。

检查:

  • 确认你的测试(或实际攻击)频率是否真的达到了这个标准?如果你手动测试,可能两次失败尝试之间间隔超过了 10 分钟。
  • 在调试阶段,可以临时 将这两个值调低,以便更快看到效果。比如:
    # /etc/fail2ban/jail.local
    [login_eiren_studio]
    # ... 其他配置 ...
    maxretry = 2
    findtime = 60 
    # ... 其他配置 ...
    
    这样设置后,只要在 60 秒内有 2 次失败尝试,就应该触发封禁。
    注意:调试完成后,记得改回合理的、符合安全策略的值!

操作步骤:

  1. 修改 jail.local 中的 findtimemaxretry
  2. 重启或重载 Fail2ban 服务。
  3. 进行测试,观察 fail2ban-client status 的变化。

7. 考虑日志轮替 (Log Rotation)

如果你的 badauth.log 文件会定期被轮替(例如,被重命名为 badauth.log.1,然后创建新的空 badauth.log),Fail2ban 需要能够适应这种情况。

原理:

Fail2ban 通常依赖文件系统的 inotify 事件来监控日志变化。日志轮替工具(如 logrotate)的操作方式可能会中断 inotify 监控。例如,如果 logrotate 的配置是 copytruncate(复制文件内容,然后清空原文件),通常没问题。但如果是 create(创建新文件)或者直接 rename,Fail2ban 可能就丢失了对原文件的监控句柄。

解决方案:

  • 检查 logrotate 配置: 查看 /etc/logrotate.conf 以及 /etc/logrotate.d/ 下是否有针对你的 badauth.log 的配置。确保使用的是与 Fail2ban 兼容的方式,或者 Fail2ban 使用的 backend (在 jail.confjail.local 中设置,默认为 auto)能处理这种轮替。
  • 使用 systemd backend: 如果你的系统使用 systemd,并且你的应用可以通过 systemd-journald 记录日志,将 Fail2ban 的 backend 设置为 systemd 通常更可靠,因为它不依赖于直接监控文件。需要在 jail.local[DEFAULT] 或特定 jail 中设置 backend = systemd,并且 logpath 需要指向相应的 journald 目标(这需要应用配合)。
  • 重启 Fail2ban: 有时,在日志轮替后简单地重启 Fail2ban 服务也能让它重新找到并监控新的日志文件。但这治标不治本。

总结一下排查思路

当你遇到 fail2ban-regex 测试通过但 Fail2ban 不工作的情况时:

  1. 先看 Fail2ban 自己的日志 (/var/log/fail2ban.log) ,查找错误和警告。
  2. 死磕 datepattern ,用 fail2ban-regex --test-datepatternfail2ban-client -d 严谨测试,确保它和你日志里的时间戳格式完全匹配。
  3. 再次审视 failregex ,确保它能匹配整行日志 中包含 <HOST> 的部分,特别是注意行首的字符。根据日志样本调整正则。
  4. 三重确认 logpath 是否正确,以及 Fail2ban 运行用户是否有权限读取日志文件和访问其所在目录
  5. 修改配置后,一定记得 reloadrestart Fail2ban 服务
  6. 检查 findtimemaxretry 的设置是否合理,或者在测试时临时调低它们。
  7. 考虑日志轮替 是否干扰了 Fail2ban 的监控。

按照这些步骤仔细排查,大概率能找到那个隐藏在细节中的“魔鬼”,让你的 Fail2ban 重新站岗放哨!