就用你现在最常看到的那台Linux服务器来说。无论是刚入行的运维新人,还是写了几年脚本的老手,passwd命令几乎天天见。它短、好记、语义直白,大多数人对它的认知停留在“改个密码嘛,敲两遍新密码就完事了”。但如果你真的在线上环境摸爬滚打过,就会知道这条命令背后藏着一堆细节:为什么有时候要你输入旧密码、有时候不用?为什么 root 能改所有人的密码,普通用户只能改自己的?为什么明明密码改成功了,桌面上还会弹一个“gkr-pam couldn't update the login keyring password”的报错?密码策略到底怎么设置才符合等保要求?这篇文章不打算只给你念一遍 man 手册,我会把passwd从原理到实操、从常用参数到故障排查完整拆一遍,配合真实案例和踩坑记录,适合所有 Linux 使用者,尤其是刚接触系统管理的朋友。
1. 为什么一条基础命令值得你再学一次
1.1 从“会改密码”到“懂改密码”
很多人第一次用passwd,是在练习环境里给自己的账户改密码。那时候的流程很简单:输入passwd,系统让你输入当前密码,然后输两遍新密码,完事。但到了真实的生产环境,你会遇到这些场景:
- 新建了一个账号,需要给账号设置初始密码,还要让用户首次登录时必须改掉。
- 某个员工离职了,需要立即锁定他的账号密码,防止他继续登录系统。
- 服务器被扫描出弱口令,你要批量把一批账号的密码统一重置。
- 服务器的 root 密码忘了,需要重启进单用户模式重置密码(这个我在 3.4 节会详细写)。
- 登录时提示“password has expired”,但你不知道该用哪个参数去调整有效期。
- 桌面版 Linux(比如 Ubuntu 带 GNOME 桌面的环境)改完密码后弹出一个 “gkr-pam couldn't update the login keyring password” 的报错,密码明明改成功了,但每次登录都弹,很烦。
这些场景里,passwd都不是那一句简单的“敲两遍新密码”能搞定的。它牵扯到 Linux 的账户认证体系、密码存储位置、密码策略、PAM 模块等一系列底层机制。所以,把这条命令真正用明白,能直接省掉你很多翻文档和熬夜排查的时间。
1.2 passwd 命令的工作逻辑与底层机制
passwd命令的核心作用,是修改/etc/shadow文件里对应账户的密码哈希值。注意,不是直接改/etc/passwd。这两个文件我简单对比一下:
| 文件 | 存放内容 | 权限 | 说明 |
|---|---|---|---|
/etc/passwd | 用户账号基础信息(用户名、UID、GID、家目录、Shell) | 所有用户可读 | 早期 Unix 把密码哈希也放这里,现在只放一个x占位符 |
/etc/shadow | 密码哈希、最后一次修改时间、有效期、锁定状态等 | 仅 root 可读 | 密码真正存放的地方,普通用户没有读取权限 |
那为什么普通用户改自己密码时,能写入/etc/shadow?因为passwd命令本身带有 SUID 特殊权限。你可以执行ls -l /usr/bin/passwd看一下,大概率会看到-rwsr-xr-x,其中那个s就表示 SUID。这表示:任何用户执行这个程序时,都会以文件属主(root)的身份运行,因此才有权限去改写/etc/shadow里自己那一行的密码哈希值。这是整个机制里最核心的一点,理解了这一点,你就能明白为什么普通用户只能改自己的密码,而 root 用户能改所有人的密码——因为 root 本来就拥有一切权限,不需要借助 SUID。
密码存储方式上,现代 Linux 几乎都使用 SHA-512 或更高强度的哈希算法,哈希值存放在/etc/shadow的第二个字段,格式通常是这样:
$6$salt$encodedhash$6$代表 SHA-512 加密,中间的salt是随机生成的盐值,后面那一长串是加了盐之后的哈希结果。passwd在验证旧密码时,会用你输入的密码加上原来的盐值做哈希,然后和 shadow 里的哈希比对;验证通过后,会用新密码重新生成盐值和哈希,写回 shadow 文件。整个过程对用户是透明的,但你要知道:Linux 里不会也不能“还原”出你的明文密码,它只有哈希校验这一条路。
1.3 安全边界:什么时候必须谨慎
写这篇教程之前,我必须先强调一个安全边界:passwd是一个权力很大的命令,尤其当你用 root 身份执行时,可以重置任何账号的密码。这正是它能成为“恢复系统”利器的原因,但也是一把双刃剑。
- 只有在你明确拥有该系统的管理权限、或获得系统所有者授权的前提下,才能重置别人的密码。
- 不要为了省事,给所有账号设置同一个弱密码,哪怕只是测试环境。等保合规和内部审计查的就是这种东西。
- 使用
--stdin等非交互方式改密时,命令会出现在 Shell 历史记录里。如果你是在共享机器上操作,很容易把密码留在.bash_history中,这是非常危险的习惯。
后面的所有操作案例,我都默认你是在自己管理的主机或虚拟机上执行。请把安全习惯摆正,再继续往下看。
2. 核心细节解析:参数与使用要点
2.1 常用参数一览
passwd的参数并不算多,但每个都有明确的用途。我把最常用的整理成一张表,方便你查:
| 参数 | 作用 | 典型场景 |
|---|---|---|
| 无参数 | 修改当前登录用户的密码 | 日常改密 |
passwd 用户名 | root 修改指定用户的密码 | 管理员重置用户密码 |
-d | 删除用户的密码(清空) | 创建无密码账户,一般不推荐 |
-l | 锁定用户密码,禁止登录 | 员工离职、账号停用 |
-u | 解锁用户密码 | 恢复被锁定的账号 |
-e | 强制用户下次登录时修改密码 | 新账号首次登录改密 |
-x 天数 | 设置密码最大有效期 | 等保合规,要求密码定期更换 |
-n 天数 | 设置密码最小修改间隔 | 防止用户频繁改回旧密码 |
-w 天数 | 设置密码过期前提醒天数 | 提前通知用户改密 |
-i 天数 | 密码过期后,账号多少天后被锁定 | 账号安全管理 |
-S | 查看用户密码状态 | 排查用户锁定/过期问题 |
--stdin | 从标准输入读取新密码 | 脚本批量改密 |
注意,passwd -S在较新的发行版上也可以用--status代替,输出格式基本一致。我在 CentOS 7/8、Ubuntu 18.04/20.04、以及国产的麒麟 V10 上都验证过,-S参数都兼容,可以放心用。
2.2 状态查看与过期策略:-S、chage、-e 的组合使用
用passwd -S 用户名查看状态,输出大概长这样:
tom L 09/14/2024 0 99999 7 -1这串输出的含义是:用户名、密码状态(L 表示锁定,P 表示可用,NP 表示无密码)、最后一次修改密码的日期、最小修改间隔天数(0 表示随时可改)、最大有效期天数(99999 表示永不过期)、过期前提醒天数(7 天)、密码失效后的宽限天数(-1 表示未设置)。
有没有觉得这个信息量很足,但可读性一般?所以我更推荐用chage -l 用户名来看人类可读的样式,它会输出类似这样的内容:
Last password change : Sep 14, 2024 Password expires : never Password inactive : never Account expires : never Minimum number of days between password change : 0 Maximum number of days between password change : 99999 Number of days of warning before password expires : 7chage和passwd在密码老化管理上是互补的。passwd -x 90 tom可以设置 tom 的密码 90 天后过期,但这和chage -M 90 tom是同一个效果。实操里我用chage会多一些,因为它的输出更直观,适合审计和自查。
看到这里你可能会问:那个-e参数到底什么时候用?我举一个最常见的场景:你给新同事创建了一个账号,先用passwd tom设置一个临时密码,然后再执行passwd -e tom,强制 tom 下次登录时必须先改成自己的密码。如果没做这一步,tom 就会一直用那个临时密码登录,隐患很大。
2.3 锁定和删除密码的边界
-l和-d这两个参数,也是新手容易搞混的。
passwd -l tom的作用,本质是在/etc/shadow中 tom 的密码哈希前面加了一个!前缀。这样,用户输入正确密码去哈希比对时,结果永远不会匹配,自然也就无法登录。这个操作不会删除原密码哈希,所以passwd -u tom可以随时把!去掉,恢复原密码。
passwd -d tom则是直接把密码哈希清空,相当于“无密码”状态。此时 tom 登录系统不需要密码。在很多系统上,这会让账号处于一种极其危险的状态,因为任何人都可以直接登录。除非你明确要创建一个仅用于某种特殊服务、且不允许登录的无密码账户,否则别用-d。而且即使是这样,我也更推荐你用usermod -s /usr/sbin/nologin这类方式去限制登录,而不是把密码删掉。
在这个小节额外提醒一句:passwd -l只锁密码,不影响 SSH 密钥登录。如果用户本来就配置了 SSH 公钥,锁密码之后他依然可以通过密钥登录系统。如果你想把用户的 SSH 登录也一起禁止,需要配合usermod -L加usermod -s /sbin/nologin,或者用chage -E 0把账号设置为立即过期,这样才能形成一个相对完整的禁用策略。
3. 实操案例:从改密码到系统级重置
3.1 场景一:普通用户修改自己的密码
这是最基础的操作,但我要把完整的交互过程写出来,因为实际输出对新手很有参考价值:
$ passwd Changing password for user tom. Current password: ← 这里输入当前密码,屏幕上不会显示任何字符 New password: ← 输入新密码,最好满足系统密码复杂度要求 Retype new password: ← 再输一遍新密码 passwd: all authentication tokens updated successfully.看到最后一行all authentication tokens updated successfully,就说明改密成功了。
这里有几个细节值得注意:
- 输入旧密码时,如果连续输错,
passwd会直接报错退出,不会给你无限制试错的机会。 - 新密码如果太短、太简单、或者和旧密码太相似,系统会根据
/etc/pam.d/passwd里面的pam_pwquality或pam_cracklib规则拒绝接收,并提示BAD PASSWORD。这不是系统故障,是对密码强度的正常校验。 - 密码输入全程没有任何回显(不会显示
***),这是终端设计使然,不是没敲进去。很多新手在这个环节会以为键盘坏了,实际只是没有回显。
3.2 场景二:root 修改指定用户密码
root 执行passwd tom时,交互过程少了一个“当前密码”的步骤:
$ sudo passwd tom New password: Retype new password: passwd: all authentication tokens updated successfully.区别很明显:root 改密不需要验证 tom 的旧密码,这是系统赋予管理员的高权限操作。也正因如此,所有 root 用户都要格外谨慎,避免批量重置密码时手滑把某个服务账号的密码改了,导致服务起不来。
如果你是在一个允许 SSH 密钥登录的服务器上,忘记某个用户密码时,最快的方式就是用 root 执行passwd 用户名重置。前提是你有 root 权限或者能sudo su -切到 root。
我个人的习惯是:root 状态下手动改密码,一定先whoami确认当前用户,再执行passwd 目标用户。因为裸敲一个passwd,改的就是 root 自己;如果你本意是想改某个普通用户,会直接把 root 密码改掉,这种情况在线上环境不是没出现过,影响面很大。
3.3 场景三:批量场景下的非交互改密
有时候你需要在一个脚本里批量创建用户并设置初始密码。虽然 Linux 的passwd默认是交互式的,但它支持从标准输入读取密码,配合管道就能实现非交互式改密。具体写法如下:
echo "NewPass@123" | passwd --stdin tom这条命令的意思,是把echo输出的字符串作为passwd的新密码输入。注意,--stdin参数主要集中在 Red Hat 系列(CentOS、Rocky、AlmaLinux)上,Debian 系(Ubuntu、Debian)默认不带这个选项。在 Ubuntu 上如果你硬要用,会看到这样的报错:
Usage: passwd [options] [LOGIN] ...所以如果你是跨发行版操作,我有一个更通用的方案,用chpasswd命令:
echo "tom:NewPass@123" | chpasswdchpasswd默认从标准输入读取用户名:密码格式的内容,批量改密特别方便,比如配合一个用户文件:
while read user pass; do echo "$user:$pass" | chpasswd done < userlist.txt有一个安全细节必须提醒:这两种方式都会让密码出现在 Shell 历史记录里。解决方法是,在命令前面加一个空格(如果 Bash 配置了HISTCONTROL=ignorespace),或者用unset HISTFILE临时关闭历史记录,更稳妥的是把密码放进一个权限为 600 的文件里,脚本读取后立即删除该文件。实测下来,最稳的还是用ansible这类自动化工具管理密码变更,把敏感信息交给 vault 存放,而不是在命令行明文裸奔。
3.4 场景四:忘记 root 密码时的单用户模式重置
在物理服务器或者虚拟机上,如果 root 密码忘了,实际是可以不重装系统、不丢失数据去重置的。这个操作在运维面试里也经常被问到。核心思路是:通过引导菜单进入单用户模式或紧急模式,以 root 权限挂载根文件系统,然后用passwd重新设置 root 密码。
以 CentOS 7/RHEL 7 系列为例,完整步骤是:
- 重启系统,在 GRUB 引导菜单出现时,快速按
e键进入编辑模式。 - 找到以
linux16开头的那一行,定位到末尾的rhgb quiet,在这两个参数后面加一个rd.break(或者直接改成rd.break enforcing=0,这个区别我后面会说)。 - 按
Ctrl + X启动,系统会进入一个switch_root环境的initramfs提示符。 - 此时根文件系统是以只读方式挂载在
/sysroot下的。执行mount -o remount,rw /sysroot,把根重新挂载为可写。 - 执行
chroot /sysroot,真正进入被遗忘密码的那个系统环境。 - 执行
passwd root,设置一个新的 root 密码。 - 如果之前加了
enforcing=0,这里还需要额外处理 SELinux 标签问题。更推荐的做法是,重置完密码后执行touch /.autorelabel,让系统下次启动时自动修复文件安全上下文。 - 输入
exit退出 chroot,再输入exit退出 initramfs,系统会继续启动,然后用新密码就可以登录了。
Ubuntu 18.04 之后的新版系统,流程略有不同:在 GRUB 菜单按e,把ro quiet splash这一行中的ro改成rw,再在末尾加一个init=/bin/bash,启动后可以直接进入一个 root 的 shell,然后执行passwd root重设密码。这种方式更直接,因为它直接以可写方式挂载根分区并跳过了启动服务的加载。
这里把rd.break和init=/bin/bash两种方式做个对比:
| 对比项 | rd.break(RHEL 系) | init=/bin/bash(Debian 系) |
|---|---|---|
| 目标 | 进入 initramfs 的 switch_root 环境 | 直接进入 root shell |
| 根挂载状态 | 只读,需手动 remount | 取决于是否把 ro 改为 rw |
| SELinux 处理 | 需 touch /.autorelabel | 一般需 touch /.autorelabel |
| 适用场景 | CentOS/RHEL/Fedora | Ubuntu/Debian |
关于这部分,我想特别补充一句安全话题:用这种方式进入系统不需要原密码,说明物理访问权限本身就是一种“最高权限”。所以服务器的物理安全、BIOS 密码、GRUB 密码保护不能忽略。如果你管理的机器有外人可接触的地方,建议给 GRUB 加个密码,否则别人插个键盘就能把你的 root 密码换了。
3.5 场景五:从“普通用户重置 root 密码”到“登录流程重建”
热搜词里有“vm虚拟机普通用户重置root用户密码”这个说法,很多实验室环境里的虚拟机,用户忘记 root 密码、但手上还有一个普通用户账号。那能不能直接通过普通用户重置 root?严格来说,普通用户的权限不足以修改/etc/shadow,所以无法直接在登录状态下用passwd root改自己的 root 密码(系统会提示Permission denied)。
能走通的路子只有两条:
- 普通用户有
sudo权限:执行sudo passwd root,本质还是借助 root 身份完成重置。 - 普通用户没有 sudo 权限:需要重启系统,走 3.4 里的单用户模式流程。因为在单用户模式下,系统默认以 root 身份提供一个 shell,不需要你输入任何密码。
所以,如果你在一台虚拟机上忘了 root 密码,只要你能控制虚拟机的电源和引导,就不需要重装系统,3.4 的流程一定能救回来。这也是热词里“虚拟机重置 root”最常见的解法。
4. 常见问题与排查技巧实录
4.1 高频报错:gkr-pam couldn't update the login keyring password
这个报错在桌面版 Linux 上非常常见,完整信息一般是:
passwd: password updated successfully passwd: gkr-pam couldn't update the login keyring password: no old password was entered从输出能看到,密码本身是改成功了,但 GNOME Keyring(登录钥匙环)没有同步更新,导致每次登录桌面时都会弹窗,让你输入旧密码来解锁钥匙环。这个问题对纯命令行服务器无影响,但如果你用的是 Ubuntu 桌面版,会非常恼人。
出现原因主要是:用户通过passwd命令改密时,PAM 的pam_gnome_keyring模块尝试用旧密码去解密钥匙环文件,但用户没有输入旧密码(比如 root 强制重置),或者钥匙环中的旧密码已经失效。解决办法有几种:
- 最直接的方法:登录桌面后,打开“密码和密钥”(Seahorse),右键点击登录钥匙环,选择“更改密码”,填写旧的系统密码,然后设置为新的系统密码,把钥匙环密码和系统密码重新同步。
- 如果已经完全不记得旧密码,可以删除当前用户的钥匙环文件,让系统重建。文件一般位于
~/.local/share/keyrings/,删除login.keyring等文件后注销重新登录,系统会重新创建一个空钥匙环,以后每次登录自动用新密码解锁。 - 命令行删除法:在纯终端环境或 SSH 会话中,也可以执行
rm ~/.local/share/keyrings/login.keyring后重启会话。
我踩过这个坑之后,现在遇到桌面环境改密,都会额外提醒一句:别只盯着密码是否改成功,还要检查钥匙环提示。否则用户第二天就会反馈“每次开机都弹窗”,这个锅你背得很冤。
4.2 密码策略导致的“新密码太简单”
很多时候用户改密码,会碰到类似这样的提示:
BAD PASSWORD: The password fails the dictionary check - it is based on a dictionary word这不是系统在刁难你,而是 PAM 的密码质量控制模块在起作用。相关配置一般能在/etc/pam.d/passwd或/etc/pam.d/system-auth中找到,常见的是pam_pwquality.so或pam_cracklib.so。它们会检查新密码的最小长度、是否包含大小写、数字、特殊字符,以及是否和用户名相关等。
我可以给你一个实际配置示例,在/etc/security/pwquality.conf里:
minlen = 12 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1 minclass = 3这段配置的含义是:密码最短 12 位;数字、大写字母、小写字母、其他字符各至少包含一位;字符类别至少 3 种。
如果你只是临时测一下,不想为某个测试账户改策略,可以直接用 root 执行:
echo "SimplePass123" | passwd --stdin tom这种情况下,root 改密码可以绕过部分密码复杂度检查(至少我实测 CentOS 上是这样的),但尽量别把这个当常规操作。生产环境里,该有的密码复杂度策略还是要有,不然内部合规检查那一关都过不去。
4.3 用户锁定与密码过期问题速查
我用一张表把常见的passwd相关状态和对应操作整理出来,排查时可以对照参考:
| 问题现象 | 可能原因 | 排查/恢复方法 |
|---|---|---|
登录提示Account locked | 密码被-l锁定 | passwd -u 用户名解锁 |
登录提示Password expired | 密码超过最大有效期 | passwd -x -1 用户名取消过期,或重设密码 |
登录提示must be changed | 启用了-e强制过期 | 正常改一次密码即可 |
登录提示Permission denied | 密码输入错误次数过多, 或 PAM 锁定 | 查看/etc/pam.d/login中的pam_faillock配置;root 执行faillock --user 用户名 --reset |
| 账户在沉睡,登录被拒 | 密码过期后超过不活跃天数 | passwd -i -1 用户名取消失效宽限,再重置密码 |
passwd命令提示Cannot identify you | 用户名不存在 | id 用户名确认账号是否存在 |
提示is not a valid user name | 用户名包含特殊字符或中文编码问题 | 检查/etc/passwd里的实际用户名格式 |
这里重点说说faillock这个玩意。很多系统默认开了失败锁定策略,比如连续 5 次输错密码就锁定账号 15 分钟。但有些服务账号会被自动化脚本反复尝试登录,可能触发这个策略,导致账号被锁。这种时候别一上来就以为密码被改了,先查faillock --user 用户名的输入输出,把锁定记录清掉再试。
4.4 中文用户名与特殊字符带来的怪问题
有网友问“麒麟 v10 的中文用户密码忘了怎么重置”,其实在基于 Linux 的系统里,用户名的编码方式通常是 UTF-8,所以中文用户名本身是能存在的,比如张三。但是问题往往出在:键盘布局、终端编码、字体渲染这些小细节上。
如果你在一个纯字符界面上要重置一个中文用户的密码,确实容易敲出乱码,因为终端可能没配好中文字体。这时候我建议你这样做:
- 先用
su -切到 root。 - 用
getent passwd | grep -P '[\x{4e00}-\x{9fa5}]'找到中文用户的原始用户名(如果你能用中文输入法或复制粘贴的话)。 - 直接在命令行里写
passwd 张三,但确保当前终端编码是UTF-8,并且有中文字体,否则 shell 自己都能把用户名搞乱,更别提密码了。 - 如果实在输入不了中文,可以用 UID 来定位:
awk -F: '$3==1001 {print $1}' /etc/passwd,然后想办法用脚本匹配到用户名。
这问题本质上和passwd命令本身没关系,但现实中它就是会让很多使用者卡住。我遇到最省事的方案:既然系统里有中文用户,那你肯定有办法切换到图形界面或者远程桌面,用图形终端重置中文用户密码最稳妥。
另外,密码中如果用了特殊字符,比如$、!、'、"、空格,在执行echo "密码" | passwd --stdin这类非交互方式时,很容易被 Shell 解析出问题。我打个比方:echo "Pass$123" | passwd --stdin tom中,$1在双引号里可能被当成 Shell 变量,导致你实际设置的新密码根本不是你以为的那个。遇到这种场景,请老老实实用交互式passwd,或者用chpasswd加单引号字符串,再或者把密码写进文件、从文件读取,别直接在命令行里拼。
5. 写在最后:几个我踩坑后养成的习惯
如果非要给这篇文章做个简单的收束,我更愿意分享几个在实际操作中形成的习惯,而不是复述命令怎么用。
第一,不要裸敲passwd。不管是 root 还是普通用户,敲之前先明确你要改的是谁。生产环境里因为手滑把 root 密码改掉的案例,我听过不止一次。宁可多打几个字符,也不要裸敲。
第二,凡是涉及非交互式改密(--stdin、chpasswd、脚本批量改),都要额外小心 Shell 历史记录、特殊字符转义、以及执行后的确认。我会在执行完后马上用passwd -S 用户名或者chage -l 用户名检查状态,确保密码真的改成功了,而不是看一眼“命令成功”就放心。
第三,密码策略和安全配置一定要跟着系统版本走。CentOS 7 和老版本 Debian 的 PAM 配置差异不小,写脚本前先确认一下/etc/pam.d/passwd里到底启用了哪些模块,别把pam_pwquality和pam_cracklib混在一起,更不要图方便注释掉策略,不然你就是在给攻击者留门。
第四,重置密码后,记得同步检查 SSH 密钥、sudo 配置、以及用户所属的组等关联信息。我们经常只关心“密码改成功没有”,却忽略了密码只是认证链路里的一环。如果用户开着密钥登录,那密码改成什么样都拦不住他;如果 sudo 配置有漏洞,重置密码也只是治标不治本。
passwd这条命令本身很简单,但围绕它的机制、安全策略和故障排查,是整个 Linux 系统管理基本功里很值得花时间去吃透的一块。希望这篇教程能帮你少走一些弯路。