1. 先搞清楚:Linux里的密码到底存在哪、怎么才算“改成功”
做运维这些年,被人问得最多的问题之一就是“帮我改一下root密码”,其次是“这个普通用户的密码忘了,怎么办”。root、普通用户、密码,这三个词背后的深意其实很实在:root密码决定你能否拿到这台机器的最高控制权,普通用户密码决定那个具体的人能否正常进系统干活。很多时候一个人密码改得对不对,直接决定半夜有没有人给你打电话。
这一篇我打算把改密码这件事从头到尾拆开讲透:正常登录时怎么改、忘了密码怎么重置、嵌入式环境怎么处理、普通用户密码如何批量初始化和管理、怎么用sudo权限组把root密码彻底藏起来,以及改完之后那些高频出现的报错怎么排查。适合刚接手服务器的初级运维,也适合准备把机器交接出去的老手回顾一遍。
1.1 密码文件和哈希算法:/etc/passwd只是门牌号
很多新手第一次打开/etc/passwd会愣住:怎么root那一行的密码字段是个x?实际上Linux早就不把密码明文或哈希放在这里了,x只是占位符,真正的密码哈希存放在/etc/shadow里。这算是个经典的安全设计:/etc/passwd是所有用户可读的,如果里面放了哈希,等于把爆破素材公之于众;而/etc/shadow默认只有root能读写,普通用户连看都看不到。
/etc/shadow里每一行对应一个用户,用冒号分隔成9个字段。我直接给你列个表,免得你去翻文档:
| 字段位置 | 含义 | 示例值 |
|---|---|---|
| 1 | 用户名 | root |
| 2 | 密码哈希 | $6$xxxx |
| 3 | 上次修改密码的日期(距1970-01-01的天数) | 19800 |
| 4 | 两次修改密码之间的最少天数 | 0 |
| 5 | 密码最长有效天数 | 99999 |
| 6 | 密码过期前警告天数 | 7 |
| 7 | 密码不活跃宽限天数 | 空 |
| 8 | 账号过期日期 | 空 |
| 9 | 保留字段 | 空 |
第二个字段里$6$代表SHA-512算法,$5$是SHA-256,$1$是MD5,$2y$是bcrypt。你在改完密码后看一眼这个字段,如果只有$6$开头的一段字符串,基本可以认为改成功了。
1.2 判断实际场景:什么时候需要改root、什么时候改普通用户
动手之前先分清楚你处于哪种场景,这决定了操作方式完全不一样:
- 正常登录时主动修改密码:命令行敲一个
passwd就能解决,不涉及任何重启,风险很低。 - 忘记root密码:需要进单用户模式或通过GRUB引导参数重置,涉及重启,而且一定要先确认这是你自己的服务器或虚拟机,别在没授权的机器上做这种操作。
- 嵌入式Linux或开发板:没有完整桌面和systemd,登录界面过不去时,通常要利用内核启动参数直接起shell,再挂载根文件系统改密码。
- 批量创建普通用户:几十上百个账号,一个个交互式设置密码不现实,要用
chpasswd或脚本批量写入。 - 密码策略管理:比如要求90天到期、首次登录强制改密、过期前提醒,这些靠
chage和cron配合。 - 用sudo替代root密码:把普通用户加进sudo权限组,日常操作完全不需要root密码出场,更安全。
下面我就按这些场景逐个给方案,命令都是我自己反复验证过的,你照着敲就行。
2. 修改root密码:正常登录、忘记密码和嵌入式环境三种打法
2.1 正常登录状态下的标准操作
如果现在你还能正常登录,修改root密码非常简单:
sudo passwd root系统提示输入新的root密码,注意输入时屏幕上不会显示任何字符,这是正常的,别以为键盘坏了。建议至少12位以上,包含大小写字母、数字和特殊符号。改完之后可以验证一下:
awk -F: '/^root/{print $2}' /etc/shadow输出如果是一长串以$6$开头的哈希,说明已经落盘。这里有个我一直强调的习惯:改完密码别急着关终端,先新开一个ssh会话测试新密码能正常登录,再关旧窗口。很多线上事故就是这么来的——改完密码手一抖打错了,旧会话一关,人就彻底被锁在外面了。
另外提一句--stdin这个选项。有些脚本里会写:
echo 'NewPass@2025' | passwd --stdin root这在CentOS/RHEL上能用,但Ubuntu的passwd并不支持--stdin,会提示“invalid option”。跨发行版做自动化时,我更推荐用chpasswd:
echo 'root:NewPass@2025' | chpasswd这条命令在主流发行版上都通用。还有个小细节:如果你在终端里直接敲过echo '密码'这条命令,那么这段密码可能已经留在bash历史文件~/.bash_history里了。要么改用交互式输入,要么输完密码后执行history -d清掉指定条目。别小看这个细节,我见过不止一次服务器被黑,就是因为初始化脚本把密码明文写进了历史记录。
2.2 忘记root密码时的重置流程(GRUB单用户模式)
这是半夜告警高发场景。假设你现在完全进不了系统,但物理或虚拟化控制台还能用,以CentOS/RHEL系为例:
- 重启服务器,在GRUB菜单出现时按
e进入编辑界面。 - 找到以
linux开头的那一行(通常是linux16或linux),在行尾追加一个参数:
rd.break- 按
Ctrl+x或F10启动,系统会进入一个initramfs环境的shell。这时根目录是只读挂载的,先重新挂载:
mount -o remount,rw /sysroot chroot /sysroot- 现在你已经进入实际的系统根目录了,直接改密码:
passwd root- 如果系统启用了SELinux,在退出前必须创建自动重打标签的标记文件:
touch /.autorelabel exit reboot为什么需要这一步?因为密码文件是在SELinux策略限制之外的环境下改写的,文件的安全上下文可能异常,不重新打标签的话,重启后可能出现登录成功却什么都干不了,甚至无法登录的诡异问题。/.autorelabel就是告诉系统下次启动时自动修复所有文件的上下文。
注意:rd.break这个方法适合RHEL/CentOS 7以上。Ubuntu/Debian系做法略有不同,通常是在GRUB的linux行尾部追加
init=/bin/bash,然后同样做mount -o remount,rw /后再passwd root。另外强调一句,这套操作只适用于你自己的服务器或虚拟机上的密码找回,不要把它用到任何无授权的设备上。
2.3 嵌入式Linux和开发板上的密码重置
做嵌入式开发的人经常会遇到:板子接上串口,root密码是上一手同事设的,问了一圈没人知道。普通发行版那套rd.break在busybox环境里往往用不了,因为bootloader、内核参数和根文件系统结构都不同。
嵌入式平台最常见的思路是通过u-boot或barebox给内核追加启动参数init=/bin/sh。系统内核启动后不会执行/sbin/init,而是直接弹出一个shell。看到shell后,先确认根分区是否可写:
mount -o remount,rw /然后直接执行:
passwd root如果板子的根文件系统里没有/etc/shadow(有些精简系统直接不装),passwd命令会把新密码写入/etc/passwd,或者直接提示无法写shadow。这时候需要手动确认/etc/passwd里root那一行有没有x占位,没有的话把哈希字段补齐即可。更稳妥的办法是:既然都进shell了,就用cat对比一下正常板子上的passwd和shadow格式,把缺失的字段补齐再改。
还有一类开发板根本不挂真实磁盘,每次启动都从只读的squashfs解包,改了密码一重启又还原。这种要在根文件系统构建阶段就设好默认密码,或者让应用层每次启动时从配置分区读取密码。说白了,嵌入式设备的密码管理本质上是rootfs设计问题,不是单纯执行一条passwd命令能解决的。我经手的项目里,绝大多数“密码丢失”最后都是靠重新打包rootfs解决的,单用户模式反而是少数派。
3. 修改普通用户密码:单用户、批量、策略一条龙
3.1 passwd命令的完整用法:改密码只是它一半的本事
普通用户改自己的密码,直接敲:
passwd注意这里不需要sudo,系统会先要求输入旧密码,然后设置两次新密码。root帮某个普通用户改密码,则是:
passwd zhangsanroot改普通用户密码时不会要旧密码,因为管理员本身就有完全权限。这点和Windows下的“管理员重设用户密码”逻辑一致。passwd命令除了改密码,还有一批实用选项,整理成一张表方便查阅:
| 命令 | 作用 |
|---|---|
passwd -l 用户名 | 锁定用户,禁止登录 |
passwd -u 用户名 | 解锁用户 |
passwd -d 用户名 | 删除用户的密码,变成无密码登录 |
passwd -e 用户名 | 强制该用户下次登录时必须改密码 |
passwd -n 天数 | 设置两次修改密码的最小间隔 |
passwd -x 天数 | 设置密码最长有效天数 |
passwd -w 天数 | 设置密码过期前的警告天数 |
-d这个选项要慎用。删除密码意味着该用户可以不输密码直接登录本地终端(如果sshd允许空密码则远程也一样),生产环境基本不碰。-e选项反而非常常用,批量创建账号后用它强制每个人首次登录就换掉初始密码。
3.2 批量处理:chpasswd和脚本化操作
一次性创建20个账号,每个人初始密码不同,还要求自动写入系统,这时别再一个个敲passwd了,chpasswd是首选:
echo 'zhangsan:Pass@2025' | chpasswd echo 'lisi:Pass@2025' | chpasswd更常见的做法是先准备好一个用户名密码列表文件,比如users.txt:
zhangsan:InitPass@001 lisi:InitPass@002 wangwu:InitPass@003然后一行命令搞定:
chpasswd < users.txtchpasswd默认从标准输入读取用户名:密码格式。还有个-c参数可以指定加密算法,比如chpasswd -c SHA512,老系统上可能需要。如果你想在批量创建用户后顺便把密码设置好,可以这么写:
while read user; do useradd -m "$user" echo "$user:InitPass@$(openssl rand -hex 4)" | chpasswd done < userlist.txt这里用openssl rand -hex 4生成一段随机初始密码,既避免所有用户相同密码带来的安全风险,又能在日志里给每个用户单独留下初始凭据。批量改完后,记得执行:
chage -d 0 用户名chage -d 0会把“上次修改密码日期”设置为0,系统会认为密码已过期,强制用户下次登录时立即修改。这样就算初始密码泄露出去了,也只在一段时间内有效,用户一改就作废了。
3.3 密码过期、强制改密和锁定策略:用chage管起来
很多单位有制度要求:密码90天必须换一次,过期前7天要警告。这个需求在Linux里由chage实现。常用的参数:
| 参数 | 含义 |
|---|---|
-M 90 | 密码最长使用90天 |
-m 7 | 两次修改间隔至少7天 |
-W 7 | 到期前7天开始警告 |
-E 2025-12-31 | 账号过期日期 |
-d 0 | 强制下次登录改密码 |
给已有用户设置策略:
chage -M 90 -m 7 -W 7 zhangsan查看某个用户的密码状态:
chage -l zhangsan输出里能看到“Last password change”、“Password expires”、“Account expires”等信息。别以为设置完就完事了,这里有个坑:chage只是修改了策略,如果系统用的是SSSD或LDAP统一认证,密码策略可能被中央服务器覆盖。本地chage设置的过期时间在LDAP环境里不一定生效,你需要确认用户的passwd记录是本地存储还是远端统一管理。
关于“密码过期提醒通知”,系统不自带弹窗或邮件提醒,我一般用cron脚本扫描/etc/shadow:
#!/bin/bash expired_users=$(awk -F: '$2!="*" && $2!="!" { int(( $(date +%s) - mktime("1970 01 01 00 00 00") ) / 86400 ) }' /etc/shadow)这个脚本写法有点绕,核心逻辑是把当前日期距离1970年的天数,和shadow里第3、5字段相加的过期天数做比较。更简单的做法是安装chage -l逐人检查,但量大了就得脚本化。我实际部署过的方案是:每天凌晨跑一个脚本,判断密码剩余天数小于7天时,通过邮件或企业微信机器人通知管理员。
还有faillock,现代CentOS/RHEL上用来做登录失败锁定。比如连续5次失败锁定账号30分钟:
faillock --user zhangsan --reset具体的次数和锁定时限配置在/etc/security/faillock.conf里。提醒一句:别把锁定次数设太激进,3次失败就锁会导致正常同事输错两次就被锁在门外,半夜解锁电话打到你手机上。
4. 不给root密码也能干活:sudo权限组的正确打开方式
4.1 把普通用户加入sudo权限组的正确姿势
root密码改得再频繁,最后还是有个问题:日常操作每次都输root密码,密码暴露面就太大。成熟的做法是给指定的普通用户sudo权限,root密码只有少数几个人知道。
CentOS/RHEL系把管理员用户放在wheel组,Ubuntu/Debian系放在sudo组。把用户加进去:
# CentOS/RHEL usermod -aG wheel zhangsan # Ubuntu/Debian usermod -aG sudo zhangsan这里一定要带上-a(append)参数。不带-a的话,usermod -G会把用户之前所在的附加组全部移除,可能导致用户丢失原本的组权限。我见过不止一次,运维执行了usermod -G docker zhangsan,结果把人家从wheel组里踢出去了,sudo权限直接消失。
验证是否生效:
groups zhangsan sudo -l -U zhangsansudo -l的输出会列出该用户可执行的sudo命令列表。如果看到(ALL) ALL,说明已经具备了完整管理员能力。此时zhangsan登录后执行sudo -i即可切换到root环境,全程不需要root密码,只需自己的密码。
4.2 sudoers关键配置与安全细节
sudo权限的核心配置文件是/etc/sudoers。永远用visudo编辑,它会在保存前做语法检查,语法错误会导致sudo直接“罢工”。常用配置我放在下面:
# 允许wheel组成员执行所有sudo命令 %wheel ALL=(ALL) ALL # 允许zhangsan免密执行特定命令 zhangsan ALL=(ALL) NOPASSWD: /usr/bin/systemctl # 只允许lisi执行用户管理和密码修改相关命令 lisi ALL=(ALL) /usr/bin/passwd, /usr/bin/chage注意第三行,如果把passwd授权给某个用户,他不仅能改自己的密码,还能改root密码。如果你想限制他只能改普通用户密码,不能动root,需要写成:
lisi ALL=(ALL) /usr/bin/passwd, !/usr/bin/passwd root!号表示排除。sudoers的匹配顺序是“先到先得”,把排除项放在允许项后面才能生效,这个坑我踩过:把!/usr/bin/passwd root写在前面,结果还是能改root密码,因为后面的允许规则又把权限加回去了。
还有几个建议:
- 尽量不要配置NOPASSWD给
ALL,免密sudo等于把root钥匙挂在普通用户脖子上,一旦普通用户密码泄露,攻击者直接拿到管理员权限。 - 少用
sudo su -,这种写法等于切到root再切到root,sudo日志里只记录su命令,看不到实际执行的子命令,审计时等于睁眼瞎。 - sudo会记录执行日志,通过
journalctl | grep sudo能查到谁在什么时间执行了什么命令,出事时这是第一手证据。
5. 实际操作中高频踩坑与排查速查
5.1 ERROR 1045 (28000):MySQL root账号的密码问题
这个报错是数据库层面的事,和系统root密码无关,很多人误以为改了Linux root密码就能解决MySQL登录,其实两码事。报错长这样:
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)意思是MySQL的root账号密码不对。标准处理流程是用MySQL的安全模式重置密码:
- 停止MySQL服务:
systemctl stop mysqld- 以跳过授权表的方式启动:
mysqld_safe --skip-grant-tables &- 无密码登录:
mysql -u root- 切换到mysql库并重置密码:
USE mysql; UPDATE user SET authentication_string=PASSWORD('NewPass@2025') WHERE User='root'; FLUSH PRIVILEGES;不同MySQL版本字段不同,5.7及以上用authentication_string,5.6及之前用password字段。MariaDB也别搞混,字段也是authentication_string。重置完记得把正常模式启动,别让服务一直停留在--skip-grant-tables状态下,否则任何人不输入密码都能登进去,这状态只能临时救急用。
5.2 改了密码之后的服务与SSH异常
改完普通用户密码后,最常见的连锁反应是SSH登录失败。先检查/etc/ssh/sshd_config:
cat /etc/ssh/sshd_config | grep -E 'PasswordAuthentication|PubkeyAuthentication|PermitRootLogin'如果PasswordAuthentication no,那么就算密码改对了也登录不了,因为SSH默认只允许密钥登录。另外别忘了/etc/nologin的存在,这是个冷门坑:只要这个文件存在,所有非root用户都会无法登录,登录时提示系统正在维护。改密码无效时,检查这个文件是否残留。
还有一类问题来自PAM锁定。如果你在/etc/security/faillock.conf里设置了失败锁定,用户连续输错几次密码后被锁,这时即使密码是对的,也会被“Authentication failure”挡在外面。解除锁定:
faillock --user zhangsan --reset另外,把用户从某个组移除后再加回来,有时会碰到文件权限异常。比如用户之前创建的文件的属主组信息还在旧组上,这不是密码问题,是组ID残留,别对着密码排查半天。
5.3 防止密码被暴力破解的几条硬经验
很多服务器被入侵都不是因为黑客多高明,而是密码太弱、暴露面太大。结合我自己的运维经验,给你几条直接能落地的硬措施:
- 禁止root直接SSH登录:在
sshd_config里设为PermitRootLogin no,所有人都用普通用户登录,再sudo提权。就算有人拿到了普通用户密码,也比直接爆破root的难度大得多。 - 启用密钥登录:
PubkeyAuthentication yes,生产环境能关掉密码登录就直接关掉,密码只留着本地控制台备用。 - 限制登录失败次数:配置好
faillock.conf,连续失败几次就临时封禁,配合fail2ban能自动封禁来源IP。 - 定期轮换密码:用上面说的
chage -M 90做强制过期,别让密码用“一辈子”。 - 检查弱密码账号:定期扫描
/etc/shadow,检测哈希为$1$(MD5)或空密码的账号,这些等于门户大开。
最后再分享一个小技巧,也是我个人的规矩:每次改完root或普通用户密码,都要在交接记录里写下“修改人、生效时间、验证方式”,并且当场用新密码完成一次登录验证再结算。密码管理这件事,技术含量不算高,但出事往往不是技术问题,而是流程漏洞。把方案做实、把审计做全,比记住多少命令都重要。