☰
Linux root密码重置与GRUB密码加固实操指南
2026/10/26 18:54:53 网站建设 项目流程

半夜被叫起来处理一台没人记得密码的 Redhat 服务器,这种场面做过几年运维的人都躲不过。重置 root 密码这件事本身不复杂,真正麻烦的是你改完之后发现同一个入口对所有人敞开——任何能碰到这台机器的人,都能用同样的手法再把密码改一遍。所以“重置 root 密码”和“修改 GRUB 配置文件设定密码”这两件事在实操里必须打包处理,前者是恢复访问,后者是收回访问,只做一半等于白干。这篇内容面向刚接手 Linux 服务器的新手,也面向需要把这套流程固化成标准操作步骤的老运维,我会把 GRUB 菜单里追加参数、remount 与 chroot 的关系、SELinux 上下文这道容易被忽略的关卡,以及三种 GRUB 加密写法的取舍全部拆开讲,配上可以直接抄的命令和踩过的坑。

1. 先搞清楚要解决的到底是什么问题

1.1 两个动作,三个真实痛点

很多人把这件事理解成“忘密码就重置一下”,实际上它至少对应三个不同的现场。第一种是真正的遗忘,交接文档缺失、前任离职没留信息、root 密码只存在于某个人的脑子里,机器还在跑业务,不能重装。第二种是合规检查提出要求,物理接触到服务器的人不应该能轻易拿到最高权限,这时候光有强密码不够,还得把单用户入口堵上。第三种是安全事件后的补救,怀疑有人动过系统,需要确认启动流程没有被植入额外参数,同时给 GRUB 加上认证。

这三个场景对操作的要求完全不同。第一种允许你停机重启,甚至可以在业务低峰期从容操作;第二种需要你先验证当前状态再改,改完还要做回归测试;第三种则要求保留证据、记录变更时间点。所以我一般不会一上来就给命令,而是先问清楚:这台机器是 BIOS 还是 UEFI 启动,/boot 是不是独立分区,SELinux 是什么状态,能不能物理接触,有没有带外管理口。这几个问题决定后面每一步的具体命令写法。

提示:在生产环境动手前,先确认这台机器有没有配置 LVM 快照或虚拟化平台的磁盘快照能力。有快照就做一个,成本几十秒,能省掉后面几小时的心跳加速。

1.2 单用户模式、rd.break、init=/bin/bash 三条路的取舍

绕过正常启动流程进入维护环境,主流有三种走法,各自的适用面差别很大。

方式进入后的环境优点明显短板
单用户模式(single/sulogin)系统正常挂载,多用户关闭简单,服务可用需要输入 root 密码才能进,密码忘了就用不上
rd.breakinitramfs 阶段中断,真实根在/sysroot不需要任何密码,SELinux 处理路径清晰需要手动 remount,命令略多
init=/bin/bash直接以 bash 作为 1 号进程命令最短服务全未启动,SELinux 上下文容易出问题,需要手动挂载 /proc /sys

密码忘了这个前提下,第一条路直接排除——单用户模式恰恰是要密码的那一步。剩下两条里我几乎总是选rd.break,原因很实际:它在 initramfs 阶段就停下来,此时系统的真实根文件系统挂在/sysroot下而且是只读的,你有机会先把它重新挂成可写,再 chroot 进去操作,整个过程对磁盘的写入是有控制的。而init=/bin/bash是让内核直接执行 bash,根文件系统挂载状态取决于内核参数,稍不注意就在只读状态下改文件,改完发现没生效,白折腾一轮。

init=/bin/bash也不是没用。在一些非常老的版本或者特殊定制内核上,rd.break参数不被识别,这时候它是兜底方案。用的时候记得把根分区显式挂成读写:mount -o remount,rw /,改完密码后不要用reboot,用exec /sbin/init或者reboot -f强制重启,否则可能卡在没卸载干净的状态。

1.3 动手前必须确认的三件事

第一件是启动方式。[ -d /sys/firmware/efi ] && echo UEFI || echo BIOS这条命令能直接告诉你答案。它的意义在于后面重新生成 GRUB 配置文件时路径不同:BIOS 机器读/boot/grub2/grub.cfg,UEFI 机器读/boot/efi/EFI/redhat/grub.cfg。写错路径的后果是配置文件看着改好了,重启后完全不生效,你会怀疑人生。

