跳至内容

Jixun's Blog 填坑还是开坑,这是个好问题。

在 Arch Linux 下启用安全启动并自动签名

Ubuntu 的系统别的不说,安全启动「Secure Boot」整起来是真的容易 —— 默认内置预签名的 shim 引导,安装时打个勾就可以自动处理好繁琐的签名步骤了。Fedora 也内置了类似的预签名引导。

※ 使用的系统并非严格意义上的 Arch Linux,而是其衍生版 EndeavourOS

安全启动是什么

微软提出的一个保护引导程序完整性的“标准”,即任何从 UEFI 引导的系统都应通过“签名”来保持信任,从而达成安全启动。该项目从诞生开始就一直饱受争议…

如果不是为了方便双启动 Windows 111 也不至于这么折腾。

Arch Linux 百科

Arch Linux 百科对应的条目内确实应有尽有,但是… 安全启动的一些坑没有说出来,而我傻傻的踩上去了。

我现在的这台主机使用的主板是华擎的 X570 Phantom Gaming 4。事后只能说果真够妖。

密钥迷云

一开始我是照着「3.1 Using your own keys」来操作的。来来回回折腾了好几次都在使用工具写出 EFI 密钥设定失败,仔细阅读后发现需要先进入设置模式(Setup Mode)才可以;于是进入 BIOS 设定把预设的密钥数据库清空了。

重启进入系统,一切正常;继续尝试写入,依旧出错。百思不得其解,只想着一会重启后手动进 BIOS 里手动更新设置好了。只是重启后…

无法启动了!屏幕无输出,键盘也无反映。

拆开主机外壳一看,启动诊断灯亮了 - 非常迷惑的「VGA」和「Boot」指示灯…

主板诊断灯亮了

网上搜索一番,没得出什么结论;官方手册里也就这么一句废话:

Post Status Checker (PSC) diagnoses the computer when users power on the machine. It emits a red light to indicate whether the CPU, memory, VGA or storage is dysfunctional. The lights go off if the four mentioned above are functioning normally.

PSC 可以在用户启动设备时对电脑进行诊断。他可以通过亮红灯来表示处理器、内存、视频输出或储存设备无法正常工作。若无灯亮起则表示这四项检测通过。

后来想了想,既然是改 BIOS 出问题了,那我重置一下吧…

于是把主机拆开。照着网上找的教程翘走 CMOS 电池(好像不是必须的?),然后短接清除 CMOS 跳线。跳线帽也不知道去哪找,最后找了一条两头凹形接口的线套了上去。

就这样来来回回折腾一晚上,终于又能启动了…

此时已经有点想放弃了,但是又随手一翻看到后面的「Using a signed boot loader」一看… 这不就是我念念不忘的 Ubuntu 所使用的方案吗!

全信用链 vs 机器信用链

正常的安全启动的信任链是这样的:

「平台密钥(Platform Key,PK,由主板厂商签发)」→「交叉认证签名(Key Exchange Key,KEK,认证微软的签名根)」→「签名数据库(Signature Database,具体的签名)」

刚才看的 3.1 方案则如此,从平台密钥开始全部签发一遍,并交叉授权微软的签名以允许启动带有微软签名的 Windows 引导程序。

微软不能为 GPL 协议的项目签名2,因此 shimpreloader 这类项目就诞生了 - 由微软预签名因而可以直接启动,然后用户手动授权指定的 efi 镜像或「机器密钥」让所有使用该密钥签名的引导与内核能正常启动。

设定机器密钥

照着百科「3.2.2.1.2 shim with key」一路继续即可。完成后再对引导与内核签名。

但还是不能正常启动;错误信息提示禁止引导:「prohibited by secure boot policy」。

最后一路爬文发现这个错误是 grub 无法正常加载所需要的模块,需要在安装的时候填写需要打包(?)的模块,并在启动时预加载… 于是又用脚本来从 grub2 的配置文件提取用到的模块:

sudo cat /boot/grub/grub.cfg | awk '/insmod/ { print $2 }' | sort | uniq

