允许 QEMU 和 KVM 基于的客户机使用安全启动 (SB)

https://blueprints.launchpad.net/nova/+spec/allow-secure-boot-for-qemu-kvm-guests

问题描述

今天,Nova 的 libvirt 驱动程序仅支持 QEMU 和 KVM 客户机的通用 UEFI 启动,但不支持安全启动(其目标是:“确保机器上不运行未签名的内核代码”)。安全启动可以保护客户机免受启动时恶意软件的侵害,并验证客户机固件执行的代码是否可信。

更确切地说,libvirt 驱动程序将 OVMF(虚拟机的 UEFI 开源实现)二进制文件的路径硬编码在变量中

[...]
DEFAULT_UEFI_LOADER_PATH = {
    "x86_64": "/usr/share/OVMF/OVMF_CODE.fd",
    "aarch64": "/usr/share/AAVMF/AAVMF_CODE.fd"
}
[...]

上述内容仅提供通用 UEFI 启动 [1],而不支持安全启动。而且,以这种方式硬编码 OVMF 二进制文件路径也不够健壮。

本规范建议扩展 Nova 的 libvirt 驱动程序中现有的 UEFI 启动支持,以同时支持安全启动。有关支持 KVM / QEMU 客户机的安全启动需要完成哪些工作,请参阅 Proposed changeWork items 部分。在本规范中,我们仅关注 x86_64 架构。

注意

Nova 的 Hyper-V 驱动程序已经支持安全启动;它是在提交 29dab99 – “Hyper-V: Adds Hyper-V UEFI Secure Boot” 中添加的 [2]

用例

一个不完全的列表

  • 保护 Nova 启动的实例免受客户机侧启动时恶意软件的侵害。

  • 安全启动将通过要求 UEFI 二进制文件具有可信签名来防止 Nova 实例运行不受信任的代码。有关更多详细信息,请参阅此处“Testing Secure Boot”指南 [3]

  • 安全启动将允许 Nova 实例中的可信代码:(a) 启用安全启动操作模式(用于自我保护);以及 (b) 防止客户机中的恶意代码绕过安全启动操作模式的实际安全性。

  • 另外,作为回顾,OVMF 白皮书的“Motivation”部分列出了使用 OVMF 的好处 [4]。对于更详细的安全启动处理,请参阅此处 [5]

提议的变更

为了允许 KVM 和 QEMU 客户机使用安全启动,以下是计划的更改的大致集合

  • 重用现有的 Nova 元数据属性 os_secure_boot(为 Hyper-V 支持添加),以允许用户请求安全启动支持。

  • 在初始实现中,Nova 将仅支持默认 UEFI 密钥,这将适用于大多数发行版(Debian、Ubuntu、SUSE、Fedora、CentOS 和 RHEL)——因为它们提供了一个包含默认 UEFI 密钥的变量文件(“VARS”)。 (如果您不信任默认 UEFI 密钥,那么这等同于您不信任运行计算节点的的文件系统。)如果以后需要,我们可以重用现有的镜像元数据属性 os_secure_boot_signature,该属性允许您指定启动加载程序的签名。

  • 让 Nova 使用 libvirt 的接口来自动选择固件二进制文件;这在 libvirt 5.2 中添加了 [6]。为什么?

    问题:目前,Nova 没有明智的方法来确定选择哪个固件二进制文件。它所看到的就是硬编码的固件二进制文件路径,这既难看又脆弱。最重要的是,很难确定该二进制文件是否支持安全启动。

    解决方案:这就是 libvirt 的固件自动选择发挥作用的地方。它利用了 QEMU 和 OVMF 中完成的大量工作,并通过提供健壮的接口来修复上述问题。也就是说,libvirt 现在可以获取正确的 OVMF 二进制文件,并启用安全启动 (SB) 和系统管理模式 (SMM),并使用方便的 XML 配置。

    <os firmware='efi'>
      <loader secure='yes'/>
    </os>
    

    我们将使用 libvirt 的正式接口来自动选择固件二进制文件——这对于 Nova 来说也减少了代码量。并且我们将记录 Nova 仅在拥有 MIN_LIBVIRT_SECURE_BOOT_VERSIONMIN_QEMU_SECURE_BOOT_VERSION 常量时才支持安全启动。

    此 libvirt 功能利用了 QEMU 的固件描述模式 [7]

  • 让 Nova 以编程方式查询 getDomainCapabilities() API,以检查 libvirt 是否支持相关的安全启动功能。引入一个 _has_uefi_secure_boot_support() 方法来检查 libvirt 是否可以支持该功能。可以通过检查 getDomainCapabilities() API 输出中的 efisecure XML 属性是否存在来完成此操作。

  • 在初始实现中,将不会有调度程序支持来隔离不支持安全启动的主机,类似于现有的基本 UEFI 启动支持。如果主机 hypervisor 不支持安全启动,Nova 将报错。

不同类型的 OVMF 构建的低级背景

[感谢:Laszlo Ersek,OVMF 维护者,感谢下面的讨论。在获得许可的情况下,我添加了 Laszlo 的大量逐字文本。]

