Transport Key Wrapping

包含您的 Launchpad 蓝图的 URL

https://blueprints.launchpad.net/barbican/+spec/add-wrapping-key-to-barbican-server

目前,密钥从 Barbican 客户端传递到 Barbican 服务器时仅由 TLS 加密。对于符合通用标准的环境,这还不够。密钥需要在源头在 TLS 流中进行加密,并且理想情况下仅在存储密钥时才解密。如果使用硬件令牌,则会在令牌中发生这种情况,这样即使攻击者能够访问 Barbican 服务器并能够检查进程内存,也无法解密任何密钥。

问题描述

见上文。

提议的变更

使用密钥包装存储密钥的机制

  1. 客户端选择使用密钥包装来存储密钥,因此将使用两步存储机制来存储其密钥。

  2. 客户端向 /secrets 发送 POST 请求,不带有效负载。客户端还在请求中指定 transport_key_needed=true。

  3. 服务器注意到 transport_key_needed=true,并查找支持使用密钥包装进行存储的插件。

逻辑如下

for (plugin; list_of_plugins):
    call plugin.get_transport_key()
    if transport_key_found:
        put transport_key URL in transport_key_ref in the response.
        return success
return failure (No plugin available 400)
  1. 请注意,plugin.get_transport_key() 将在抽象的 secret_store 类中定义为返回 None。这意味着希望支持密钥包装的插件需要覆盖此方法以返回 transport_key_id(传输密钥仓库中的密钥引用)。

  2. 预计插件将负责将正确的密钥填充到 TransportKeyRepo 中。这可以在模块启动时发生,或者在第一次调用 get_transport_key() 时发生。

  3. 客户端从传输密钥资源获取传输密钥。为了提高性能,客户端可能会缓存此密钥。

  4. 客户端使用传输密钥按预期执行密钥包装。具体来说 - 使用会话密钥包装密钥,然后使用传输密钥包装会话密钥,然后将其放入 ASN1 结构中。

  5. 客户端在 PUT /secret/{id} 请求中返回加密的 blob 和传输密钥引用。我们可以使用参数 transport_key_ref。

  6. 服务器注意到已设置 transport_key_ref。它从 TransportKeyRepo 中查找与该传输密钥关联的插件。如果插件不可用或传输密钥不存在,则抛出失败(400)

  7. 将参数 transport_key_id 传递到 SecretDTO 中,并调用 store_secret()。

  8. 插件检测到已设置 transport_key_id,并相应地处理。

使用密钥包装检索密钥的机制

  1. 客户端选择使用密钥包装来检索密钥。它首先使用 GET 请求检索元数据。

  2. 如果存储密钥的插件支持 key_wrapping 并且存在传输密钥,则在 transport_key_ref 中返回传输密钥的 URL。

  3. 客户端获取传输密钥并将其缓存。

  4. 客户端生成对称密钥(会话密钥)并使用传输密钥对其进行包装。客户端将 trans_wrapped_session_key 作为 GET 请求中秘密的一部分发送到服务器。

  5. 服务器将此密钥(trans_wrapped_session_key)写入 secret_metadata 并调用 get_secret()

  6. 插件检测到已写入此参数并相应地检索密钥。存储服务器将提取会话密钥并使用会话密钥加密响应。

  7. 在返回密钥时,插件将从 secret_metadata 中删除 trans_wrapped_session_key。

  8. 客户端将使用会话密钥解密来自服务器的响应。

备选方案

上述解决方案是亚特兰大峰会上的一系列设计讨论的结果。旧设计可以在这里找到:http://pki.fedoraproject.org/wiki/Barbican_server_add_wrapping_key

数据模型影响

此更改需要通过 REST 接口提供并存储在数据库中 - 传输密钥。

实现此新资源/模型的代码已经合并。 https://review.openstack.org/94875 - 添加传输密钥作为资源。

