Cinder 证书验证

https://blueprints.launchpad.net/cinder/+spec/certificate-validate

OpenStack 现在支持对签名镜像进行签名验证。但是,它不支持对用于生成镜像签名的证书进行强证书验证。具体来说,cinder 没有机制来识别受信任的证书。虽然 cinder 使用 cursive 验证已签名镜像的签名,但没有办法确定用于生成和验证该签名的证书是否是用户信任的证书。此更改将为 cinder API 引入一个补充,允许用户在从镜像创建卷时指定受信任证书列表。这些受信任证书将与 cursive 中的签名验证一起进行证书验证,为用户提供对正在创建的镜像的身份和完整性的信心。

问题描述

Cinder 能够使用 cursive(OpenStack 签名验证库 [1])验证已签名镜像的签名。签名验证确保从 glance 获取未修改的镜像数据。但是,当前用于生成已签名镜像签名的证书的验证仅限于时间戳有效性检查,仅确保证书在签名验证时有效。没有机制来确保使用的证书是最终用户批准的证书。如果攻击者可以访问 glance,他们可以用攻击者的证书替换用户的已签名镜像,该证书存储在 OpenStack 部署的证书管理器中,并用恶意镜像签名。如果要求从修改后的镜像创建卷,cinder 将检索镜像及其对应的证书,验证镜像签名,并使用恶意镜像数据构建卷。在 cursive 中提供对证书验证的支持,有助于 cinder 检测此攻击场景并采取措施提醒用户潜在的风险。

请注意,此威胁模型将 glance 视为不受信任的,不包括对 cinder 的完整性、可用性或机密性的威胁。它假定:(1)攻击者可以访问证书管理器并能够存储用于镜像签名的证书,以及(2)此攻击者无法访问其他用户拥有的任意证书公钥/私钥对。如果攻击者拥有此访问权限,他们将能够冒充用户,替换已签名镜像并根据需要完美地更新相应的镜像签名和元数据以掩盖攻击。

用例

Cinder 用户希望确保他们正在从他们信任的镜像创建,方法是控制用于签名镜像的证书集。

通过此更改,如果启用了签名验证和证书验证,cinder 用户可以在从已签名镜像创建卷时指定受信任证书的身份。预计这些受信任证书之一是用于生成镜像签名的证书的签名证书。

启用证书验证后,只有当镜像签名证书是由受信任证书生成的时,镜像签名验证才会成功。这允许用户参与签名验证过程,要求成功启动需要有效的证书信息。请注意,如果部署配置为信任用户不知情的某些证书,则部署者仍然可以通过用户无法使用此功能检测到的方式操纵镜像。

提议的变更

支持证书验证需要进行几项更改。最初的更改添加了一对新的配置选项。第一个配置选项 enable_certificate_validation 将在进行镜像签名验证时启用 cursive 证书验证例程(参见下面的第五项更改)。只有当 verify_glance_signatures 设置为 enabled 时,此选项才会被使用,并且默认值为 False。如果将此选项设置为 True,则将执行证书验证。

第二个配置选项 default_trusted_certificate_ids 将包含一个证书 ID 列表,这些 ID 被 cinder 部署指定为受信任的。只有在启用证书验证且用户未提供受信任证书 ID(参见下面的第二项和第三项更改)时,才会使用此受信任证书 ID 列表。管理员应定义此列表,因为它将是 cinder 部署的默认受信任证书 ID 集。此选项的默认值将是一个空列表,如果留空,则需要用户提供的受信任证书 ID 集。如果用户提供了受信任证书 ID 的列表,则将忽略默认受信任证书 ID 的列表。

第二个更改在 cinder API 的卷创建命令中添加了一个参数 trusted_image_certificates。该参数的值是一个数组,包含用于验证已签名镜像的签名证书的受信任证书的字符串 ID。这些 ID 是在上传受信任证书时由证书管理器分配的。为了给用户提供灵活性,这里允许使用多个 ID。用户可能无法知道哪个特定证书对应于他们的镜像。允许用户定义一组受信任证书消除了对镜像/证书映射的需求,简化了用户体验。当提供时,cinder 会将这些值传递给 cursive 中的证书验证例程,覆盖默认受信任证书 ID 列表(参见上面的第二个配置选项)。Cursive 将使用它们通过 castellan 获取受信任的证书。如果提供,该参数的值将保存在镜像元数据中。

第三个更改将证书验证例程集成到签名验证工作流程中。当证书验证例程获取镜像的签名证书时,它使用用户提供的受信任证书构建验证上下文。然后,它将签名证书通过上下文进行证书验证。如果验证成功,签名验证可以正常进行。如果验证失败,签名验证也会失败,并且卷将置于 ERROR 状态。

