让系统更安全 - Archlinux开启安全启动 实录

前言

简单说一下自己折腾安全启动的目的。

弄好了系统盘加密之后,我觉得系统的保护还不够彻底,但加密Boot分区很拖累开机时间。于是我就想到了安全启动,它的效果并不比前者好多少,但是聊胜于无。

方案分析

安全启动的实现有两种方法,使用自签名安全证书以及使用经认证的第三方 (微软)证书,下表简单对比了这两种方法的优劣。

方案优势劣势
自建签名可控
无感知
兼容Windows安全启动
需自建密钥
需修改BIOS (的证书数据库)
不能复用
第三方 (微软)签名 (PreLoader.efi&shim.efi)方便
不修改BIOS设置
复用
安装简单,但操作对新手不友好
维护麻烦
GRUB无法链式加载EFI二进制文件

第三方签名

这意味着需要使用微软的证书来给引导器签名,或者使用微软签名过的特殊引导器来引导Linux引导器。

当然我们不可能拿到微软的私钥,但是我们确实有可用并签名过的微软引导器。

使用签名的引导加载程序意味着使用用Microsoft密钥签名的引导加载程序。有两种已知的带签名的引导加载程序PreLoader和shim,它们的目的是链加载其他EFI二进制文件(通常是引导加载程序)。由于Microsoft永远不会对自动启动任何未签名二进制文件的引导加载程序进行签名,因此PreLoader和shim使用称为机器所有者密钥列表的白名单,缩写为MokList。如果二进制文件(Preloader和shim)或密钥的二进制签名(shim)的SHA256哈希在MokList中,则他们执行它,否则,它们将启动密钥管理实用程序,该实用程序允许注册哈希或密钥。

Google Translate

简单说,Preloader和shim可以链式加载EFI二进制文件并不会校验文件的签名 (GRUB会校验EFI文件签名,GRUB引导的EFI文件都要签名),我们可以利用这点借微软证书来启动Archlinux。

不过,此方法有很大的局限性。Preloader下针对GRUB或系统内核更新时的重签名很繁琐 (必须手动签名,安装时怎么做的更新之后就得再做一遍)。shim下引导的GRUB无法链式引导其他EFI文件 (无法通过GRUB启动EFI小工具,甚至无法用GRUB引导Windows的EFI启动文件)。这些局限性让本地环境的安全启动变得非常地鸡肋,几乎没什么用了。我个人认为这种方法适用于构建安装盘等几乎不需要更新引导器和内核的场景。

自建签名

我个人更推荐这种方法,通用性更强,就是首次配置会比较麻烦,但是后续的维护可以完全自动化,且只要保留自建签名的私钥,也是可以复用的。

创建自签名证书

  1. 安装efitools

几乎所有的以下部分需要你安装的efitools包。

  1. 生成所有者标识UUID

    uuidgen --random> GUID.txt

    生成平台密钥 (PK)

    openssl req -newkey rsa:4096 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Platform Key/" -out PK.crt
    openssl x509 -outform DER -in PK.crt -out PK.cer
    cert-to-efi-sig-list -g "$(< GUID.txt)" PK.crt PK.esl
    sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth

    my Platform Key可自定,文件名也可自定,但是推荐不变,以免与后续创建的文件混淆。

    在“用户模式”下,签名一个空文件以允许删除平台密钥

    sign-efi-sig-list -g "$(< GUID.txt)" -c PK.crt -k PK.key PK /dev/null rm_PK.auth

    生成密钥交换密钥 (KEK)

    openssl req -newkey rsa:4096 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Key Exchange Key/" -out KEK.crt
    openssl x509 -outform DER -in KEK.crt -out KEK.cer
    cert-to-efi-sig-list -g "$(< GUID.txt)" KEK.crt KEK.esl
    sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt KEK KEK.esl KEK.auth

    生成签名数据库密钥 (db)

    openssl req -newkey rsa:4096 -nodes -keyout db.key -new -x509 -sha256 -days 3650 -subj "/CN=my Signature Database key/" -out db.crt
    openssl x509 -outform DER -in db.crt -out db.cer
    cert-to-efi-sig-list -g "$(< GUID.txt)" db.crt db.esl
    sign-efi-sig-list -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db db.esl db.auth

    这部分也可以使用辅助脚本自动执行。

操作完成后,一份安全启动需要的自签名证书就完成了。

在固件中注册证书

  1. 清除BIOS原有的密钥数据库,使其进入设置模式。这部分需要进入BIOS设置。由于不同平台的操作方式不同,这步请各位自行探索。

  2. 注册密钥。这里有三种方法,各不相同,其中第三种方法最保险,可以通用。

    • 使用sbkeysync工具。教程

      这个工具的问题在于可能会卡在最后一步注册PK文件的时候报错失败。

    • 使用BIOS自带工具

      这个就不赘述了,可以自行探索一下。不过并不是所有BIOS都带有这个功能,没有就是没有。

    • 使用KeyTool

      1. 首先需要安装efitools的工具包,然后需要对这个EFI文件进行签名,才能让它在设置模式的安全启动下被启动。使用ROOT权限执行

        sbsign --key db.key --cert db.crt --output ${ESP}/KeyTool-signed.efi /usr/share/efitools/efi/KeyTool.efi

        esp处替换为自己的BOOT分区路径。

      2. 将之前制作好的PK,KEK,db文件复制到BOOT分区中。

      3. 重启并引导这个签名过的EFI工具。可以提前添加到启动项也可以在引导程序中手动选择EFI文件,这里示范一下使用GRUB命令行启动KeyTool

        search --no-floppy --set=root --file /EFI/KeyTool-signed.efi
        chainloader /EFI/KeyTool-signed.efi
        boot
      4. 根据图形化界面的指引导入各个数据库即可。

签名已有EFI引导器和内核

使用sbsign签名内核和启动管理器,用ROOT权限执行

sbsign --key db.key --cert db.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux
sbsign --key db.key --cert db.crt --output esp/EFI/BOOT/BOOTX64.EFI esp/EFI/BOOT/BOOTX64.EFI

也可以顺便用这样的命令签名一下EFI文件夹里所有的EFI文件。

部署全自动签名hook

使用mkinitcpio的pacman挂钩在安装和更新时对内核进行签名。 ArchWiki链接

hook和sbupdate二选一即可,GRUB用户推荐使用hook。

合并微软签名

制作签名数据库的时候也可以将微软的签名公钥一并写入文件中,这样你制作的数据库就会同时兼容Windows的安全启动以及Windows启动盘的安全启动。这个实现方法很简单,可以参考ArchWiki相关条目


所有的都完成之后就可以重启电脑了。进入系统后可通过bootctl status查看安全启动是否设置成功。至此,安全启动就设置完成了。

参考资料

Unified Extensible Firmware Interface/Secure Boot - ArchWiki


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!