可以构建到 OVMF 中的一个功能是“安全启动功能”。这与名为“安全启动”(SB) 的操作模式不同。如果固件包含该功能,则客户机可以启用或禁用操作模式。如果固件不包含该功能,则客户机无法启用操作模式。

可以构建到 OVMF 中的另一个功能称为“SMM”(安全管理模式)。这意味着一个驱动程序堆栈,它由一组在 SMM 中运行的特权驱动程序和另一个接口集,即仅格式化请求的非特权驱动程序,并解析其响应。一旦 SMM 功能构建到 OVMF 中,QEMU 平台上的 SMM 模拟就是强制的,是必需的。

安全启动功能和 SMM 功能堆栈是正交的。您可以在所有四种配置中构建 OVMF。但是,如果您希望您的客户机中的可信代码能够启用安全启动操作模式(用于自我保护),并且希望防止客户机中的恶意代码绕过安全启动操作模式的实际安全性,那么您必须将这两个功能都构建到 OVMF 中。

注意

不同的发行版发布不同类型的构建。例如,Fedora 发布了两种 OVMF 固件二进制文件:一种没有 SB 或 SMM,另一种同时具有 SB 或 SMM。其他发行版发布了不同的构建,并且在不同的路径下。即使他们发布了 SB+SMM OVMF 构建,固件二进制文件的路径也可能不同。

幸运的是,Nova 不需要弄清楚 OVMF 二进制文件路径。这由以下组合处理:(a)Linux 发行版发布固件描述文件(描述 UEFI 固件二进制文件的详细信息的小型 JSON 文件,例如固件二进制文件路径、其架构、支持的机器类型、NVRAM 模板)与 EDK2/OVMF 一起;以及 (b) libvirt >=5.3,以利用所述固件描述文件。

OVMF 二进制文件和变量存储(“VARS”)文件路径

每个发行版都有其自己的(略有不同的)OVMF 路径

  • SUSE
    • 软件包名称:qemu-ovmf-x86_64

    • /usr/share/qemu/ovmf-x86_64-opensuse-code.bin 是使用 SB 和 SMM 构建的固件二进制文件

    • /usr/share/qemu/ovmf-x86_64-opensuse-vars.bin 是与上述二进制文件匹配的变量存储模板

  • Fedora
    • 软件包名称:“edk2-ovmf”(x86_64)

    • /usr/share/edk2/ovmf/OVMF_CODE.fd 是没有 SB 或 SMM 构建的固件二进制文件

    • /usr/share/edk2/ovmf/OVMF_CODE.secboot.fd 是使用 SB 和 SMM 构建的固件二进制文件

    • /usr/share/edk2/ovmf/OVMF_VARS.fd 是与上述两个二进制文件匹配的变量存储模板

    • /usr/share/edk2/ovmf/OVMF_VARS.secboot.fd 是包含默认 UEFI 密钥的变量存储模板

  • RHEL-7.6 和 RHEL-8
    • 软件包名称:“ovmf”(x86_64)

    • /usr/share/OVMF/OVMF_CODE.secboot.fd 是使用 SB 加上 SMM 构建的固件二进制文件

    • /usr/share/OVMF/OVMF_VARS.secboot.fd 是匹配的变量存储模板

  • Debian (Buster)
    • 软件包名称:“ovmf”(x86_64)

    • /usr/share/OVMF/OVMF_CODE.fd 是使用 SB 加上 SMM 构建的固件二进制文件。

  • Ubuntu (Eoan)
    • 软件包名称:“ovmf”(x86_64)

    • Eoan 版本也通过 EDK2 软件包发布了我们需要的固件描述文件(参考下文)

这其中一部分比较棘手,但幸运的是,libvirt 5.2 版本大大简化了 OVMF 文件名处理——通过提供一个自动选择固件的接口(反过来,利用 QEMU 提供的固件描述文件,QEMU 版本为 2.9 及更高版本)。

备选方案

无。

数据模型影响

无。

REST API 影响

无。

安全影响

有了此功能,基于 KVM 和 QEMU 的 Nova 实例就可以获得安全启动支持。从而保护客户机免受启动时恶意软件的侵害,并确保固件执行的代码只有受信任的代码。

通知影响

无。

其他最终用户影响

不会对冷迁移或实时迁移产生影响;libvirt 已经具备必要的保障措施来处理。

性能影响

无。

其他部署者影响

要使用此功能,以下是版本要求:QEMU >=4.1.0,libvirt >=5.3,发布了 JSON 描述文件的 OVMF/EDK2 软件包。详情请参阅 Dependencies 部分。

开发人员影响

无。

升级影响

无。

实现

负责人

主要负责人

Kashyap Chamarthy <kchamart@redhat.com>

功能联络人

功能联络人

johnthetubaguy

工作项

