为rEFInd启用Secure Boot

发布时间:

最后更新:

总字数:
3.7k

不论如何,至少电脑没有变万元大砖头,可喜可贺!其实不太可能变砖。

注:如果你不想了解部分技术细节,你可以直接跳到本文的第四部分:上手操作。

操作引导需谨慎

为了写这篇文章,特意用官方工具升级了一下rEFInd,结果成功把引导搞崩了,花了一个小时才救回来。

这件事情告诉我们:当你想要折腾引导这种系统关键组件,一定要保证自己有足够的知识从错误中恢复!虽然现在的计算机往往有充足的防呆设计,不太可能因为用户的正常操作变黑砖,但是在操作前一定要谨慎。

一个案例是:在根目录执行sudo rm -rf / --no-preserve-root可能导致一些设备变成黑砖,只能返厂上编程器救。怎么回事?不就是删除了硬盘上的文件,UEFI还在,怎么会变黑砖呢?原来,存储在NVRAM里的EFI变量是默认挂载在/sys/firmware/efi/efivars的,而且挂载为可读写,不然efibootmgr之类的工具怎么起作用呢?结果就是,不知不觉间,rm把这些性命攸关的EFI变量都抹除了。当你以为这行命令只不过是清空硬盘,整死操作系统的时候,它其实在美滋滋地擦写你的固件。虽说UEFI规范要求这种情况下计算机应当可以继续启动(至少有一个恢复的机会),但是如果这台电脑的UEFI实现得非常糟糕,那么就等着傻眼吧!

为什么这么重要的东西竟然会挂载为可读写呢?不过某linux重要基础设施的维护人员似乎认为这无伤大雅。虽然大家一般不写EFI变量,但总有人是要写的。所谓:欲敲sudo,必承其重!

可是,谁能想得到rm -rf竟会把固件整死了呢?

前因

人难免是爱折腾的。此前安装了第三方的引导rEFInd,换了比较好看的主题,总而言之摆脱了grub的大黑框(grub当然也可以设置主题,但那是另一回事了)。rEFInd的安装倒是没什么可说的,网上已经有很多人写过相关的博文了,不过大多数的教程都建议关闭安全启动(Secure Boot),因为安全启动下加载第三方引导会非常麻烦。这倒也无伤大雅,总而言之我就把安全启动禁用了。

然而好景不长。有时电脑莫名地卡死,长按关机键重启,在电脑启动时会看到“CMOS校验出现错误”。所谓CMOS,就是是存储UEFI配置信息的地方。校验出错,说明配置可能已经损坏,于是接下来便恢复默认值了。安全启动默认开启,于是rEFInd无法加载,grub的大黑框又回来了。于是,我不得不进入UEFI配置,把安全启动关掉。

不过,这并称不上一种优雅的办法。实际上,有办法令rEFInd在安全启动下加载,而且相当简单,可惜国内的教程往往对此语焉不详,或者直接建议关掉安全启动。具体的做法,其实在rEFInd的官网上有很详细的介绍可供参阅。这份文档不但说明了如何在启用安全启动的情况下安装rEFInd,而且很清晰地介绍了Secure Boot的概况。

关于安全启动

众所周知,早期的恶意软件都是在操作系统启动之后才可能加载的,所以早期的安全手段都注重系统启动后的防御。后来涌现了各种各样的Bootkit,在系统启动之前运行,来一招釜底抽薪。Legacy BIOS时代一个最经典的做法就是修改主引导记录(Master Boot Record, MBR)。当然,自从UEFI普及之后,就再也没有MBR这种东西了,然而类似的攻击方式却不断进化。总而言之,核心思想就是:恶意软件把自己伪装成操作系统,诱骗固件去加载。

Secure Boot应运而生。其思想也非常简单,就是在固件加载引导程序时对其进行验证,仅加载可信的引导程序。在此不赘述证书、签名等艰深的密码学原理;简而言之,对一个程序进行数字签名之后,就可以用这个签名对应的密钥验证其是否经过篡改。

假设有一台运行Windows的电脑。在UEFI下,操作系统的启动器其实是一个个efi应用程序(.efi文件)。对于Winodws来说,往往是bootmgfw.efi,这个程序由微软进行签名。然后,在每台电脑的签名数据库(Signature Database, db)中,都保存着微软的密钥。使用这个密钥验证操作系统加载器,就可以保证其没有受到恶意软件的篡改。

