我有一个很深的体会Linux 下那些看似不起眼的“账号小配置”平时没人关心真出问题的时候往往能把人折腾到半夜。密码过期时间就是典型例子。某次我被拉去处理一个线上故障定时任务连续几天没跑检查 crontab 日志全是权限报错最后才发现是执行任务的账号密码早就过期服务彻底丧失了登录认证能力。另一次更离谱给新员工建完账号第二天对方说登不进服务器我登上去一看临时密码确实改过了但邮箱里堆满系统发来的密码过期提醒——因为创建账号时没有把密码策略调整到“首次登录必须修改”的状态新旧策略叠加到一起账号直接在第一次认证时就被卡住。在 Linux 环境里查看和修改用户密码过期时间说到底就是围绕chage、passwd这两个命令外加一个很关键的账号文件/etc/shadow来操作。只要把这三个东西的原理和用法吃透不管是要临时看某个账号还能不能用还是要批量给几百个账号统一设置密码策略都能快速搞定。这篇文章我按照自己的实操习惯从底层逻辑、查看方法、修改参数、生产环境落地再到故障排查完整梳理一遍希望能让你少踩几个坑。1. 搞清楚密码过期时间的底层逻辑1.1 /etc/shadow 才是真正的“账本”很多刚开始接触 Linux 的同学会默认密码信息在/etc/passwd里这个方向其实只对了一半。/etc/passwd里保存的是用户的基础信息比如用户名、UID、主目录、登录 Shell而真正存储密码摘要和密码策略的是权限更严格的/etc/shadow文件。/etc/shadow只能用 root 或具备 sudo 权限的账号读取普通用户是看不到的。用sudo cat /etc/shadow查看一行对应一个用户典型内容长这样oracle:$6$YKzGtQrB$aT1k9...:19905:10:90:7:30::这串内容用冒号分隔成 9 个字段含义分别如下字段位含义示例值1用户名oracle2加密后的密码摘要$6$YKzGtQrB$aT1k9...3最后一次修改密码的日期从 1970-01-01 起算的天数199054两次修改密码之间的最小间隔天数105密码最长有效天数超过则密码过期906过期前提前提醒的天数77密码过期后还能宽限登录的天数超过则账号禁止登录308账号过期日期同样从 1970-01-01 起算空值表示永不过期空9保留字段空第3字段等于 19905换算成日期大概是2024-07-01也就是说该账号最后一次改密码是在这一天。如果第5字段是 90那么第 90 天后也就是2024-09-29密码进入过期状态。这些字段就是 Linux 判断“密码过期时间”的原始依据。1.2 密码过期和账户过期是两个概念这是非常多新手容易混淆的地方。网络上不少教程把“密码过期”和“账户过期”混为一谈但真实场景里它们完全是两回事。密码过期指的是密码本身失效。账号还在用户也还存在只是当前密码不允许继续使用用户登录时会被要求立即重设密码。如果一切正常重设密码后账号立刻恢复使用。账户过期指的是该账号整体失效无论密码是否正确都不允许再登录。这在/etc/shadow里对应第8字段在chage -l里体现为Account expires。判断一张 Linux 账号是否还能用不能只看密码有没有过期还要看账户本身有没有被“作废”。尤其是外包人员、临时协作账号很多公司习惯直接用账户过期时间来实现“到点自动失效”而不是手动一个个删。后面讲修改方法时我会重点演示如何设置和清除账户过期时间。1.3 默认策略是从哪来的新创建的用户密码策略不是凭空生成的它的默认值来自/etc/login.defs文件里的几个参数PASS_MAX_DAYS 90 PASS_MIN_DAYS 0 PASS_WARN_AGE 7PASS_MAX_DAYS是密码最长有效天数PASS_MIN_DAYS是最小修改间隔PASS_WARN_AGE是过期前提醒天数。很多业内常见的做法是把PASS_MAX_DAYS设置为 90也就是三个月强制改一次密码这是为了满足等保、ISO 27001 或者其他安全合规要求。而某些个人开发机或者内部实验环境默认可能是 99999约等于永不过期。这里要特别说一句改/etc/login.defs只影响之后新建的用户对已有的存量用户不生效。已经有用户的密码策略还是需要逐个或批量用chage去调整。2. 查看用户密码过期时间的实操方法2.1 首选命令 chage -l查看单个用户的密码过期时间最直观的命令是chage -l。比如我想看 oracle 用户的密码状态sudo chage -l oracle输出是这样的Last password change : Jul 01, 2024 Password expires : Sep 29, 2024 Password inactive : Oct 29, 2024 Account expires : never Minimum number of days between password change : 10 Maximum number of days between password change : 90 Number of days of warning before password expires : 7每一行含义都很直白。Last password change是最后一次改密码日期Password expires是密码预计过期日期Password inactive是密码过期后还能宽限使用的最后日期超过这个日期账号就不能再登录了Account expires是账户整体失效日期never表示永不过期。下方三行对应最小天数、最大天数、提醒天数。看到这些信息基本就能判断一个账号目前处于什么状态密码是否快到期是否有宽限期账户是否被设置了失效时间。2.2 passwd -S 和直接查看 shadow如果不想看那么详细的输出也可以用passwd -S快速了解状态sudo passwd -S oracle输出类似oracle P 07/01/2024 10 90 7 30从左到右分别表示用户名、密码状态、最后修改日期、最短修改天数、最长有效天数、提前提醒天数、宽限天数。这里的密码状态位常见有三种P表示密码可用L表示密码被锁定NP表示该用户没有设置密码。还有一种方式就是直接查看/etc/shadow文件。虽然字段不如前两种友好但在某些脚本场景下反而更直接因为可以一次性带出所有字段方便用awk继续处理sudo awk -F: NR1{print $1,$5,$6,$7,$8} /etc/shadow看到这里你会理解chage -l展示的“密码过期时间”本质上就是/etc/shadow里第五个字段的天数换算出来的。掌握 shadow 的字段含义会更容易理解各种命令输出之间的对应关系。2.3 批量盘点所有用户单个用户用好chage -l就行但如果你管理的是几十上百个账号一个个手动敲命令效率太低。这时候建议写个小循环把需要关注的账号捞出来统一打印关键信息。我常用的一个脚本片段是这样for user in $(getent passwd | awk -F: $31000 $365534 {print $1}); do echo $user sudo chage -l $user | grep -E Last password change|Password expires|Account expires done用getent passwd而不是直接读/etc/passwd好处是当系统接入了 LDAP、NIS 或者其他认证源时也能把远端用户一起查出来不会漏掉。过滤条件里的 UID 范围可以根据实际情况调整目的是排除系统服务账号。批量盘点的意义在于提前发现风险账号。我建议每隔一个月跑一次把即将过期的账号列表拉出来该通知的提前通知该重置的提前处理别等到用户投诉了才去翻日志。3. 修改用户密码过期时间参数与场景3.1 chage 命令的关键参数修改密码过期时间chage依然是首选。命令格式是chage [选项] 用户名常用参数如下参数作用-m修改密码的最小间隔天数两次修改之间至少相隔多少天-M密码最大有效天数这是最常用的参数-W密码过期前多少天开始提醒-I密码过期后宽限多少天超过宽限期账号锁定-E设置账户过期日期可以直接写YYYY-MM-DD也可以用天数-d把“最后修改密码日期”改成指定日期0 表示立即过期单独看参数可能觉得抽象结合具体场景就好理解了。3.2 常见修改场景演示场景一给现有用户设置 90 天密码有效期提前 7 天提醒密码最短使用 0 天过期后宽限 30 天。sudo chage -M 90 -m 0 -W 7 -I 30 oracle执行后再次查看sudo chage -l oracle能看到最大天数变成 90提醒天数变成 7。这个场景一般用于安全基线整改把公司内部所有人工账号统一调到 90 天过期。场景二强制用户下次登录时修改密码。运维在处理初始密码或临时密码时经常用sudo chage -d 0 oracle把“最后修改日期”设为 0系统会认为密码已经严重过期用户下次登录时必须先重置密码之后才能进入会话。这个操作比手动改密码再告诉用户“你改一下”更稳妥因为系统层面强制执行用户没办法跳过。场景三设置账户在某天整体失效比如临时账号只需要用到年底sudo chage -E 2024-12-31 tempuser这个操作的场景很明确外包协作、临时维护、项目授权时间到了账号自动不可用不需要管理员记着去删除。场景四取消所有过期限制让账号“永不过期”sudo chage -M 99999 -E -1 oracle-E -1会清除账户过期日期-M 99999相当于把密码有效期拉到非常长。这个方法一般只建议用在服务账号、机器账号上人工账号建议保留合理的有效期。3.3 用 passwd 完成部分修改passwd命令也能设置部分密码老化参数只是不像chage那么全。常用写法是sudo passwd -x 90 -w 7 -i 30 oracle-x对应最大有效天数-w对应提前提醒天数-i对应宽限天数。用passwd的好处是命令更短适合临时执行但像账户过期时间-E、强制过期-d这样的能力还是得用chage来实现。所以我的建议是统一用chage管理避免两套命令混着用导致混乱。3.4 修改后的验证建议修改完策略以后不要急着走人先确认一手sudo chage -l oracle重点看Password expires和Account expires两行是否符合预期。另外要注意修改是立即生效的不需要重启服务也不需要重启机器。但如果用户当前已经处于登录状态老的会话不会因为策略变更被强制踢出新登录才会受新策略约束。4. 生产环境中的策略落地与自动化4.1 新员工账号首登强制修改给新员工建账号正确流程不是设个密码就完事而是把“首登强制修改密码”这个动作做进去。否则就会出现开头提到的尴尬情况临时密码有效期没设置好用户还没来得及登录密码就过期了。推荐的创建流程是sudo useradd -m -s /bin/bash zhangsan echo Temp12345 | sudo passwd --stdin zhangsan sudo chage -d 0 zhangsan sudo chage -M 90 -m 0 -W 7 zhangsan第一行创建用户第二行写入临时密码第三行让密码立即过期第四行设置常规的 90 天有效期和提醒策略。用户首次登录时会被强制要求设置新密码之后按照 90 天有效期正常轮换。4.2 批量整改存量账号公司在做安全基线整改时经常需要对一批账号统一设置密码策略。假如有一份user_list.txt每行一个用户名脚本可以这样写#!/bin/bash while read -r user; do if id $user /dev/null 21; then chage -M 90 -m 0 -W 7 $user echo $user 策略更新完成 else echo $user 不存在 fi done /root/user_list.txt脚本里的id $user用来判断用户是否存在避免因为拼写错误或者已删除账号导致命令执行报错。批量操作之前一定要先在测试环境跑一遍或者先拿两三个账号试点确认逻辑无误后再全量执行。这个习惯不是小题大做而是避免把生产环境账号策略一次性改错的兜底手段。4.3 到期前预警脚本管理的账号一多光靠人脑记着谁哪天过期不现实。更好的方案是用脚本定期扫描提前把即将过期的账号列出来再通过邮件、企业微信消息等方式通知管理员。我写过的一个相对简单的预警脚本如下#!/bin/bash # 密码过期预警脚本建议配合 crontab 每天执行一次 WARN_DAYS7 TODAY_EPOCH$(date %s) EXPIRED_SOON() for user in $(getent passwd | awk -F: $31000 $365534 {print $1}); do expire_str$(sudo chage -l $user | awk -F: /Password expires/{print $2}) if [ $expire_str never ] || [ -z $expire_str ]; then continue fi expire_epoch$(date -d $expire_str %s) diff_days$(( (expire_epoch - TODAY_EPOCH) / 86400 )) if [ $diff_days -le $WARN_DAYS ] [ $diff_days -ge 0 ]; then EXPIRED_SOON($user 还剩 ${diff_days} 天过期) fi done if [ ${#EXPIRED_SOON[]} -gt 0 ]; then printf %s\n ${EXPIRED_SOON[]} | mail -s 密码即将过期的账号提醒 opsexample.com fi脚本中核心是把Password expires的文本日期通过date -d转成时间戳再计算与当前时间差。这种方案的优点是跨平台兼容性好如果直接用/etc/shadow里的天数计算还得额外处理“永不过期”和空字段各种边界情况。配合 crontab 使用时可以每天上午 9 点跑一次0 9 * * * /usr/local/sbin/password_expire_check.sh /var/log/password_expire_check.log 21日志写到独立文件里后面排查问题更方便也方便确认脚本是否真的被执行。4.4 服务账号与安全边界批量设置密码策略时一定要小心服务账号。像mysql、nginx、redis这类程序运行账号很多是靠密钥文件或服务配置启动的密码过期并不影响服务正常运行但如果某些脚本依赖 su 切换身份密码过期就会出现偶发失败。服务账号建议单独维护一套策略要么不做密码有效期限制要么用chage -M 99999拉长有效期同时通过密钥认证的方式管理避免密码口令暴露在脚本和配置文件里。我给服务账号做个备注统一规定“不参与人工账号的密码过期策略”省得每到三个月整改期服务账号也跟着被迫改密码引发不必要的故障。5. 常见问题与排错实录5.1 改了策略为什么不生效比较常见的情况是管理员用chage -M 90给存量用户改了策略但在/etc/login.defs里看到默认值还是 99999就以为没生效。实际上chage命令修改的是用户层面的策略优先级高于/etc/login.defs的默认配置修改完成以后用chage -l立刻能看到变化。“不生效”的错觉多半是没有正确解读输出或者忘记用sudo导致命令其实执行失败了。另一种情况更隐蔽如果用户是通过 LDAP 或 NIS 集中认证Linux 本地的/etc/shadow可能根本没有该用户的记录chage命令无从改起。这时候需要在中心目录服务上做策略调整而不是单台服务器上折腾。5.2 账号状态判断不清导致登录失败SSH 登录时报Your password has expired这种情况一般说明密码确实已过期但账号还算“活着”用户有机会在登录流程中直接修改密码。如果报的是Account is locked或者Account expired就要重点检查两个方向一是 shadow 密码字段是否以!开头二是账户过期日期是否已经抵达。我整理过一个快速诊断表平时排查时对照着看效率很高。现象可能原因处理方式Password has expired密码有效期到了登录时按提示重置或管理员用chage -d 0强制重置Account is locked密码字段以!开头sudo passwd -u 用户名解锁Account expired账户过期日期已到sudo chage -E -1 用户名清除过期限制Cannot set password最小间隔天数限制调小或清零最小天数sudo chage -m 0 用户名密码正确但登录失败可能是认证方式被 PAM 限制检查/etc/security/下对应模块配置5.3 快速诊断速查表排错现场最忌讳来回试命令效率太低。我建议把下面的命令组合记熟基本能覆盖九成以上的密码过期问题查看单个用户完整策略sudo chage -l 用户名强制用户下次登录改密码sudo chage -d 0 用户名设置 90 天有效期和 7 天提醒sudo chage -M 90 -W 7 用户名清除账户过期限制sudo chage -E -1 用户名查看账号是否被锁定sudo awk -F: /^用户名:/{print $2} /etc/shadow如果第2字段以!或*开头说明密码锁定状态有问题需要先用passwd -u解锁。5.4 我的几条实操纪律和密码过期时间打交道久了我慢慢形成了几条固定习惯写在这里供你参考。第一账号创建时就把策略定好别留着默认配置。临时密码、首次强制修改、有效期这三个动作必须一次到位。第二服务账号单独维护不参与人工账号的统一过期策略避免三个月一改的节奏干扰线上服务。第三预警脚本要固定执行周期并且把执行日志留好不仅为了发现问题后追溯也为了让自己安心——有没有真正跑过日志一眼就能看出来。第四每次修改完策略都要重新执行chage -l看一眼输出。这看起来是重复动作但恰恰是避免“改了等于没改”的最有效方法。密码过期时间看似是个小配置但它直接决定了账号能不能继续使用也关系到账号安全策略能否落地。把这里的逻辑和命令真正吃透不管是日常管理还是应急排障都会顺手很多。