1. 踩坑场景回顾:半夜接到电话,说root密码没了
做运维的朋友应该都有过这种经历:某台虚拟机安安静静跑了大半年,突然某天某位同学离职前交接不清,或者轮换密码时漏了台账,然后所有人瞪着登录页发呆——root密码输入十遍全错,sudo用户也不存在,业务还要等着重启。我遇到过最夸张的一次,是某交付项目在验收前夜发现测试环境的所有root密码都被上一批外包统一改成了一段随机串,没人记得备份。
这种时候,第一反应往往是“重装系统”。但重装一台跑着数据库或者对接了内网DNS的虚拟机,代价远大于把密码重置掉。所以“重置虚拟机root用户密码”本质上不是什么高深技术,它是一套在失去登录凭据后,通过引导阶段介入操作系统恢复控制权的方法论。这篇文章不讨论入侵、不讨论破解他人系统,只讲一个合法且有边界的使用场景:你自己负责的虚拟机,因为遗忘、轮换遗漏或交接失误导致root不可用,如何在不重装的前提下把密码重新改回来。
文章会覆盖三种主流恢复路径:
- 单用户模式重置:最常用,适用于能进GRUB菜单、根文件系统没坏的情况;
- 救援模式/挂载重置:适用于引导直接崩了、或者根分区是LVM/加密卷的特殊case;
- Alternative方案:不用重启的办法,比如借助sudo、SSH密钥、云厂商元数据注入等绕过密码的通道。
另外会花比较大的篇幅写“重置完之后的坑”——很多人卡在SELinux、cloud-init、时间同步这类后遗症上,改完密码进不去,还以为是自己操作错了。看完这篇文章,你不仅能自己搞定,还能把排查思路讲给别人听。
2. 动手之前,先判断环境与恢复路径
很多人上来就搜“grub按e加single”,然后照着敲,结果进了单用户却挂载不了根分区,或者在恢复了旧的shadow文件之后重启又被cloud-init把密码改回去。我有责任先泼一盆冷水:重置root密码的第一件事不是敲命令,而是判断环境。
2.1 先确认虚拟化平台与启动模式
虚拟化平台决定了你能不能在虚拟机之外“插手”系统启动:
- 本地KVM/libvirt虚拟机:通过VNC或console进GRUB,改启动参数即可;
- VMware/vSphere虚拟机:同样能通过vCenter的Web Console与GRUB交互,操作路径基本一致;
- VirtualBox虚拟机:也能在图形界面上直接按e进编辑态;
- 云厂商实例:大多数云平台不开放GRUB交互(尤其Windows实例完全没法用这套),只能走云厂商自己的救援/重置通道,或者用“重置密码”按钮——其底层原理要么是挂载一块临时盘去改,要么是通过元数据注入初始化脚本;
- 物理服务器:走BMC/IPMI的远程控制台,原理和VM控制台一样。
这里还要再分一个层级:BIOS启动和UEFI启动在grub交互阶段略有差异。老式BIOS直接按e编辑启动项即可,UEFI系统在少数发行版上可能需要先按Esc把GRUB子菜单调出来再编辑。不过99%的日常操作都不涉及这个差异,你只要知道:能看得见GRUB菜单,就能进单用户。
2.2 判断“这台机器能不能随便重启”
重置root密码的绝大多数方法都依赖重启虚拟机,所以动手前必须评估业务影响:
- 这台VM是不是集群里的一个节点?是否有其他副本在对外提供服务?
- 它有没有挂载独立的数据盘?重置系统盘密码不会碰数据盘,但如果引导链本身已经坏了,应该优先处理数据盘备份;
- 你有没有权限关机?有些测试环境卡在云平台的“实例释放”保护里,重启按钮都按不了,这种情况要及时找平台管理员。
这一环节没有太多技巧,核心就是“先备份,后操作”。如果是生产环境且没有冗余,建议先做快照——无论KVM的virsh snapshot还是云平台的控制台快照,十几分钟的事,能为后面所有操作兜底。
2.3 恢复路径选择判断表
我习惯先画一个大致的判别表出来,现场操作时照着判断,比临时搜教程快得多:
| 系统状态 | 推荐路径 | 难度 | 是否需重启 |
|---|---|---|---|
| GRUB可见,内核能引导,只是密码失效 | 单用户模式修改shadow | 低 | 是 |
| GRUB可见,但引导后卡在initramfs报错 | 救援模式/initramfs shell | 中 | 是 |
| 整个系统起不来,控制台无GRUB | LiveCD挂载磁盘修改 | 中 | 是 |
| 不想重启,且有sudo或SSH密钥通道 | 通过sudo执行passwd / 密钥登录 | 低 | 否 |
| 云平台实例 | 云厂商重置密码/救援模式 | 低 | 视平台而定 |
| root文件系统是LVM或加密卷 | 单用户基础上增加vgchange/luksopen步骤 | 中高 | 是 |
这张表我建议收藏一下,它省掉了不少“哪种方法才是最优”的纠结。
3. 单用户模式重置:最经典路径,也是最容易踩坑的路径
单用户模式重置的思路概括起来就一句话:让内核在切换到完整多用户环境之前,先给你一个root shell。在这个shell里,系统只启动最小必要组件,普通用户、网络服务什么都不加载——所以你可以绕过登录认证,直接操作文件系统。
3.1 RHEL/CentOS系:rd.break参数
以CentOS 7/8、RHEL 7/8、Rocky/Alma为代表,这类系统用的是grub2配合systemd,重置密码标准的做法是“rd.break”。操作流程如下:
- 在GRUB菜单界面,选中要启动的内核条目,按
e进入编辑模式; - 找到以
linux16(旧版)或linux(新版)开头的那一行,这是内核启动参数所在行; - 在该行的末尾追加
rd.break,注意前后留一个空格; - 按
Ctrl+X或直接按b启动系统; - 系统会进入一个
switch_root:/#或类似提示符的initramfs紧急shell。
很多教程到这里就不写了,其实最关键的下一步是:当前根文件系统处于只读挂载状态,必须重新挂载可写才能修改密码。
mount -o remount,rw /sysroot chroot /sysroot passwd root touch /.autorelabel exit exit这里mount -o remount,rw /sysroot是把原系统根分区以可读写方式重新挂载;chroot /sysroot是切进原来的系统环境;passwd root设置新密码;touch /.autorelabel是给SELinux打上“重启后自动重新标记文件安全上下文”的标记,防止因为修改了shadow文件导致SELinux上下文异常,从而无法正常登录。
提示:如果你跳过
touch /.autorelabel,重启后大概率会卡在登录界面或者出现各种SELinux拒绝访问,我当时第一次操作就被坑过一次。宁可让它多等几分钟做relabel,也不要偷这个懒。
3.2 Debian/Ubuntu系:single或recovery模式
Debian和Ubuntu系的路径略有不同。Ubuntu的GRUB菜单里通常自带“Advanced options for Ubuntu”,里面能看到带(recovery mode)字样的内核条目,选它进入恢复菜单:
- 在恢复菜单里选择
root - Drop to root shell prompt; - 此时系统提示当前是只读权限,需要先执行
mount -o remount,rw /; - 然后直接
passwd root或者passwd 用户名; - 重启即可。
如果没有recovery菜单项,也可以在编辑启动参数时,在linux行末尾追加single或init=/bin/bash,效果类似。相对RHEL系来说,Debian系没有SELinux那层麻烦,重置操作会顺利很多,但Ubuntu的默认root账号是锁定的,如果你改的是root密码,后续需要用sudo passwd root显式解锁才行。
3.3 为什么必须向根分区传“rw”参数?
这两个例子都涉及“只读”和“可写”的切换,我刚学这个操作时也很困惑:为什么要先以只读方式启动,再手动改可写?
其实这是内核启动阶段的设计使然:initramfs阶段挂载根分区时,默认不会直接启用完整的写模式,因为此时真正的系统还没有被完全初始化,贸然打开写权限存在风险。所以大部分发行版会以只读方式挂载根分区,等系统真正ready后再切换到写状态。我们在单用户模式下获得shell后,系统的“真正ready”永远没有发生——因此必须手动补上remount,rw这一步。
理解了这一点,你以后遇到“明明执行了passwd,重启后密码没变”的问题时,就知道第一时间该查挂载状态了。虽然命令就一行,但背后的原理确实值得理解。
3.4 为什么chroot之后才执行passwd
另一个被很多人问的点是:为什么要在rd.break后先chroot,而不是直接passwd?
原因是rd.break进入的shell其实是initramfs这个小环境,密码文件/etc/shadow根本不在这个临时根目录里,真正的密码数据库要等你切进/sysroot下的原系统才能访问。chroot /sysroot相当于把原系统的根目录“平移到”了当前shell的根位置,后面的passwd才能作用于真实系统。
同理,挂载/proc、/sys、/dev这些虚拟文件系统也是为了让chroot环境里的命令正常工作。如果你进入shell后只是改个密码,不做其他操作,其实不挂载这三个目录也没问题,但如果要执行yum/apt或者systemctl这类依赖proc信息的命令,就必须先挂载。
4. 重置之后,别急着收工:这几件事不做等于白干
密码改成功、系统也能正常登录,看起来流程走完了。但根据我的实测经验,至少有四类“后遗症”会在这时候等着你。
4.1 SELinux重新标记
我在前面提过touch /.autorelabel,这一步在RHEL系里特别重要。因为你用单用户模式改shadow密码文件时,文件的安全上下文可能会和策略数据库不一致,导致重启后SELinux拒绝sshd读取shadow文件,表现就是“密码明明对了,就是登录不进”。
设置了自动relabel之后,重启会在早期阶段重新扫描并标记所有文件的安全上下文,相当于做了一次SELinux的“体检”。代价是首次重启会比较慢,尤其文件多的机器可能要等多几分钟,这很正常,别中途重启。
如果你希望手动触发重新标记,也可以等进入系统后执行:
touch /.autorelabel reboot4.2 cloud-init的反弹:密码被“改回去”
云平台虚拟机通常会安装cloud-init,它的一个职责是启动时从元数据服务拉取用户数据并应用配置。如果你在重置密码后重启,而云平台侧还残留着之前的初始化脚本或密码配置,cloud-init很可能在系统启动时把你的新密码再次覆盖掉。
我见过一个具体案例:有人在某测试云主机上手动改完root密码,重启后又变回旧密码,反复三次,最后才定位到是user-data里的一段runcmd脚本在每次启动都重新chpasswd。解决方案是:
# 清理不必要的cloud-init实例数据 cloud-init clean # 或者直接编辑 /etc/cloud/cloud.cfg.d/ 下相关的用户配置当然,如果你用的云平台本身就有“重置密码”功能,最稳妥的反而是直接用平台的功能重置,不要自己手动改密码后再和cloud-init打架。
4.3 账号锁定状态与shadow文件的细节
平时我们设置的密码在/etc/shadow里长这样:
root:$6$salt$hash:19000:0:99999:7:::如果字段的第二个值是!或*,表示这个账号被锁定了。有些人在重置时图省事,直接手动编辑shadow文件填入了加密哈希,但忘了用户密码字段里还带着!前缀,结果怎么看都像是密码“没改成功”。
所以执行完passwd之后,建议顺手验证一下:
chage -l root passwd -S root看到root ... P这样的状态才是正常的。另外,如果你在单用户shell里用chroot改了密码,退出后一定记得执行sync再重启,避免缓存没有落盘。
4.4 时间同步与证书问题
重置操作本身不会动时间,但长期没有正常登录的系统,时间不同步会导致后续的证书验证、Kerberos认证等全部出问题。如果这台机器很久没开过机或者休眠过,建议在重置完密码后顺手检查:
timedatectl status chronyc tracking检测到时间漂移就立即chronyc makestep或重启chronyd服务。
5. 不用重启方案的替代路径:有sudo就够了
前面提到重启是重置密码最常见的代价,但并不是所有场景都必须重启。如果系统里还有可用的sudo用户,或者你保留过SSH密钥,那完全可以绕开重启。
5.1 通过sudo直接改密码
假设有一个用户alice在sudo组里,且知道alice的密码,那没有任何悬念:
sudo passwd root或者批量把密码传给标准输入:
echo 'new_password' | sudo chpasswd密钥登录也是一个隐藏的通道:如果root或某sudo用户的~/.ssh/authorized_keys里存在有效公钥,且sshd允许密钥登录,你可以直接登录进去执行上面的操作。这也是我之前为什么一直建议运维人员为关键服务器提前配置好SSH密钥对——哪怕密码全忘光了,密钥还在就意味着系统没对你关死门。
5.2 批量环境下的密码轮换
如果你管理几十台虚拟机,且都有ansible等自动化工具的连接通道,那重置密码就更不必一台台重启。写个Playbook把所有机器的root密码统一轮换掉:
- hosts: all become: true vars: new_password: "{{ lookup('password', '/dev/null length=20 chars=ascii_letters,digits') }}" tasks: - name: Change root password user: name: root password: "{{ new_password | password_hash('sha512') }}" register: result - name: Save password to local file local_action: module: copy content: "{{ inventory_hostname }}:{{ new_password }}" dest: "./passwords/{{ inventory_hostname }}.txt"这段playbook的任务是逐台机器地生成随机密码、通过提权方式修改root密码,并把每台机器的密码保存到本地文件里。它最大的好处是:不需要重启任何机器,也不依赖物理控制台,适合那种上百台机器的场景批量执行。
5.3 云厂商的“重置密码”按钮底层原理
很多云平台提供的“重置密码”功能,本质上也不是在虚拟机内部执行的——平台会先关闭实例,把系统盘作为数据盘挂载到一台临时的辅助机器上,然后在辅助机器上挂载并修改系统盘中的shadow文件,最后卸载、重新启动实例。这和后面要讲的LiveCD方案几乎同源。
有点讽刺的是,这个机制也提醒我们:只要拿得到系统盘,密码验证就只是一个文件层面的访问控制,而不是什么不可攻破的堡垒。这也是虚拟化环境下做密码恢复比物理机方便得多的根本原因。
6. 引导彻底坏了时:LiveCD和救援模式的完整操作
有些情况下,系统连GRUB都进不去,比如grub配置文件损坏、内核被误删、文件系统出现脏标记。此时单用户模式也没法用了,只能走“外来者”路径。
6.1 通过ISO镜像启动Live环境
在KVM里,可以用宿主机挂载一个Linux发行版的安装ISO,设置虚拟机从CD-ROM引导;如果是VirtualBox,在设置里把启动顺序改成光驱优先。启动到Live桌面或Live命令行环境后,我们已经有了一套独立的、运行在内存里的Linux系统,此时原虚拟机的硬盘只是一块普通的数据盘。
6.2 挂载原分区,然后chroot
挂载操作要根据原系统的分区情况来。先看磁盘结构:
fdisk -l /dev/vda lsblk假设根分区是/dev/vda2,那么:
mkdir -p /mnt/sysroot mount /dev/vda2 /mnt/sysroot mount --bind /proc /mnt/sysroot/proc mount --bind /sys /mnt/sysroot/sys mount --bind /dev /mnt/sysroot/dev chroot /mnt/sysroot进入chroot环境后,直接passwd root。这里补充一个细节:mount --bind是Linux的“绑定挂载”,能把当前系统里的某个目录“镜像”到另一个挂载点,Live环境里的原系统看不到/proc下的实时内核信息,所以必须把它绑定到原系统根目录里,chroot里的工具才能正常工作。
6.3 如果根分区是LVM
很多企业环境的根分区都不是传统分区,而是LVM逻辑卷,Live系统里不会有现成的卷组激活。需要先:
vgchange -ay lvscan然后找到逻辑卷路径,比如/dev/vg_system/lv_root,再mount。第一次做LVM挂载的人容易懵的是:fdisk -l看不到root分区,以为磁盘没被识别。其实逻辑卷要等vgchange -ay激活卷组后才能通过/dev/映射路径访问。
6.4 如果根分区是加密卷
加密根分区在Live环境里会更麻烦一点。由于系统盘被LUKS加密了,挂载前必须先解密:
cryptsetup luksOpen /dev/vda2 myroot系统会交互式要求输入LUKS密码(注意这不是系统登录密码,而是磁盘加密的密码)。如果连这个密码都不知道,那只能说明这整台机器的凭据管理已经全线崩溃,需要找到当初部署时记录的密钥备份。
6.5 云平台的救援模式
云平台(各厂商叫法略有不同,有的叫“救援模式”或“单用户”功能)本质上是平台自动帮你挂载系统盘到一台临时救援实例,然后允许你执行类似上面的chroot流程。使用救援模式的要点是:操作前必须关闭原实例,而且不同云厂商的救援实例默认OS可能与原系统不一致,挂载时要注意文件系统格式兼容,比如原系统是XFS,救援机是ext4工具链,可能需要单独安装xfsprogs。
7. 最容易翻车的五个细节,以及预防建议
这部分归纳我多次实际排查后总结出的高概率翻车点。它们不算高深,但往往决定了整个重置流程是五分钟搞定还是一小时起步。
7.1 只读挂载导致“改完没生效”
发生在单用户模式里,很多人执行passwd之后系统没有任何报错,但重启后密码不变。原因是根分区此时是只读的,passwd命令“误以为”自己写成功了,实际内容根本没有落盘。所以无论你用什么方式进入恢复环境,执行任何写操作前第一件事一定是确认当前根挂载是可写的。
7.2 GRUB参数拼写错误且无提示
常见的是把rd.break写成了rdbreak,把single写到了linux16行的开头而不是末尾。这类错误通常不会被提示,系统会正常启动到多用户模式——好比你把钥匙放错了抽屉,锁还是锁着。建议输入完成后多用光标检查一遍,确认空格分割没问题。
7.3 磁盘顺序漂移导致的挂载错位
在多磁盘虚拟机上,如果用LiveCD挂载系统盘,新环境里的磁盘枚举顺序很可能和原系统不同,/dev/sda未必是原来的系统盘。建议用blkid按UUID识别分区,而不是按设备名。否则挂错了盘、改了另一个分区的密码,事后本人可能都说不清问题出在哪。
7.4 修改shadow文件权限或属主
有些教程会教你直接编辑shadow文件来替换密码哈希,这是可行方案,但容易在编辑后把文件权限改乱。shadow文件必须保持000或600权限、属主root:root,否则系统会直接拒绝使用该文件——表现是登录时“认证失败”,连报错都不给你看细节。如果你非要走编辑shadow这条路,一定要在事后执行:
chmod 600 /etc/shadow chown root:root /etc/shadow7.5 密码复杂度策略拦截
有些企业镜像预置了pam_pwquality模块,要求新密码满足长度和复杂度。如果你在单用户模式下设置了一个过于简单的密码,当时可能没报错,但后续通过ssh登录时会被PAM拒绝。建议至少用8位以上+大小写+数字组合,避免为了图省事设一个“123456”结果半天进不去。
预防永远比救援便宜。除了上面这些操作层面的建议,我更想强调制度层面的三个事:第一,关键机器提前配置SSH密钥,别只依赖密码;第二,密码变更必须同步更新运维台账,并设置周期轮换提醒;第三,每台虚拟机在交付时就应该记录好管理员凭据的应急处置方式——比如是否支持单用户模式、是否加密盘、是否会自动被cloud-init重置等。
等到事故发生时再研究这些,恰恰是最贵的时候。
我自己的习惯是,在每次重置完密码后,会顺手写一行带时间戳的操作记录,内容包括:触发原因、重置路径、是否触发过SELinux relabel、新密码何时轮换。短期看是多了一行字,长期看在季度审计或交接时能少掉很多“这个root密码到底是谁设的”这种毫无意义的争论。密码管理本质上是个“可追溯”问题,而不是单纯的技术问题。