第二件是 SELinux 状态。getenforce返回Enforcing的话,改完密码后有一步不能省,后面单独讲。返回Disabled就轻松很多,但也要注意:如果之前是 Enforcing、只是临时 setenforce 0,磁盘上的标签仍然是启用的,重启后照样会校验。

第三件是访问通道。能不能接显示器键盘,有没有 IPMI、iDRAC、iLO 这类带外管理,或者云平台的 VNC 控制台。这一步看着像废话,但我见过太多人在 SSH 里琢磨怎么进 GRUB 菜单——远程是进不去的,必须走控制台。云主机的话在控制台里通常能发送Ctrl+Alt+Del或者直接选“重启并进入 GRUB”,但有些平台默认隐藏启动菜单,需要在开机瞬间连按方向键才能把菜单唤出来。

2. 打通 rd.break 通道:把 root 密码改回来

2.1 在 GRUB 菜单里追加 rd.break 的正确姿势

重启机器,在固件自检结束、GRUB 菜单出现的那一两秒内,按方向键(上或下)让倒计时停下来。有些系统默认GRUB_TIMEOUT=0,菜单一闪而过,这时候需要在/etc/default/grub里把超时改成 5 秒再执行一次grub2-mkconfig,但这台机器密码都不知道,改不了——所以只能靠手速,或者用带外管理里的“发送 NMI/重启并捕获启动”功能。

菜单停住之后,选中默认那一项,不要按回车,按e进入编辑。屏幕上会出现一段类似脚本的文本,找到以linux16(RHEL 7 系列)或者linux(RHEL 8/9 系列)开头的那一行。这一行最后通常跟着ro、rhgb、quiet、crashkernel=auto之类的内容。

操作要点:把光标移到这一行末尾,敲一个空格,然后追加rd.break。如果你想让启动信息全部打到屏幕上便于观察,顺手把rhgb和quiet删掉。改完之后不要按回车,按Ctrl+X或者F10引导。

# 修改前(RHEL 8/9 典型形态) linux ($root)/vmlinuz-5.x.x-xxx.el9.x86_64 root=/dev/mapper/rhel-root ro crashkernel=auto rhgb quiet # 修改后 linux ($root)/vmlinuz-5.x.x-xxx.el9.x86_64 root=/dev/mapper/rhel-root ro crashkernel=auto rd.break

这里有个新手常犯的错误:看到linux16和linuxefi两行就懵了。RHEL 7 的 UEFI 机器上通常有两行,一行linuxefi一行initrdefi,实际被使用的是带efi的那对。判断方法很简单——看哪一行的末尾参数和当前系统实际参数吻合,或者干脆两行都加rd.break,加错了也不影响。

2.2 remount /sysroot 与 chroot 的关系

成功进入rd.break后,提示符会变成switch_root:/#。这时候输入mount | grep sysroot看一眼,通常会看到/dev/mapper/rhel-root on /sysroot type xfs (ro,relatime...),注意那个ro。

现在解释一下为什么要 remount。initramfs 为了让系统能安全地做文件系统检查,默认把真实根以只读方式挂载。你在这个状态下执行passwd会报错,因为/etc/shadow写不进去。所以第一步:

# 把 /sysroot 重新挂载为可读写 mount -o remount,rw /sysroot

执行完再mount | grep sysroot,ro应该变成rw。这一步是后面所有操作的前提,没做的话后面passwd一定失败,而失败信息有时候很含糊,容易让人以为是别的原因。

接着是 chroot。当前环境是 initramfs 的精简系统,passwd命令也许存在但用的是 initramfs 里的库和配置,改的不是真实系统。chroot 的作用是把进程的根目录切换到/sysroot,让后续命令都在真实系统的文件树里执行:

chroot /sysroot

执行后提示符会变成sh-4.4#或sh-5.1#之类,说明已经进到真实系统里了。此时cat /etc/redhat-release能看到真实的版本号,这就是判断 chroot 成功与否最直观的方法。我一般还会顺手cat /etc/shadow | grep root确认一下文件确实存在且可读。

2.3 passwd 执行的细节与密码策略冲突

进到 chroot 环境后直接执行passwd root。系统会提示输入新密码两次。这里有几个坑。

第一个坑是密码复杂度。真实系统里的/etc/pam.d/passwd通常引用了pam_pwquality,会强制长度、字符种类、字典检查。在救援环境里设一个123456会被直接拒绝,报BAD PASSWORD: The password is shorter than 8 characters或者dictionary check failed。这时候不要急着去改 PAM 配置——那是给自己挖坑。正确做法是先设一个足够复杂的临时密码,比如大小写字母加数字加符号凑够 14 位,进去之后再按正常流程改成目标密码。