加到参数里再尝试,还是一样的错误,估计还是需要声明更多的模块,但根本不知道去哪找这个参数,还试过枚举所有可用的模块文件来构建 grub 引导:

MODULES="$(ls /usr/lib/grub/x86_64-efi/ | grep -E '\.mod$' | cut -d. -f1)"

也不是不能用,但是界面不仅很丑还很慢… 估计是打包到一些兼容/调试模式用的模块了。

最后在一篇教学文章中找到了一个相对带有部分注解的模块列表:

# GRUB doesn't allow loading new modules from disk when secure boot is in
# effect, therefore pre-load the required modules.
MODULES=
MODULES="$MODULES part_gpt fat ext2"           # partition and file systems for EFI
MODULES="$MODULES configfile"                  # source command
MODULES="$MODULES verify gcry_sha512 gcry_rsa" # signature verification
# ... 后略

这篇文章也有些年头了,比如 verifylinuxefi 模块已经找不到了。

于是我照着这个列表缝缝补补了一番,最后整合了个签名脚本在 grub 更新后自动执行:

#!/bin/sh
#MODULES="$(ls /usr/lib/grub/x86_64-efi/ | grep -E '\.mod$' | cut -d. -f1)"

# GRUB doesn't allow loading new modules from disk when secure boot is in
# effect, therefore pre-load the required modules.
MODULES=
MODULES="$MODULES part_gpt fat ext2"              # partition and file systems for EFI
MODULES="$MODULES configfile"                     # source command
MODULES="$MODULES crypto"                         # signature verification? no idea.
MODULES="$MODULES echo normal linux"              # boot linux
MODULES="$MODULES all_video efi_gop efi_uga font" # video output
MODULES="$MODULES search search_fs_uuid"          # search --fs-uuid
MODULES="$MODULES reboot sleep"                   # sleep, reboot
MODULES="$MODULES tpm"                            # Added per Arch Linux wiki
MODULES="$MODULES gettext gfxmenu gfxterm gzio"   # <-- Used in grub.cfg,
MODULES="$MODULES part_gpt part_msdos png"        # <--   via "insmod" command
MODULES="$MODULES probe test true"                # Indirect dependency to some of the commands
MODULES="$MODULES chain"                          # Chainload Windows bootloader, not sure if needed.
                                                  # what could go wrong?
# Regenerate grub bootloader
grub-install \
  --no-nvram \
  --target=x86_64-efi \
  --efi-directory=/boot/efi \
  --modules="$MODULES" \
  --sbat /usr/share/grub/sbat.csv

mok_sign() {
  # sign if not already done so.
  if ! /usr/bin/sbverify --list "$1" 2>/dev/null | /usr/bin/grep -q "signature certificates"; then
    printf 'Signing %s...' "$1"
    sbsign --key /etc/efi-keys/mok/MOK.key --cert /etc/efi-keys/mok/MOK.crt --output "$1" "$1"    
  else
    printf 'Skip sign: %s\n' "$1"
  fi
}

mok_sign /boot/efi/EFI/endeavouros/grubx64.efi

事后检查一下 sbctl status 的环境检测:

$ sbctl status
Installed:     ✗ sbctl is not installed
Setup Mode:    ✓ Disabled
Secure Boot:   ✓ Enabled
Vendor Keys:   microsoft

Grub vs rEFInd

rEFInd 需要签名的文件少,理论上来说用起来会更方便。

但是我每次通过 shim 引导 rEFInd 都提示引导镜像的签名不对,遂放弃。

后记

手动配置安全引导真麻烦。要是以后还需要重装 Linux 的话,还是用 Ubuntu 系列的发行版算了。

手上有个笔记本实现的安全引导是个残缺版,需要手动将入口镜像加入到数据库 - 但因为该实现不会对后续加载的镜像进行验证,因而让我产生了配置安全引导很简单的错觉。


  1. Windows 11 的系统需求之一。 ↩︎

  2. 据说是因为如果签了,就需要公开签名的私钥。 ↩︎

知识共享许可协议 本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。

评论区