这些是新对象。每当调用 get_transport_key() 方法时,任何支持密钥包装的插件都会填充它们。

REST API 影响

以下 API 方法将被更改

  • POST /secrets

    • 将添加一个新的可选参数“transport_key_needed”,它将具有“true|false”的可能值。参数默认为“false”。

    • 当使用两步存储机制时,此参数将生效。在这种情况下,如果没有有效负载,如果 transport_key_needed 设置为 true,则会找到合适的插件,并在响应的“transport_key_ref”字段中返回传输密钥 URL。

    • 如果 transport_key_needed=true 时找不到合适的插件/传输密钥,则返回错误 400。

  • POST /secrets (带有有效负载)
    • 将添加一个新的可选参数:“transport_key_ref”。它将包含用于编码密钥的传输密钥的 URL。使用此参数意味着密钥已被包装。

    • 如果找不到合适的插件/传输密钥,则返回错误 400。

  • PUT /secrets/foo
    • 将添加一个新的可选参数:“transport_key_ref”。它将包含用于编码密钥的传输密钥的 URL。使用此参数意味着密钥已被包装。

    • 如果找不到合适的插件/传输密钥,则返回错误 400。

  • GET /secrets/foo (仅元数据)
    • 如果秘密由支持 key_wrapping 的插件存储,则将在元数据响应中添加一个新的参数“transport_key_ref”。它将包含该插件的传输密钥的引用。

  • GET /secrets/foo (实际的密钥)
    • 将向此方法添加一个新的可选查询参数 trans_wrapped_session_key。此参数将包含由传输密钥包装的会话密钥,用于检索密钥。

  • GET/DEL/POST /transport-keys
    • 这里提到是为了完整性。这些资源的这些方法已经合并到代码中。

    • 客户端将使用 GET 来检索传输密钥。

    • 插件管理员可以使用 POST/DEL 来添加或删除传输密钥。这些操作将不可用于普通用户。

安全影响

此更改应提高安全性,为密钥提供端到端加密。

通知与审计影响

没有,除了如果使用此机制而不是常规机制,可能会发生一些更改的通知。

其他最终用户影响

性能影响

执行额外的加密意味着存储和检索密钥将需要两步,而不是一步,以便可以检索正确的传输密钥。但是,可以通过客户端缓存传输密钥来缓解此问题 - 尤其是考虑到传输密钥很少更改。

客户端将在加密和解密密钥方面执行一些额外的工作,但这可能不会造成过大的负担。

其他部署者影响

需要客户端工作。我们将在单独的规范中介绍这一点。

开发人员影响

实现

负责人

主要负责人

alee-3

其他贡献者

redrobot (客户端)

工作项

  • CR 执行以下操作以使用传输密钥进行存储:* 将 transport_key_needed 布尔值添加到 POST 请求流程 * 将 get_transport_key() 方法添加到 SecretStore * 如果请求,则返回传输密钥。 * 修改 secret PUT 以添加 transport_key_ref。 * 查找与该 transport_key 关联的 secret_store 插件 * 修改 secret_store SecretDTO 以接受可选的传输密钥

  • CR 执行以下操作以使用传输密钥进行检索 * 修改 secret 元数据 GET 以添加传输密钥的引用 * 将 trans_wrapped_session_key 添加到 secret GET。 * 将此参数写入 secret_metadata 并提供给插件。 * 修改插件以相应地处理。

  • CR 实现 Dogtag 中的传输密钥获取和端到端测试。

依赖项

测试

所有新功能和 API/secret store/Dogtag 插件中的更改都将进行单元测试。此外,我们将使用 Dogtag 作为后端执行端到端集成测试。理想情况下,这些端到端测试将成为门控测试的一部分,Dogtag 将使用 Chef 脚本安装。

此外,最终传输密钥功能将添加到开发插件中,因此也将对其进行测试。

文档影响

这是一个全新的功能,需要在文档中进行描述。

参考资料