首页 / 资讯动态 / 服务器ssh登录警告:基于时间的登录尝试限制

服务器ssh登录警告:基于时间的登录尝试限制

当你盯着终端屏幕上那行“Last failed login:”后面跟着一长串来自陌生IP的暴力破解记录时,那种感觉就像有人在你家门口反复试钥匙。这不是科幻电影里的情节,而是任何一台暴露在公网的Linux服务器每天都会遇到的真实状况。解决这个问题的核心思路,不是等到攻击者试出密码再后悔,而是直接拉长他们的时间成本。基于时间的登录尝试限制,也就是通过fail2ban或pam_tally2这类工具强制插入“冷却时间”,是目前最有效的被动防御手段之一。

为什么传统的失败计数锁死还不够

很多人习惯直接设置“连续输错5次密码就锁定账户”。这种方式确实能防止纯暴力破解,但它有个致命缺陷:攻击者可以故意反复尝试所有常见用户名,导致你的root或admin账户被锁死,你自己反而登不进去,这就是典型的拒绝服务攻击。更麻烦的是,如果攻击者每隔几分钟试一次,每次只试两三个密码,传统的计数机制根本触发不了锁定阈值,但时间一长,密码还是有可能被慢慢撞开。

基于时间的限制逻辑完全不同。它不只看“失败了几次”,更看重“在多长时间内失败了这么多次”。一旦触发阈值,不是永久锁死账户,而是暂时禁止该IP或该账户继续尝试,比如禁止10分钟。10分钟后自动解封,既不影响你正常登录,又让攻击者的效率跌到谷底。原本一秒能试1000次,现在10分钟只能试一次,暴力破解就失去了数学上的可行性。

fail2ban的核心机制与时间策略

fail2ban的工作原理是监控日志文件,用正则表达式匹配失败事件,然后动态修改防火墙规则。它在时间维度上的控制远比想象中精细。打开/etc/fail2ban/jail.local,你会看到三个关键时间参数:findtime、maxretry、bantime。很多人只改maxretry,却忽略了这三个参数之间的乘数效应。

findtime定义观察窗口,比如600秒,也就是10分钟。maxretry定义在这个窗口内允许的最大失败次数。bantime定义触发后的封禁时长。真正硬核的地方在于:如果你把findtime设得很短,比如60秒,maxretry设成3次,bantime设成1800秒,那就意味着任何人在1分钟内输错3次密码,就要被关半小时小黑屋。这种配置对自动化脚本是毁灭性打击,因为脚本通常会在极短时间内密集尝试。

更进阶的做法是利用fail2ban的增量封禁策略。在jail配置里加入bantime.increment参数,设置为true,并配合bantime.factor和bantime.maxtime。这样每次同一IP再次被封,封禁时间会自动翻倍,比如第一次10分钟,第二次20分钟,第三次40分钟,直到达到设定的上限。这种动态惩罚机制专门针对那些会换着时间点来撞库的顽固攻击者,几次下来他们就会发现等待时间长得不值得。

pam_tally2与pam_faillock的时间锁策略

如果你不想依赖外部服务,只想用系统自带的PAM模块实现时间限制,pam_faillock是比老旧的pam_tally2更好的选择。在/etc/pam.d/sshd里配置pam_faillock,关键参数是fail_interval和unlock_time。fail_interval指定失败计数的累积时间窗口,比如900秒,unlock_time指定锁定后的解锁时间,比如600秒。

这里有一个容易被忽略的细节:pam_faillock的计数是“连续失败”还是“累积失败”,取决于你是否配置了silent参数和审计逻辑。如果只配了preauth和authfail,它会在每次失败后更新计数器,但一旦成功登录,计数器清零。这意味着攻击者如果在900秒内失败了3次,然后你管理员成功登录了一次,计数器就归零了,攻击者又可以继续试。要避免这种“被洗白”的情况,可以在配置中增加even_deny_root和admin_group参数,把管理员账户纳入监控范围,同时确保只有真正需要的人才能通过物理终端或带外管理重置计数。

还有一个极其实用的配置是结合pam_exec,在账户被时间锁定时执行自定义脚本。比如当某个账户触发锁定后,立刻通过企业微信或钉钉机器人发一条告警,把来源IP、尝试次数、锁定时长全部推送到运维群。这样你不仅能防御,还能实时感知到攻击态势,判断这是大规模扫描还是针对性的APT攻击。

SSH配置本身的时间参数优化

在折腾fail2ban和PAM之前,sshd_config里就有几个直接影响时间维度的参数,但90%的教程都只让你改端口和禁用root登录,完全忽略了这些细节。LoginGraceTime默认是120秒,意思是客户端在连接后有多长时间来完成认证。攻击者的脚本通常会在建立TCP连接后立即开始尝试密码,如果你把这个值降到30秒甚至20秒,那些慢速连接或者故意拖时间的探测行为就会被直接切断。