x86_64 架构为例。以下是启用 QEMU 和 KVM 客户机安全启动支持的工作项目

  1. 确保 Nova 在请求安全启动时配置客户机 XML 中的 SMM(系统管理模式)hypervisor 功能

    <features>
      [...]
      <smm state='on'/>
    </features>
    

    请注意,在使用 libvirt 的固件自动选择功能时,libvirt 在启动客户机时会自动添加 SMM 功能,因为 SMM 和 SB 是密不可分的。

  2. 确保 OVMF loadernvram 相关的客户机 XML 片段如下(对于使用启用 SMM + SB 的 OVMF 构建的 Fedora 客户机和 Q35 机器类型)

    <os>
      <type arch='x86_64' machine='pc-q35-3.0'>hvm</type>
      <loader readonly='yes' secure='yes' type='pflash'>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</loader>
      <nvram template='/export/vmimages/fedora_VARS.secboot.fd'>/var/lib/libvirt/qemu/nvram/fedora_VARS.secboot.fd</nvram>
      <boot dev='hd'/>
    </os>
    

    请注意,Nova 无需担心 NVRAM 存储的文件管理,因为 libvirt 的固件自动选择功能还会检测与固件映像关联的 NVRAM 存储,将其复制到客户机的私有路径,并要求客户机使用它。

    NB-1:UEFI 二进制文件的路径因不同的发行版而异,但 libvirt 将为我们处理这些问题。

    注意 2:使用 OVMF 的安全启动必须使用 Q35 机器类型。

  3. 为了让客户机真正获得安全启动,我们需要确保非易失性存储(“VARS”)文件(在上面的示例中,fedora_VARS.secboot.fd)已注册默认 UEFI 密钥。

    有两种方法可以实现这一点。第一种,使用您的 Linux 发行版发布的“VARS”模板文件(注册 UEFI 密钥);这是首选方法。第二种,您可以使用各种 Linux 发行版(作为其 EDK2 / OVMF 软件包的一部分)提供的 UefiShell.iso + EnrollDefaultKeys.efi 工具在“VARS”文件中注册默认 UEFI 密钥,并将其放置在适当的位置。一些 Linux 发行版提供了一个工具(参考下文)来自动执行密钥注册过程。该工具的使用方法如下

    1. 运行 ovmf-vars-generator 工具(根据发行版调整参数)一次

      $> ./ovmf-vars-generator \
            --ovmf-binary /usr/share/edk2/ovmf/OVMF_CODE.secboot.fd \
            --uefi-shell-iso /usr/share/edk2/ovmf/UefiShell.iso \
            --ovmf-template-vars /usr/share/edk2/ovmf/OVMF_VARS.fd \
            --fedora-version 31 \
            --kernel-path /tmp/kernel \
            --kernel-url /path/to/vmlinuz \
            template_VARS.fd
      ...
      INFO:root:Created and verified template_VARS.fd
      
    2. 使用指向上述 template_VARS.fd 的唯一副本重新启动客户机。此时,您实际上会看到安全启动已启用。可以通过 dmesg 进行验证

      (fedora-vm)$ dmesg | grep -i secure
      [    0.000000] secureboot: Secure boot enabled
      [    0.000000] Kernel is locked down from EFI secure boot; see man kernel_lockdown.7
      

    但是,如前所述,无需手动运行上述步骤。大多数常见的 Linux 发行版(SUSE、Fedora、RHEL)已经发布了一个带有默认 UEFI 密钥注册的“VARS”文件。Debian 和 Ubuntu 正在积极开展这项工作 [8]

    如果您的发行版没有发布带有默认 UEFI 密钥注册的“VARS”文件,请在此处 [9] 找到一个小的 Python 工具 ovmf-vars-generator,它将自动执行上述三个步骤。这在 Fedora 中作为 EDK2/OVMF 的子 RPM 打包,称为“edk2-qosb”。Ubuntu 已经将其工具包含在其固件包中。

  4. 记录使用工具 ovmf-vars-generator 生成上述“VARS”文件的方法。该工具已经作为主要 ‘edk2’ / OVMF 的子包(名为:‘edk2-qosb’)在不同的发行版中发布。Ubuntu 和 Debian 也在努力发布此脚本。

依赖项

  • 对于 SMM(系统管理模式)功能,仅支持 QEMU Q35 机器类型。

  • QEMU >=2.4 以获得安全启动支持。

  • QEMU >=4.1.0(于 2019 年 8 月发布),以获得符合 QEMU 的 firmware.json 规范的固件描述文件。以下 [10] 是所述“固件描述文件”的一些示例。

  • libvirt >=5.3(于 2019 年 5 月发布),用于固件自动选择功能以及查询 efi [11] 固件可用性的能力通过 getDomainCapabilities() API。

  • Ubuntu (Eoan) 版本中的 OVMF 0~20190606.20d2e5a1-2ubuntu1,以提供 JSON 描述文件 [12]

测试

如果满足上述最小的 libvirt 和 QEMU 版本要求,则应该可以在上游 gating 环境中测试此功能。Nova 实例应该能够启动使用 OVMF 的安全启动 KVM 客户机,并在 dmesg 中验证安全启动是否实际生效。

文档影响

记录如何使用 OVMF 为 QEMU 和 KVM 客户机启动 x86_64 Nova 实例,并更新 Glance 的“Useful image properties”文档 [13]

参考资料

历史

修订

发布名称

描述

Train

引入

Ussuri

重新提出