Multiple Secret Backend Support

https://blueprints.launchpad.net/barbican/+spec/multiple-secret-backend

Barbican 提供了通过其插件机制创建和存储密钥的功能。Barbican 具有 KMIP、PKCS11 和 Dogtag KRA 插件,用于在 HSM(硬件安全模块)设备中创建和管理密钥,以及 store_crypto 插件,用于将其密钥存储在其自身数据库中。企业通常拥有 HSM 设备,以安全地保护其密钥材料(静态数据),并满足合规性要求。

问题描述

Barbican 支持在 HSM 设备以及数据库中存储密钥。到目前为止,Barbican 具有配置一个活动插件用于密钥存储的隐式概念,这意味着所有新密钥都将通过相同的插件(即相同的存储后端)存储。这种方法可能会限制 barbican 在云部署中的使用,因为并非所有服务/应用程序都具有相同的数据敏感性,因此没有必要使用相同的机制来保护其密钥材料。此外,HSM 是昂贵的设备,与数据库相比,其存储容量和性能特征有限。当前一个活动密钥存储插件的方法对于以下用例而言不够灵活。

  • 在部署中,部署者可能可以接受使用低安全性的密钥存储(例如仅软件加密后端)来存储其开发/测试资源,但可能希望使用 HSM 支持的密钥存储来存储生产资源。

  • 在部署中,对于某些需要客户端对存储密钥进行高并发访问的用例,HSM 可能不是一个好的存储后端。此外,相对于数据库,水平扩展它们以提供更高的可扩展性是一种代价高昂的方法。

  • HSM 设备通常具有有限的存储容量,因此部署必须主动监控其存储密钥的大小,以保持在限制之下。这在 KMIP 后端比在 PKCS11 后端中更适用,因为插件具有不同的存储方法。这种方面也可能源于上述用例场景,其中部署正在 HSM 中存储非敏感的(来自开发/测试环境)加密密钥。

  • Barbican 作为 IaaS 服务或平台组件运行,其中某些类别的客户端服务具有严格的合规性要求(例如 FIPS),因此将使用 HSM 支持的插件,而其他服务可能可以接受将密钥存储在仅软件加密插件中。

目前没有简单的解决方案来支持上述用例。建议部署为每个插件提供一个单独的 barbican 端点,这不是一个好的使用体验。有关更多详细信息,请参见 Alternatives 部分。由于只有一个密钥存储后端可用,因此客户端(或服务类别)没有选择后端的选择,无论其与保护这些密钥相关的要求如何。

提议的变更

建议允许在一个 Barbican 部署中提供多个密钥存储后端。作为此更改的一部分,客户端将在项目级别选择首选后端。

为此,Barbican 部署将至少启用 2 个密钥存储后端,并且需要指定一个后端作为部署级别(全局)默认选项。项目管理员可以选择预先选择一个后端作为在该项目下创建的密钥的首选选项。在该项目下创建的任何密钥都将使用首选后端来存储其密钥材料。如果没有选择项目级别的存储后端,则新密钥将使用全局密钥存储后端。

由于每个密钥已经具有捕获其后端信息的元数据,Barbican 可以允许客户端在创建或存储 Barbican 中的新密钥时指定特定的存储后端。如果存在对此要求的实际用例,则可以作为未来的增强功能。目前此功能不在本次工作的范围内。

此外,为了管理此功能,将添加一个标志 enable_multiple_secret_stores。默认情况下,此标志将禁用(设置为 false)。因此,任何现有的逻辑更改都需要考虑该标志值,并且仅在启用此标志时才进行修改。当 Barbican 使用启用此标志启动时,需要确保全局默认插件存在于配置文件中。

新密钥生成和存储的变化

当前,密钥存储插件通过其 supports API 机制公开其生成和存储能力。此机制检查输入的密钥规范(算法、长度)是否受插件后端支持。

当项目定义了其首选密钥存储插件时,则检查该插件是否满足支持标准。如果首选插件不满足给定的输入标准,则会向客户端返回带有相关消息的 400 错误。为此,get_active_plugins 逻辑将返回项目级别的首选插件(如果已定义),否则返回全局默认插件。

