风味和镜像定义的临时存储加密

https://blueprints.launchpad.net/nova/+spec/ephemeral-storage-encryption

本规范概述了 Nova 中临时存储加密的一种新方法,允许用户通过使用具有特定额外规格的风味或具有特定属性的镜像来选择其临时存储在静态时的加密方式。目标是将 Nova 中的临时存储加密体验与 Cinder 提供的块存储加密实现保持一致,在 Cinder 中可用的用户可选择的加密卷类型

注意

本规范仅涵盖 API 和计算层的高层更改,特定虚拟驱动程序中的实现留待单独的规范说明。

问题描述

目前,仅 libvirt 虚拟驱动程序在使用 lvm imagebackend 时提供内置的临时存储加密支持。当前的实现为基础操作员提供主机特定的临时磁盘静态加密支持,所有给定计算节点上的实例都强制使用 dm-crypt PLAIN 加密格式进行加密。

这并非理想状态,并且使临时存储加密对最终用户完全不透明,与 Cinder 提供的块存储加密支持相反,在 Cinder 中,用户能够选择使用管理员定义的加密卷类型来确保其存储在静态时被加密。

此外,当前的实现使用单个对称密钥来加密与实例关联的所有临时存储。由于使用了 PLAIN 加密格式,因此无法就地轮换此密钥。

用例

  • 作为用户,我希望通过选择特定的风味或镜像来请求加密我所有的临时存储在静态时。

  • 作为用户,我希望通过选择特定的风味或镜像来选择如何加密我的临时存储在静态时。

  • 作为管理员/操作员,我希望能够强制对风味或镜像进行临时加密。

  • 作为管理员/操作员,我希望为我的最终用户提供有关如何加密其临时存储在静态时的合理选择。

  • 作为虚拟驱动程序维护者/开发人员,我希望表明我的驱动程序支持使用特定加密格式的临时存储加密。

  • 作为虚拟驱动程序维护者/开发人员,我希望为希望加密其临时存储在静态时的用户提供合理的默认加密格式和选项。我希望这些与加密存储相关联,直到它被删除为止。

提议的变更

为了启用此功能,将引入新的风味额外规格、镜像属性和主机可配置项。这些将控制何时以及如何为实例启用临时存储静态加密。

注意

以下 hw_ephemeral_encryption 镜像属性与镜像在 Glance 服务中是否加密在静态时无关。它们仅与使用 Nova 中配置的实例的临时存储将如何加密在静态时有关。

镜像属性的单独文档已在 Glance 镜像加密Cinder 镜像加密 规范中记录,以涵盖镜像如何在 Glance 中静态加密。

允许通过风味、镜像或配置来配置临时加密

为了启用每个实例的临时加密,将引入以下基于布尔值的风味额外规格和镜像属性

  • hw:ephemeral_encryption

  • hw_ephemeral_encryption

以上将为实例启用临时存储加密,但不控制所使用的加密格式或相关的选项。为此,将引入以下风味附加规格、镜像属性和可配置项。

使用的加密格式将由以下风味附加规格和镜像属性控制

  • hw:ephemeral_encryption_format

  • hw_ephemeral_encryption_format

当以上两者都没有提供,但仍然请求临时加密时,将使用额外的宿主机可配置项来为每个计算节点提供默认格式,该格式最初将默认为 luks

  • [ephemeral_storage_encryption]/default_format

这可能会导致对不同云的请求导致使用的临时加密格式不同,但由于这对于实例内的最终用户来说是透明的,因此不应产生任何实际影响。

格式将作为字符串提供,该字符串映射到 BlockDeviceEncryptionFormatTypeField oslo.versionedobjects 字段值

  • plain 用于纯 dm-crypt 格式

  • luks 用于 LUKSv1 格式

为了启用使用临时加密的实例的快照和搁置功能,加密密钥的 UUID 将存储在密钥管理器中,并作为镜像属性与生成的镜像一起保留。

  • hw_ephemeral_encryption_secret_uuid

在从临时加密的快照创建实例或取消搁置临时加密的实例时,需要此密钥 UUID。

为每个新的加密磁盘镜像创建一个新的密钥管理器密钥

磁盘镜像密钥的方法是不在不同的磁盘镜像之间共享密钥,并且每个磁盘镜像都有一个唯一的密钥。这样做是为了解决 1) 安全隐患和 2) 清理不再使用的密钥的后勤问题。

例如

假设 Instance A 有 3 个磁盘:一个根磁盘、一个临时磁盘和一个交换磁盘。每个磁盘都有自己的密钥。

