1. 项目概述:为什么passwd命令远不够用?
如果你管理过几台Linux服务器,尤其是那些暴露在公网上的,你大概率遇到过这样的场景:新来的同事或者用户,随手设置了一个“123456”或者“password”作为密码,然后你心里一紧,赶紧跑过去让他改掉。或者,你发现某个服务账户的密码已经用了好几年,从来没换过。这些看似不起眼的小事,在安全领域都是实实在在的风险点。很多人,包括一些有经验的运维,在设置密码时,第一反应就是打开终端,输入passwd命令,然后敲入两遍新密码,完事。这确实能改密码,但它就像一把没有锁芯的锁——只能关门,却无法定义什么样的钥匙(密码)是合格的。
passwd命令本身只是一个修改密码的接口,它背后真正的“裁判”是PAM。PAM,全称 Pluggable Authentication Modules,即可插拔认证模块。它是Linux系统身份验证的基石,像一个高度可定制的安检系统。当你输入密码时,passwd只是把密码交给了PAM,PAM则会调用一系列预先配置好的“安检规则”(模块),来检查这个密码是否符合要求,比如长度够不够、字符种类多不多、是不是在常见弱密码字典里等等。如果我们不主动配置这些规则,PAM就会使用默认的、非常宽松的策略,这也就是为什么只用passwd无法实现强密码策略的根本原因。
所以,这个项目的核心,就是绕过passwd这个简单的“前台”,直接深入到PAM这个“后台管理系统”,去定制一套属于我们自己服务器的、严格的密码安检规则。我们将在CentOS 7和CentOS 8系统上实战,目标非常明确:分别对普通自建用户和特权root用户,强制执行一套企业级密码策略,包括最小密码长度8位、必须包含大小写字母/数字/特殊符号中的至少4种字符类型、并且同一类字符(比如数字)不能连续出现超过2个。这不仅仅是敲几个命令,更是理解Linux系统安全底层逻辑的一次深度实践。
2. 核心思路与PAM模块选型解析
在动手之前,我们必须搞清楚PAM是如何工作的,以及有哪些“武器”(模块)可供我们调遣。PAM的配置文件通常位于/etc/pam.d/目录下,每个需要认证的服务(如passwd,login,sshd)都有一个对应的配置文件。当passwd命令被执行时,系统就会去读取/etc/pam.d/passwd这个文件里的规则。
PAM的规则由一行行的“模块调用”组成,每一行定义了一个检查步骤。其通用格式是:
模块类型 控制标志 模块路径 模块参数- 模块类型:定义这个模块用来做什么,比如
auth(认证)、account(账户管理)、password(修改密码)、session(会话管理)。我们配置密码策略,主要关注password类型。 - 控制标志:决定这个模块的成功或失败对整体认证结果的影响,常见的有
required(必须成功,但失败后仍会继续执行后续模块)、requisite(必须成功,一旦失败立即终止并返回失败)、sufficient(如果成功,则跳过后续同类型模块)、optional(可选,结果通常不影响整体)。 - 模块路径:模块文件在系统中的位置,通常是
/lib64/security/或/usr/lib64/security/下的.so文件。 - 模块参数:传递给模块的具体选项,这是我们配置策略的关键。
为了实现我们的密码策略目标,我们需要组合使用以下几个核心的PAM模块:
- pam_pwquality.so (或旧版的pam_cracklib.so):这是我们的主力部队,负责密码复杂度检查。在CentOS 7及以后的版本中,
pam_cracklib逐渐被功能更强大的pam_pwquality取代。它能检查密码长度、字符类型、重复字符、常见字典词等,几乎涵盖了我们对密码复杂度的所有要求。 - pam_unix.so:这是后勤保障部队,负责实际的密码哈希计算、更新
/etc/shadow文件等底层操作。pwquality模块检查通过后,才会由pam_unix来执行最终的密码修改动作。 - pam_pwhistory.so:这是可选的历史档案员,用于防止用户重复使用最近用过的旧密码。对于要求定期更换密码且不能与近期历史密码重复的场景非常有用。
我们的策略设计思路是:在/etc/pam.d/passwd配置文件中,让pam_pwquality模块作为password类型的第一道关卡,对用户输入的新密码进行严格审查。只有通过了它的所有规则,流程才会继续交给pam_unix模块去实际更新密码。通过这种“先审查,后执行”的管道式设计,我们就能牢牢把控密码的质量。
注意:在CentOS 8/RHEL 8及更新版本中,
pam_pwquality的配置方式与CentOS 7略有不同,它更倾向于使用一个独立的配置文件/etc/security/pwquality.conf,而不是将大量参数直接写在PAM配置行里。这是本次实战中需要特别注意的版本差异点。
3. 实战环境准备与关键配置文件解读
工欲善其事,必先利其器。在开始修改配置之前,我们需要确认环境,并理解几个关键文件的作用,避免误操作导致系统登录故障(这可是个“坑”点)。
3.1 系统版本确认与模块安装
首先,通过cat /etc/redhat-release命令确认你的系统是CentOS 7还是CentOS 8。这个区别很重要。 对于CentOS 7,pam_pwquality模块通常默认已安装。如果没有,可以通过yum install libpwquality来安装。 对于CentOS 8,同样使用dnf install libpwquality确保其存在。
3.2 核心配置文件解读
/etc/pam.d/passwd:这是我们主要的操作对象。它控制着passwd命令的认证流程。在修改前,务必先备份:cp /etc/pam.d/passwd /etc/pam.d/passwd.bak。这是你的“后悔药”。/etc/pam.d/system-auth和/etc/pam.d/password-auth:这两个是全局性的PAM配置文件。很多其他服务(如login,sshd,su)会通过include指令引用它们。这意味着,如果你在这两个文件里配置了密码策略,它将影响几乎所有通过PAM进行密码认证的场景。对于新手,我强烈建议先从/etc/pam.d/passwd这个单一入口开始实验,成功后再考虑是否应用到全局。直接修改全局文件,一旦配置错误,可能导致所有用户(包括root)无法登录,那就只能通过单用户模式或救援模式来挽救了,这是第一个大坑。/etc/security/pwquality.conf(CentOS 8重点):这是CentOS 8/RHEL 8中为pam_pwquality模块提供的独立配置文件。模块会优先读取这里的参数,这使得配置更加清晰和集中。在CentOS 7中,这个文件也可能存在,但PAM配置行中的参数会覆盖它。
3.3 安全操作准则
- 保持一个活跃的root会话:在修改PAM配置的整个过程中,确保当前已经通过SSH或控制台登录了一个root权限的会话,并且不要退出。如果新配置导致密码修改失败,你还可以在这个会话里回滚配置。
- 开两个终端窗口:一个用于编辑配置文件(终端A),另一个用于测试
passwd命令(终端B)。在终端A保存配置后,立即在终端B用普通用户测试,而不要用root测试。因为root用户有时会绕过某些策略,用普通用户测试更能反映真实效果。 - 理解“回退”方法:如果配置错误导致
passwd命令完全无法使用,你的救生索就是之前备份的passwd.bak文件,或者那个未关闭的root会话。记住命令:cp /etc/pam.d/passwd.bak /etc/pam.d/passwd。
4. CentOS 7 详细配置步骤与参数详解
现在,我们进入CentOS 7的实战环节。我们的目标是编辑/etc/pam.d/passwd文件,在其中插入pam_pwquality的检查规则。
4.1 编辑PAM配置文件
使用你熟悉的编辑器,比如vi或nano,打开配置文件:
vi /etc/pam.d/passwd你会看到类似如下的默认内容(不同版本可能略有差异):
#%PAM-1.0 auth include system-auth account include system-auth password substack system-auth -password optional pam_gnome_keyring.so use_authtok我们需要在password类型的区域,具体是在substack system-auth这一行之前,添加我们的pam_pwquality模块行。因为PAM按顺序执行,我们需要先做复杂度检查,再做密码更新。
修改后的password部分应该像这样:
#%PAM-1.0 auth include system-auth account include system-auth password requisite pam_pwquality.so try_first_pass local_users_only retry=3 minlen=8 minclass=4 maxrepeat=2 enforce_for_root password substack system-auth -password optional pam_gnome_keyring.so use_authtok关键参数逐行解析:
requisite:控制标志。这里使用requisite是因为我们希望密码复杂度检查一旦失败,就立即终止整个密码修改过程,并向用户报错。这比required更严格,能提供更即时的反馈。pam_pwquality.so:调用的模块。try_first_pass:这个参数非常有用。它告诉模块先尝试使用之前(由其他模块)已经输入过的密码,避免向用户重复索要密码。在passwd命令流程中,这通常就是用户输入的新密码。local_users_only:只对本地用户(/etc/passwd中的用户)强制执行此策略。这可以避免对LDAP、NIS等网络账户源产生影响。retry=3:允许用户重试的次数。如果密码不符合策略,用户有3次机会重新输入。minlen=8:最小密码长度,这里设置为8。注意,这个长度可能受到系统默认值的微调,但设置8会确保底线是8。minclass=4:最小字符类别数。这是实现“4种字符类型”要求的关键参数。pam_pwquality将字符分为4类:大写字母(ucredit)、小写字母(lcredit)、数字(dcredit)、特殊符号(ocredit)。minclass=4意味着密码必须同时包含这全部4类字符。maxrepeat=2:同一字符连续出现的最大次数。设置为2,意味着像 “aaa”、“111” 这样的连续3个相同字符是不允许的,但 “aa”、“11” 是允许的。这有效防止了过于简单的重复模式。enforce_for_root:至关重要的参数。默认情况下,密码策略对root用户是豁免的。加上这个参数,将强制root用户也必须遵守同样的复杂度规则。从安全角度,这非常必要。
4.2 补充配置:/etc/security/pwquality.conf
虽然我们在PAM行里指定了参数,但也可以同时配置/etc/security/pwquality.conf文件来设置默认值。编辑这个文件:
vi /etc/security/pwquality.conf找到并修改或添加以下行:
minlen = 8 minclass = 4 maxrepeat = 2 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1这里dcredit = -1等参数的含义是,要求密码中至少包含1个数字。如果设置为正数(如dcredit = 1),则代表密码中每包含一个数字,密码长度要求可以减1(这是一种鼓励使用数字的权重机制,但不如minclass直接要求全面包含来得严格)。为了清晰匹配“4类字符”的要求,我们使用minclass=4并配合负数的credit参数来强化。
4.3 测试验证
保存所有文件后,打开另一个终端(终端B),切换到一个普通测试用户(不要用root):
su - testuser passwd然后尝试设置各种密码,观察策略是否生效:
- 尝试
123456-> 应失败,提示长度不足或字符类别不足。 - 尝试
abcdefgh-> 应失败,提示字符类别不足(只有小写字母)。 - 尝试
Abc123!-> 应失败,提示长度不足(7位)。 - 尝试
Abc123!!->可能成功也可能失败。虽然长度8,有大小写、数字、特殊符号4类,但特殊符号!!连续出现了2个以上?不,maxrepeat=2指的是同一类字符的连续出现。!和!是同类(特殊符号),连续2个是允许的(等于2)。所以这个密码应该符合规则。 - 尝试
AAbc123!-> 应失败,因为大写字母A连续出现了2次以上(AAA不允许,但AA是允许的,这里AA连续2次,是允许的边界)。等等,这个例子中AA是连续2个大写字母,并未超过maxrepeat=2,所以应该通过。一个更好的失败例子是AAAbc123!,这里A连续3次,会触发maxrepeat错误。 - 尝试
Root123@-> 成功(如果当前用户是testuser)。 - 在终端A的root会话中,尝试为root自己修改密码:
passwd,然后输入一个简单密码如12345678。由于我们设置了enforce_for_root,这个操作也必须失败。这是检验策略是否真正应用到root的关键测试。
5. CentOS 8 / RHEL 8 配置差异与实战
CentOS 8 在PAM配置上更推崇使用独立的pwquality.conf文件,使得PAM配置文件本身更加简洁。但理解其工作原理同样重要。
5.1 配置文件调整
首先,查看/etc/pam.d/passwd,你会发现它可能已经包含了pam_pwquality,但参数可能很少:
password requisite pam_pwquality.so try_first_pass local_users_only或者,它可能通过substack system-auth引用了全局配置。无论哪种情况,我们配置的主战场变成了/etc/security/pwquality.conf。
编辑这个文件:
vi /etc/security/pwquality.conf你需要取消相关参数的注释并修改值。一个满足我们要求的配置示例如下:
# 最小长度 minlen = 8 # 最少字符类别(数字、大写、小写、其他) minclass = 4 # 相同字符最大连续次数 maxrepeat = 2 # 至少包含1个数字 dcredit = -1 # 至少包含1个大写字母 ucredit = -1 # 至少包含1个小写字母 lcredit = -1 # 至少包含1个特殊符号 ocredit = -1 # 拒绝包含用户名(正向或反向) usercheck = 1 # 检查是否基于旧密码简单变形 difok = 5 # 启用root用户策略 enforce_for_root = 1重点参数补充说明:
usercheck = 1:这是一个很好的安全实践,它会拒绝密码中包含用户名(正序或逆序)的情况。例如,用户名为john,那么密码john123!或nhoj!@#都会被拒绝。difok = 5:要求新密码与旧密码至少有5个字符不同。这防止了用户只做微小修改(如Password01改为Password02)来应付密码更换策略。
5.2 PAM配置行的确认
确保/etc/pam.d/passwd中包含了pam_pwquality.so模块行,并且没有在行内重复定义minlen、minclass等参数(除非你想覆盖pwquality.conf的设置)。通常,保持一行简单的requisite pam_pwquality.so引用即可,所有复杂策略都在pwquality.conf中定义。
5.3 测试验证(同CentOS 7)
测试方法与CentOS 7完全一致。使用普通用户和root用户进行多轮测试,确保策略在两种系统上都按预期工作。特别注意enforce_for_root在CentOS 8的pwquality.conf中配置是否生效。
6. 针对不同用户实施差异化策略
我们的初始需求是“分别设置自建用户和root用户”。上面的配置通过enforce_for_root实现了对root的同等强度要求。但如果想实现差异化策略(比如对root要求更严,或者对某些管理用户放宽),该怎么办?PAM本身可以通过pam_succeed_if等模块进行条件判断,但配置起来较为复杂且容易出错。
一个更清晰、更易维护的方案是:不修改全局PAM规则,而是通过配置pwquality.conf的不同包含路径,或者为特定用户/组设置Linux用户密码过期策略来间接实现。
6.1 使用pam_pwquality的local_users_only和用户组判断
pam_pwquality模块的local_users_only参数已经帮我们区分了本地用户和网络用户。但对于本地用户内部的区分,PAM行内参数很难动态变化。一个取巧但不完美的方法是:如果你有两组本地用户需要不同策略,可以创建两个不同的PAM配置文件(如passwd-strict和passwd-normal),里面引用不同参数的pam_pwquality行。然后通过修改用户的shell或者创建自定义命令别名的方式,让不同用户组使用不同的命令来改密。但这破坏了passwd命令的统一性,不推荐在生产环境大规模使用。
6.2 结合chage命令进行账户策略差异化
对于“差异化”,更常见的需求可能不是复杂度,而是密码有效期、过期警告期、失效宽限期等。这些可以通过chage命令对每个用户进行精细控制。例如:
chage -M 90 user1:设置user1密码90天后过期。chage -M 0 root:设置root密码永不过期(-M 0)。请注意,让root密码永不过期是一个有争议的安全实践,需谨慎评估。chage -W 7 user1:在密码过期前7天开始警告user1。chage -l user1:列出user1的所有账户老化信息。
6.3 实现思路总结
因此,对于“分别设置”的需求,最务实且安全的做法是:
- 密码复杂度策略统一且严格:使用上述的PAM配置,对所有用户(包括root)实施统一的、高强度的基础密码复杂度要求(长度、字符种类、连续性)。这是安全的底线,不应妥协。
- 密码生命周期策略差异化:使用
chage命令,针对不同的用户或用户组,设置不同的密码最大有效期(-M)、最小修改间隔(-m)等。例如,给普通用户设置90天强制改密,给服务账户设置更长的有效期或永不过期(需结合其他监控手段)。 - 对于特权用户(如root):除了同样遵守复杂度策略,更应该强调使用SSH密钥登录、禁用密码登录、配置sudo日志审计等更高级别的安全措施,而不是仅仅在密码生命周期上做区别。
7. 常见问题排查与实战心得
在实际配置和运维中,你肯定会遇到各种“坑”。下面是我总结的一些典型问题及解决方法。
7.1 问题:配置后passwd命令报错 “Unknown module ‘pam_pwquality.so’ 或 ‘pam_cracklib.so’”
- 原因:模块未安装,或者模块路径不正确。
- 排查:
- 确认模块是否安装:
rpm -qa | grep -E 'libpwquality|cracklib'。 - 查找模块确切路径:
find /usr/lib* /lib* -name \"pam_pwquality.so\" 2>/dev/null。通常路径是/usr/lib64/security/pam_pwquality.so。 - 在PAM配置行中使用完整的绝对路径,例如:
password requisite /usr/lib64/security/pam_pwquality.so try_first_pass ...。
- 确认模块是否安装:
7.2 问题:密码明明符合要求,但系统一直提示“BAD PASSWORD”
- 原因:
- 字典检查:
pam_pwquality默认会检查密码是否基于常见字典单词。像Password123!这种,即使满足长度和字符类要求,也可能因为包含Password这个字典词而被拒绝。 difok参数:新密码与旧密码相似度太高。usercheck参数:新密码包含了用户名。
- 字典检查:
- 排查:
- 可以临时在PAM参数中或
pwquality.conf中加入dictcheck=0来禁用字典检查(仅用于测试,生产环境慎用),看是否通过。 - 检查
pwquality.conf中的difok和usercheck设置。 - 查看系统日志获取更详细错误信息:
tail -f /var/log/secure。当密码修改失败时,这里通常会有来自PAM模块的、比命令行更详细的拒绝原因。
- 可以临时在PAM参数中或
7.3 问题:root用户修改密码似乎不受策略限制
- 原因:没有在配置中添加
enforce_for_root参数。 - 解决:确保在
pam_pwquality模块的参数末尾加上了enforce_for_root。在CentOS 8的pwquality.conf中,也要单独设置enforce_for_root = 1。
7.4 问题:修改PAM配置后,所有用户都无法登录了(最严重的情况)
- 预防:这就是为什么强调要在活跃的root会话中操作,并且先备份,先测试
passwd,而不是直接测试登录。 - 恢复:
- 如果你还有一个有效的root会话,直接还原备份文件:
cp /etc/pam.d/passwd.bak /etc/pam.d/passwd。 - 如果已经无法登录,需要重启服务器,在GRUB引导时,编辑内核启动参数,在
linux行末尾添加single或init=/bin/bash进入单用户模式或bash环境,然后挂载文件系统并还原配置。这是最后的手段,操作需谨慎。
- 如果你还有一个有效的root会话,直接还原备份文件:
7.5 实操心得与建议
- 测试,测试,再测试:每修改一次PAM配置,立即用一个非特权、非关键的测试用户进行
passwd测试。验证符合要求的密码能成功,不符合要求的密码被明确拒绝。 - 使用
pwscore和pwmake工具:libpwquality包提供了两个有用的命令行工具。echo 'YourPassword' | pwscore可以给密码打分并指出问题。pwmake 64可以生成一个符合当前策略的随机密码(数字64代表熵的位数,数字越大密码越复杂)。它们是你制定策略和培训用户时的好帮手。 - 策略宣导:在实施严格的密码策略前,最好能通知用户,并给出符合要求的密码示例(例如:
MyDog@2024!),甚至可以提供一个简单的密码生成方法。这能减少支持压力。 - 记录与审计:将最终的、测试通过的PAM配置和
pwquality.conf配置纳入你的配置管理(如Ansible Playbook, Salt State),并记录变更。定期使用chage -l <username>检查关键账户的密码状态。 - 密码策略只是第一道防线:不要迷信密码复杂度。启用SSH密钥认证、配置Fail2ban防止暴力破解、定期更新系统、实施最小权限原则,这些共同构成了服务器安全的纵深防御体系。