这种首选密钥生成和存储的逻辑也需要应用于订单处理端。同步或异步订单处理逻辑需要进行修改,以便在查找新密钥的生成插件时,提供项目级别的首选插件,然后是全局默认插件。对称密钥存储和证书生成成功后证书存储的逻辑也相同。

Barbican 具有密钥包装支持(在一些插件中),以确保密钥以只有客户端和后端存储才能解密的的方式进行预加密。为此,客户端需要访问传输密钥才能在存储或读取该密钥时进行预加密。使用多个后端支持,读取传输密钥不会受到影响。对于现有密钥,客户端调用密钥元数据 API,该 API 在请求时返回密钥传输密钥 ID。此 ID 源自密钥插件关联和插件传输密钥关联。对于新密钥,如果指定了项目级别的首选后端,则返回的传输密钥特定于该后端。如果项目没有项目首选后端,则将返回特定于全局默认后端的传输密钥。这样,不应出现传输密钥和密钥存储后端不同的情况。

备选方案

之前提出的一个替代方案是部署单独的 Barbican 服务器,为每个受支持的插件提供不同的端点。这可行,但从客户端的角度来看,这不是一个好的使用体验。现在客户端有责任在其配置中维护不同的 barbican 端点,并记住哪种密钥是通过哪个端点访问的。此外,当前的一些服务集成通常仅支持一个 barbican 端点,因此这对于现有的服务集成可能不可行。然后是管理这些端点在 keystone 服务目录中的标准问题。

服务配置影响

为了支持多个插件配置,需要以下配置格式,因为当前的多个插件配置并未定义密钥存储和加密插件之间的明确关系

[secretstore]
enable_multiple_secret_stores = True
stores_lookup_suffix = software, kmip, pkcs11, dogtag

[secretstore:software]
secret_store_plugin = store_crypto
crypto_plugin = simple_crypto

[secretstore:kmip]
secret_store_plugin = kmip_plugin
global_default = True

[secretstore:dogtag]
secret_store_plugin = dogtag_plugin

[secretstore:pkcs11]
secret_store_plugin = store_crypto
crypto_plugin = p11_crypto

当启用多个密钥存储支持时,将使用此新的列表属性 stores_lookup_suffix 来查找 oslo 配置部分中构造的使用模式 ‘secretstore:{suffix}’ 的相关受支持插件名称。其中一个插件必须显式标识为 global_default = True。对此要求进行验证。后缀的顺序无关紧要。

当启用多个密钥存储时,属性 enabled_secretstore_pluginsenabled_crypto_plugins 不用于实例化受支持的插件。而是使用上述机制来实例化存储和加密插件。

数据模型影响

将创建一个新的表,secret_stores,用于存储密钥存储后端信息。在 barbican 应用程序启动时,如果不存在,此表将根据 secret_store_plugincrypto_plugin 组合为受支持的插件添加新条目。secret store 插件表中的一个条目将具有 global_default 列值为 true。