此表旨在说明在各种情况下处理密钥的方式。

+--------------------+-------------+--------------+------------------------------------------------------+
| Instance or Image  | Disk        | Secret       | Notes                                                |
|                    |             | (passphrase) |                                                      |
+====================+=============+==============+======================================================+
| Instance A         | disk (root) | Secret 1     | Secret 1, 2, and 3 will be automatically deleted     |
|                    +-------------+--------------+ by Nova when Instance A is deleted and its disks are |
|                    | disk.eph0   | Secret 2     | destroyed                                            |
|                    +-------------+--------------+                                                      |
|                    | disk.swap   | Secret 3     |                                                      |
+--------------------+-------------+--------------+------------------------------------------------------+
| Image Z (snapshot) | disk (root) | Secret 4     | Secret 4 will *not* be automatically deleted and     |
| created from       |             | (new secret  | manual deletion will be needed if/when Image Z is    |
| Instance A         |             |  is created) | deleted from Glance                                  |
+--------------------+-------------+--------------+------------------------------------------------------+
| Instance B         | disk (root) | Secret 5     | Secret 5, 6, and 7 will be automatically deleted     |
| created from       +-------------+--------------+ by Nova when Instance B is deleted and its disks are |
| Image Z (snapshot) | disk.eph0   | Secret 6     | destroyed                                            |
|                    +-------------+--------------+                                                      |
|                    | disk.swap   | Secret 7     |                                                      |
+--------------------+-------------+--------------+------------------------------------------------------+
| Instance C         | disk (root) | Secret 8     | Secret 8, 9, and 10 will be automatically deleted    |
|                    +-------------+--------------+ by Nova when Instance C is deleted and its disks are |
|                    | disk.eph0   | Secret 9     | destroyed                                            |
|                    +-------------+--------------+                                                      |
|                    | disk.swap   | Secret 10    |                                                      |
+--------------------+-------------+--------------+------------------------------------------------------+
| Image Y (snapshot) | disk (root) | Secret 8     | Secret 8 is *retained* when Instance C is shelved in |
| created by shelve  |             |              | part to prevent the possibility of a change in       |
| of Instance C      |             |              | ownership of the root disk secret if, for example,   |
|                    |             |              | an admin user shelves a non-admin user's instance.   |
|                    |             |              | This approach could be avoided if there is some way  |
|                    |             |              | we could create a new secret using the instance's    |
|                    |             |              | user/project rather than the shelver's user/project  |
+--------------------+-------------+--------------+------------------------------------------------------+
| Rescue disk        | disk (root) | Secret 11    | Secret 11 is stashed in the instance's system        |
| created by rescue  |             | (new secret  | metadata with key                                    |
| of Instance A      |             |  is created) | ``rescue_disk_ephemeral_encryption_secret_uuid``.    |
|                    |             |              | This is done because a BDM record for the rescue     |
|                    |             |              | disk is not going to be persisted to the database.   |
+--------------------+-------------+--------------+------------------------------------------------------+

带有临时加密的实例的快照

当具有临时加密的实例被快照时,会创建一个新的加密密钥,并且其密钥管理器密钥 UUID 会作为镜像属性 hw_ephemeral_encryption_secret_uuid 保留,并将镜像上传到 Glance。

当从加密镜像创建新实例时,镜像属性 hw_ephemeral_encryption_secret_uuid 会被传递到较低层,方法是将它存储在实例的系统元数据中,键为 image_hw_ephemeral_encryption_secret_uuid。这是因为在较低层(例如,调用 qemu-img convert 时),我们不再可以访问镜像元数据,否则需要重构以将镜像元数据传递给多个较低层方法,或者类似的操作。

由带有临时加密的实例创建的快照

当具有临时加密的实例被搁置时,现有的根磁盘加密密钥将被保留,并在以后用于取消搁置该实例。这样做是为了防止在管理员用户搁置非管理员用户的实例时,根磁盘加密密钥的所有权发生潜在变化。如果创建了一个由管理员用户拥有的新密钥,那么拥有该实例的非管理员用户将无法取消搁置该实例。

但是,如果我们可以使用实例的用户和项目而不是搁置者的用户和项目来创建新的加密密钥,就可以避免这种行为。如果这是可能的,我们就不需要重用加密密钥。

通过救援具有临时加密的实例创建的救援磁盘镜像