注意:相应的特性 [2] 已经合并到 Cursive 中。

如果 (1) cinder 未配置为进行证书验证,并且 (2) 用户提供了受信任的证书 ID,期望证书验证发生,则可能会发生静默失败场景。在这种情况下,cinder 不会进行证书验证并启动实例,导致用户认为证书验证成功,即使它从未发生过。为了防止这种情况发生,创建工作流程将被更新为如果创建请求与受信任的证书 ID 相关联,则进行签名和证书验证。这符合用户的期望,并防止静默失败场景的发生。请注意,此覆盖仅在用户指定证书 ID 时发生。默认受信任证书 ID 列表仅在启用该功能时使用,因此永远不会触发覆盖。

第四个更改更新了 cinderclient,以支持卷创建命令的 trusted_image_certificates 参数。这包括对新环境变量 OS_TRUSTED_CERTIFICATE_IDS 的支持,该变量可用于定义逗号分隔的受信任证书 ID 列表。如果未使用 trusted_image_certificates 参数,则客户端将提取环境变量的值并使用它。在传递之前,该值将被转换为列表。

如果用户未通过 trusted_image_certificates 参数(显式或通过 OS_TRUSTED_CERTIFICATE_IDS 环境变量)提供值,cinder 将从 default_trusted_certificate_ids 配置选项中提取受信任证书 ID 的列表。如果将此选项保留为空列表,则 nova 将无法获得用于证书验证的受信任证书。在这种情况下,将无法确定镜像的签名证书是否受信任,因此签名验证将失败,进而导致卷创建失败。

为了明确起见,我们仅在从 glance 下载镜像并将内容复制到主机上的卷时才验证镜像的签名。因此,当通过 clone_imageclone_image_volume 创建镜像卷时,我们将跳过此验证过程,为了不让最终用户感到困惑,我们还将在卷镜像元数据中添加一个标志,以指示我们在卷创建期间是否验证了证书,并且如果后端证书验证失败,该服务将生成用户消息。

请注意,这些受信任的证书存储在独立于卷服务的证书管理器中。对于这项工作,证书管理器是任何由 castellan 支持的证书对象管理操作的服务后端。证书管理通常是通用密钥管理器功能的子集,通用密钥管理器能够管理不同类型的加密密钥(例如,加密密钥、密码)。截至 Pike 版本,barbican(OpenStack 密钥管理服务)是唯一满足证书管理器要求的 OpenStack 服务。在未来,任何由 castellan 支持并提供证书管理的 OpenStack 或第三方服务都可以代替 barbican 使用。

备选方案

此处证书验证的替代方法是支持证书信任库,这些信任库是与单个用户或项目关联的受信任证书的集合。在创建新服务器时,用户将指定他们的信任库作为受信任证书的来源,从而替换 trusted_image_certificates 参数中提供的证书列表。有许多方法可以支持信任库,包括:包含存储在计算主机上的本地受信任证书文件的文件系统目录信任库、由 nova 或 keystone 等服务支持的元数据/托管资源方法,以及由 barbican 等服务支持的基于容器的密钥存储方法。虽然信任库在定义受信任证书的集合方面很有用,但它们可能难以扩展到大型云部署,从而带来管理和维护方面的挑战。信任库还引入了一种用户必须信任的新结构,尤其是如果用户不直接负责维护其信任库时。

用户提供受信任证书的替代方法,或将受信任证书存储在信任库(或只是数据库表)中,将是使用存储在正在验证的签名证书的 Private Internet Extension 中的信息动态获取证书。这种方法允许部署者和用户使用签名证书,而无需预先提取完成证书验证过程所需的所有根证书和中间证书。但是,这种方法需要计算服务具有对所有可能的证书存储库的持久网络访问权限,这些存储库可能存储根证书和中间证书。在许多情况下,这将包括对公共互联网的网络访问,这对于通用部署而言可能不可行。

增强的证书验证例程将包括证书撤销,支持常用的方法,例如证书撤销列表 (CRL) 和/或在线证书状态协议 (OCSP)。支持证书撤销将允许计算服务动态确定证书何时因泄露而失效,从而进一步提高启动签名镜像的安全性。但是,支持证书撤销涉及动态获取和信任网络资源,通常由第三方控制和授权。这对于某些部署而言可能不可行。可以将证书撤销集成到计算服务之外,例如在证书管理器或通过另一个第三方服务中。这将使 nova 获得及时的撤销的好处,而不会使 nova 本身中的签名验证和证书验证功能复杂化。

