应用程序凭证

bp application-credentials

实现特定于应用程序的凭证作为新的凭证类型,以提高安全性,通过限制访问权限和不在配置文件中嵌入用户凭证来实现。

问题描述

目前,自动化的 API 交互——例如服务到服务的通信、部署工具和云原生应用程序——必须以 keystone 用户身份进行身份验证,才能获得访问所需资源的权限。这种模式虽然对 keystone 来说很方便,但会增加帐户泄露的风险,因为需要分发未加密的密码。它还会加剧帐户泄露可能造成的损害,因为使用用户凭证的应用程序将拥有用户的所有权限。该模式会阻碍泄露的缓解,因为更改泄露的密码需要使用该密码的分布式应用程序停机。以下用例展示了当今运营商和最终用户面临的情况以及对应用程序身份验证的替代模式的需求。

注意

为了避免混淆,在本文档中,“用户”将用于指 Keystone 用户资源,“消费者”将用于指从 OpenStack 云中消耗资源的个人,“部署者”将用于指管理 OpenStack 部署的个人,而“应用程序”将用于指非人类的 OpenStack API 消费者。虽然“应用程序”一词可能带有含义,但此处应理解为任何在没有直接人工干预的情况下预期与 OpenStack API 交互的代码或自动化。

用例

  • 作为 OpenStack 消费者,我希望创建能够以编程方式请求令牌并为我的项目中的资源进行 API 调用的应用程序。

为了实现这一点,通常会将用户凭证存储在配置文件中,例如 clouds.yaml,以便脚本可以在没有人工干预的情况下访问 OpenStack API。将用户凭证存储在配置文件中和/或将其嵌入到虚拟机中,可能会存在潜在的安全风险,因为这些凭证可能用于其他系统,例如联合身份验证。

  • 作为 OpenStack 消费者,我希望将应用程序可以执行的操作限制为预定义的我的用户可以访问的操作的子集。

通过使用用户的凭证,应用程序将拥有该用户的所有访问权限。这可能比需要的访问权限多。例如,今天,普通消费者无法设置只能将镜像上传到 Glance 但无法在 Nova 中创建或删除服务器的应用程序,因为他们需要创建一个角色和自定义策略。

  • 作为 OpenStack 消费者和 OpenStack 部署者,我需要能够优雅地更新应用程序使用的凭证,而不会导致应用程序停机。

当前的用户名和密码系统需要同时更改密码并更新任何应用程序配置。对于部署者而言,这意味着同时更新所有 OpenStack 服务的配置文件。在密码更改和配置更新之间,应用程序将无法与 OpenStack API 通信并会停止运行。应用程序凭证应允许创建新凭证、推出新凭证,然后删除旧凭证以进行凭证轮换,而不会导致停机。

  • 作为 OpenStack 部署者,我需要配置一些 OpenStack 服务,以便它们能够对其他 OpenStack 服务进行 API 调用。

服务用户的信息当前存储在配置文件中。例如,nova 服务用户当前位于每个 nova-api、nova-compute 和 neutron 配置文件的配置中。在运行时,这些服务使用这些用户凭证来执行服务到服务的操作。

从安全角度来看,将用户凭证写入磁盘上的文件,尤其是当用户在项目上具有提升的权限时,可能被认为是不利的。应该能够应用最小权限原则 (POLP),并将访问权限限制到完成任务所需的最低级别。对于消费者而言,这意味着能够选择不使用完整的用户凭证进行自动化。对于部署者而言,这意味着能够轻松地对服务特定的授权进行配置,并将其放入配置文件中。

提议的变更

实现一种新的受限凭证类型(应用程序凭证),供应用程序使用。应用程序凭证将在创建时被分配与创建用户相同的角色。

注意

最初的想法是将其称为“API 密钥”,因为该想法与其他行业中关于为应用程序生成用于 API 的密钥的用法类似。该计划已被取消,因为该术语虽然在行业中定义不明确,但带有关于其用法和实现的特定含义,而该规范不符合这些含义。“应用程序凭证”这个名称被选择作为描述其预期用途的通用术语,即仅供应用程序使用的身份信息。

