改进的 OpenID Connect 支持¶
基于 OpenID Connect 的用户访问在 keystone 中得到支持,它利用 Apache mod_auth_openidc 模块和 keystone 联合身份验证 API。
这涉及将 Apache 服务器设置为 OpenID Connect 客户端(依赖方),它将执行配置的身份验证流程,从标准的 OpenID Connect userinfo 端点获取用户信息(即声明)。这些额外的声明包括电子邮件地址、首选用户名、姓名、姓氏等信息。但是,在使用 OpenStack CLI 时,oidc 依赖方是 CLI 本身。CLI 将从 OpenID Connect 提供程序处获取 access_token,该令牌将与受 Oauth 2.0 保护的 URL 进行交换(先前由 keystone 操作员配置为使用 mod_auth_openidc 进行令牌内省或本地验证)。在这种情况下,只有包含在访问令牌中或由内省端点返回的声明才存在,因为 userinfo 端点是 OpenID Connect 特有的。
上述情况使得难以实现依赖于 userinfo 端点返回的信息(例如电子邮件地址)的复杂策略,并且在 keystone 设置中存在行为不一致的问题。此蓝图旨在通过为 OpenID Connect 插件添加额外的用户信息检索来解决此问题。
问题描述¶
目前,OpenID Connect 提供程序 (OP) 作为外部身份提供程序 (IdP) 得到支持,方法是使用
配置为 OpenID Connect 依赖方的 Apache +
mod_auth_openidc。启用了联合身份验证驱动程序的 Keystone,使用
keystone.auth.plugins.mapped.Mapped身份验证插件。
根据 OpenID Connect 规范,依赖方应该是与 OP 联系以获取访问/ID 令牌并最终从相应端点获取其他用户信息的 OAuth 2.0 客户端应用程序。OAuth 2.0 规范指出,客户端是代表资源所有者发出受保护资源请求的应用程序,并经其授权,不假定其执行位置。
在上述仪表板案例中,OpenID 依赖方是 Apache 服务器,因此 Apache 配置了将在 OP 的任何受支持的授权类型中使用的 OpenID Connect 客户端 ID 和密钥。因此,keystone 管理员将在 OP 中注册一个 OpenID 客户端,并将客户端 ID/密钥添加到 mod_auth_openidc 配置中。在这种情况下,由于所有内容都在 Apache 和 mod_auth_openidc 中处理,Keystone 会收到来自 userinfo 端点的 access_token、id_token 和所有其他授权,这些授权位于 HTTPD 环境变量中。用户除了通常确认正在对依赖方进行身份验证之外,无需对 OP 进行任何操作。
但是,如果正在使用 OpenStack CLI,则依赖方不是 Apache 服务器,而是 CLI(实际上是 keystoneauth1)。在这种情况下,用户必须将客户端 ID 和密钥提供给 keystoneauth1,因此用户必须访问 OP 并创建一个新的 OpenID Connect 客户端,获取发现文档端点、客户端 ID 和客户端密钥,并将其全部传递给库。然后通过 keystoneauth 执行使用请求的授权类型对 OP 进行身份验证流程,最终获得 access_token。然后,此 access_token 将与受 oauth20 保护的 URL 进行交换,该 URL 需要配置为像此 配置指南 中那样对 RP 进行令牌内省。由于此端点是 OAuth 2.0 端点,因此无法从 userinfo 端点获取任何其他声明,因为这是 OpenID Connect 特有的。
因此,keystone 服务器没有从 userinfo 端点获得的任何其他声明,除了已经存在于令牌中的声明,因此不可能基于此创建任何映射(例如组 membership、电子邮件地址等)。令牌可能包含其他声明,但这在标准中不是必需的,取决于 OP 的实现。例如,Google 的 OAuth 2.0 内省端点返回这些其他声明。
遵循 OpenID Connect 术语,依赖方应该是 keystone,而不是用户客户端。如果是这样,当用户想要使用 OpenID Connect 作为 IdP 进行身份验证时,客户端应联系受 OpenID Connect 保护的联合身份验证 URL。然后,身份验证流程应与 horizon+websso 案例中的相同,所有内容均由 mod_auth_openidc 处理,keystone 应用程序将获得所有 OpenID Connect 声明(即来自 id 令牌和 userinfo)。这样,keystoneauth 不应实现任何 oidc 授权类型,而只需拦截重定向到登录端点的请求并弹出浏览器(如 [2] 中所示)。
[2] https://review.openstack.org/#/c/330006/
但是,这样做有几个缺点
每个提供程序只能配置一种授权类型,因此,如果在 keystone 服务器(依赖方)中配置了授权码的授权类型,则用户将无法使用客户端凭据授权,即使 OP 允许这样做
已发布的所有 keystoneauth 中有关 OpenID Connect 的代码都将变得无用,并且应弃用,因为它不应处理任何 oidc 授权类型。
如果配置的授权类型是授权码,则规范指出需要与用户代理(例如浏览器)进行交互,因此这不能用于服务库(按设计)。
但它有一个很大的优势
用户无需在 OP 中创建和管理 OpenID 客户端(因此无需处理客户端 ID、密钥等)。
然而,CLI 用户可能期望获得与其他云提供程序(如 Google Cloud Engine)获得类似的体验,在这些云提供程序中,行为方式与我们当前的方式相同(即用户需要创建 OpenID Connect 客户端并使用获得的客户端 ID 和密钥)。
但是,如果我们继续这种设计,我们可以将所有内容保持原样,但我们需要一个特定的 OpenID Connect 插件在 keystone 中,该插件能够仅在接收到 id 令牌时从 userinfo 端点获取其他声明。这样,keystone 将获得所有这些其他声明,并且管理员设置的映射可以基于它们。如果这样做,操作员应配置此插件,而不是当前的映射插件 (keystone.auth.plugins.mapped.Mapped)。
提议的变更¶
建议的更改是为 Keystone 实现一个特定的本机 OpenID Connect 插件。当使用此插件时,它将处理所有 OpenID Connection 操作和流程,获取所有用户声明。然后,该插件将继续作为普通的映射插件,但额外的声明将存在。
备选方案¶
另一种选择是让 Apache 服务器(Keystone 运行的服务器)完成所有 OpenID Connect 流程。这些优点和缺点在“问题描述”部分中描述。
安全影响¶
此代码将管理 keystone 和 OpenID Connect 提供程序之间的协商。但是,存在 Python 模块(如 pyoidc),它们是 OpenID Connect 认证实现。我们将仅实现这些插件之上的所需逻辑。
通知影响¶
无。
其他最终用户影响¶
没有,使用建议的解决方案。
性能影响¶
需要对外部端点进行额外的调用,这可能会在响应用户时引入延迟。这已经在 Apache 模块中发生。
其他部署者影响¶
对外部模块有额外的依赖关系(但这将删除对 Apache 模块的依赖关系)。
它需要额外的配置选项和部分(每个提供程序一个)。
开发人员影响¶
无。
实现¶
负责人¶
主要负责人:* Alvaro Lopez (aloga)
其他贡献者:* 无
工作项¶
创建一个额外的映射插件,实现描述的逻辑。
依赖项¶
无。
文档影响¶
需要添加有关如何配置 OpenID Connect 提供程序的新文档。
参考资料¶
OpenID Connect 规范 <https://openid.net/developers/specs/>
OAuth 2.0 规范 <https://tools.ietf.org/html/rfc6749>
使用 Google OAuth 2.0 <https://developers.google.com/identity/protocols/OAuth2>
使用 Google OpenID Connect <https://developers.google.com/identity/protocols/OpenIDConnect>
使用 GCE 的 Python 客户端 <https://cloud.google.com/compute/docs/tutorials/python-guide>
OpenID Connect 认证实现 <https://openid.net/developers/certified/>
pyoidc <https://github.com/OpenIDC/pyoidc>