第二个坑是 NIS/LDAP 之类的集中认证。如果系统配置了远程认证,passwd可能会尝试连接认证服务器而卡住或者改到远程去。救援环境下网络通常不可用,表现出来就是命令长时间无响应。规避办法是直接用passwd -n不行,正确姿势是绕过 PAM:用openssl passwd生成哈希再手写进/etc/shadow。

# 生成 SHA-512 哈希(salt 随机) openssl passwd -6 'YourTempPassw0rd!' # 输出形如 $6$随机盐$哈希值 # 然后用 vipasswd 或者 ed 编辑 /etc/shadow,把 root 行的第二个字段替换掉

要注意不同版本openssl passwd的算法参数写法有差异,老版本用-1(MD5),新版本支持-6(SHA-512)。RHEL 8/9 默认是 SHA-512,字段以$6$开头。如果目标系统用的是yescrypt(部分新发行版默认),那还得用mkpasswd或者python3 -c "import crypt; print(crypt.crypt('pass','\$y\$j9T\$...'))"生成。改完/etc/shadow之后记得检查字段数量还是 8 个,多一个冒号就锁死了。

第三个坑是账户锁定。有些系统对 root 做过passwd -l或者usermod -L,/etc/shadow密码字段前面会带!!。passwd执行成功会把这个标记清掉,但如果是用手写哈希的方式,一定要确认没把!!留下来,否则密码改了照样登不上。

注意:rd.break 环境下passwd执行后如果提示passwd: Authentication token manipulation error,八成是/sysroot还是只读状态,回去做 remount 那一步。

2.4 那个 .autorelabel 到底要不要 touch

这是整套流程里最容易出安全事故的一步,也是网上教程里最语焉不详的一步。

背景是这样的:SELinux 给每个文件都打了安全上下文标签,/etc/shadow的标签是shadow_t。在正常的 Enforcing 系统里,passwd修改这个文件时,SELinux 策略允许它保留原有标签。但在rd.break这个特殊环境里,策略加载状态和正常运行环境不一样,有概率导致修改后的文件标签不对。标签错了会怎样?下次启动时systemd或者login读取/etc/shadow被 SELinux 拒绝,表现是输入了正确的 root 密码仍然提示登录失败,而且/var/log/secure里会出现avc: denied记录。

touch /.autorelabel的作用就是在根目录创建一个标记文件,下次启动时systemd检测到这个文件,会对整个文件系统做一次完整的 SELinux 重新打标签。代价是耗时——小盘几分钟,几个 TB 的大盘可能要一两个小时,期间系统看起来像卡住了,实际上是在跑restorecon。

# 标准收尾动作 touch /.autorelabel exit # 退出 chroot exit # 退出 rd.break 的 shell,触发重启

如果你确定机器盘很大、停机窗口紧张,有个更精准的替代方案:在 chroot 里只对涉及的文件做恢复。

# 在 chroot 环境内 restorecon -v /etc/shadow /etc/passwd /etc/group /etc/gshadow # 或者查看当前标签是否已经正确 ls -Z /etc/shadow # 期望看到 system_u:object_r:shadow_t:s0

标签正确就不用 touch 那个文件。但这个判断需要经验,如果拿不准,宁可花时间做全盘 relabel。我个人在第一次接触一台陌生机器时,一律走全量 relabel 路线,稳。等确认过几次标签没问题,后续再考虑走精准恢复。

还有个细节:/.autorelabel文件创建之后,如果因为某种原因没重启成功又被删了,或者系统启动完成后这个文件自动消失是正常现象——它是一次性的标记。如果重启后发现问题依旧,检查一下是不是relabel中途失败了,日志在/var/log/audit/audit.log和journalctl -b。

2.5 重启验证与失败回滚

exit两次之后系统会继续启动流程。这时候有件事要提前想好:如果你的 GRUB 密码策略要求认证,而你在上一轮改密码前还没配 GRUB 密码,那这次启动是免认证的,能顺利进系统。等会儿配好 GRUB 密码之后再重启,才会开始要密码。

进系统后的验证清单我一般会走一遍:

