1. 项目概述:为什么需要加固你的Ubuntu登录防线
最近在帮几个朋友处理他们托管在云上的Ubuntu服务器,发现一个挺普遍的现象:很多人装完系统,设个“123456”或者“admin”的密码就完事了,SSH端口也默认开着22。这简直是把自家大门钥匙插在锁上,还贴了张“欢迎光临”的纸条。其中一位的服务器甚至因为弱密码被暴力破解,成了矿机,收到云厂商的天价账单才慌了神。这件事让我觉得,是时候系统性地聊聊Linux服务器,特别是Ubuntu,在登录安全上那些必须做的基础配置了。
我们今天要聊的核心就两件事:密码复杂度和连续登录失败处理。听起来可能有点枯燥,像是运维手册里的条条框框,但它们实际上是构筑服务器安全防线的第一道,也是成本最低、效果最显著的“门卫”和“警报器”。密码复杂度策略决定了攻击者猜测你密码的难度,是静态防御;而连续登录失败处理(比如账户锁定)则是在攻击发生时动态响应,直接掐断暴力破解的尝试,是动态防御。两者结合,才能有效应对从“撞库”到“暴力破解”的各种常见威胁。
无论你是刚拥有第一台VPS的个人开发者,还是需要维护中小规模集群的运维工程师,这些配置都是你应该像“出门锁门”一样形成的肌肉记忆。下面,我就结合最新的Ubuntu LTS版本(如22.04, 24.04),带你一步步把这些配置落到实处,并解释清楚每一个参数背后的考量。
2. 核心安全模块解析:PAM与工具链
在动手修改任何配置之前,我们必须先理解幕后工作的核心机制——PAM。如果你直接去搜“如何修改密码策略”,可能会找到一堆直接编辑/etc/pam.d/common-password的命令。但知其然更要知其所以然,否则出了问题连排查方向都没有。
2.1 PAM:可插拔认证模块
PAM 是 Linux 系统上处理身份认证的一套框架。你可以把它想象成一个高度模块化、可定制的“认证流水线”。当用户登录、切换身份(如su)、或修改密码时,相关的程序(如login,sshd,passwd)并不会自己处理认证逻辑,而是去调用 PAM。
PAM 的配置文件通常位于/etc/pam.d/目录下,每个需要认证的服务都有一个对应的文件。例如,/etc/pam.d/sshd用于 SSH 登录认证,/etc/pam.d/common-password则用于密码修改操作。这些配置文件由一系列“规则”组成,每条规则定义了在认证的哪个阶段(auth, account, password, session)调用哪个模块,以及模块的行为(required, requisite, sufficient, optional)。
对于我们今天的主题:
- 密码复杂度主要由
pam_pwquality(旧版叫pam_cracklib) 模块在password阶段控制。 - 连续登录失败处理则通常由
pam_tally2或pam_faillock模块在auth阶段控制。
理解 PAM 是自由配置的前提,它让你明白你的修改究竟作用于认证流程的哪一个环节。
2.2 关键工具:libpam-pwquality 与 pam_faillock
在 Ubuntu 上,实现我们所需的功能,主要依赖两个包:
libpam-pwquality: 这是设置密码复杂度策略的核心。它取代了旧的libpam-cracklib,提供了更丰富和清晰的配置选项。它会检查新密码的长度、字符类别(大小写字母、数字、特殊符号)、是否在字典中、是否与旧密码太相似等。pam_faillock模块: 这是处理连续登录失败的主流现代模块。它会在用户连续认证失败一定次数后,临时锁定该用户的账户一段时间。它的状态信息(失败次数)默认存储在/var/run/faillock目录下,是内存式的,重启后清零。也可以配置为写入文件。
注意:在一些旧教程或 CentOS/RHEL 体系中,你可能会看到
pam_tally2。在 Ubuntu 22.04 及更新版本中,pam_faillock是更推荐的方式,它功能更强,支持同时锁定本地和远程登录。
安装与检查: 在开始配置前,先确保它们已安装。通常libpam-pwquality是默认安装的,而pam_faillock可能需手动安装。
# 检查 pwquality 是否安装 dpkg -l | grep libpam-pwquality # 安装 pam_faillock (如果未安装) sudo apt update sudo apt install libpam-faillock安装后,你可以在/lib/x86_64-linux-gnu/security/或/lib/security/目录下找到对应的.so文件(如pam_pwquality.so,pam_faillock.so),这就是被 PAM 调用的模块。
3. 密码复杂度策略深度配置
密码策略的目标是强制用户设置一个强密码。我们通过配置libpam-pwquality来实现。主要配置文件有两个地方,它们有优先级关系:
/etc/security/pwquality.conf: 这是 pwquality 模块的主配置文件。在这里设置是全局生效的。- PAM 配置文件中的模块参数: 在
/etc/pam.d/common-password文件中,调用pam_pwquality.so时可以直接传递参数,这里的设置会覆盖pwquality.conf。
我个人的习惯是优先使用/etc/security/pwquality.conf,因为它的配置集中、清晰,并且有详细的注释说明每个参数的含义,更适合管理和维护。
3.1 解读与配置 pwquality.conf
让我们打开这个文件,看看里面有哪些可以调整的“旋钮”:
sudo vim /etc/security/pwquality.conf你会看到很多被注释掉的配置行,格式是参数 = 值。我们来逐一解析几个最关键的核心参数:
minlen = 12: 这是密码的最小长度。这是最基础的防线。我强烈建议设置为至少12。长度是密码复杂度的首要因素。即使只使用小写字母,一个12位随机密码的搜索空间也极其巨大。dcredit = -1: 要求密码中至少包含1位数字。-1表示“至少1个”,-2表示“至少2个”,正数则表示“最多给这么多分”,但通常我们用负数来设置“至少”的要求。ucredit = -1: 要求密码中至少包含1个大写字母。lcredit = -1: 要求密码中至少包含1个小写字母。ocredit = -1: 要求密码中至少包含1个特殊符号(如!@#$%^&*)。minclass = 4: 这个参数非常有用!它要求密码必须来自至少 N 种不同的字符类别(数字、大写、小写、特殊)。如果你设置了minclass=3,那么像Password123这样的密码(有大写、小写、数字,共3类)就符合要求,而password123(只有小写和数字,2类)则不行。这比单独设置每个类别的数量更灵活,能防止用户用“Password”后面简单加两个数字来应付。maxrepeat = 3: 允许同一个字符连续出现的最大次数。设置为3可以防止出现aaaB123!这样的密码。maxsequence = 4: 允许连续字符序列(如abcd,1234)的最大长度。防止使用键盘上的简单序列。dictcheck = 1: 是否检查密码是否基于字典中的单词。1为开启。这会调用系统字典,防止使用apple2024这类弱密码。usercheck = 1: 是否检查密码中包含用户名(正序或逆序)。1为开启。防止用户使用自己的名字作为密码。retry = 3: 设置新密码时,允许用户重试的次数。
一个我推荐的配置示例:
# /etc/security/pwquality.conf minlen = 12 minclass = 3 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1 maxrepeat = 2 maxsequence = 3 dictcheck = 1 usercheck = 1 retry = 2 enforcing = 1这个策略意味着:密码至少12位,必须包含数字、大写、小写、特殊符号中的至少3类,且同一字符不能连续出现超过2次,不能有超过3位的连续序列,不能是字典词或包含用户名,最多允许2次设置尝试。
3.2 在PAM中启用并测试策略
配置好pwquality.conf后,需要确保 PAM 的密码修改流程调用了它。检查/etc/pam.d/common-password文件:
sudo cat /etc/pam.d/common-password | grep pwquality你应该会看到类似这样的一行:
password requisite pam_pwquality.so retry=3如果存在,并且没有try_first_pass或use_authtok等参数冲突,它就会使用我们刚才在pwquality.conf里的配置。如果这一行被注释了,取消注释即可。
现在来测试一下:
- 为自己创建一个测试用户(避免误操作影响当前用户):
sudo adduser testuser_complex - 切换到该用户并尝试修改密码:
sudo su - testuser_complex passwd - 尝试设置弱密码,观察系统的拒绝信息。例如,输入
123456,你会看到类似Bad password: too short的错误。输入Test123,可能会看到Bad password: does not contain enough different characters或类别不足的提示。这些具体的错误信息正是pam_pwquality模块根据你的配置返回的,是调试策略是否生效的直接依据。
实操心得:配置密码策略后,务必自己先按策略要求设置一个强密码并成功登录一次。我曾经遇到过在配置了非常严格的策略后,没有更新自己的密码,结果某次登录缓存失效后,因为密码不符合新策略而无法通过 PAM 认证,把自己锁在了服务器外面。对于 root 用户或关键的管理员账户,最好在配置前就先按照即将启用的策略修改好密码。
4. 连续登录失败锁定策略实战
密码策略是防“猜”,而失败锁定是防“暴力试”。想象一下,攻击者用自动化脚本以每秒数百次的速度尝试各种密码。失败锁定功能就是在探测到这种异常行为时,临时“冻结”这个账户,让攻击无法继续。
4.1 配置 pam_faillock 模块
如前所述,我们使用pam_faillock。配置同样涉及两个文件:
/etc/security/faillock.conf: 主配置文件(如果不存在,可以创建)。- PAM 配置文件: 在
/etc/pam.d/common-auth中插入该模块。
首先,编辑或创建/etc/security/faillock.conf:
sudo vim /etc/security/faillock.conf写入以下核心配置:
# 账户在连续失败几次后被锁定 deny = 5 # 锁定多长时间(单位:秒)。21600秒=6小时。也可以写成`6h`(如果版本支持)。 unlock_time = 21600 # 在多久的时间窗口内计算失败次数(单位:秒)。86400秒=24小时。 fail_interval = 86400 # 是否在达到失败次数时,向系统日志(syslog)发送消息。 silent = false # 是否在用户被锁定时显示提示信息。 audit = true # 锁定的范围。`user`表示只锁单个用户,`host`则锁整个主机(针对该用户)。一般用user。 scope = user这个配置的意思是:在24小时内,如果同一个用户连续5次登录失败,则该用户账户将被锁定6小时。在此期间,即使输入了正确的密码,认证也会失败。
然后,修改PAM配置。我们需要在认证栈的合适位置插入pam_faillock.so。编辑/etc/pam.d/common-auth:
sudo vim /etc/pam.d/common-auth找到文件开头部分,在auth required pam_deny.so这行之前,添加如下两行:
auth required pam_faillock.so preauth silent audit deny=5 unlock_time=21600 auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=21600同时,在auth required pam_deny.so这行之后,添加:
auth sufficient pam_faillock.so authsucc audit deny=5 unlock_time=21600配置行解析:
preauth: 在用户输入密码之前检查该用户是否已被锁定。authfail: 当认证失败时,增加失败计数。[default=die]表示如果这个模块失败(即用户已被锁定或达到失败次数),则立即终止认证流程,不再继续后面的模块。authsucc: 当认证成功时,重置该用户的失败计数。sufficient表示如果认证成功,这个模块的结果就足够了,可以跳过后面的一些模块(但不是全部,PAM流程比较复杂)。
重要提示:修改 PAM 配置是高风险操作。错误的配置可能导致所有用户(包括root)无法登录。强烈建议在修改前备份原文件,并且确保你有一个可以通过“恢复模式”或云服务器控制台VNC访问的途径。
4.2 测试失败锁定功能
配置完成后,需要测试是否生效。
- 从另一个终端或另一台机器,尝试SSH登录。使用一个存在的用户名(比如刚才创建的
testuser_complex),但故意输入错误密码。 - 连续错误5次后,第6次即使输入了正确密码,也应该会被拒绝,并看到类似
Account locked due to 5 failed logins的提示。 - 查看失败记录:
这个命令会显示指定用户的登录失败记录、次数和锁定状态。sudo faillock --user testuser_complex - 手动解锁用户(在锁定时间内如果需要恢复访问):
这个命令会将该用户的失败计数清零,立即解除锁定。sudo faillock --user testuser_complex --reset
4.3 针对root用户的特殊考量
默认情况下,上述配置对root 用户是否生效,取决于 PAM 配置中的一个常见设置。在/etc/pam.d/common-auth文件中,你可能会看到这样一行:
auth [success=1 default=ignore] pam_unix.so nullok_secure或者在某些配置中,会有针对root的特别规则。有些安全策略会刻意允许 root 不受失败锁定限制,因为在高强度攻击下,如果 root 被锁定,且没有其他解锁途径(如物理控制台),可能导致系统完全无法管理,造成更大的风险。
我的建议是:对于生产服务器,禁止 root 用户直接通过 SSH 登录(修改/etc/ssh/sshd_config中的PermitRootLogin no),而是使用一个普通用户通过sudo来提权。这样,失败锁定策略就应用在这个普通用户上,即使它被锁定,你还可以通过其他管理员账户或控制台去解锁,避免了单点故障。这才是更安全的做法。
5. 配置的持久化、验证与边界场景处理
配置不是改完文件就万事大吉了。你需要验证它是否按预期工作,并考虑一些边界情况。
5.1 验证配置生效
- 密码策略验证: 使用
passwd命令为测试用户设置密码,尝试触发各种规则(太短、太简单、无数字等),确保错误信息与你配置的策略相符。 - 失败锁定验证: 如上节所述,通过故意输错密码来触发锁定,并使用
faillock命令查看状态。 - 检查系统日志: 锁定事件和认证失败都会被记录。使用
journalctl或查看/var/log/auth.log来确认。
你应该能看到包含sudo tail -f /var/log/auth.log # 然后尝试一次失败登录,观察日志输出pam_faillock和用户名的失败记录。
5.2 配置的持久化与影响范围
- PAM配置的生效: PAM 配置是即时生效的。一旦你保存了
/etc/pam.d/下的文件,下一次认证请求就会使用新规则。无需重启服务。 - SSH服务: 虽然 PAM 配置生效了,但确保你的 SSH 服务(
sshd)是启用了 PAM 的。检查/etc/ssh/sshd_config中是否有UsePAM yes。通常这是默认设置。修改 SSH 配置后需要重启服务:sudo systemctl restart sshd。 - 其他登录方式: 这些 PAM 配置不仅影响 SSH,还会影响本地终端登录(tty)、
su命令、图形界面登录(如 GDM)等所有通过 PAM 进行认证的入口。这是一个全局性的安全提升。
5.3 边界场景与注意事项
- 服务账户: 对于像
nginx、mysql、redis这类服务账户,它们通常使用密钥或套接字认证,不涉及密码登录。失败锁定策略对它们一般没有影响。但如果你为这些账户设置了密码并用于某些管理操作,则需要留意。 - sudo 操作:
sudo命令本身也会进行认证(验证你的用户密码)。连续输错sudo密码是否会被锁定?这取决于/etc/pam.d/sudo这个 PAM 配置文件是否也包含了pam_faillock模块。通常为了管理便利,sudo的失败可能不计入登录失败锁定,或者有独立的策略。你需要根据实际安全要求来配置。 - 锁定后的恢复:
- 手动解锁: 使用
faillock --user <username> --reset。 - 自动解锁: 等待
unlock_time设置的时间过后,账户会自动解锁。 - 紧急恢复: 如果唯一的管理员账户被锁,且无法通过控制台,你可能需要重启进入单用户模式或恢复模式来修改 PAM 配置或重置密码。
- 手动解锁: 使用
- 配置备份与版本管理: 将
/etc/pam.d/下的关键文件和/etc/security/pwquality.conf、/etc/security/faillock.conf进行备份。如果使用自动化工具(如 Ansible, Puppet),将这些配置纳入版本管理,以便在新建服务器时快速、一致地部署。
6. 高级策略与自动化监控
基础配置完成后,可以考虑一些增强措施,让安全防护更智能、更省心。
6.1 设置密码过期与强制更换
强密码也不是一劳永逸的。可以通过/etc/login.defs文件设置密码的最大有效期,强制用户定期更换。
sudo vim /etc/login.defs找到并修改以下参数:
PASS_MAX_DAYS 90 # 密码最多使用90天 PASS_MIN_DAYS 7 # 密码修改后,至少7天内不能再次修改 PASS_WARN_AGE 14 # 在密码过期前14天开始警告用户这些设置对新创建的用户立即生效。对于已存在的用户,需要使用chage命令来修改:
# 查看testuser_complex的密码策略 sudo chage -l testuser_complex # 修改其密码最大有效期 sudo chage -M 90 testuser_complex6.2 与系统日志和监控告警集成
安全配置的最后一环是“可观测性”。你需要知道什么时候有人触发了你的防线。
- 集中查看认证日志:
grep和journalctl是你的好朋友。可以定期运行一些命令来检查异常。# 查看过去一小时所有的失败登录尝试 sudo journalctl --since -1h _COMM=sshd | grep -i fail # 查看所有被锁定的账户事件 sudo journalctl -g "pam_faillock.*locked" - 配置日志告警: 使用像
logwatch、fail2ban(虽然fail2ban本身是主动封锁IP的,但它也依赖日志分析)这样的工具,或者将系统日志 (/var/log/auth.log) 发送到中央日志服务器(如 ELK Stack, Graylog),并设置告警规则。例如,可以设置“同一用户在一分钟内失败登录超过3次”就发送邮件或Slack通知给管理员。
6.3 使用Fail2ban进行IP层面的主动防御
pam_faillock锁定的是用户账户,而Fail2ban则可以更进一步,它扫描日志文件(如/var/log/auth.log),当发现某个IP地址在短时间内有多次失败登录尝试时,会自动修改防火墙规则(如 iptables 或 ufw),临时禁止该IP地址访问特定端口(如SSH的22端口)。
这是一种在应用层(PAM)和网络层(防火墙)之间的协同防御。即使攻击者使用不同的用户名进行尝试,只要来自同一个IP,Fail2ban也能将其屏蔽。
安装和配置Fail2ban是另一个话题,但其核心思想是:PAM失败锁定 + Fail2ban IP封锁,构成了从用户维度到IP维度的双重立体防护,能极大地增加攻击者的成本和难度。
7. 常见问题与排查技巧实录
在实际配置和运维中,你肯定会遇到各种“坑”。下面是我总结的一些典型问题及其解决方法。
7.1 配置后所有用户(包括自己)无法登录
症状: 修改/etc/pam.d/common-auth后,退出当前会话,发现无法再登录,即使密码正确。
原因: 几乎可以肯定是在 PAM 配置文件中出现了语法错误或逻辑错误,导致认证栈被破坏。例如,模块顺序错乱、使用了错误的控制标志(required,sufficient等)。
解决方案(紧急):
- 利用云服务器控制台VNC/串口: 这是最可靠的恢复方式。通过云服务商提供的网页控制台登录,你拥有的是物理终端权限,不受PAM配置影响。
- 进入单用户/恢复模式: 如果服务器有物理访问权限,在启动时进入GRUB菜单,选择恢复模式(Recovery Mode),通常会挂载根文件系统并提供一个root shell。
- 从备份恢复: 如果你遵循了建议,修改前备份了原文件(例如
sudo cp /etc/pam.d/common-auth /etc/pam.d/common-auth.backup),在恢复模式的root shell中,将其覆盖回去即可。 - 逐行检查: 在恢复环境中,仔细检查你添加或修改的每一行。特别注意
pam_faillock.so那几行的顺序和参数。一个常见的错误是把authsucc模块放错了位置。
预防:永远在测试环境或虚拟机上先验证配置。修改生产环境的PAM前,先打开另一个活跃的SSH会话(不要退出!),在新会话中测试su到另一个用户或使用ssh localhost来验证认证是否正常。确认无误后再关闭那个“保命”的会话。
7.2 密码策略对某些用户不生效
症状: 为新用户设置的密码策略,对已有的老用户似乎不起作用。
原因: 密码策略在用户修改密码时生效。老用户的密码是在旧策略下设置的,只要不修改,系统不会强制要求其符合新策略。
解决方案:
- 使用
chage命令强制用户在下次登录时修改密码:
这样,用户下次登录时系统会强制要求其设置新密码,此时新密码就必须符合当前的复杂度策略了。sudo chage -d 0 username - 编写脚本批量处理现有用户,要求他们定期更新密码。
7.3 失败锁定对sudo无效或行为异常
症状: 用户登录失败会被锁定,但连续输错sudo密码却不会。
原因:/etc/pam.d/sudo配置文件默认可能没有包含pam_faillock模块。sudo的认证是独立的PAM栈。
解决方案: 根据你的安全策略决定是否需要统一。如果需要让sudo失败也计入锁定,可以参照common-auth的配置方式,在/etc/pam.d/sudo文件中相应位置添加pam_faillock模块。但务必谨慎,因为锁定了sudo权限可能导致用户无法进行必要的管理操作。
7.4 系统重启后失败计数被清零
症状: 配置了pam_faillock,用户被锁定。但服务器重启后,该用户又可以登录了。
原因:pam_faillock默认将失败计数存储在/var/run/faillock/目录下。这个目录是tmpfs(内存文件系统),重启后数据自然丢失。
解决方案: 如果你需要持久化的失败计数(即重启后锁定状态依然保持),需要修改配置,将计数存储到磁盘上。
- 创建一个持久化目录,例如
/var/lib/faillock:sudo mkdir -p /var/lib/faillock - 在
/etc/security/faillock.conf中或 PAM 模块参数中指定dir选项:
或者在dir = /var/lib/faillock/etc/pam.d/common-auth的pam_faillock.so行中添加dir=/var/lib/faillock参数。
7.5 如何为不同用户组设置不同的密码策略?
这是一个更进阶的需求。PAM 本身可以通过pam_succeed_if等模块实现条件判断,但配置较为复杂。一个更清晰的方法是结合使用libpam-pwquality的另一个特性:通过/etc/security/pwquality.conf.d/目录进行覆盖配置。
你可以为特定用户或组创建单独的配置文件。例如,为admin组设置更严格的策略:
- 创建文件
/etc/security/pwquality.conf.d/admin.conf。 - 在其中写入更严格的配置,如
minlen=16,minclass=4。 - 然而,标准的
pam_pwquality模块并不直接支持按用户/组读取不同配置文件。要实现这一点,通常需要更复杂的 PAM 栈编排,或者考虑使用像libpam-sss配合外部身份管理系统(如 LDAP)来实现中心化的、细粒度的密码策略。对于大多数单机或小规模场景,统一的严格策略通常是更简单有效的选择。