适用场景
Linux GRUB 引导加固针对的是「控制台访问」这条常被忽略的权限链路。默认配置下,任何能接触服务器键盘(机房托管、办公测试机)或能打开带外管理台(iDRAC、iLO、IPMI、云主机 VNC 控制台)的人,都能在开机 GRUB 菜单上按 e 编辑内核命令行,追加 init=/bin/bash、rd.break 或 single,几秒内拿到无密码的 root shell,并直接改写 /etc/shadow 重置管理员密码——也就是说,前面做的 SSH 加固、fail2ban、密钥登录全部形同虚设。
本文适用于 RHEL / CentOS / Rocky / AlmaLinux 7 至 9 与 Debian / Ubuntu 18.04 以上系统,覆盖 GRUB2 超级用户密码、菜单项权限、引导文件权限、systemd 救援模式与 initramfs 五个加固点,每条命令均可直接复制执行。
前置条件
- 系统:GRUB2 引导(
grub2-install或grub-install已安装),UEFI 或 Legacy BIOS 均可。 - 权限:root 或具备
sudo全权。 - 必要备份:
cp -a /boot/grub2/grub.cfg /root/grub.cfg.bak(Debian 为/boot/grub/grub.cfg);引导配置错误会导致无法开机,务必保留一份可回滚的副本与一条带外访问通道。 - 变更窗口:必须安排维护窗口重启验证,或确认带外控制台可用。
原理说明
GRUB2 的权限模型
GRUB2 通过两个指令控制访问:set superusers="admin" 声明超级用户,password_pbkdf2 admin grub.pbkdf2.sha512.… 声明其口令哈希。这两个指令必须写进最终生成的 grub.cfg。一旦生效,GRUB 会要求认证后才允许编辑菜单项或进入命令行;若只希望「可正常自动启动、但改参数要密码」,需为菜单项追加 --unrestricted 修饰符。
为什么单靠 GRUB 密码还不够
GRUB 密码只能拦截「在 GRUB 阶段改参数」。仍存在两条旁路:一是 systemd 的 rescue/emergency target,若 root 账户无口令,sulogin 会直接放行;二是部分发行版在菜单中预置 Recovery Mode 条目,该条目本身需要一并纳入权限控制或直接关闭。
操作步骤
步骤 1:备份现有引导配置
# RHEL 系
cp -a /boot/grub2/grub.cfg /root/grub.cfg.bak.$(date +%F)
# Debian 系
cp -a /boot/grub/grub.cfg /root/grub.cfg.bak.$(date +%F)
# 记录当前内核与引导参数,便于比对
grubby --default-kernel 2>/dev/null || cat /proc/cmdline
步骤 2:生成 PBKDF2 口令哈希
RHEL 系使用 grub2-mkpasswd-pbkdf2,Debian 系使用 grub-mkpasswd-pbkdf2。交互式输入两次口令后,输出形如 grub.pbkdf2.sha512.10000.A1B2… 的整行哈希,只保留 grub.pbkdf2 开头的部分。
# RHEL / CentOS / Rocky / Alma
grub2-mkpasswd-pbkdf2
# Enter password: ************
# Reenter password: ************
# PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.5A8E...(复制整行)
# Debian / Ubuntu
grub-mkpasswd-pbkdf2
关键参数:口令长度建议 16 位以上,且与系统 root、BMC 口令三者互不相同,避免单点泄露串联。
步骤 3:写入超级用户配置
方式一(推荐,RHEL 7+ 内置命令,自动写入 /boot/grub2/user.cfg):
grub2-setpassword
# Enter password: ************
# Confirm password: ************
ls -l /boot/grub2/user.cfg # 应存在,权限 600
方式二(跨发行版通用,写入自定义脚本):
cat > /etc/grub.d/40_custom <<'EOF'
#!/bin/sh
exec tail -n +3 $0
set superusers="grubadmin"
password_pbkdf2 grubadmin grub.pbkdf2.sha512.10000.将上一步的哈希粘贴到此处
EOF
chmod 700 /etc/grub.d/40_custom
关键点:exec tail -n +3 $0 这一行必须保留,它让 GRUB 跳过脚本头部注释;set superusers 与 password_pbkdf2 的用户名必须完全一致。
步骤 4:关闭 Recovery 菜单并重新生成 grub.cfg
# Debian / Ubuntu:关闭恢复模式条目,缩短菜单等待
sed -i 's/^#\?GRUB_DISABLE_RECOVERY=.*/GRUB_DISABLE_RECOVERY="true"/' /etc/default/grub
grep -q '^GRUB_DISABLE_RECOVERY' /etc/default/grub || echo 'GRUB_DISABLE_RECOVERY="true"' >> /etc/default/grub
sed -i 's/^#\?GRUB_TIMEOUT=.*/GRUB_TIMEOUT=3/' /etc/default/grub
# 重新生成引导配置
# RHEL 系(BIOS 引导路径)
grub2-mkconfig -o /boot/grub2/grub.cfg
# RHEL 系(UEFI 引导路径,如存在)
[ -f /boot/efi/EFI/redhat/grub.cfg ] && grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg
# Debian 系
update-grub
如使用 BLS(Boot Loader Specification,RHEL 9 默认),需同时确认 /boot/loader/entries/*.conf 未被单独放开权限,做法见步骤 5。
步骤 5:收紧引导文件权限
# RHEL 系
chmod 600 /boot/grub2/grub.cfg
chmod 600 /boot/grub2/user.cfg
chown root:root /boot/grub2/grub.cfg /boot/grub2/user.cfg
# Debian 系
chmod 600 /boot/grub/grub.cfg
chown root:root /boot/grub/grub.cfg
# 目录层:仅 root 可读
chmod 700 /boot/grub2 2>/dev/null || chmod 700 /boot/grub
chmod 700 /boot
# 校验结果:不应出现 other/group 可读项
find /boot/grub2 /boot/grub -maxdepth 1 -type f -perm /o+r 2>/dev/null
步骤 6:封堵 systemd 救援模式旁路
确保 root 账户本身有强口令,sulogin 才会在 rescue/emergency 时真正要求认证:
# 为 root 设置独立强口令(与 GRUB 口令不同)
passwd root
# 确认救援模式确实调用 sulogin
grep -h 'ExecStart' /usr/lib/systemd/system/{rescue,emergency}.service
# 期望输出包含:-o root -p "Give root password for maintenance" -- sulogin
# 若未设置,可通过 override 强制要求口令
systemctl edit rescue.service # 在打开的编辑器中写入 [Service] ExecStart= 后按上方格式重写
步骤 7:控制 rd.break 绕过与外部介质启动
设置 GRUB 密码后 rd.break 仍可能被利用(它绕过 systemd 直接进入 initramfs shell)。彻底封堵需要组合措施:
# 1) 对根分区启用 LUKS 全盘加密(安装时选择,事后迁移成本高)
cryptsetup luksDump /dev/sda3 2>/dev/null | head -5
# 2) 重新构建 initramfs,确保 dracut/initramfs-tools 未残留可交互 shell
dracut -f --regenerate-all # RHEL 系
update-initramfs -u -k all # Debian 系
# 3) 在固件层设置开机口令并关闭外部介质启动
# UEFI/BIOS 中设置 Supervisor Password
# 禁用 USB/CD 引导,或把硬盘置为第一引导项
# 设置正确的 Boot Order 并锁定
# 4) 带外管理口与业务网络隔离,BMC 使用独立强口令
ipmitool user list 1 2>/dev/null | head
配置验证
# 1) 确认超级用户与口令哈希已写入最终引导配置
grep -nE 'superusers|password_pbkdf2' /boot/grub2/grub.cfg | head
# Debian: grep -nE 'superusers|password_pbkdf2' /boot/grub/grub.cfg | head
# 2) 确认菜单项带权限限制(无 --unrestricted 时即需口令)
grep -n 'menuentry' /boot/grub2/grub.cfg | head
# 3) 确认文件权限
ls -l /boot/grub2/grub.cfg /boot/grub2/user.cfg 2>/dev/null
# 4) 确认恢复模式已关闭(Debian)
grep -c 'recovery mode' /boot/grub/grub.cfg
# 5) 空跑一次配置语法,避免重启失败
grub2-script-check /boot/grub2/grub.cfg 2>/dev/null && echo "grub.cfg syntax OK"
随后在维护窗口重启,在 GRUB 菜单界面按 e 尝试编辑内核行:预期弹出 Enter username: 提示,输入错误口令三次后返回菜单且无法编辑;直接回车启动应正常进入系统。若菜单可直接编辑,说明 grub.cfg 未重新生成或 superusers 未生效。
常见问题
FAQ 1:忘记 GRUB 超级用户口令,如何恢复?
需要一次物理或带外访问。从安装介质进入救援模式,chroot 到系统盘后删除凭据文件并重建引导配置:
# 救援环境中(假设根分区为 /dev/sda2)
mount /dev/sda2 /mnt && mount --bind /dev /mnt/dev && mount --bind /proc /mnt/proc
chroot /mnt /bin/bash
rm -f /boot/grub2/user.cfg
sed -i '/password_pbkdf2/d;/set superusers/d' /etc/grub.d/40_custom
grub2-mkconfig -o /boot/grub2/grub.cfg
exit && umount -R /mnt && reboot
这正说明了一件事:GRUB 口令属于「提高门槛」而非「绝对防护」,真正的边界是机房物理与 BMC 访问控制。
FAQ 2:已经设置了 GRUB 口令,为什么仍能进单用户模式?
常见三个原因:一是只写了 /etc/grub.d/40_custom 却没有执行 grub2-mkconfig,配置从未生效;二是 UEFI 与 BIOS 两套 grub.cfg 只更新了一套,实际引导路径仍是旧文件;三是 GRUB 版本存在已知绕过漏洞(如 BootHole CVE-2020-10713 系列),需升级 grub2-common/grub-efi 包并重装引导程序:grub2-install /dev/sda(BIOS)或 grub2-install --target=x86_64-efi(UEFI)。
FAQ 3:设置了 GRUB 密码后,服务器断电自启会卡住吗?
默认会。若 set superusers 生效且菜单项未加 --unrestricted,自动启动需要人工输入口令,托管服务器断电恢复后将无法自动上线。解决办法是给常规启动项加 --unrestricted:编辑 /etc/grub.d/10_linux,把 menuentry 行改为 menuentry '...' --unrestricted ...,然后重新生成 grub.cfg。这样正常启动免密码、改参数需密码,兼顾可用性与安全性。
FAQ 4:云服务器(ECS / CVM)也需要做吗?
需要。云主机的 VNC 控制台凭据一旦泄露(弱口令、工单截图、内部越权),攻击者获得的能力与物理接触等价。建议云主机同样配置 GRUB 口令,并把控制台访问收敛到跳板机,开启云账号的多因素认证与操作审计。
总结
引导层是主机安全的最后一道门,也是最容易被漏掉的一道。加固顺序建议为:root 强口令与 systemd sulogin 到位 → GRUB 超级用户口令(grub2-setpassword 或 40_custom)→ 重新生成 grub.cfg 并核对实际引导路径 → 引导文件权限收紧至 600/700 → 关闭 Recovery 菜单并加 --unrestricted 平衡自启需求 → 固件与 BMC 口令、外部介质引导禁用。完成后再用 grep superusers grub.cfg 与一次真实重启回归验证,即可把「重启改密码」这条攻击路径从数秒成本提升到需要物理入侵的程度。