# 1. 确认当前身份 id # 2. 确认密码确实生效(不改,只验证) su - root -c 'echo ok' # 3. 确认 SELinux 状态和标签 getenforce ls -Z /etc/shadow # 4. 确认没有残留的登录失败记录在持续增长 tail -20 /var/log/secure # 5. 确认 selinux relabel 是否执行过 grep -i relabel /var/log/messages | tail

如果第 2 步失败,提示密码错误但你有百分百把握输对了,那基本就是 SELinux 标签问题,救急办法是在 GRUB 里追加selinux=0或者enforcing=0启动一次,进去执行touch /.autorelabel后重启。这个操作会临时降低安全级别,仅用于排障,不要当成长期方案。

3. 给 GRUB 上锁:从明文到 PBKDF2 的三条实现路线

3.1 为什么必须加密,明文密码等于没设

GRUB 的配置文件是纯文本,放在/boot/grub2/grub.cfg,任何能读这个文件的人都能看到里面的内容。如果你写的是:

set superusers="admin" password admin MySecret123

这个MySecret123就是明文躺在磁盘上。攻击者拿到磁盘镜像、或者有一次短暂的 root 权限,直接cat一下密码就到手了。更糟的是,这个密码往往和系统里其他地方的密码重复使用。

正确的做法是存哈希。GRUB 支持 PBKDF2 算法,生成一串以grub.pbkdf2.sha512.10000.开头的字符串。这个算法带迭代次数(10000 是默认值),本身就是为抗暴力破解设计的。配置里只存这个哈希,GRUB 在启动时把用户输入的密码用同样参数算一遍再比对。

这里要澄清一个常见误解:GRUB 密码不是用来保护系统的,它保护的只是“启动菜单”这个入口。它阻止的是别人通过编辑菜单追加rd.break或者init=/bin/bash来绕过系统认证。攻击者如果能把磁盘拆下来挂到别的机器上,GRUB 密码一点用都没有——那种情况要靠磁盘加密。所以 GRUB 密码的定位是“提高物理接触的门槛”,属于纵深防御的一层,不是万能锁。

3.2 路线一:grub2-setpassword 一条命令搞定

RHEL 7.3 之后的版本提供了专门的工具,这是最省事的方式。

# 交互式设置,会提示输入两次密码 grub2-setpassword

执行后会生成/boot/grub2/user.cfg,内容形如:

GRUB2_PASSWORD=grub.pbkdf2.sha512.10000.8A5B...(一长串)

同时它会把superusers自动设为root。也就是说,设置完成后启动菜单需要输入用户名root加上你刚设的密码。这个默认行为对很多人来说不方便,因为用户名固定是 root,没法自定义。如果想要自定义用户名,就得走路线二。

设置完建议补一条重新生成配置,虽然某些版本上不执行也能生效,但多做一个动作成本极低:

grub2-mkconfig -o /boot/grub2/grub.cfg

这个工具的好处是操作简单、不易出错、生成的哈希符合规范。坏处是控制力弱——用户名固定、没法给不同菜单项设不同权限、也没法配置多个超级用户。

还有一个实际使用中的细节:grub2-setpassword生成的user.cfg是个独立文件,grub2-mkconfig重新生成grub.cfg时会自动把这个文件的内容合并进去。这意味着你升级内核、重装 GRUB 包之后,密码配置不会丢。这是它比手工改grub.cfg强的地方——手工改的内容会在下次grub2-mkconfig时被覆盖掉。

3.3 路线二:40_custom 手工拼装,控制力最强

如果你需要自定义用户名、或者需要配置多个不同权限的账户,就得自己生成哈希再写进自定义脚本。

第一步,生成哈希:

grub2-mkpasswd-pbkdf2 # 输入密码两次后输出: # PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.3C2E...

把整串grub.pbkdf2.sha512.10000.后面的内容完整复制下来,一个字符都不能少,包括后面的 salt。

第二步,编辑/etc/grub.d/40_custom。这个文件默认长这样:

#!/bin/sh exec tail -n +3 $0 # This file provides an easy way to add custom menu entries.

在最后一行后面追加:

set superusers="bootadm" password_pbkdf2 bootadm grub.pbkdf2.sha512.10000.3C2E...(完整哈希) export superusers

这里的bootadm是自定义用户名,可以换成任何你方便记的。export superusers这行的作用是让这个变量在 GRUB 的各个阶段都可见,某些版本上不加会出现“密码设了但菜单不认”的现象,加上更保险。