当救援一个实例并且指定了一个加密的救援镜像时,镜像属性中的救援镜像密钥 UUID 将被存储在实例的系统元数据中,键为 rescue_image_hw_ephemeral_encryption_secret_uuid,以便将其传递到较低层。这被认为是与 image_hw_ephemeral_encryption_secret_uuid 分开的,后者指的是创建该实例的加密镜像。保持它们分开的另一个原因是避免对阅读或使用代码的人造成混淆。

当创建救援磁盘时,会创建一个新的加密密钥,并且其 UUID 会被存储在实例的系统元数据中,键为 rescue_disk_ephemeral_encryption_secret_uuid。这是因为救援磁盘的块设备映射记录将不会持久化到数据库中。

相应的 virt 驱动程序密钥名称模式是 <instance UUID>_rescue_disk,并且当请求新的救援时,virt 驱动程序会删除所有具有该名称的现有密钥。

当实例被取消救援时,救援磁盘的新加密密钥将从密钥管理器中删除,并且 virt 驱动程序密钥也会被删除。

临时加密密钥的清理

与它们关联的磁盘被销毁时,临时加密密钥将从密钥管理器和 virt 驱动程序中删除。其方法是,加密密钥仅在关联的磁盘被销毁时才会被删除。

当创建快照时创建的加密密钥将永远不会被 Nova 删除。只有在快照镜像被删除时,才允许删除该密钥。必须由用户或管理员手动删除 Glance 中已删除镜像的密钥。

注意

在撰写本文时,最新的 Ceph 版本 v17 (Quincy) 不支持使用与父镜像不同的加密密钥创建克隆镜像。因此,对于指定了临时加密的实例,将不会启用写时复制克隆。

在 Ceph 的下一个版本中应该支持使用与父镜像不同的加密密钥创建克隆镜像。当我们能够要求 Ceph 版本 >= v18 时,就可以启用具有临时加密的写时复制克隆。有关参考,请参阅 https://github.com/ceph/ceph/commit/1d3de19

BlockDeviceMapping 更改

将扩展 BlockDeviceMapping 对象,以包含封装上述信息的以下字段,每个实例中的临时磁盘

encrypted

一个简单的布尔值,指示块设备是否已加密。最初,这仅在临时加密使用时才会被填充,但将来也可以轻松地用于加密的卷。

encryption_secret_uuid

顾名思义,这将包含磁盘关联的加密密钥的 UUID。此处使用的密钥类型将取决于所使用的加密格式和 virt 驱动程序,不应假定它始终是当前 Cinder 提供的所有加密卷都是对称密钥。

encryption_format

一个新的 BlockDeviceEncryptionFormatType 枚举和相关的 BlockDeviceEncryptionFormatTypeField 字段,列出加密格式。可用的选项将与 os-brick 当前提供的常量保持一致,并且如果两者都可以共享这些类型和字段,则将来可能会合并。

encryption_options

一个简单的、未版本化的字符串字典,包含特定于 virt 驱动程序实现、底层 hypervisor 和所用格式的加密选项。

注意

由于安全性和升级影响,encryption_options 字段最初将不会被使用或暴露给最终用户。对于第一次尝试,将对密码算法、密码模式和初始化向量生成器算法进行硬编码,而不是使用有意义的默认值。

在开发出解决安全问题并处理所有升级场景的适当设计后,加密选项将来可以暴露给最终用户。

在构建期间填充临时加密 BlockDeviceMapping 属性

当通过镜像或 flavor 请求使用临时加密启动实例时,对于 destination_type 值为 local 的每个 BlockDeviceMapping 记录,BlockDeviceMapping.encrypted 属性将被设置为 True。 这将在原始 API BDM 字典转换为 Compute API 中的对象之后,但在调度实例之前发生。

如果提供,encryption_format 属性也将从镜像或风味获取其值。镜像和风味对此的任何差异或冲突将引发 API 抛出 409 Conflict 错误。

使用 COMPUTE_EPHEMERAL_ENCRYPTION 兼容性特征

Wallaby 期间引入了一个 COMPUTE_EPHEMERAL_ENCRYPTION 计算兼容性特征,virt 驱动程序将报告该特征,以指示对使用这种新方法进行临时存储加密的总体支持。 此特征将始终由以下部分概述的预过滤器使用,在请求了临时加密时,无论请求中是否指定了格式,从而允许最终处理请求的计算选择其支持的格式,使用 [ephemeral_storage_encryption]/default_format 可配置项。