table_exists = ctx.dialect.has_table(con.engine, 'secret_stores')
if not table_exists:
   op.create_table(
         'secret_stores',
         sa.Column('id', sa.String(length=36), nullable=False),
         sa.Column('created_at', sa.DateTime(), nullable=False),
         sa.Column('updated_at', sa.DateTime(), nullable=False),
         sa.Column('deleted_at', sa.DateTime(), nullable=True),
         sa.Column('deleted', sa.Boolean(), nullable=False),
         sa.Column('status', sa.String(length=20), nullable=False),
         sa.Column('store_plugin', sa.String(length=255), nullable=False),
         sa.Column('crypto_plugin', sa.String(length=255), nullable=True),
         sa.Column('global_default', sa.Boolean(), nullable=False),
         sa.Column('name', sa.String(length=255), nullable=False),
         sa.PrimaryKeyConstraint('id'),
         sa.UniqueConstraint('store_plugin', 'crypto_plugin',
                             name='_secret_stores_plugin_names_uc'),
         sa.UniqueConstraint('name',
                             name='_secret_stores_name_uc'
   )

在服务启动时,将添加逻辑以填充 secret_stores 数据。有关 API,请参见 Getting Global Default Backend。这个新表将具有 crypto_plugin 字段来考虑仅软件和 PKCS11 插件的情况。这两个插件都使用 store_crypto 作为密钥存储后端,区别在于加密插件名称,即 simple_cryptop11_crypto 插件。

每个密钥存储和加密插件实现都将在其插件特定配置中添加一个新的配置 ‘plugin_name’。此名称旨在是用户友好的名称,我们可以添加默认值,如果需要可以通过配置进行更新。此插件名称将在创建新的密钥存储条目时使用。对于 PKCS11 和 DB 后端,加密插件名称将在 name 字段中使用,因为它们不直接扩展密钥存储基类。

将创建一个新的表,project_secret_stores,用于存储每个项目的密钥存储链接数据。

table_exists = ctx.dialect.has_table(con.engine, 'project_secret_stores')
if not table_exists:
   op.create_table(
         'project_secret_stores',
         sa.Column('id', sa.String(length=36), nullable=False),
         sa.Column('created_at', sa.DateTime(), nullable=False),
         sa.Column('updated_at', sa.DateTime(), nullable=False),
         sa.Column('deleted_at', sa.DateTime(), nullable=True),
         sa.Column('deleted', sa.Boolean(), nullable=False),
         sa.Column('status', sa.String(length=20), nullable=False),
         sa.Column('secret_store_id', sa.String(length=36), nullable=False),
         sa.Column('project_id', sa.String(length=36), nullable=False),
         sa.PrimaryKeyConstraint('id'),
         sa.ForeignKeyConstraint(['secret_store_id'], ['secret_stores.id'],),
         sa.ForeignKeyConstraint(['project_id'], ['projects.id'],),
         sa.UniqueConstraint('secret_store_id',
                             'project_id',
                             name='_project_secret_stores_id_project_uc')
   )
   op.create_index(op.f('ix_project_secret_stores_store_id'), 'project_secret_stores',
                   ['secret_store_id'], unique=False)
   op.create_index(op.f('ix_project_secret_stores_project_id'), 'project_secret_stores',
                   ['project_id'], unique=False)

每当设置项目级别的首选存储时,将填充此表。在更新现有的项目首选值时,将删除现有的条目并创建新的条目。

不需要数据库迁移,因为这是一个新功能。

REST API 影响

现有 barbican API 没有影响。将添加以下新的 REST API 以支持此新功能。

如果未启用多个密钥存储后端并且响应不适用(/secret-stores/{ID}/preferred),secret-stores 相关的 API 将导致错误 404。

列出所有密钥存储后端

将添加一个新的 API 资源树 /secret-stores 用于管理可用的密钥存储后端。此 API 将提供可用密钥存储后端的列表。只有项目管理员(具有管理员角色的用户)才有权调用此 API。

REST API: GET /secret-stores

Request:
   GET /secret-stores
   Headers:
      X-Auth-Token: 'f9cf2d480ba3485f85bdb9d07a4959f1'

Response:
   HTTP/1.1 200 OK

   {
      "secret-stores":[
         {
            "name": "PKCS11 HSM",
            "global_default": True,
            "secret_store_ref": "https://:9311/v1/secret-stores/4d27b7a7-b82f-491d-88c0-746bd67dadc8",
            "store_plugin": "store_crypto"
            "crypto_plugin": "p11_crypto",
            "secret_store_id": "4d27b7a7-b82f-491d-88c0-746bd67dadc8",
            "status": "ACTIVE",
            "created": "2016-08-22T23:46:45.114283",
            "updated": "2016-08-22T23:46:45.114283"
         },
         {
            "name": "KMIP HSM",
            "global_default": False,
            "secret_store_ref": "https://:9311/v1/secret-stores/93869b0f-60eb-4830-adb9-e2f7154a080b",
            "store_plugin": "kmip_plugin",
            "crypto_plugin": None,
            "secret_store_id": "93869b0f-60eb-4830-adb9-e2f7154a080b",
            "status": "ACTIVE",
            "created": "2016-08-22T23:46:45.124554",
            "updated": "2016-08-22T23:46:45.124554"
         },
         {
            "name": "Software Only Crypto",
            "global_default": False,
            "secret_store_ref": "https://:9311/v1/secret-stores/0da45858-9420-42fe-a269-011f5f35deaa",
            "store_plugin": "kmip_plugin",
            "crypto_plugin": "simple_crypto",
            "secret_store_id": "0da45858-9420-42fe-a269-011f5f35deaa",
            "status": "ACTIVE",
            "created": "2016-08-22T23:46:45.127866",
            "updated": "2016-08-22T23:46:45.127866"
         }
   }

读取密钥存储后端

任何 barbican 用户都可以调用给定的密钥存储后端信息。返回的名称源自服务配置中定义的友好插件名称。每个密钥存储插件和加密插件都有默认名称,可以在 plugin_name 下的插件部分中覆盖。只有项目管理员(具有管理员角色的用户)才有权调用此 API。

REST API: GET /secret-stores/{secret_store_id}

Request:
   GET secret-stores/4d27b7a7-b82f-491d-88c0-746bd67dadc8
   Headers:
      X-Auth-Token: 'f9cf2d480ba3485f85bdb9d07a4959f1'

Response:
   HTTP/1.1 200 OK

   {
      "name": "PKCS11 HSM",
      "global_default": True,
      "secret_store_ref": "https://:9311/v1/secret-stores/4d27b7a7-b82f-491d-88c0-746bd67dadc8",
      "store_plugin": "store_crypto"
      "crypto_plugin": "p11_crypto",
      "secret_store_id": "4d27b7a7-b82f-491d-88c0-746bd67dadc8",
      "status": "ACTIVE",
      "created": "2016-08-22T23:46:45.114283",
      "updated": "2016-08-22T23:46:45.114283"
   }

首选密钥存储后端

项目管理员(具有管理员角色的用户)可以为其项目添加、更新或删除首选密钥存储后端。项目信息是从项目范围的令牌派生的。在没有 keystone 设置的 barbican 部署中,项目信息是从 X-Project-Id 请求标头获取的。

获取每个项目的密钥存储后端

只有项目管理员才能请求先前分配的首选密钥存储。如果为项目设置了首选密钥存储,则新项目密钥将使用该存储后端存储。如果未启用多个密钥存储支持,则此资源将返回 404(未找到)错误。

REST API: GET /v1/secret-stores/preferred

Request:

GET /v1/secret-stores/preferred
Headers:
   X-Auth-Token: "f9cf2d480ba3485f85bdb9d07a4959f1"
   Accept: application/json

Response:

HTTP/1.1 200 Ok
Content-Type: application/json

{
   "name": "KMIP HSM",
   "global_default": False,
   "secret_store_ref": "https://:9311/v1/secret-stores/93869b0f-60eb-4830-adb9-e2f7154a080b",
   "store_plugin": "kmip_plugin",
   "crypto_plugin": None,
   "secret_store_id": "93869b0f-60eb-4830-adb9-e2f7154a080b",
   "status": "ACTIVE",
   "created": "2016-08-22T23:46:45.124554",
   "updated": "2016-08-22T23:46:45.124554"
}

设置每个项目的密钥存储后端

只有项目管理员才能设置每个项目的后端。此 API 将在进行更改后设置或更新任何新密钥的每个项目后端。

REST API: POST /secret-stores/{secret_store_id}/preferred

Request:

POST /secret-stores/7776adb8-e865-413c-8ccc-4f09c3fe0213/preferred
Headers:
   X-Auth-Token: '2f30ca66d27d4d0cbff51d44bc5ac66e'

Response:

HTTP/1.1 204 No Content

删除每个项目的密钥存储后端

只有项目管理员才能删除每个项目的后端。

REST API: DELETE /secret-stores/{secret_store_id}/preferred

Request:

DELETE /secret-stores/7776adb8-e865-413c-8ccc-4f09c3fe0213/preferred
Headers:
   X-Auth-Token: '2f30ca66d27d4d0cbff51d44bc5ac66e'

Response:

HTTP/1.1 204 No Content

全局默认密钥存储后端

全局默认密钥存储用于 barbican 中的项目没有首选密钥存储设置时。在这种情况下,在该项目下创建的新密钥将使用全局密钥存储来生成和存储密钥。

全局默认密钥存储值预计不会经常更改,因此其值通过服务配置进行管理。在服务启动时,必须在插件相关的配置部分中显式设置 global_default 值。

在配置中更改全局默认密钥存储插件不应影响没有首选插件的项目现有密钥,因为这些密钥的元数据在数据库中已经具有关于其关联插件后端的的信息。

获取全局默认后端

只有项目管理员才能读取当前的全局默认后端值。

REST API: GET /secret-stores/global-default

Request:

GET /secret-stores/global-default
Headers:
   X-Auth-Token: '2f30ca66d27d4d0cbff51d44bc5ac66e'
   Accept: 'application/json'

Response:

HTTP/1.1 200 Ok
Content-Type: application/json

{
   "name": "PKCS11 HSM",
   "global_default": True,
   "secret_store_ref": "https://:9311/v1/secret-stores/4d27b7a7-b82f-491d-88c0-746bd67dadc8",
   "store_plugin": "store_crypto"
   "crypto_plugin": "p11_crypto",
   "secret_store_id": "4d27b7a7-b82f-491d-88c0-746bd67dadc8",
   "status": "ACTIVE",
   "created": "2016-08-22T23:46:45.114283",
   "updated": "2016-08-22T23:46:45.114283"
}

不允许对 /secret-stores/global-default 进行 POSTDELETE 操作。因此,调用该资源应生成 405(方法不允许)错误。

安全影响

没有影响,因为这些是新的 API。一些新的 API 将需要服务管理员权限才能进行更改。作为此更改的结果,部署可以选择将数据库用作某些密钥的密钥存储后端。数据库后端被认为是一种安全性低于 HSM 存储选项的后端。但是,此功能本身不会引入安全风险,因为数据库后端是 barbican 现有的后端,部署者可以选择不使用它。

通知与审计影响

Python 和命令行客户端影响

这些新的 API 需要在 barbican 客户端 API 和 CLI(命令行界面)中支持。

其他最终用户影响

不应影响现有部署,因为这是一个新功能。默认情况下,此功能将禁用。一旦启用此功能,客户端就可以选择定义和使用特定后端来用于项目。

性能影响

最小。在 API 使用级别,预计这将是不频繁的管理员操作。与创建新密钥和存储密钥流程相关的冲击也将非常小。

其他部署者影响

此新功能默认情况下将被禁用。在 barbican 配置的 secretstore 部分添加了一个新的标志来启用,即 enable_multiple_secret_stores。此外,对于多个插件支持,部署者需要定义上面提到的受支持的插件配置(与当前支持的方式不同)。

不需要迁移,并且部署应该可以与默认配置一起工作。

开发人员影响

不应影响插件开发人员和现有的 API。

实现

负责人

主要负责人

arunkant

其他贡献者

工作项

  • 添加密钥存储 API 的文档。

  • 添加模型层更改。

  • 填充 secret_stores 表数据的逻辑。

  • 添加逻辑以解析多个插件配置。

  • 添加控制器层变更以支持新的 API。

  • 修改密钥创建和存储逻辑以利用此功能。相关的变更需要使用功能标志。

  • 为新的 API 添加功能测试。

  • 更新/添加现有功能测试,围绕多后端支持。

依赖项

测试

现有的单元和功能测试将被修改以反映相关的变更。新的单元和功能测试将为新的 secret-stores API 添加。

文档影响

这是一个新功能,将为新的 API 及其用法编写文档。

参考资料

1. 传输密钥包装 https://github.com/openstack/barbican-specs/blob/master/specs/juno/add-wrapping-key-to-barbican-server.rst