如果微软的系统引导器出现了可供利用的严重安全漏洞怎么办?那么,它的hash或者签名就会被添加到禁止数据库(Forbidden Database, dbx)中。如果固件发现dbx中的密钥可以验证程序,或者其中保存的hash与程序相匹配,那么固件就会拒绝加载该程序,并抛出EFI_SECURITY_VIOLATION错误。

由上可知,我们有时是有增删db或dbx的需求的。可是我们该怎么保证对db和dbx的修改也是可信的呢?当然是用另一个密钥来验证。这个密钥叫做密钥交换密钥(Key Exchange Key, KEK)。顾名思义,KEK主要是用来修改db和dbx中的密钥的。如果没有KEK的签名,那么就不能修改db和dbx。KEK可以有多个,一般来说包含了微软的密钥和计算机厂商的密钥。

最后,谁来确保这些KEK都是可信的呢?答案是计算机厂商。它们控制着平台密钥(Platform Key, PK),它是整个可信系统的根源。每台电脑只能有一个PK,这个PK拥有至高无上的权限。如果将PK修改成自己的密钥,那么就可以完全控制安全启动。

事情到这里就很清晰了。首先,厂商在生产计算机时,会把自己的密钥作为PK,这是安全启动的基石。然后,便可以用PK来设置KEK。KEK一般都包含微软密钥,还可能包含计算机厂商自己的密钥。最后,微软或者厂商可以在执行系统更新的时候,使用KEK修改db或者dbx,以将已知的恶意程序加入dbx的黑名单,或者将新的密钥加入白名单。

目前为止事情看起来很美好。可是,这其中却浮现了一个大问题:linux世界被完全排除在外了。实际上,安全启动就就是微软和Intel牵头做出来的,自然会首先考虑自家系统。可是如果我想运行Windows以外的操作系统,该怎么办呢?db中包含了微软的两个密钥,一个用来给微软自己的东西签名,另一个则用来给第三方程序签名。只要交99美元,就可以用微软的第三方密钥签名,想签多少签多少。

然而争论并没有减少。且不论由微软给linux签名是否合适,签名的费用该由谁来出呢?linux内核和grub都可能会进行频繁的更新,每次都要由微软重新签名,这在实践上是很难接受的。而且,用户自己编译的linux该怎么办呢?

目前有两种解决方案:Shim和Preloader,其中Shim是主流。Shim脱胎于fedora的一个项目,基本的原理是:由微软为Shim签名,然后由Shim验证并加载其他linux发行版厂商的签名。Shim的版本应当是很稳定的,所以不需要频繁地找微软进行签名。

shim使用下列密钥验证启动镜像:

  • 安全启动密钥。就是前文所述的db、dbx、KEK、PK。
  • Shim Keys。这是嵌入在Shim中的密钥,一般属于linux发行版厂商。
  • Machine Owner Key(MOK)。MOK和db、dbx、KEK、PK一样,都存储在NVRAM中。但不同的是,MOK是可以由用户直接添加的。随Shim附带了一个名为MokManager.efi(现在更常见的名字是mmx64.efi)的程序,由微软进行签名,它的作用就是管理MOK。用户可以将自己编译的linux用自己的密钥签名,然后利用MOK Manager程序将自己的密钥添加到MOK中。于是,用户就可以加载他所愿意加载的程序。

shimx64.efi默认加载同目录下的grubx64.efi。

那么,要为rEFInd启用安全启动就很简单了。rEFInd二进制是由作者Rod Smith进行签名的,你可以直接将他的密钥导入MOK。或者,也可以用你自己的密钥进行签名。

上手操作

在开始之前,我认为我有必要提醒你:同一件事,不同的设备的具体操作往往是非常不同的,尤其是固件这种与硬件联系非常紧密的东西。我下方给出的截图,只能代表我这一台设备的情况。尽管如此,大体的流程应当是相似的。不过,以防万一,请你先熟悉你的设备厂商提供的固件界面,以及出现问题之后的恢复手段。

操作需谨慎!

准备shim