在 Wallaby 期间,还向 os-traits 添加了 COMPUTE_EPHEMERAL_ENCRYPTION_$FORMAT 计算兼容性特征,virt 驱动程序将报告该特征,以指示对特定临时存储加密格式的支持。 例如

  • COMPUTE_EPHEMERAL_ENCRYPTION_LUKS

  • COMPUTE_EPHEMERAL_ENCRYPTION_LUKSV2

  • COMPUTE_EPHEMERAL_ENCRYPTION_PLAIN

只有当初始请求中提供了 hw_ephemeral_encryption_format 镜像属性或 hw:ephemeral_encryption_format 额外规格时,这些特征才将与 COMPUTE_EPHEMERAL_ENCRYPTION 特征一起使用。

引入一个临时加密请求预过滤器

将引入一个新的预过滤器,当提供上述镜像属性或 flavor 额外规格时,它会将上述特征作为必需项添加到请求规格中。 如上所述,这将始终包括 COMPUTE_EPHEMERAL_ENCRYPTION 特征,在请求了临时加密时,如果请求中包含格式,则可以选择性地包含一个特定于格式的特征。

通过 block_device_info 暴露临时加密属性

一旦 BlockDeviceMapping 对象更新并且实例被调度到计算节点,这些对象将再次转换为 virt 层理解的 block_device_info 字典,目前该字典包含以下内容

root_device_name

实例使用的根设备路径。

ephemerals

一个 DriverEphemeralBlockDevice 字典对象的列表,详细说明了附加到实例的临时磁盘。 请注意,这不包括用于实例的初始基于镜像的磁盘,该磁盘在临时加密功能的上下文中被归类为临时磁盘。

block_device_mapping

一个 DriverVol*BlockDevice 字典对象的列表,详细说明了附加到实例的基于卷的磁盘。

swap

一个可选的 DriverSwapBlockDevice 字典对象,详细说明了交换设备。

例如

{
    "root_device_name": "/dev/vda",
    "ephemerals": [
        {
            "guest_format": null,
            "device_name": "/dev/vdb",
            "device_type": "disk",
            "size": 1,
            "disk_bus": "virtio"
        }
    ],
    "block_device_mapping": [],
    "swap": {
        "swap_size": 1,
        "device_name": "/dev/vdc",
        "disk_bus": "virtio"
    }
}

如上所述,block_device_info 并不提供与实例关联的存储的完整概述。 为了使其在临时存储加密的上下文中有用,我们需要扩展该字典以始终包含与本地基于镜像的磁盘相关的信息。

因此,将引入一个新的 DriverImageBlockDevice 字典类,涵盖基于镜像的块设备,并通过 block_device_info 字典中的一个额外的 image 键提供给 virt 层,当实例使用此类磁盘时。 与其他 Driver*BlockDevice 字典类一样,这将代理访问底层的 BlockDeviceMapping 对象,从而允许 virt 层查找先前列出的 encryptedencryption_* 属性。

虽然超出此规范的范围,但上述内容突出了代码库中存储配置处理方式的巨大复杂性和技术债务。 从长远来看,我们应该计划删除 block_device_info 并将其替换为对基于 BlockDeviceMapping 的对象的直接访问,确保始终将整个配置暴露给 virt 层。

通过元数据 API 报告磁盘处于加密状态

扩展元数据 API,以便用户可以确认他们的临时存储是否通过实例中可访问的元数据 API 进行加密。

{
    "devices": [
        {
            "type": "nic",
            "bus": "pci",
            "address": "0000:00:02.0",
            "mac": "00:11:22:33:44:55",
            "tags": ["trusted"]
        },
        {
            "type": "disk",
            "bus": "virtio",
            "address": "0:0",
            "serial": "12352423",
            "path": "/dev/vda",
            "encrypted": "True"
        },
        {
            "type": "disk",
            "bus": "ide",
            "address": "0:0",
            "serial": "disk-vol-2352423",
            "path": "/dev/sda",
            "tags": ["baz"]
        }
    ]
}

这也应该扩展到由加密卷提供的磁盘,但这显然超出了此实现的范围。

在具有不同 hw:ephemeral_encryption 设置的风味之间进行块大小调整

预计临时数据将在大小调整过程中保留,因此在配置了临时加密不同的 flavor(一个启用,另一个禁用或格式等)之间进行大小调整将导致我们就地转换此数据。 这并不容易,因此对于此初始实现,在配置了临时加密不同的 flavor 之间进行大小调整将被阻止。

提供从遗留实现迁移的路径

将引入新的 nova-managenova-status 命令,以便在未来版本中删除遗留 libvirt virt 驱动程序实现之前,迁移使用该实现的任何实例。