第三步,确认可执行权限。这个文件如果不是可执行的,grub2-mkconfig会直接跳过它,你就白写了:

chmod +x /etc/grub.d/40_custom ls -l /etc/grub.d/ | grep 40

第四步,重新生成配置。这一步必须根据启动方式选对路径:

# 判断启动方式 [ -d /sys/firmware/efi ] && echo "UEFI" || echo "BIOS" # BIOS 机器 grub2-mkconfig -o /boot/grub2/grub.cfg # UEFI 机器 grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg

保险起见,UEFI 机器上我通常两条都执行,因为有些环境的/boot/grub2/grub.cfg是个符号链接或者副本,两边都刷一遍能避免“到底读的哪个”这种扯皮。

第五步,验证配置确实写进去了:

grep -E 'superusers|password_pbkdf2' /boot/grub2/grub.cfg # 或者 UEFI 路径 grep -E 'superusers|password_pbkdf2' /boot/efi/EFI/redhat/grub.cfg

能看到你写的用户名和哈希串,说明生效了。然后重启测试。

这里有一个必须提前知道的行为变化:一旦设置了superusers,所有菜单项默认都要求认证。也就是说以后每次重启,包括正常启动,都要先输用户名密码。这在无人值守重启的场景下是灾难——机器重启后卡在 GRUB 界面等人输密码,业务起不来。

解决办法是给菜单项加--unrestricted。但自动生成的菜单项在10_linux脚本里,不容易直接改。实际可行的做法是编辑/etc/grub.d/10_linux,找到menuentry的生成位置,在参数里加上--unrestricted,然后再grub2-mkconfig。这个改动有点侵入性,升级 GRUB 包之后可能被覆盖,需要记录在变更文档里。

或者更简单的思路:接受每次启动输密码这个代价,把它写进重启操作手册,同时确保带外管理随时可用。这个取舍取决于团队的运维成熟度。

3.4 路线三:写入 00_header 的暴力做法

还有一种做法是直接改/etc/grub.d/00_header,在文件末尾追加同样的set superusers和password_pbkdf2。这个文件是每次grub2-mkconfig最先执行的,改它的好处是优先级最高,坏处是它属于 GRUB 包自己的文件,包升级时可能被当成配置文件冲突处理,rpm会生成.rpmnew或者.rpmsave,配置就丢了。

