1. 先把两个概念掰开账户过期和密码过期不能混为一谈很多刚接触 Linux 用户管理的朋友第一次看到chage的输出或者/etc/shadow里的字段时都会有点懵明明我只想查一下密码什么时候过期怎么还冒出来一个“账户过期”其实这是两个完全不同的机制。我之前在帮客户做服务器基线检查时就发现不少人把这两者搞混结果要么该锁的没锁要么把正常用户的账户给弄锁了折腾半天才恢复。账户过期Account Expiry可以理解成大门的闸机。到了指定日期闸机直接关闭这个用户无论密码对不对、有没有效都进不了系统。也就是说账户过期是“门禁级别”的封禁比密码过期更彻底。密码过期Password Aging则是“定期换锁”的机制。它管的是密码本身的有效期密码用了多久、什么时候必须改、过期之后还能不能登录、宽限几天等。密码过期之后用户依然可以登录但系统会强制要求先改密码改完才能继续干活——除非超过宽限期inactive也没有改账户才会被锁定。搞清楚了这两层再往下看就顺了。在 Linux 里这两个状态都记录在同一个文件里/etc/shadow。我直接说结论shadow 文件第 7 个字段记录账户过期时间第 3 到第 6 个字段则负责密码老化策略。很多教程会直接教你看 shadow但说句实话日常运维我更推荐用chage命令输出可读性高得多也不容易数错字段。注意/etc/shadow是 root 权限才能读的文件普通用户只能通过chage -l看自己的一部分信息。这也是为什么我建议先用chage的原因——它封装好了底层细节。2. 查看过期时间chage -l 是首选但看懂输出比敲命令更重要2.1 chage -l 输出逐行解读查看某个用户的密码和账户过期情况最直接的就是chage -l username比如我随机挑一台服务器上的一个普通用户看[rootserver01 ~]# chage -l zhangsan Last password change : Jul 24, 2024 Password expires : Oct 22, 2024 Password inactive : Nov 21, 2024 Account expires : never Minimum number of days between password change : 7 Maximum number of days between password change : 90 Number of days of warning before password expires : 7逐行翻译一下Last password change上次改密码的日期。这是所有后续计算的起点。Password expires密码到期日。算法是“上次改密码日期 最大有效期90天”算出来就是 2024-07-24 90 天 2024-10-22。Password inactive密码失效日。这个日期 密码到期日 宽限天数。上面的例子中10月22日到期后还有 30 天宽限期到了 11月21日密码彻底失效账户会出现锁定状态这时甚至没法用旧密码登录去改密码。Account expires账户过期日。如果显示 never说明账户本身不会被自动禁用。Minimum number of days between password change两次改密码之间的最小间隔。设置这个参数主要是防止用户改了密码之后马上又改回原来的。Maximum number of days between password change密码最长使用天数。Number of days of warning before password expires密码到期前几天开始警告用户。这里我要多说一句Password inactive和Account expires表达的是不同的东西inactive 是密码进入不可用状态的日子而 account expires 是账户整体的死期。很多人看到 Locked 就以为是账户过期其实经常是密码 inactive 触发的锁定。2.2 其他查看方式passwd -S 与 shadow 字段速查除了chage -l还有一些场合我会用到别的命令。passwd -S username可以快速看密码状态[rootserver01 ~]# passwd -S zhangsan zhangsan PS 2024-07-24 90 7 30 7这串字母和数字的含义从左到右分别是用户名、密码状态PS 表示可用LK 表示锁定NP 表示无密码、上次改密码日期、最大有效期、两次修改最小间隔、宽限天数、到期前警告天数。这个输出信息量很密集适合脚本里去提取字段。如果想看底层原始数据直接 grep shadow[rootserver01 ~]# grep zhangsan /etc/shadow zhangsan:$6$xxxxx$yyyyy:19728:7:90:30:7::这里第 3 个字段19728是上次改密码日期距离 1970-01-01 的天数第 4、5、6、7 个字段分别对应最小间隔、最大有效期、宽限天数、警告天数第 8 个字段是账户过期日距 1970-01-01 的天数空值表示永不过期。我的建议是日常排查用 chage -l脚本采集用 passwd -S 或直接读 shadow别反过来用。因为 chage 的输出需要逐行解析而另外两种格式更适合批量处理。2.3 从一次真实输出反推系统策略我把实际排查中的一个案例简化一下演示怎么通过查看结果判断这台机器的安全基线。某台测试服务器的策略如下[rootserver02 ~]# chage -l devuser Last password change : Jun 01, 2024 Password expires : Aug 30, 2024 Password inactive : never Account expires : never Minimum number of days between password change : 0 Maximum number of days between password change : 90 Number of days of warning before password expires : 14从输出可以反推这套系统的设置密码最长使用 90 天到期前 14 天开始提醒没有宽限期账户不自动过期。这对应的是/etc/login.defs里的PASS_MAX_DAYS 90、PASS_WARN_AGE 14这些默认值。这种“通过单个用户反推全局默认值”的思路很实用。因为/etc/login.defs只影响新建用户老用户的策略可能还是按创建时的旧规则算的。想确认一个用户当前实际生效的策略最靠谱的就是chage -l而不是看全局配置文件。这是我踩过好几次坑之后养成的习惯。3. 修改过期时间从安全策略到具体命令的完整链路3.1 chage 核心参数速查chage命令既可以查看也可以修改修改时常用参数我整理了一张表参数作用示例-M设置密码最大有效天数chage -M 90 zhangsan-m设置两次改密码最小间隔天数chage -m 7 zhangsan-W设置到期前警告天数chage -W 7 zhangsan-I设置密码失效宽限天数chage -I 10 zhangsan-E设置账户过期日期格式 YYYY-MM-DDchage -E 2025-12-31 zhangsan-d手动指定上次改密码日期影响所有后续计算chage -d 2024-01-01 zhangsan-l查看策略前面的内容chage -l zhangsan这里要注意一个细节-d这个参数很容易被忽略但它其实非常有用。比如你新创建一个用户默认上次改密码时间是创建当天。如果希望这个用户 30 天后必须改密码又不愿意让他从今天开始算你可以显式指定一个过去或未来的日期来调整整个老化周期。3.2 场景一把某用户密码设为永不过期很多内部工具账号、自动化脚本用的服务账号密码不该被强制轮换不然运维脚本会突然断掉。把密码设为永不过期的标准操作是chage -M 99999 -d 0 username chage -l username第一行命令里-M 99999把最大有效期调成了一个极大的值但实际上光改这个还不够彻底。更多时候更直接的做法是用chage -E -1 username-E -1的意思是把账户过期时间设为“never”。但注意这个只针对账户过期对密码老化没有影响。业界还有一个常见习惯就是修改/etc/login.defs里的PASS_MAX_DAYS默认值但这只影响新建用户对存量账户无效。所以对单个账户处理直接用chage改别指望改全局配置能立刻生效。在大多数比较新的发行版中其实还有一条更省事的命令passwd -x -1 username passwd -x -1 是“设置密码最大使用天数为 -1”也就是永不过期有些老系统不支持passwd -x -1会报错所以我个人的习惯还是优先chage -M 99999兼容性最好。3.3 场景二新员工账号强制三个月改一次密码新员工入职通常会建一个临时密码然后要求首次登录必须修改之后每 90 天换一次。我一般这样配置chage -d 0 zhangsan chage -m 7 -M 90 -W 14 -I 30 zhangsan第一条命令-d 0的含义是“上次改密码时间设为 epoch1970-01-01”这样用户第一次登录时系统会判断密码已经过期强制要求立即修改。这是实现“首次登录强制改密码”的标准手法。后面一条设置了最小间隔 7 天、最大有效期 90 天、提前 14 天警告、宽限期 30 天。也就是说用户改了密码之后至少 7 天内不能再次修改第 90 天到期到期前两周会看到警告提示如果到期后 30 天内还没改密码失效账户进入锁定状态。这里值得多说一句“宽限期”的设计。30 天的宽限期看起来很长但在实际情况中很多企业账号用的就是这种策略到期后还有一个月缓冲期既给了员工足够的时间又不至于无限期拖下去。如果你想更严格一点可以把-I改成 0到期当天不改就锁定。3.4 场景三给临时账号设置硬性到期日内网里有供应商或外包人员时通常要设置一个明确的账户到期时间防止人走了账号还在。操作起来非常简单chage -E 2025-06-30 wangwu这个命令执行后wangwu这个账户会在 2025 年 6 月 30 日当天自动过期。你可能会问这样设置真的靠谱吗会不会到期了还留着答案是账户过期之后这个账号确实无法登录了但它在/etc/passwd里的记录还在家目录也还在。它只是“锁门”不是“拆房子”。如果你需要更彻底的处理还是要在过期之后手动删除或归档这个用户。临时账户的场景里我还习惯同时处理密码策略chage -E 2025-06-30 -M 30 -W 7 wangwu这样既控制了账户总寿命也控制了一次性密码的轮换频率算是双重保险。3.5 修改之后如何立即验证所有修改结束后不要急着结束务必复查chage -l wangwu输出确认Account expires确实是Jun 30, 2025Maximum number of days between password change是 30就基本靠谱了。再进一步验证可以用passwd -S wangwu看看状态是PS然后退出重登一下确认账户可以正常登录、没有因为策略冲突被锁定。4. 批量场景下的几个实用技巧脚本、模板与合规这里要插一句实际工程里单人操作不常有批量处理才是常态。比如你接手了一套历史遗留系统里面有几百个账号密码过期策略混乱你总不可能一个个敲chage。下面分享三个我常用的批量思路。4.1 使用模板用户统一新账户策略每次新建用户都手动设置十来个参数太容易出错了也容易被遗忘。我的做法是维护一个“模板用户”比如template_user把所有标准策略都配在它上面chage -m 7 -M 90 -W 14 -I 30 -E -1 template_user之后真正新建用户时直接复制模板的策略useradd -m -s /bin/bash newuser chage -d 0 -m 7 -M 90 -W 14 -I 30 -E -1 newuser这样既保证了策略的一致性也避免了手工输入错误。另一种更高级的做法是把策略写进/etc/default/useradd的EXPIRE字段这样每次 useradd 都会带上默认过期日。但注意这只是账户过期密码老化策略还是要靠/etc/login.defs或chage。4.2 批量查看所有用户的过期状态写一个简单的循环把所有普通用户的密码到期情况扫一遍直接输出到表格for user in $(awk -F: $31000 $365534 {print $1} /etc/passwd); do echo $user chage -l $user | grep -E Password expires|Password inactive|Account expires|Last password change done这个命令会遍历 UID 从 1000 到 65533 的用户也就是正常登录用户排除系统账户逐个输出关键日期。如果你需要更清晰的报告可以把它重定向输出到文件再整理for user in $(awk -F: $31000 $365534 {print $1} /etc/passwd); do exp$(chage -l $user | awk /Password expires/{print $NF}) inac$(chage -l $user | awk /Password inactive/{print $NF}) acc$(chage -l $user | awk /Account expires/{print $NF}) printf %-20s %-15s %-15s %-15s $user $exp $inac $acc done /tmp/user_expiry_report.txt这样生成一份“哪些用户快过期了”的清单配合 cron 定时跑就是最简单的过期预警系统。4.3 密码过期前的提醒通知说到预警有个比较省力的思路利用 Linux 自带的pam_lastlog或pam_pwhistory相关能力在登录时提示用户密码即将到期同时配合监控脚本发通知给管理员。我现在比较常用的做法是每天凌晨用 cron 跑一个脚本筛选出 7 天内密码将要到期的用户把名单发到运维群里#!/bin/bash # 每天检查密码即将到期的用户并输出告警 expiring$(awk -F: $31000 $365534 {print $1} /etc/passwd) for user in $expiring; do # 计算密码距离到期还有多少天 last$(chage -l $user | awk /Password expires/{print $NF}) # 用 date 转成时间戳简单做减法 ... done具体脚本逻辑可以根据自己的告警渠道来写。核心思路是提前感知而不是等用户被锁定之后才收到工单。这里我必须提醒一句不要过度依赖外部工具来做密码过期提醒。新版本的 Linux 发行版在用户登录时如果密码即将过期会直接显示类似“Warning: your password will expire in 7 days”的提示这是pam模块自带的职责。你在做自己的告警脚本时要注意不要把通知重复发太多否则用户会产生告警疲劳。5. 这些坑我替你踩过了权限、锁定、版本差异与恢复方法5.1 chage 对某些系统账户不起作用对 root 本身执行chage -l root是可以的但如果你尝试修改某些系统账户比如bin、daemon这些就会发现部分策略根本不适用。原因很简单这些账户在/etc/shadow里通常没有有效密码字段密码老化机制不会生效。遇到这类情况别纠结看一眼 shadow 里对应行如果显示*或者!说明这个账户本来就不能登录密码过期策略对它没有意义。5.2 把账户改过期之后如何紧急恢复这是我在一次压测环境里亲手搞出来的问题给一个测试账号设置了-E 2024-01-01结果忘了第二年是闰年账户提前锁定导致自动化任务全部失败工单直接爆炸。恢复方法非常简单chage -E -1 testuser-E -1是解除账户过期最常见的方式。执行后立即可登录。但这个场景下有个更隐蔽的坑如果账户是密码达到 inactive 而被锁定的你光-E -1没用必须把密码重新激活usermod -U testuser或者更简单粗暴点重置密码passwd testuser这就说明了一个事情看到“账户锁定”先别急着恢复先把锁定的原因搞清楚。我一般按这样的顺序排查先chage -l username看账户过期时间是不是已经过了。再看Password inactive是不是已经过了。再passwd -S username看密码状态是不是LK。根据原因分别用chage -E -1或者usermod -U处理。5.3 不同发行版/厂商工具链的差异可能有的朋友用的是 RHEL 系、openEuler、Ubuntu 这些不同发行版实际命令上大同小异但有几个差异点值得注意在 Ubuntu/Debian 系上chage默认就装好但某些精简版系统可能没有需要apt install passwd或apt install shadow-utils。在 RHEL 9 或 openEuler 上安全加固策略有时会直接改/etc/shadow的权限和字段格式某些字段为!!或!时chage -S会输出奇怪的字符串。国产系统如统信 UOS、麒麟对账户策略的默认值可能不同甚至部分系统还带了 PAM 模块强制覆盖/etc/login.defs中的配置。如果你在国产化环境里做事改了 chage 不生效先去检查/etc/pam.d/system-auth或/etc/security/pwquality.conf里的配置大概率是被 PAM 策略盖住了。5.4 记录变更日志的好习惯读到这你可能会觉得这个主题太基础了还有什么可写的但我真心建议凡是涉及用户过期、密码策略变更的操作都要保持记录。我见过太多线上事故最后一查就是有人在晚上手滑把某账号的过期时间改了。你没法阻止别人操作失误但你至少要能在事后快速了解到是什么时候、谁改了哪个字段。我自己的习惯是所有涉及chage的变更都会写入后台审计日志或者至少操作完成后随手执行chage -l username /var/log/user_account_$(date %Y%m%d_%H%M%S).log\textbf{反正记录这件事平时看着没用真出事的时候这就是救命稻草。}6. 几个常用参数组合的参考速查最后把几个常见需求的组合命令整理在这里方便直接抄需求命令组合查看某用户全部策略chage -l username密码永不过期chage -M 99999 -d 0 username账户永不过期chage -E -1 username首次登录必须改密码chage -d 0 username90 天改一次7 天前提醒30 天宽限chage -m 7 -M 90 -W 14 -I 30 username设置账户到期日chage -E 2025-12-31 username立即锁定账户usermod -L username解锁账户usermod -U username我自己在实际运维中最常用的组合基本就上面这几条。特别提醒一下不要动不动就usermod -L锁账户这个命令会导致用户无法登录但不会修改密码本身的过期策略下次排查的时候容易造成误解。如果你在写自动化脚本建议把所有变更操作都加上-l复查形成一个“变更后必验证”的闭环。另外密码策略不是设置一次就完事的随着业务变化、安全要求提高这些策略迟早要调整。定期用前面那个批量查询脚本扫一遍全部用户把它当成月度的例行巡检比等到用户被锁、工单来袭时要从容得多。