应用程序凭证将具有以下特征

  • 不可变。

  • 允许选择性地设置限制,例如每个用户或项目最多 5 个应用程序凭证,以防止滥用资源。

  • 在创建时被分配创建用户在项目上拥有的当前角色集,或者可选地,创建用户在项目上拥有的角色的子集列表。

  • 密钥仅在创建 API 响应时暴露一次。

  • 有限的操纵身份对象的能力(参见 施加的限制

  • 支持过期。

  • 当关联用户被删除时将被删除。

应用程序凭证将被视为凭证而不是授权令牌,因为这符合 keystone 模型,并且与其他提供应用程序身份验证的 API 保持一致。它还避免了创建一种可能永远不会过期并且具有自定义验证的新令牌类型的安全性和性能影响。

注意

将来,消费者可以在创建时进一步限制应用程序凭证可以执行的操作。意图是这种进一步的限制将以 REST 调用的粒度表示。该功能未在此处描述或实现,但对于完全满足预定义的子集用户操作用例是必需的。

应用程序凭证管理

用户可以为自己创建、列出和删除应用程序凭证。例如,添加应用程序凭证

POST /v3/users/{user_id}/application_credentials
{
    "application_credential": {
        "name": "backup",
        "description": "Backup job...",
        "expires_at": "2017-11-06T15:32:17.000000",
        "roles": [
            {"name": "Member"}
        ]
    }
}

name 必须在用户应用程序凭证中是唯一的,但 name 仅保证在该用户下是唯一的。name 对于希望拥有可读配置文件的消费者来说可能很有用。

description 是一个用于存储有关应用程序凭证用途信息的长描述。它主要在应用程序凭证的报告或列表中有用。

expires_at 是应用程序凭证到期的时间。“null”表示应用程序凭证不会自动过期。expires_at 采用 ISO 日期时间格式,如果未包含显式时区偏移量,则假定为 UTC。

roles 是一个可选的角色名称或 ID 列表,该列表是在创建时创建用户在项目上拥有的角色的子集。创建用户在项目上没有的角色是一个错误。

在初始实现中,应用程序凭证将假定创建用户或给定的子集的角色,并且我们不会实现超出此范围的细粒度访问控制。

响应示例

{
    "application_credential": {
        "id": "aa4541d9-0bc0-44f5-b02d-a9d922df7cbd",
        "secret": "a49670c3c18b9e079b9cfaf51634f563dc8ae3070db2...",
        "name:" "backup",
        "description": "Backup job...",
        "expires_at": "2017-11-06T15:32:17.000000",
        "project_id": "1a6f968a-cebe-4265-9b36-f3ca2801296c",
        "roles": [
            {
                "id": "d49d6689-b0fc-494a-abc6-e2e094131861",
                "name": "Member"
            }
        ]
    }
}

响应中的 id 是应用程序凭证标识符,将在 get 或 list API 调用中返回。id 在云中是全局唯一的。

secret 是一个随机字符串,仅通过创建 API 调用返回。Keystone 将仅存储 secret 的哈希值,而不是 secret 本身,因此丢失的 secret 无法恢复。后续查询应用程序凭证将不会返回 secret 字段。

注意

需要确定生成可接受的密钥的正确方法。Nova 在服务器创建时生成管理员密码,这可能是一个好的起点。如果采用这种方法,则应将 Python 3 中 random.choice 的使用替换为 secrets.choice

roles 是角色名称和 ID 的列表。它是信息性的,可供消费者用来验证应用程序凭证是否继承了消费者期望的用户角色。这不是策略执行,只是为了人工验证。

如果消费者希望生成自己的 secret,他们可以这样做并在创建调用中提供它。Keystone 将存储给定的 secret 的哈希值。Keystone 将在创建时以与生成时相同的方式返回密钥一次,但不会存储密钥本身,也不会在初始创建后返回它。

消费者可以列出他们现有的应用程序凭证

GET /v3/users/{user_id}/application_credentials
{
  "application_credentials": [
    {
        "id": "aa4541d9-0bc0-44f5-b02d-a9d922df7cbd",
        "name:" "backup",
        "description": "Backup job...",
        "expires_at": "2017-11-06T15:32:17.000000",
        "project_id": "1a6f968a-cebe-4265-9b36-f3ca2801296c",
        "roles": [
            {
                "id": "d49d6689-b0fc-494a-abc6-e2e094131861",
                "name": "Member"
            }
        ]
    }
  ]
}

消费者可以获取有关特定现有应用程序凭证的信息

GET /v3/users/{user_id}/application_credentials/{application_credential_id}
{
  "application_credentials": [
    {
        "id": "aa4541d9-0bc0-44f5-b02d-a9d922df7cbd",
        "name:" "backup",
        "description": "Backup job...",
        "expires_at": "2017-11-06T15:32:17.000000",
        "project_id": "1a6f968a-cebe-4265-9b36-f3ca2801296c",
        "roles": [
            {
                "id": "d49d6689-b0fc-494a-abc6-e2e094131861",
                "name": "Member"
            }
        ]
    }
  ]
}

消费者可以删除他们自己现有的应用程序凭证以使其失效

DELETE /v3/users/{user_id}/application_credentials/{application_credential_id}

注意

过期的应用程序凭证将被删除。另一种选择是允许它们永远累积,希望保留它们将使调查应用程序无法工作的原因更容易,但这种做法的唯一真正好处是提供不同的错误消息。需要更多的思考和反馈,但对于第一轮工作来说不是必需的。

当应用程序凭证的创建用户被删除时,或者如果他们在应用程序凭证的作用域的项目上被取消分配角色时,该应用程序凭证也会被删除。

除了删除之外,应用程序凭证是不可变的,并且不能被修改。

使用应用程序凭证获取令牌

可以使用应用程序凭证进行身份验证以请求范围令牌,从而遵循 Keystone 的正常授权流程。例如

POST /v3/auth/tokens
{
    "auth": {
        "identity": {
            "methods": [
                "application_credential"
            ],
            "application_credential": {
                "id": "aa4541d9-0bc0-44f5-b02d-a9d922df7cbd",
                "secret": "a49670c3c18b9e079b9cfaf51634f563dc8ae3070db2..."
            }
        }
    }
}

Keystone 将通过使用 passlib 匹配与 ID 关联的密钥 secret 的哈希值来验证应用程序凭证,类似于 Keystone 当前执行密码身份验证的方式。

如果应用程序凭证由 name 引用,则需要提供 user_iduser_nameuser_domain_name 的组合,以便 Keystone 可以查找用户的应用程序凭证。

POST /v3/auth/tokens
{
    "auth": {
        "identity": {
            "methods": [
                "application_credential"
            ],
            "application_credential": {
                "name": "backup",
                "user": {
                    "id": "1a6f968a-cebe-4265-9b36-f3ca2801296c"
                },
                "secret": "a49670c3c18b9e079b9cfaf51634f563dc8ae3070db2..."
            }
        }
    }
}

作为当前使用服务用户的替代方案,部署者可以为每个服务创建一个服务用户和一个应用程序凭证。甚至可以创建一个 Nova 用户,然后为每个 Nova 实例提供自己的应用程序凭证。虽然此时应用程序凭证没有限制 API 使用的能力,但开始为每个服务分配应用程序凭证并执行到期和轮换可能是一个可取的步骤,可以通过添加限制应用程序凭证的 API 访问权限来进一步增强。

施加的限制

通常,应防止应用程序凭证自行生成其他应用程序凭证,因为我们不希望允许受损的应用程序凭证复制自身。因此,它们应被阻止创建信任关系,因为自我信任也可能用于生成另一个应用程序凭证。由于此时未实现 API 访问列表,Keystone 将默认显式阻止从应用程序凭证生成的令牌执行以下操作

  • POST /v3/users/{}/application_credentials

  • DELETE /v3/users/{}/application_credentials/{}

  • POST /v3/OS-TRUST/trusts

  • DELETE /v3/OS-TRUST/trusts/{}

它将通过在令牌对象的“methods”键中检查“application_credential”值来执行此操作。

可以通过在创建应用程序凭证时将 "allow_application_credential_creation": true 属性添加到应用程序凭证来禁用此阻止。这是为了支持 Heat 的特定用例,在该用例中,Heat 用户的应用程序凭证应允许创建新的应用程序凭证以注入到实例中。即使目的是适应这项特定的服务,任何用户都可以有效地声明“我接受风险”并在创建时添加此属性来启用这种潜在的危险行为。

将来,更细粒度的 API 访问列表控制将消除这种阻止和阻止的解决方法。

设计理由

将应用程序凭证管理实现为具有默认策略的正常 CRUD,普通用户可以访问,从而增加了能力,而无需部署者执行额外的设置或管理额外的外部服务。

将应用程序凭证消耗实现为身份验证类型插件,意味着任何以任何方式支持可插拔身份验证的客户端代码都应该能够轻松地使用新功能。尚未实现可插拔身份验证支持的客户端代码应该具有添加它的令人信服的动机。

备选方案

增强令牌,允许将角色的子集委托给令牌。这解决了向应用程序授予过多访问权限的问题,但仍然需要将应用程序绑定到用户,并且令牌过期使其不适用于应用程序使用。

增强用户,添加新的凭证类型,然后允许将角色分配给凭证类型而不是用户。常规用户通常没有创建用户的权限,尤其是在使用 LDAP 或 AD 等身份后端的地方。

直接使用 OAuth。Keystone 已经支持基于 OAuth 的身份验证。但是,在现有的身份验证系统之上添加 OAuth 是一项部署者可以选择的任务,需要部署者付出相当大的努力。如果目标是添加对一个始终可靠存在的概念的支持,那么 OAuth 代表的负担过重,不合理。

增强信任关系,将它们与委托人分离。信任关系仍然需要用户的密码进行身份验证,因此会阻止无缝轮换。此实现可以建立在信任关系的现有框架之上,但必须构建一个与用户密码分开的密钥,以允许轮换。

关于命名:GCE 使用术语“服务帐户”并讨论了“应用程序访问”的概念。两者都可以作为应用程序凭证的替代术语。缺点是“服务帐户”使用了“帐户”一词,这可能会引起理解上的混淆。“应用程序访问”很好地描述了我们希望提供的内容,但“访问”不能很好地表示可以请求和提交的唯一信息单元,而“凭证”可以。

Github 公开了类似的概念,即 GitHub App,这些是专门用于非人工 API 访问的特殊帐户,例如机器人或其他自动化程序。它们同样旨在成为项目级别的资源,而不是用户级别的资源。“App”可以作为这个名称。但是,Github 的概念与人们可以注册来对其帐户执行操作的服务目录密不可分,这超出了此提案的范围。此外,“App”一词带有可能不适用于将此结构作为通用自动化系统一部分的人们使用的含义。使用该术语可能会造成比好处更多的混淆。

安全影响

这将产生积极的安全影响

  • 与其为每个服务拥有一个服务用户,不如让所有服务使用单个服务用户和多个应用程序凭证。这通过减少需要攻击的帐户数量,降低了获取特权操作访问权限的攻击向量。

  • 用户名和密码不会保存在配置文件中。虽然应用程序凭证仍然非常敏感,但如果被泄露,它们不会允许攻击者从配置文件中获取服务用户密码约定。

  • 应用程序凭证将增强有限访问权限的能力,因此转向它们是朝着有限访问权限凭证迈出的一步。

  • 应用程序凭证可以被优雅地轮换并定期删除,从而允许消费者和部署者防止被泄露的用户,而无需在短时间内更换凭证,这可能会导致服务中断或停机。

  • 虽然我们长期以来一直在考虑允许应用程序凭证在其创建用户生命周期之外存在,以便在用户离开团队时允许应用程序正常运行时间,但不幸的是,这会带来过高的滥用风险。确保在删除或从项目删除用户时删除应用程序凭证,将防止恶意或懒惰的用户在他们不再应该拥有访问权限时获得访问权限。

添加新的凭证类型和更改身份验证详细信息存在固有的风险。其中一项风险是允许为同一用户帐户创建许多凭证。

通知影响

其他最终用户影响

  • 拥有监控或与 OpenStack 服务交互的应用程序的消费者应该能够利用此功能来提高其应用程序的整体安全性和可管理性。

  • 消费者可以通过创建新的应用程序凭证、更新配置文件以使用新的应用程序凭证,最后删除旧的应用程序凭证,从而在不发生停机的情况下优雅地轮换应用程序的应用程序凭证。

  • 不开始使用应用程序凭证的消费者不应受到任何影响。

性能影响

这应该与今天的用户名/密码身份验证具有相同的性能影响,因为将使用相同的机制来比较哈希以获得答案。

其他部署者影响

部署者应该注意到以下维护改进

  • 部署者只需要对单个服务用户强制执行安全策略,而不是多个。

  • 服务用户的密码轮换策略不再需要立即重新部署服务配置文件。用户密码更改不会影响各种服务配置文件中现有的应用程序凭证。

  • 部署者可以通过部署在不发生停机的情况下优雅地轮换应用程序凭证。

开发人员影响

实现

负责人

主要负责人

  • Monty Taylor

其他贡献者

  • Colleen Murphy

工作项

  1. 开发后端以支持应用程序凭证

  2. 实现 CRUD 操作的 API、业务逻辑和验证

  3. 将凭证删除添加到用户删除

  4. 添加新的应用程序凭证身份验证插件

  5. 阻止访问 Keystone 应用程序凭证 API

  6. 将消费支持添加到 keystoneauth

  7. 文档

  8. 通知部署项目有关使用应用程序凭证 (Devstack, OpenStack-Ansible, Tripleo, OpenStack Puppet 等)

  9. 通知非 Python SDK 关于使用应用程序凭证 (Fog, gophercloud, JClouds)

依赖项

文档影响

树内安装指南应更新以考虑应用程序凭证和服务用户。用户指南也应更新,以反映用户可以使用应用程序凭证进行应用程序身份验证的能力。

参考资料

http://lists.openstack.org/pipermail/openstack-dev/2017-May/116596.html