Fail2ban 入侵防御指南:它防什么、怎么工作、规则怎么写
一台 VPS 接入互联网之后,被扫描只是时间问题。SSH 端口一旦暴露在公网,日志里很快就会出现成串的失败登录:不同的来源 IP,用常见的用户名挨个试弱口令。这类攻击大多并不针对你个人,而是僵尸网络撒网式作业——成本极低,命中一台算一台。Fail2ban 就是用来对付这种“量大、廉价、不挑目标”的骚扰的:它不负责识别攻击者的身份,只负责让这种试探变得不划算。
理解这一点很重要,因为围绕 Fail2ban 有两种常见的误解。一种是把它当成安全体系本身,装上就觉得服务器固若金汤;另一种是觉得它没用——反正攻击者换个 IP 就能继续。两种看法都只说对了一半。Fail2ban 的真实位置,是纵深防御里的一层自动响应:它不能替代密钥登录、防火墙基线与及时更新,但它能把无意义的噪声挡在门外,让真正需要你注意的异常更容易被看见。
它防什么,不防什么
先划边界。Fail2ban 擅长处理的是重复、机械、可观测的失败行为:同一个 IP 在一段时间内反复认证失败。SSH 口令爆破是最典型的场景,Web 登录接口的撞库、邮件服务的密码试探也属于同一类。它的判断依据全部来自日志里已经发生的失败记录,而不是对流量的猜测,这意味着它几乎不会误伤正常请求——正常用户不会在短时间内连续输错十几次密码。
但它防不住的东西同样要说清楚。第一,它防不住凭证已经泄露的情况:如果攻击者拿到了正确的密钥或口令,一次登录成功,Fail2ban 没有任何理由拦截。第二,它防不住应用层漏洞:SQL 注入、未授权接口、过期组件的远程执行,这些攻击往往一次就得手,根本不会在日志里留下多次失败。第三,它对“慢速分布式”试探效果有限:攻击者把尝试分散到大量 IP、拉长到几天,每台来源的失败次数都压在阈值之下,规则就触发不了。第四,它挡不住针对你个人的定向攻击,对方完全可以换 IP、换节奏、换入口慢慢磨。
所以 Fail2ban 的合理期待应该是:消灭自动化爆破带来的噪声与资源消耗,抬高攻击者的试探成本,同时为你争取反应时间。把它和 SSH 密钥登录 搭配起来看就顺了:密钥登录解决“口令可能被猜中”这个根本问题,Fail2ban 解决“猜的人太多太吵”这个现实问题。两者不是二选一,而是先后手——先上密钥,再用 Fail2ban 清理门外的动静。
工作机制:日志、过滤器、封禁动作
Fail2ban 的工作方式可以拆成三个环节,理解了这三个环节,后面的配置就只剩细节。
第一个环节是日志。Fail2ban 本身不产生判断,它读的是服务已经写好的日志:SSH 的认证日志、Web 服务的访问与错误日志、邮件服务的日志。日志是唯一的事实来源,这也意味着一个前提:如果服务没记日志,或者日志格式被你改得面目全非,Fail2ban 就无从下手。部署之前先确认目标服务的日志确实在写、且路径与你配置里写的一致,比调任何参数都重要。现代系统上日志可能走 systemd journal 而不是纯文本文件,Fail2ban 支持以后端方式读取 journal,选哪种后端取决于你的系统现状,而不是哪种听起来更高级。
第二个环节是过滤器。过滤器是一组正则表达式,它的任务是在日志行里认出“某次失败来自哪个 IP”。这里的关键概念是锚定:正则必须把 IP 地址抽出来作为封禁对象,抽错了就会封错人。Fail2ban 自带了大量针对常见服务的过滤器,SSH、Nginx、Apache、Postfix、Dovecot 这些都有现成的,多数场景不需要自己写。只有当你保护的是自有应用、日志格式与众不同时,才需要动手写过滤器。写之前先用自带的测试工具对着真实日志跑一遍,看它到底匹配了哪些行、漏了哪些行,这一步省下的时间比事后排障多得多。
第三个环节是封禁动作。匹配到阈值后,Fail2ban 会调用动作去改防火墙规则,把来源 IP 加进黑名单一段时间,到期自动放出来。动作与防火墙后端必须一致:系统用 iptables,就走 iptables 的动作;用 firewalld 或 UFW,就走对应的动作。如果动作与现有防火墙体系打架,规则可能写不进去,或者写进去却不生效,表面上看服务在跑,实际上谁也没拦住。多个防护层共存时,先想清楚每一层负责什么,再决定 Fail2ban 的动作接在哪一层。
把这三个环节串起来的单位叫 jail,可以理解为“针对某项服务的一套完整规则”:它指定读哪份日志、用哪个过滤器、封禁条件是什么、动作用哪种。一个 jail 只对一项服务负责,SSH 一个,Web 登录一个,邮件一个,互不干扰。这种拆分方式的好处是,调整某项服务的策略时不会波及全局;坏处是容易贪多——把所有服务全都配上 jail,看起来很周全,实际上很多 jail 永远触发不了,反而增加了维护负担。按需启用,永远比全量启用明智。
核心配置:三个参数的权衡
Fail2ban 的核心配置只有三个参数,但它们的权衡值得花篇幅讲清楚,因为多数误封与漏封都源于这里。
第一个是检测窗口:在多长的时间跨度内累计失败次数。窗口太短,攻击者放慢节奏就能绕过;窗口太长,正常用户的偶发失误也可能被累计进去。第二个是最大容忍次数:窗口内失败达到几次就封禁。次数定得太低,自己手抖输错几次就可能被关在门外;定得太高,爆破脚本能多试很多次才被发现。第三个是封禁时长:封多久。时间太短,攻击者等一会儿卷土重来;时间太长,误封的代价就大——被封的可能是共用出口 IP 的正常用户,或者是你自己。
这三个参数没有放之四海而皆准的数值。个人服务器与面向公众的服务、SSH 与 Web 登录、固定 IP 的管理员与流动的访客,合适的组合都不一样。调参的正确姿势是先宽后严:先用较宽松的组合跑一段时间,观察日志里触发了哪些、误伤了哪些,再逐步收紧。直接抄一份“推荐配置”上生产,等于把别人的场景假设套在自己的服务器上。
配置文件的组织方式体现了同样的谨慎。Fail2ban 自带的默认配置集中在一个文件里,官方建议不要直接改它,因为软件升级时会覆盖。正确的做法是另起一个本地覆盖文件,只写你自己的改动,优先级高于默认文件。这样升级时你的策略不会丢,回滚时也只需要处理一个文件。这个习惯本身比任何具体参数都重要:防护规则是会演进的,配置必须经得起升级与回滚。
安装本身没有太多可讲的。主流发行版的官方软件源里都有 Fail2ban,Debian 与 Ubuntu 系用包管理器直接装;RHEL 系需要先启用额外的社区软件源再装,具体命令以发行版当时的官方文档为准。装完之后先别急着调参数,第一件事是确认服务在跑、默认的 SSH 监狱已经启用,再看一眼状态输出里有没有正在累计的失败计数。服务跑起来、日志读得到、状态看得见,这三件事确认了,配置才有意义。
下面是一份最小的本地配置骨架,作用是示意结构而非给出数值:窗口、次数与时长请按你的场景填写,具体字段含义与现行版本以官方文档为准。
[DEFAULT]
# 本机回环地址不受封禁;有固定公网 IP 的管理员可追加在这里
ignoreip = 127.0.0.1/8 ::1
# 封禁时长、检测窗口、最大容忍次数:三者配合,先宽后严
bantime = 86400
findtime = 600
maxretry = 3
[sshd]
enabled = true
# 若 SSH 不在默认端口,此处须写实际端口,否则规则对不上服务
port = ssh需要扩展到 Web 或邮件服务时,思路是相同的:为每项服务单独起一个 jail,指定它的日志路径与过滤器,而不是把所有规则塞进同一个 jail。启用之前先确认对应服务的日志里确实有失败记录——一个从来没有失败日志的 jail,配得再精细也不会触发。
自定义规则:什么时候值得动手
自带过滤器覆盖不了的场景,典型是自有应用。自己写过滤器之前,先问三个问题:日志格式是否稳定、失败行为是否可辨认、误匹配的代价有多大。如果日志格式经常变,过滤器会频繁失效;如果失败与成功在日志里长得差不多,正则很容易误伤;如果这项服务面向公众用户,误封的代价就比内部工具大得多。三个问题有一个答案是否定的,都值得先缓一缓,或者先用观察模式跑一段时间。
动手写时,有几个原则比正则技巧更重要。第一,只匹配你确认过的失败模式,不要试图用一条正则包揽所有异常——包揽的代价是误匹配。第二,把 IP 锚定写死在正则里,确保抽出来的一定是来源地址,而不是日志里顺带出现的其他地址。第三,写完立刻用测试工具对着真实日志验证,数一数匹配行里有没有正常请求混进来。第四,先以较短的封禁时长试运行,确认无误再放宽到正常值。自定义规则最常见的事故不是写不出来,而是写出来没验证就直接上了生产。
还有一个容易被忽略的点:过滤器与服务版本有关。服务升级后日志格式可能微调,原来好好的正则突然匹配不上了,表现为“攻击明明在发生,Fail2ban 却一声不吭”。把过滤器纳入服务升级的检查清单——升级后顺手看一眼封禁计数有没有正常增长——能避免这种悄悄失效。
和其他防护层怎么分工
Fail2ban 很少单独工作,它身边通常还有两三层防护,分工不清就会互相拆台。最常见的一层是系统自带的防火墙基线:哪些端口对外开放、哪些只对内网开放,这是静态规则,应当在 Fail2ban 之前就定好。Fail2ban 的动态封禁是叠加在这层之上的临时规则,两者由同一个防火墙后端执行才不会打架。如果基线用一套工具管、动态封禁又走另一套后端,规则可能写不进去,或者写进去却排在放行规则之后,等于没生效。上线之前先确认动态规则在规则链里的位置,而不是只看服务状态是绿的。
另一层是反向代理与 CDN。服务前面一旦站了代理,应用日志里看到的来源地址可能不再是真实访客,而是代理节点的地址。这时如果直接用 Fail2ban 去封,封掉的会是代理本身,影响的是所有经由该节点进来的正常用户。这个问题不能靠 Fail2ban 解决,必须在应用层先把真实来源地址还原出来——让服务记录代理转发的原始地址字段,再让过滤器读还原后的地址。还原之前,宁可让相关 jail 保持关闭,也不要对着代理地址封禁。顺序错了,防护就变成自伤。
告警也值得单独想一想。Fail2ban 可以在每次封禁时发通知,刚开始会觉得很有安全感,但攻击量上来之后,通知本身就成了新的噪声。更合理的做法是分级:封禁事件只记日志,真正值得打扰你的,是异常信号本身的变化——比如某个监狱的封禁量突然翻了几倍、某个过滤器连续几天零匹配、服务自身报错停了。告警的目的是让你注意到“情况变了”,而不是把每一次封禁都复述一遍。通知渠道可以是邮件,也可以是消息机器人,形式不重要,重要的是阈值设在哪里。
最后要承认一个现实:封禁终究是治标。IP 地址可以换,僵尸网络的节点池深不见底,指望封禁把攻击者“封光”是不现实的。Fail2ban 真正的收益是两笔账:一笔是成本账,让自动化试探的每一次尝试都可能换来一段时间的封禁,从而抬高对方的试探成本;另一笔是信噪比账,把日志里绝大多数无意义的爆破记录挡掉之后,剩下的异常才值得你花时间看。这两笔账算清楚了,就不会对它期望过高,也不会因为它“封不完”而觉得它无用。
误封之后的三条出路
误封不可怕,可怕的是误封之后没有出路。自己的 IP 被封在门外时,通常有三条路。第一条是用另一条通道进去解封:云服务商的控制台终端就是为此准备的,它不走 SSH,自然不受 Fail2ban 影响。第二条是暂时停掉 Fail2ban 服务,规则随之解除,再进去慢慢查。第三条是事后把地址写进白名单并重载配置。应急时选最快的一条,事后再补白名单与复盘,不要在被锁在门外的时候现学配置语法。
预防误封的根本办法,是把白名单做在封禁之前。自己的固定公网 IP、监控节点的出口 IP、团队常用的办公出口,这些地址应当在第一版配置里就写进白名单,而不是等到被封了才想起来。没有固定 IP 的管理员更要留心:家庭宽带的出口地址会变,今天的白名单明天可能就失效,这种情况下控制台通道就是最后的保险,务必确认它可用,而不是假设它可用。
日常维护其实只有几件事。定期看一眼各监狱的状态:当前封禁了多少、累计封禁了多少、失败计数是不是在正常增长——计数长期为零未必是太平无事,也可能是日志路径变了、过滤器失效了。日志本身要轮转,Fail2ban 自带轮转配置,一般不需要动,但要确认它确实在工作,否则日志文件会悄悄长大。服务升级后复查过滤器,前面已经提过。配置改动后用重载而不是重启,让规则平滑生效;只有在怀疑服务本身状态异常时,才需要重启。
最后回到定位。Fail2ban 是一层自动响应,不是安全体系的终点。它的价值体现在两个地方:一是让自动化爆破的成本高于收益,把攻击者的注意力推向更容易的目标;二是把日志里的噪声降下来,让你能看清真正值得注意的异常。密钥登录、防火墙基线、及时更新、最小权限,这些才是体系本身。Fail2ban 坐在它们的外围,负责把门外的喧闹挡掉大半——这件事做好了,整套防线的信噪比都会变好。
延伸阅读
免责声明
本文仅供技术交流和学习参考。涉及第三方服务的链接可能包含 sponsored 标记,请自行核实服务条款、价格和可用性,并遵守当地法律法规。