跳至内容

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 国际许可协议进行许可。

评论区