Ubuntu服务器默认的认证机制本身并不限制密码尝试次数,这意味着一旦SSH服务暴露在公网,任何人都可以用脚本无限次尝试登录。要解决这个问题,最直接有效的方法不是安装额外软件,而是利用Ubuntu自带的PAM模块pam_faillock,它能精确控制失败次数、锁定时间和解锁策略,而且配置后立即生效,无需重启服务。
先检查当前系统是否已启用faillock。在Ubuntu 20.04及之后的版本中,pam_faillock已经内置,但默认配置并未激活。你可以直接编辑/etc/pam.d/common-auth文件,在文件顶部加入以下两行:
auth required pam_faillock.so preauth silent deny=5 unlock_time=600 auth required pam_faillock.so authfail silent deny=5 unlock_time=600
这里deny=5表示连续失败5次后锁定账号,unlock_time=600表示锁定600秒后自动解锁。注意preauth和authfail两行缺一不可,前者在密码验证前检查当前账号是否已被锁定,后者在验证失败后记录失败次数。如果只写authfail这一行,锁定功能不会完整生效。
接下来还要在/etc/pam.d/common-account文件中加入一行,确保锁定状态能被系统正确识别:
account required pam_faillock.so
完成上述配置后,普通用户和root用户都会受到同样的失败次数限制。但这里有一个关键点:对于root账号,很多管理员担心锁定后自己也无法登录。如果你不希望root被锁定,可以在pam_faillock.so后面加上even_deny_root参数,这样root也会被锁定;如果不加这个参数,root账号默认不受失败次数限制。从安全角度出发,建议加上even_deny_root,因为攻击者往往首先尝试root账号。
配置完成后,你可以通过faillock命令查看当前失败记录。执行faillock --user 用户名可以查看特定用户的失败次数和锁定状态。如果测试时不小心把自己锁在门外,可以使用以下命令手动解锁:
faillock --user 用户名 --reset
这个命令会清除该用户的所有失败记录,立即解除锁定。如果你在服务器上配置了sudo权限的其他管理账号,即使某个账号被锁定,也可以通过另一个账号登录后执行解锁操作。
除了pam_faillock,还有一种更轻量的方式是通过SSH配置来限制失败次数,但它的作用范围仅限于SSH登录,对本地终端或控制台登录无效。如果你只关心SSH暴力破解,可以在/etc/ssh/sshd_config中设置MaxAuthTries参数。例如:
MaxAuthTries 3
这个参数表示每次SSH连接最多允许3次密码尝试,超过后SSH服务会直接断开连接。它的优点是配置简单,不需要修改PAM模块;缺点是攻击者可以重新建立连接继续尝试,无法实现真正的账号锁定。因此MaxAuthTries更适合作为pam_faillock的补充,而不是替代方案。
另一个值得关注的配置是pam_tally2。在较老的Ubuntu版本中,pam_tally2是常用的失败次数限制模块,但在Ubuntu 18.04之后,官方推荐使用pam_faillock替代pam_tally2,因为pam_tally2在某些场景下存在计数不准确的问题。如果你还在使用pam_tally2,建议迁移到pam_faillock。迁移时先注释掉common-auth中所有pam_tally2相关行,然后按照上述方法加入pam_faillock配置即可。
在实际部署中,仅仅限制失败次数还不够。你应该同时考虑以下措施来增强登录安全。第一,修改SSH默认端口,虽然不能完全阻止扫描,但可以过滤掉大量自动化攻击。第二,禁用密码登录,改用密钥认证,这是最彻底的解决方案。第三,如果必须保留密码登录,可以结合fail2ban这类工具,在防火墙层面封禁频繁失败的IP地址。pam_faillock锁定的是账号,fail2ban封禁的是IP,两者互补,能有效降低暴力破解的成功率。
关于锁定阈值的选择,deny=5是一个相对合理的默认值。设置太小容易导致正常用户因为手误被锁定,设置太大则削弱防护效果。如果你的用户群体技术水平较高,可以设置为3;如果服务器主要面向普通用户,建议设置为5或6。unlock_time建议设置为600秒到1800秒之间,太短攻击者可以反复尝试,太长则影响正常用户恢复访问。对于高安全要求的服务器,可以设置unlock_time=3600甚至更长,同时配合手动解锁流程。
还有一个容易被忽略的细节:pam_faillock的失败计数是基于连续失败次数,一旦用户成功登录,计数会自动清零。这意味着攻击者如果在锁定时间内没有继续尝试,锁定到期后计数会重置。如果你希望锁定时间更长,可以在pam_faillock.so后面加上unlock_time参数的同时,设置root_unlock_time来单独控制root账号的解锁时间。
测试配置是否生效时,不要直接在生产服务器上尝试错误密码,建议先在一台测试机上验证。你可以使用su命令模拟登录失败:
su - testuser
连续输入错误密码5次后,再输入正确密码,系统会提示账号已被锁定。此时查看/var/log/auth.log日志,可以看到类似“Account locked due to 5 failed logins”的记录。确认无误后再将配置应用到生产环境。
如果你管理的Ubuntu服务器使用了LDAP或Active Directory进行集中认证,pam_faillock同样可以工作,但需要注意PAM模块的调用顺序。在common-auth中,pam_faillock的preauth行必须放在LDAP认证模块之前,authfail行放在LDAP认证模块之后。否则失败计数可能无法正确记录,或者锁定状态无法在LDAP认证前被检查。
对于使用双因素认证的环境,pam_faillock的失败计数通常只针对第一因素(密码)。如果第一因素验证通过但第二因素失败,默认情况下不会计入失败次数。你可以通过调整PAM配置中的模块顺序来改变这一行为,但建议保持默认,因为第二因素失败往往不是暴力破解的目标。
最后需要强调的是,任何账号锁定机制都存在被滥用的风险。攻击者如果知道某个账号的用户名,可以故意多次输入错误密码导致该账号被锁定,从而造成拒绝服务。为了缓解这个问题,你可以在pam_faillock配置中加入per_user参数,为不同用户设置不同的锁定阈值。例如,为管理员账号设置更高的阈值,为普通用户设置较低的阈值。配置方式如下:
auth required pam_faillock.so preauth silent deny=5 unlock_time=600 per_user
然后在/etc/security/faillock.conf文件中为特定用户设置覆盖规则。这个配置文件在Ubuntu 20.04及以上版本中默认存在,你可以直接编辑。例如:
[default] deny = 5 unlock_time = 600 [admin] deny = 10 unlock_time = 300
这样admin账号允许10次失败尝试,锁定时间缩短为300秒,而其他用户保持5次和600秒的默认值。这种细粒度的控制在实际运维中非常实用,既能保护普通账号,又能避免关键管理账号被轻易锁定。
配置完成后,建议定期检查/var/log/auth.log中的失败登录记录,分析攻击来源和尝试的用户名。如果发现某个账号频繁被尝试,即使没有被成功破解,也应该考虑禁用该账号或更改用户名。同时,结合防火墙规则限制SSH服务的访问来源,只允许特定IP段访问,可以进一步降低暴力破解的风险。
总结来说,Ubuntu服务器限制账号登录失败次数的核心是配置pam_faillock模块,通过deny和unlock_time参数控制失败阈值和锁定时间。SSH的MaxAuthTries可以作为辅助手段,但无法替代PAM级别的账号锁定。配置时注意preauth和authfail两行必须同时存在,测试时使用faillock命令查看状态,必要时手动解锁。结合密钥认证、防火墙和fail2ban,可以构建多层次的登录安全防护体系。
