RHEL 9系统救援:root密码重置与GRUB密码加固实战
1. 系统救援场景下的密码恢复整体设计思路
1.1 为什么重置root密码和GRUB密码要放在一起做
很多人在运维现场遇到的情况是这样的:一台跑了几年业务的生产服务器,root密码被前任工程师带走,或者交接文档里压根没记录;同时这台机器的GRUB引导菜单还是裸奔状态,任何能接触到物理机或带外管理口的人,重启时按一下方向键就能进单用户模式,等于系统安全形同虚设。这两件事本质上是同一个安全链条上的两个环节——root密码是系统内的最后一道门,GRUB密码是系统外的第一道门。只重置root密码而不锁GRUB,等于把钥匙换了但窗户还开着;只设GRUB密码而不确认root密码可用,万一引导参数改错连救援入口都进不去。
所以我把这两个操作合并成一次完整的救援流程来设计。核心思路是:先通过引导加载器的编辑能力拿到一个临时的、无需密码的root shell,完成密码重置;然后趁着手上有root权限,立刻把GRUB的编辑权限和引导参数锁定,形成闭环。这个顺序不能反——先设GRUB密码再重置root密码,会导致你下次想进救援模式时被自己设的密码挡住,虽然也能解,但多绕一圈。
1.2 方案选型:为什么走rd.break而不是init=/bin/bash
在RHEL 9这类使用systemd和SELinux的发行版上,重置root密码有几种常见路径,我逐一说明取舍逻辑。
第一种是传统的init=/bin/bash方式,在GRUB编辑界面把ro改成rw init=/bin/bash。这种方式能拿到shell,但它绕过了systemd的初始化流程,SELinux上下文不会被正确加载,/etc/shadow的标签可能出问题,改完密码后重启偶尔会遇到登录异常。而且RHEL 9默认开启SELinux enforcing,这种粗暴方式的风险明显偏高。
第二种是rd.break方式,在引导参数末尾加rd.break,系统会在initramfs阶段暂停,挂载的是只读的sysroot。这种方式的好处是initramfs环境干净、可控,通过mount -o remount,rw /sysroot重新挂载后,再用chroot /sysroot切进真实系统,此时SELinux的重新打标机制(/.autorelabel)可以正常触发。这是RHEL官方文档推荐的做法,也是我在生产环境反复验证过最稳的路径。
第三种是救援模式(Rescue Mode)从安装介质引导,适合系统完全无法引导的极端情况。但它需要额外的ISO或U盘,现场不一定具备,所以作为备选而非首选。
综合下来,rd.break是首选,理由是:不需要外部介质、不破坏SELinux上下文、步骤可逆、对文件系统损伤最小。下面所有实操都基于这条路径展开。
1.3 整体流程的四个阶段划分
我把整个救援过程拆成四个阶段,每个阶段有明确的输入输出,方便你在现场按节奏推进,也方便出问题时定位卡在哪一步。
- 阶段一:引导中断与参数注入——在GRUB菜单阶段拦截引导,进入编辑模式,追加
rd.break参数。 - 阶段二:initramfs环境下的挂载与切换——重新挂载sysroot为可写,chroot进入真实根文件系统。
- 阶段三:密码重置与SELinux打标——用passwd改密码,创建
/.autorelabel触发重启后自动重打标签。 - 阶段四:GRUB密码加固——系统正常启动后,用
grub2-setpassword设置引导密码,并验证编辑权限已被锁定。
这四个阶段环环相扣,阶段三的/.autorelabel如果漏掉,阶段四重启后可能因为SELinux标签错乱导致服务起不来,这是新手最容易踩的坑,后面会详细讲。
2. 核心细节解析与实操前的关键准备
2.1 进入GRUB编辑界面的时机把握
RHEL 9默认的GRUB超时是5秒,而且很多云环境或带外管理口下,启动画面一闪而过。实操时我的习惯是:机器上电后立刻开始连续敲击Esc键(部分固件是Shift),不要等看到菜单再按。如果错过了,就等系统起来后reboot重来,别在启动过程中乱按其他键,容易触发固件层面的中断。
看到GRUB菜单后,光标停在默认启动项上,按e进入编辑模式。这里有个细节:RHEL 9的GRUB菜单可能显示的是Red Hat Enterprise Linux (9.0) ...这样的条目,你要编辑的是当前要高亮启动的那一项,别选错了内核版本。编辑界面里会看到以linux开头的一长行,这就是内核引导参数行。
注意:编辑界面是vi风格的简易编辑器,方向键可能不灵,用
Ctrl+E跳到行尾更可靠。改完之后用Ctrl+X直接引导,不要用F10,某些固件下F10会被拦截。
2.2 rd.break参数的作用原理拆解
rd.break这个参数的字面意思是"在ramdisk阶段中断"。要理解它,得先知道Linux启动的大致链条:固件→GRUB→内核→initramfs(内存根文件系统)→切换根→systemd→用户空间。initramfs是一个临时的小型根文件系统,负责加载必要的驱动、挂载真实根分区,然后通过switch_root把控制权交给真实系统。
rd.break的作用就是在initramfs完成挂载、但还没执行switch_root之前,抛出一个调试shell。此时真实根文件系统已经被挂载到/sysroot目录下,但它是只读的。这个设计非常巧妙——只读挂载保证了你在救援过程中不会意外破坏数据,同时/sysroot路径让你能访问到真实系统的所有文件。
对比一下rd.break=pre-mount、rd.break=cmdline这些变体,默认的rd.break(等价于rd.break=pre-pivot)是最适合改密码的,因为此时根分区已经挂载好了,你不需要自己去找分区设备名。
2.3 实操前的环境确认清单
动手之前,花两分钟确认几件事,能省掉后面大量返工。
| 确认项 | 检查方法 | 不确认的后果 |
|---|---|---|
| 是否有带外管理或物理接触权限 | 确认iDRAC/iLO/IPMI/KVM可用 | 无法在GRUB阶段拦截 |
| 系统是否启用全盘加密 | 回忆安装时是否勾选LUKS | 加密盘需先解锁才能进rd.break |
| 是否有近期备份 | 确认备份任务状态 | 操作失误时无回退余地 |
| 是否记录当前GRUB配置 | 启动后cat /etc/default/grub | 改错参数难以还原 |
| 业务是否允许重启 | 确认维护窗口 | 生产环境贸然重启影响业务 |
特别强调全盘加密这一项。如果根分区是LUKS加密的,rd.break阶段会先要求输入磁盘解锁密码,输对了才能继续。如果你连磁盘密码都没有,那这条路径走不通,只能考虑从备份恢复或重装。这个判断要在动手前做,别进了rd.break才发现卡在解锁环节。
2.4 SELinux上下文为什么必须重新打标
这是整个流程里技术含量最高、也最容易被忽视的点。RHEL 9默认SELinux策略是enforcing,每个文件都带有一个安全上下文标签(比如/etc/shadow的标签是shadow_t)。当你用passwd修改密码时,文件内容变了,但更关键的是——在chroot环境下,某些操作可能导致新写入的文件继承错误的标签。
/.autorelabel这个空文件是一个触发器。系统下次启动时,如果检测到这个文件存在,会在早期启动阶段对整个文件系统做一次完整的SELinux重新打标,然后自动删除该文件并再次重启。这个过程可能耗时几分钟到几十分钟(取决于文件数量),期间系统看起来像卡住了,其实是正常的。
提示:如果不创建
/.autorelabel,轻则密码改了但登录被SELinux拒绝,重则系统启动后大量服务因标签错误无法启动。这个文件是保命的,千万别省。
3. 完整实操过程与核心环节实现
3.1 阶段一:拦截引导并注入rd.break参数
机器重启,在固件自检画面过后、GRUB菜单出现的瞬间,连续按Esc(或Shift)停住菜单。光标默认停在第一项,按e进入编辑。
找到以linux开头的那一行,它大概长这样(不同硬件略有差异):
linux ($root)/vmlinuz-5.14.0-70.el9.x86_64 root=/dev/mapper/rhel-root ro crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet把光标移到行尾(Ctrl+E),在quiet后面加一个空格,然后输入rd.break。改完后这一行末尾应该是:
... rhgb quiet rd.break按Ctrl+X引导。系统会快速滚过一堆initramfs的日志,最后停在一个提示符:
switch_root:/#看到这个提示符,说明阶段一成功,你已经站在initramfs的调试shell里了。
3.2 阶段二:重新挂载sysroot并chroot切换
此时/sysroot是只读的,直接改密码会失败。先重新挂载为可写:
mount -o remount,rw /sysroot这条命令的意图很明确:-o remount,rw是在不卸载的前提下改变挂载属性,比先umount再mount安全得多,因为根分区上可能有正在被引用的文件。执行后没有任何输出就是成功,Linux的哲学是"沉默即正常"。
接着切换根:
chroot /sysrootchroot的作用是把/sysroot当作新的根目录,之后所有命令都在真实系统的文件系统里执行。切换后提示符会变成sh-5.1#之类的形式。此时你操作的/etc/shadow就是真实系统的那个文件,不是initramfs里的临时副本。
验证一下切换是否成功,可以看一眼:
ls /etc/redhat-release能正常列出文件,说明chroot生效。
3.3 阶段三:重置root密码并触发SELinux打标
现在可以改密码了:
passwd root系统会提示输入新密码两次。这里有个实操细节:RHEL 9默认的密码复杂度策略(pwquality)可能拒绝过于简单的密码,如果提示"BAD PASSWORD",要么换复杂点的,要么临时降低策略(不推荐在生产上这么做)。输入时屏幕不显示字符,这是正常的,别以为键盘坏了。
密码改完后,立刻创建打标触发器:
touch /.autorelabel这一步不能忘。创建完可以用ls -la /.autorelabel确认文件存在。
然后退出chroot并重启:
exit reboot如果reboot命令在initramfs环境下不响应(偶发),可以用reboot -f强制重启,或者直接按机箱电源键(前提是文件系统已同步,sync一下更保险)。
重启后系统会自动进行SELinux重新打标,屏幕会显示类似*** Warning -- SELinux targeted policy relabel is required ***的提示,然后自动再重启一次。这个过程中千万不要断电,打标中断可能导致文件系统标签处于半完成状态,后续排查非常麻烦。耐心等它跑完,看到正常登录提示符就说明root密码重置成功。
3.4 阶段四:用grub2-setpassword锁定引导编辑权限
用新密码登录root后,开始加固GRUB。RHEL 9提供了专门的工具grub2-setpassword,比手动改配置文件可靠得多。
直接执行:
grub2-setpassword系统会提示输入GRUB用户名(默认root,可以直接回车)和两次密码。这个密码和系统root密码是独立的,可以设成一样方便记忆,也可以设成不同的增强隔离性。我个人的习惯是设成不同的,因为GRUB密码是在引导阶段明文交互的,泄露面比系统登录更大。
执行成功后,工具会在/boot/grub2/目录下生成user.cfg文件,里面存储的是密码的PBKDF2哈希,不是明文。你可以验证一下:
cat /boot/grub2/user.cfg看到GRUB2_PASSWORD=grub.pbkdf2.sha512...这样的内容就对了。
3.5 验证GRUB密码是否真正生效
设完不算完,必须验证。重启机器,在GRUB菜单出现时按e,此时应该弹出用户名和密码的输入提示。如果还能直接进编辑界面,说明密码没生效,需要排查。
验证通过后,还要确认正常引导不受影响——输入正确密码后能进编辑界面,或者直接回车正常启动。这两点都确认了,整个加固才算闭环。
另外,如果你希望连"选择其他启动项"都受保护,可以检查/etc/grub.d/下的配置,确保grub2-mkconfig生成的配置里包含了密码保护逻辑。grub2-setpassword通常已经处理好了,但不同小版本行为略有差异,验证一步不能省。
4. 常见问题与排查技巧实录
4.1 问题速查表
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| 按e没反应,直接启动了 | 错过了GRUB菜单时机 | 重启,上电即连续按Esc/Shift |
| rd.break后要求输入密码 | 磁盘启用了LUKS加密 | 输入磁盘解锁密码,无密码则此路不通 |
| chroot后passwd报错 | sysroot未remount为rw | 重新执行mount -o remount,rw /sysroot |
| 改完密码登录被拒 | 漏了/.autorelabel | 重进rd.break补创建,重启打标 |
| 打标过程卡住很久 | 文件数量多,正常现象 | 耐心等待,切勿断电 |
| grub2-setpassword命令不存在 | 极简安装未装grub2-tools | dnf install grub2-tools后重试 |
| 设了GRUB密码仍能编辑 | 配置未重新生成 | 执行grub2-mkconfig -o /boot/grub2/grub.cfg |
| 重启后GRUB密码失效 | 引导分区被覆盖或重装 | 重新执行grub2-setpassword |
4.2 三个我踩过的坑
第一个坑:以为改完密码就万事大吉,结果SELinux把登录挡了。早期我图省事跳过/.autorelabel,密码确实改了,但登录时PAM报SELinux拒绝,日志里一堆avc: denied。后来老老实实补上打标流程才解决。这个教训让我养成了"改shadow必打标"的肌肉记忆。
第二个坑:在云主机上按Esc没反应。某些云平台的Web控制台会把Esc键映射成浏览器行为,根本传不到虚拟机。解决办法是改用Shift键,或者在控制台设置里切换键盘模式。如果都不行,用带外管理口的"发送Ctrl+Alt+Del"重启后立刻用Shift拦截。
第三个坑:grub2-setpassword设完,忘了验证,结果某次维护时发现密码根本没生效。原因是那次系统做过内核升级,grub.cfg被重新生成,而user.cfg的引用在某些配置下会丢失。从那以后我每次设完密码都强制重启验证一遍,并且在内核升级后复查GRUB保护状态。
4.3 一个容易被忽略的收尾动作
密码都设好之后,建议把这次操作的记录写进运维文档:包括重置时间、操作人、GRUB密码的保管方式(比如放进密码管理器)、以及/.autorelabel是否已自动删除(ls /.autorelabel应该报文件不存在)。这些记录在下次交接或审计时价值极高,比事后回忆靠谱得多。
另外,如果你管理的机器数量较多,可以考虑把GRUB密码的设置纳入标准化装机流程,用配置管理工具统一推送,避免每台机器手工操作带来的不一致。这个扩展方向我在几个环境里试过,效果比事后补救好得多。