nova-manage 命令将确保任何具有设置 ephemeral_key_uuid 的现有实例都将更新其关联的 BlockDeviceMapping 记录,以引用所述密钥,宿主机上的 plain 加密格式和配置选项,然后再清除 ephemeral_key_uuid

此外,libvirt virt 驱动程序还将在生成期间尝试迁移具有 ephemeral_key_uuid 设置的实例。 这应该允许至少一些实例在 W 版本中移动,以便在 X 之前进行操作。

nova-status 命令将简单地报告任何 ephemeral_key_uuid 设置但没有相应 BlockDeviceMapping 属性启用的实例的存在。

弃用现在遗留的实现

libvirt virt 驱动程序中的遗留实现将在未来版本中弃用,以便在迁移能力到位后删除。

备选方案

继续使用透明的主机可配置项,并扩展对其他加密格式(如 LUKS)的支持。

数据模型影响

有关上述 flavor 额外规格、镜像属性、BlockDeviceMappingDriverBlockDevice 对象更改,请参见上文。

REST API 影响

  • 将引入 flavor 额外规格和镜像属性验证,用于提供的任何临时加密选项。

  • 将拒绝在临时加密选项不同的 flavor 之间进行大小调整的尝试。

  • 允许在具有不同临时加密选项的镜像之间重建。

  • 元数据 API 将更改为允许用户确定他们的临时存储是否已加密,如上所述。

安全影响

希望这会是积极的,因为每个磁盘和用户可见的选择都使用唯一的密钥来加密临时存储。

此外,这应该允许其他 virt 驱动程序支持临时存储加密,同时也允许 libvirt virt 驱动程序增加对更多 imagebackend(例如 qcow2 和 rbd)的功能覆盖范围。

注意

Nova 本地存储的内部基础镜像将不会加密静态数据。

通知影响

N/A

其他最终用户影响

用户现在需要通过其image或flavors的选择选择启用临时存储加密,从而选择使用临时存储加密。

性能影响

额外的预过滤器将在调度实例时增加少量开销,但如果未通过镜像或 flavor 请求临时加密,则应该快速失败。

实例增加使用临时存储加密的性能影响留待 virt 驱动程序特定的规范讨论,因为这将因 hypervisor 而异。

其他部署者影响

N/A

开发人员影响

virt 驱动程序开发人员可以使用新引入的计算兼容性特征来指示对特定临时存储加密格式的支持。

升级影响

计算特征应确保在滚动升级期间,使用临时加密的混合计算(N-1 和 N)进行实例调度请求能够正常工作。

如上文所述,未来的升级需要为现有的临时存储加密用户提供迁移到遗留实现的路径。 这应该很简单,但在 W 周期期间可能需要在 CI 中进行额外的炸弹作业来证明迁移路径。

实现

负责人

主要负责人

melwitt

其他贡献者

lyarwood

功能联络人

功能联络人

melwitt

工作项

  • 引入 hw_ephemeral_encryption* 镜像属性和 hw:ephemeral_encryption 风味附加规格。

  • 引入一个新的 encryptedencryption_secret_uuidencryption_formatencryption_options 属性到 BlockDeviceMapping 对象。

  • 通过 Driver*BlockDevice 层和 block_device_info 字典连接新的 BlockDeviceMapping 对象属性。

  • 通过元数据 API 报告临时存储加密。

  • 引入新的 nova-managenova-status 命令,允许现有用户迁移到此新实现。 但是,这应该在 virt 驱动程序实现着陆之前被阻止。

  • 在 virt 驱动程序实现着陆之前,将在功能测试中验证所有上述内容。

依赖项

测试

目前,没有 virt 驱动程序实现,这将完全在我们的单元和功能测试套件中进行测试。

一旦有 virt 驱动程序实现,就可以编写额外的集成测试(在 Tempest 中)和白盒测试。

测试从遗留实现迁移的路径将需要一个额外的炸弹作业,但这需要先完成 libvirt virt 驱动程序实现。

文档影响

  • 新的主机可配置项、flavor 额外规格和镜像属性应记录在案。

  • 应编写新的用户文档,涵盖从 Nova 角度来看该功能的整体使用。

  • 应更新 BlockDeviceMapping 对象等的参考文档,以记下新的加密属性。

参考资料

历史

可选部分,旨在每次更新规范时使用,以描述新的设计、API 或任何数据库模式更新。有助于让读者了解随着时间的推移发生了什么。

修订版

发布名称

描述

Wallaby

引入

Xena

重新提出

瑜伽

重新提出

Zed

重新提出

2023.1 Antelope

重新提出

2023.2 Bobcat

重新提出