首先要介绍EFI分区,也就是所谓的ESP(EFI System Partition)。我们前面提过,电脑上电之后,首先加载UEFI固件。UEFI已经是一个功能齐备的系统了,在上面可以运行EFI应用程序(.efi文件)。操作系统的启动器,其实就是一个EFI应用程序。这些EFI应用程序都存储在EFI分区中。EFI分区必须使用FAT文件系统,因为FAT足够简单而无需额外的复杂的文件系统驱动。现在的机器上,EFI分区可能是唯一FAT格式的分区。

一般来说,在linux系统中,EFI分区被挂载在/boot/efi目录下,但是需要root权限才能访问。如果你找不到这个目录,那么你有可能需要手动挂载。

而rEFInd的典型安装路径是/boot/efi/EFI/refind。在这里,你可以看到rEFInd本体refind.efi。但是,正如上文所述,为了实现安全启动的大业,我们还需要一个shim程序shimx64.efi,以及一个MOK管理工具mmx64.efi(或者叫做MokManager.efi)。大多数发行版应该自带了shim,你可以在EFI目录下找一找。这个shim应当是由微软签名的。如果找不到,你可以尝试在发行版官网搜索一下。有些发行版可以通过包管理器安装shim,一般来说名字类似于shim-signed。例如,对于ubuntu:

1
sudo apt install shim-signed

当你准备妥当shim和mokmanager之后,把他们复制到rEFInd安装目录下。

注意:请不要使用shim 0.1,因为这个版本不支持启动rEFInd。实际上,我不建议你使用shim的任何早期版本,因为它们有大量的已知安全漏洞,而且有些过早的版本已经被纳入了dbx。

同时,如果你使用版本比较新的shim,那么你也需要使用版本比较新的rEFInd。这是因为新版本的shim引入了一个名为Secure Boot Advanced Targeting(SBAT)的安全机制,而更早的rEFInd不支持这一机制,会被拒绝加载。

你还需要检查一下在/boot/efi/EFI/refind/keys/目录下是否有一个名为refind.cer的文件。这是用于验证rEFInd作者Rod Smith签名的密钥,我们需要把它导入MOK。如果没有的话,你需要去rEFInd官网下载。

准备录入MOK

我们还需要两个命令行工具:efibootmgrmokutil

  • efibootmgr用于调整UEFI的启动设置。有的UEFI自带此功能,所以这个工具不是必要的,但是efibootmgr是一个比较通用的办法。
  • mokutil用于辅助导入MOK。mokutil本身并没有导入MOK的能力,但是它可以帮助你做好准备工作。真正执行导入操作的是mmx64.efi。

efibootmgr一般是包含在各大发行版之中的。然而,你需要自行安装mokutil。大多数发行版的包管理器仓库应当都有以上两种工具。

准备就绪之后,运行:

1
sudo mokutil -i /boot/efi/EFI/refind/keys/refind.cer

这行命令将/boot/efi/EFI/refind/keys/refind.cer加入准备注册的密钥。命令执行时会要求你设置一个密码,这是一个一次性密码,用于重启后录入MOK。要注册MOK,我们不能在已经启动的电脑上操作,因此我们需要重启,使用mmx64.efi操作。mokutil已经帮我们做了设置,下次重启时就会进入MokManager界面。然后,你需要输入刚才设置的密码,以证明这一操作是由你进行的。

进行MOK录入

重启后,你会直接进入一个蓝色页面,要求你按任意键以开始录入MOK。请不要对这样的页面感到害怕,虽然它们的设计原始而且有点吓人,但是本质上讲它们是为了人的方便而设计的工具。仔细看看给出的选项,一步步操作即可。

如果你的操作没有问题,你将会看到如下的菜单:

mmx64.efi

按上下键移动光标,选择Enroll MOK这一项,回车。如果你的菜单没有这一项,你也可以选择Enroll key from disk,在EFI分区中找到你需要录入的密钥。

如果你愿意,你可以选择View Key以查看密钥信息。然后选择Continue,继续录入。

mmx64.efi

接下来会弹出窗口,询问你是否继续。选择Yes。

mmx64.efi

然后输入密码,回车。

mmx64.efi

于是录入就完成了。选择Reboot重启。如果你卡在了任何一个步骤希望重来,可以按下Ctrl+Alt+Delete组合键重启。

mmx64.efi

设置启动项

最后,你需要使用efibootmgr将shimx64.efi设置为首个启动项,然后把refind.efi改名为grubx64.efi。完成之后,你可以尝试启用Secure Boot,检验设备能否正常启动。