MaxStartups参数更值得深挖。它的完整格式是“start:rate:full”,比如10:30:60。意思是当有10个未完成认证的连接时,开始以30%的概率拒绝新连接,当达到60个连接时,所有新连接全部拒绝。这个机制天然带时间属性,因为它控制的是并发连接的速率。攻击者如果同时发起大量连接试图加速暴力破解,反而会触发概率拒绝,导致他们自己的连接被随机丢弃,破解效率进一步降低。

还有一个冷门但极其有效的参数是PerSourceMaxStartups,在较新版本的OpenSSH中可用。它直接限制单个来源IP的最大未认证连接数。把它设为3或5,意味着同一IP最多只能同时有3个连接在尝试登录,多出来的直接拒绝。这比任何第三方工具都更底层、更高效,因为拒绝发生在SSH协议握手阶段,根本不消耗PAM认证资源。

多层时间限制的协同防御架构

单一层面的时间限制总有盲区。真正稳健的方案是把TCP层、SSH服务层、PAM认证层、应用层的时间限制串成一条链。TCP层用iptables的recent模块做第一道闸门,比如60秒内新连接超过10个的IP直接DROP,这条规则在数据包到达SSH服务之前就生效了,效率最高。

iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 10 -j DROP

SSH服务层用上面提到的MaxStartups和PerSourceMaxStartups做第二道闸门。PAM层用pam_faillock做账户级时间锁。应用层用fail2ban做日志分析和动态封禁,同时开启增量封禁。这四层的时间参数要错开配置,形成梯度防御:TCP层反应最快但封禁时间最短,比如封5分钟;fail2ban反应稍慢但封禁时间最长,比如第一次封1小时,增量到24小时。这样即使攻击者绕过了某一层,也会在下一层被更重的惩罚拦住。

另外要特别注意白名单策略。你自己的固定IP、公司出口IP、跳板机IP,必须在所有层面都加入白名单,否则一旦你自己输错几次密码,就会被自己的防御机制关在外面。fail2ban用ignoreip参数,iptables用RETURN规则放在DROP规则之前,PAM用pam_succeed_if模块跳过特定用户或组。白名单不是安全漏洞,而是保证可用性的必要手段,关键是白名单的IP范围要严格控制,不要图省事放行整个C段。

日志监控与时间阈值调优

配置完所有规则之后,真正的技术活在于根据实际日志数据反向调优时间参数。很多人设完bantime=86400就觉得万事大吉,结果发现自己的fail2ban数据库里封了上万个IP,iptables规则膨胀到几千条,网络栈性能明显下降。这时候你需要分析/var/log/auth.log里攻击的间隔分布。

用一条简单的awk命令就能统计出攻击者两次尝试之间的平均时间间隔。如果大部分攻击是每秒10次以上的高频扫描,那你的findtime设60秒、maxretry设5次就足够抓住它们。但如果日志里出现了大量每小时只试两三次的低速慢扫,你就需要把findtime拉长到3600秒甚至更长,同时降低maxretry到3次。低速慢扫是APT攻击的常见特征,攻击者愿意花几个月时间慢慢撞,你的时间窗口必须比他们的耐心更大。

还有一个容易被忽视的指标是解封后的再犯率。通过分析fail2ban的日志,找出那些被封了一次、解封后又立刻回来继续试的IP。对这些IP应该启用增量封禁,或者直接把它们加入永久黑名单。如果某个IP在24小时内被反复封了5次以上,基本可以判定是恶意主机,永久封禁也不为过。

时间限制策略在容器化和多因素认证环境下的适配

现在很多生产环境跑在Kubernetes或者Docker里,SSH服务可能不是直接暴露在宿主机上,而是通过LoadBalancer或者NodePort转发。这种情况下,源IP可能会被SNAT篡改,导致你的时间限制策略误伤一大片。解决方法是在Ingress或外部负载均衡器上开启Proxy Protocol或者X-Forwarded-For头传递真实IP,然后在fail2ban里用自定义的正则去匹配这个头里的IP,而不是直接取TCP连接的源地址。

如果你已经启用了SSH密钥认证或者多因素认证,时间限制策略依然不能撤掉。因为MFA的OTP验证码通常只有6位数字,理论上仍然存在被暴力猜解的可能性,只是概率极低。针对MFA场景,时间限制的重点应该放在“验证码尝试次数”上,而不是密码尝试次数。很多MFA模块比如google-authenticator的PAM模块,本身就支持rate_limit参数,可以设置每30秒只允许尝试一次验证码,这本身就是一种时间维度的防护,要和fail2ban的策略配合使用,避免两层限制互相干扰。

最后提醒一个运维层面的事:无论你配置了多么完美的时间限制策略,都要确保你有带外管理通道。带外管理可以是云厂商的VNC控制台、IPMI、或者一台只允许内网登录的跳板机。时间锁策略最怕的就是把自己也锁在外面,而带外通道是你在任何极端情况下都能恢复访问权的最后保障。没有带外管理,再好的安全策略都是在给自己挖坑。