应该注意的是,此功能的未来工作将添加对证书撤销的支持。

数据模型影响

REST API 影响

  1. 添加一个新的微版本以支持在创建卷时指定 trusted_image_certificates

    POST: /V3/{tenant_id}/volumes
    
{
  "volume": {
      "size": 1,
      "name": "image_volume",
      "imageRef": "CAB56EC2-4BDC-45D4-9898-3F88A7003261",
      "trusted_image_certificates": [
          "00000000-0000-0000-0000-000000000000",
          "11111111-1111-1111-1111-111111111111",
          "22222222-2222-2222-2222-222222222222"
      ],
  }

请注意,虽然在这些示例中 trusted_image_certificates 中的值是 UUID,但不能保证如此。证书管理器使用不同的 ID 分配方案;虽然有些使用严格的 UUID,但有些使用简单的递增整数或原始十六进制字符串。对于此功能,trusted_image_certificates 的类型将是一个包含零个或多个 JSON 字符串值的数组。

以下是 trusted_image_certificates 参数的 JSON 模式描述

{
    "type": "array",
    "minItems": 0,
    "maxItems": 50,
    "uniqueItems": true,
    "items": {
        "type": "string"
    }
}

请注意 trusted_image_certificates 参数中包含的证书 ID 的上限和下限。如果对已签名镜像进行 API 调用并超过允许的证书 ID 数量上限,则 API 调用将失败。

这些更改需要 API 版本升级。

安全影响

通过此功能提供的附加验证步骤,启用的情况下,提高了已签名镜像验证功能的安全性。

通知影响

其他最终用户影响

随着对 OS_TRUSTED_CERTIFICATE_IDS 环境变量的支持的添加,鼓励用户通过他们的 openrc 文件设置该变量,以及他们的身份验证凭据。OS_TRUSTED_CERTIFICATE_IDS 环境变量的值是受信任证书 ID 的逗号分隔字符串,该字符串将被转换为 trusted_image_certificates 参数的证书 ID 列表。

以下是一个示例 openrc 文件,它使用与 API 示例中相同的受信任证书 ID(请参阅上文的 REST API 影响)。

export OS_USERNAME=username
export OS_PASSWORD=password
export OS_TENANT_NAME=projectName
export OS_AUTH_URL=https://identityHost:portNumber/v2.0
export OS_TRUSTED_CERTIFICATE_IDS=00000000-0000-0000-0000-000000000000,
11111111-1111-1111-1111-111111111111,22222222-2222-2222-2222-222222222222

性能影响

Cinder 将在每次执行签名验证时,通过 cursive 加载用户的受信任证书。根据证书的大小和数量以及签名验证的频率,这可能会给卷服务带来性能负担,例如消耗 Cinder 镜像缓存中的镜像。为了缓解这种情况,请参阅上文的“替代方案”,了解有关持久化证书信任存储以及从远程存储动态加载证书的信息。

其他部署者影响

包含两个新的配置选项,enable_certificate_validation 和 default_trusted_certificate_ids,将有助于平滑过渡,以便部署可以启用此功能。

开发人员影响

实现

负责人

主要负责人

Yikun Jiang(yikunkero@gmail.com)

其他负责人

tommylikehu(tommylikehu@gmail.com)

工作项

  • 添加两个新的配置选项,enable_certificate_validation 和 default_trusted_certificate_ids。第一个选项将在启用签名验证时启用证书验证的使用。第二个选项将提供一个默认的受信任证书 ID 列表,如果卷请求中未提供受信任证书 ID,则可以使用该列表。

  • 更新 cinder 中的现有签名验证流程,以合并证书验证,使用 cursive 中的 verify_certificate 例程来验证签名证书。

  • 在卷创建命令中添加一个新的 cinder API 参数,trusted_image_certificates。当从 glance 下载镜像时,需要将此参数的值传递到签名验证步骤。

  • 更新 cinderclient 以支持 trusted_image_certificates 参数。

  • 更新 cinderclient,以便在用户未提供 trusted_image_certificates 参数时,从 OS_TRUSTED_CERTIFICATE_IDS 环境变量中提取值。

依赖项

测试

将包含单元测试,以测试在 cinder 和 cinderclient 中实现的功能。还将实现 Tempest 测试,以测试 glance 和 cinder 之间的端到端功能。

文档影响

需要添加关于 trusted_image_certificates API 参数和两个新配置选项的文档,以及定义 OS_TRUSTED_CERTIFICATE_IDS 环境变量及其用法的说明。

参考资料

[1] “Cursive。” 在线:https://launchpad.net/cursive

[2] “添加证书验证。” https://review.openstack.org/#/c/357202/