我不推荐这条路线,但要知道它的存在,因为接手别人的机器时你会遇到。判断方法很简单:grep -l password_pbkdf2 /etc/grub.d/*,看看密码到底写在哪几个文件里。清理的时候要一起清理,否则会出现“改了一个地方,另一个地方的旧密码还在生效”的诡异现象。

注意:如果在一台机器上发现 GRUB 密码配置文件不止一处,先全部注释掉,生成一次干净的配置,再按统一方案重新加。多头配置是后续所有排障噩梦的根源。

3.5 grub.cfg 重新生成与双路径的坑

关于重新生成这一步,有几个具体的坑值得单独说。

第一个是路径判断错误。有些人的机器是 UEFI 启动,但习惯性执行grub2-mkconfig -o /boot/grub2/grub.cfg,命令成功返回,配置文件也更新了,重启发现没变化——因为固件读的是/boot/efi/EFI/redhat/grub.cfg。避免办法是每次都用判断语句,或者写个小脚本固化下来:

#!/bin/bash if [ -d /sys/firmware/efi ]; then grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg echo "已更新 UEFI 配置" else grub2-mkconfig -o /boot/grub2/grub.cfg echo "已更新 BIOS 配置" fi

第二个是 secure boot 相关的警告。某些版本的grub2-mkconfig在 UEFI 环境下会打印一行关于grubenv或者EFI variable的警告,大部分情况下不影响功能,但如果看到error:字样就要停下来看仔细。曾经遇到过因为/boot分区满了导致写入失败,命令返回非零但被-o参数掩盖了,重启后配置是旧的。

第三个是改动之后一定要cat一下目标文件确认时间戳和内容。stat /boot/grub2/grub.cfg看修改时间,grep看关键行,两个都对上再重启。省这一步的人最后往往要花十倍时间在控制台前面怀疑人生。

4. 排查实录:踩过的坑比命令本身值钱

4.1 现象、原因与处置速查表

下面这张表是我这些年在这套流程里遇到过的真问题,按出现频率排列,遇到卡壳可以直接对照。

现象最可能的原因处置方法
passwd报Authentication token manipulation error/sysroot仍是只读回上一步执行mount -o remount,rw /sysroot
密码已改,登录仍提示失败,/var/log/secure有avc: deniedSELinux 标签错乱救援模式进去touch /.autorelabel后重启
密码已改,登录失败,提示Permission denied但无 avc 日志/etc/shadow格式错误或账户被锁检查字段数量,确认密码字段无!!前缀
GRUB 菜单改了没生效配置路径与启动方式不匹配按 UEFI/BIOS 判断后写对应路径
设置 superusers 后每次启动都要密码菜单项未加--unrestricted改10_linux或接受现状并写进操作手册
grub2-mkconfig静默失败/boot分区空间不足df -h /boot检查,清理旧内核
重启后进入dracut紧急 shell内核参数或 initramfs 损坏移除追加参数重启,重装内核包

表格里第四条和第五条是最容易被忽略的,因为它们“看起来”成功了——命令返回 0,文件也改了,就是没效果。遇到这种情况不要推翻重来,先按表格顺序核对。

4.2 改完密码 SSH 登不上,控制台却能进

这个现象我第一次遇到时迷惑了很久。控制台输密码能进,SSH 一样输就是Permission denied。原因是 SSH 的认证路径和本地登录路径完全独立,它受/etc/ssh/sshd_config里的PermitRootLogin、PasswordAuthentication控制,还受 PAM 配置、/etc/ssh/sshd_config.d/下的片段文件影响。

排查顺序是这样的:先看sshd_config里PermitRootLogin是不是no,是的话改成prohibit-password(允许密钥)或者yes(允许密码),然后sshd -t测配置语法,systemctl reload sshd重载。如果这一层没问题,再看是不是AllowUsers、DenyUsers、Match块把 root 排除了。

还有一种更隐蔽的情况:改密码的时候把/etc/shadow的权限写坏了。SSH 在做密码校验时对文件权限敏感,/etc/shadow必须是000或者0000,属主 root、属组 root。如果救援操作时不小心chmod错了,本地login可能容忍,SSH 就直接拒绝。修复很简单:

chmod 000 /etc/shadow chown root:root /etc/shadow restorecon -v /etc/shadow

4.3 GRUB 密码忘了,以及忘记自己设过密码

这是最尴尬的循环——为了安全设了 GRUB 密码,然后自己忘了,进不去系统,也进不去救援模式。

GRUB 密码和 root 密码不是一回事,它在磁盘上以哈希形式存在,没法“重置”,只能替换。而替换它需要能访问文件系统,访问文件系统需要能启动系统——死循环。打破循环只有几条路:把磁盘挂到另一台 Linux 机器上改user.cfg或者40_custom;用可启动介质引导进入救援;或者在虚拟化平台里挂载磁盘到另一台虚拟机。

# 假设磁盘挂载到 /mnt 下 rm -f /mnt/boot/grub2/user.cfg # 或者从 40_custom 里注释掉 password_pbkdf2 那行

删掉后重启,GRUB 就不要求认证了。进系统之后再按正常流程重新设置。注意如果是 UEFI 机器,还要检查/boot/efi/EFI/redhat/下有没有独立的user.cfg。

另一种常见情形是接手别人的机器,不确定有没有设过 GRUB 密码。检查方法就一条命令:

grep -rE 'superusers|password_pbkdf2|password ' /boot/grub2/ /boot/efi/EFI/*/ 2>/dev/null

有输出就说明设过。这时候先别急着改,跟负责人确认密码是啥、能不能重设。贸然清空密码在某些合规环境里会触发告警。

4.4 内核升级后密码“消失”了

yum update或者dnf update升级了内核,grub.cfg被自动重新生成,然后发现 GRUB 密码没了。原因通常是当初把密码写在了grub.cfg里直接改的,而不是写进/etc/grub.d/下的脚本。包管理器重新生成配置时,直接改的内容全部丢失。

