Linux passwd命令深度解析:从底层原理到故障排查
2026/9/13 9:13:01 网站建设 项目流程

就用你现在最常看到的那台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 : 7

chagepasswd在密码老化管理上是互补的。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 -Lusermod -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_pwqualitypam_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" | chpasswd

chpasswd默认从标准输入读取用户名:密码格式的内容,批量改密特别方便,比如配合一个用户文件:

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 系列为例,完整步骤是:

  1. 重启系统,在 GRUB 引导菜单出现时,快速按e键进入编辑模式。
  2. 找到以linux16开头的那一行,定位到末尾的rhgb quiet,在这两个参数后面加一个rd.break(或者直接改成rd.break enforcing=0,这个区别我后面会说)。
  3. Ctrl + X启动,系统会进入一个switch_root环境的initramfs提示符。
  4. 此时根文件系统是以只读方式挂载在/sysroot下的。执行mount -o remount,rw /sysroot,把根重新挂载为可写。
  5. 执行chroot /sysroot,真正进入被遗忘密码的那个系统环境。
  6. 执行passwd root,设置一个新的 root 密码。
  7. 如果之前加了enforcing=0,这里还需要额外处理 SELinux 标签问题。更推荐的做法是,重置完密码后执行touch /.autorelabel,让系统下次启动时自动修复文件安全上下文。
  8. 输入exit退出 chroot,再输入exit退出 initramfs,系统会继续启动,然后用新密码就可以登录了。

Ubuntu 18.04 之后的新版系统,流程略有不同:在 GRUB 菜单按e,把ro quiet splash这一行中的ro改成rw,再在末尾加一个init=/bin/bash,启动后可以直接进入一个 root 的 shell,然后执行passwd root重设密码。这种方式更直接,因为它直接以可写方式挂载根分区并跳过了启动服务的加载。

这里把rd.breakinit=/bin/bash两种方式做个对比:

对比项rd.break(RHEL 系)init=/bin/bash(Debian 系)
目标进入 initramfs 的 switch_root 环境直接进入 root shell
根挂载状态只读,需手动 remount取决于是否把 ro 改为 rw
SELinux 处理需 touch /.autorelabel一般需 touch /.autorelabel
适用场景CentOS/RHEL/FedoraUbuntu/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.sopam_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,所以中文用户名本身是能存在的,比如张三。但是问题往往出在:键盘布局、终端编码、字体渲染这些小细节上。

如果你在一个纯字符界面上要重置一个中文用户的密码,确实容易敲出乱码,因为终端可能没配好中文字体。这时候我建议你这样做:

  1. 先用su -切到 root。
  2. getent passwd | grep -P '[\x{4e00}-\x{9fa5}]'找到中文用户的原始用户名(如果你能用中文输入法或复制粘贴的话)。
  3. 直接在命令行里写passwd 张三,但确保当前终端编码是UTF-8,并且有中文字体,否则 shell 自己都能把用户名搞乱,更别提密码了。
  4. 如果实在输入不了中文,可以用 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 密码改掉的案例,我听过不止一次。宁可多打几个字符,也不要裸敲。

第二,凡是涉及非交互式改密(--stdinchpasswd、脚本批量改),都要额外小心 Shell 历史记录、特殊字符转义、以及执行后的确认。我会在执行完后马上用passwd -S 用户名或者chage -l 用户名检查状态,确保密码真的改成功了,而不是看一眼“命令成功”就放心。

第三,密码策略和安全配置一定要跟着系统版本走。CentOS 7 和老版本 Debian 的 PAM 配置差异不小,写脚本前先确认一下/etc/pam.d/passwd里到底启用了哪些模块,别把pam_pwqualitypam_cracklib混在一起,更不要图方便注释掉策略,不然你就是在给攻击者留门。

第四,重置密码后,记得同步检查 SSH 密钥、sudo 配置、以及用户所属的组等关联信息。我们经常只关心“密码改成功没有”,却忽略了密码只是认证链路里的一环。如果用户开着密钥登录,那密码改成什么样都拦不住他;如果 sudo 配置有漏洞,重置密码也只是治标不治本。

passwd这条命令本身很简单,但围绕它的机制、安全策略和故障排查,是整个 Linux 系统管理基本功里很值得花时间去吃透的一块。希望这篇教程能帮你少走一些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询