判断方法:grep -n password_pbkdf2 /etc/grub.d/*,如果没有任何输出,说明密码只存在于grub.cfg里,是个“孤儿配置”。修法是把哈希挪到40_custom里,或者直接用grub2-setpassword重新设一遍——后者的user.cfg是独立文件,不会被重新生成覆盖。

这也是我坚持推荐grub2-setpassword或者40_custom、绝不直接编辑grub.cfg的原因。省下的那点时间,会在下一次内核升级时加倍还回来。

5. 流程固化和日常维护

5.1 物理安全与固件层

GRUB 密码能挡住会按e的人,挡不住会进 BIOS 改启动顺序的人。所以完整的防护链条应该往上再走一层。

固件层的第一件事是设 BIOS/UEFI 管理员密码,并关闭从外部介质启动。这样即使有人插入启动盘也引导不了。第二件事是开启安全启动(Secure Boot),它能阻止未签名的引导程序加载,对篡改启动流程有一定拦截作用。第三件事是设置启动顺序锁定,禁止通过外设优先启动。

但这里有个现实问题:设了固件密码之后,维护人员自己就麻烦了。所以密码要进密码管理系统,不能只存在某个人的记事本里。我见过因为固件密码丢失导致整台机器报废的案例——重置固件密码在某些主板上需要短接跳线,在整机柜里根本够不着。

还有一层逻辑:如果这台机器在受控机房里,机柜上锁、门禁有记录,那么物理接触的门槛已经很高了,GRUB 密码的紧迫性相对降低。反过来,如果机器放在办公区角落、谁都能碰,那这套配置就是必需的。评估风险时把物理环境算进去,不要机械照搬网上的加固清单。

5.2 变更记录与交接清单

这套操作涉及启动流程和认证体系,任何一次改动都应该留下记录。我习惯记这几项:

记录项内容示例为什么要记
改动日期与执行人具体到时间点出问题时能定位到具体变更
改前状态GRUB 是否已有密码、SELinux 状态回滚的依据
执行的命令完整命令,含路径参数复现和审计
验证结果重启后角色登录、SSH 登录结果证明改动有效
密码存放位置密码管理系统里的条目编号交接时不用口头传

交接的时候最忌讳口头说“密码是那个什么”。我一般会要求把 GRUB 用户名、密码、root 密码、固件密码全部录入统一的密码管理条目,同时在机器上的/root/README-CHANGE里写一份不含明文密码的说明,指明去哪里查。这样即使人员变动,接手的人也有路径可循。

还有个小习惯值得推广:每次做这类改动之后,刻意重启两次再移交。第一次验证改动生效,第二次验证稳定性——有些配置问题会延迟一次启动才暴露出来。多花十分钟重启,比事后被叫起来处理生产故障划算得多。

5.3 定期验证与演练

配置放上去不动,半年后没人敢动,这是运维里的常态。我建议每季度至少做一次验证演练,用一台非生产机器把整套流程走一遍:故意忘掉 root 密码,走rd.break改回来,再验证 GRUB 密码是否按预期拦截。

演练能暴露的问题往往比想象中多。比如某次演练发现新采购的服务器是纯 UEFI 不加 CSM,老的启动盘引导不了;又比如某次发现新版本系统的grub2-mkconfig输出路径变了,之前的脚本失效。这些问题在真故障时才发现,代价是几小时的停机。

演练时还有个细节值得关注:确认rd.break这条通道在设置 GRUB 密码之后确实被堵上了。做法是设完密码后重启,在菜单上按e,正常情况下应该弹出认证提示。如果还能直接编辑,说明密码配置有问题,可能是写错了文件位置,或者是superusers没生效。这个验证比怎么看配置文件都直接。

顺便说一句,rd.break被堵上之后,紧急情况下自己也可能进不去。所以带外管理通道的凭据必须独立保存,不能和系统密码混在一起。云主机的话,控制台访问权限要确保至少两个人有,避免单点。

最后分享一个我一直在用的检查小脚本,放在/usr/local/bin/grub-check.sh,改完配置跑一下,能覆盖大部分低级错误:

#!/bin/bash set -u echo "=== 启动方式 ===" [ -d /sys/firmware/efi ] && echo "UEFI" || echo "BIOS" echo "=== 配置路径 ===" ls -l /boot/grub2/grub.cfg 2>/dev/null ls -l /boot/efi/EFI/redhat/grub.cfg 2>/dev/null echo "=== 密码配置分布 ===" grep -lE 'superusers|password_pbkdf2' /etc/grub.d/* 2>/dev/null [ -f /boot/grub2/user.cfg ] && echo "user.cfg 存在" echo "=== 生效配置中的关键行 ===" grep -E 'superusers|password_pbkdf2' /boot/grub2/grub.cfg 2>/dev/null echo "=== /boot 空间 ===" df -h /boot

跑完看一眼输出,心里就有底了。这个脚本本身不含任何密码信息,可以放心放在机器上。

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

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

立即咨询