From 1d246348112cc890db5d0d6fe8d091161e05d831 Mon Sep 17 00:00:00 2001 From: Alvaro Lopez Garcia Date: Wed, 21 Sep 2016 11:57:29 +0200 Subject: [PATCH] OpenID Connect improved support OpenID Connect is supported in Keystone by leveraging the Apache mod_auth_oidc module and the Keystone Federation plugin. OpenID Connect works fine when accessing OpenStack thorugh the dashboard, but it requires additional configuration steps to make it work when using the OpenStack CLI tools. This spec aims at improving the support, so that the same outcome is obtained, regardless of the way the user accesses the cloud. Implements blueprint: improved-oidc-support Change-Id: I97f7a34d398ba673d7733fb2aaa490ebc9298afd --- .../backlog/oidc-improved-support.rst | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 specs/keystone/backlog/oidc-improved-support.rst diff --git a/specs/keystone/backlog/oidc-improved-support.rst b/specs/keystone/backlog/oidc-improved-support.rst new file mode 100644 index 00000000..d3592044 --- /dev/null +++ b/specs/keystone/backlog/oidc-improved-support.rst @@ -0,0 +1,231 @@ +.. + This work is licensed under a Creative Commons Attribution 3.0 Unported + License. + + http://creativecommons.org/licenses/by/3.0/legalcode + +=============================== +Improved OpenID Connect Support +=============================== + +`bp improved-oidc-support `_ + + +User access based on OpenID Connect is supported in keystone by leveraging the +Apache ``mod_auth_openidc`` module and the keystone federation APIs. + +This involves setting the Apache server as an OpenID Connect client (Relying +Party) that will perform the configured authentication flow, getting the user +information (i.e. claims) from the standard OpenID Connect userinfo endpoint. +These extra claims include information such as email address, preferred +username, name, surname, etc. However, when using the OpenStack CLI, the oidc +RP is the CLI itself. The CLI will obtain an ``access_token`` from the OpenID +Connect Provider, and this token will be exchanged with an Oauth 2.0 protected +URL (previously configured by the keystone operator to do token instrospection +or local validation using ``mod_auth_openidc`` as well). In this case, only the +claims contained in the access token or returned by the introspection endpoint +will be present, as the userinfo endpoint is specific to OpenID Connect. + +The above situation makes it difficult to implement complex policies that rely +on the information returned by the userinfo endpoint (such as email address) +and it presents a lack of behaviour consistency in the keystone setup. This +blueprint aims at fixing this issue, by adding an additional user information +retrieval for the OpenID Connect plugin. + +Problem Description +=================== + +Currently OpenID Connect Provider (OP) as an external Identity Provider (IdP) +is supported by using: + +* Apache + ``mod_auth_openidc`` configured as an OpenID Connect Relying Party + (RP). + +* Keystone with the Federation drivers enabled, using the + ``keystone.auth.plugins.mapped.Mapped`` auth plugin. + +According to `OpenID Connect specification`_, the Relying Party should be the +Oauth 2.0 client application that will contact the OP in order to get the +access/id tokens and eventually the additional user info from the corresponding +endpoint. The `Oauth 2.0 specification`_ states that a client is an application +making protected resource requests on behalf of the resource owner, and with +its authorization, not making assumptions on where it is being executed. + +.. _OpenID Connect specification: https://openid.net/specs/openid-connect-core-1_0.html +.. _Oauth 2.0 specification: https://tools.ietf.org/html/rfc6749#section-1.1 + +In the dashboard case mentioned above, the OpenID RP is the Apache server, +therefore Apache is configured with the OpenID Connect client id and secret +that will be used for any of the OP grant types supported. Therefore, the +keystone administrator would register an OpenID Client in the OP, and add +its client id/secret to the ``mod_auth_openidc`` configuration. In this case, +since everything is handled within Apache and ``mod_auth_openidc``, Keystone +receives the access_token, id_token and all the additional grants obtained +from the userinfo endpoint in the HTTPD environment variables. The user +does not need to do anything with the OP, apart from the usual confirmation +that she is authenticating against the RP. + +However, if the OpenStack CLIs are being used the RP is not the Apache server, +but the CLI (actually, ``keystoneauth1``). In this case, the user has to feed +the client id and secret to ``keystoneauth1``, therefore the user has to go to +the OP and create a new OpenID Connect client, fetch the discovery document +endpoint, client id and client secret and pass all to the library. Then through +keystoneauth performing the authentication flow using the requested grant type +against the OP, eventually obtaining an ``access_token``. This access_token is +then exchanged with an ``oauth20`` protected URL, that needs to be configured +to do token introspection against the RP, as in this `configuration guide`_. +Since this endpoint is an OAuth 2.0 endpoint it is not able to fetch any +additional claims from the userinfo endpoint, as this is something specific to +OpenID Connect. + +.. _configuration guide: https://developer.ibm.com/opentech/2015/06/17/use-websphere-liberty-as-an-openid-connect-provider-for-openstack + +Therefore, the keystone server does not have any additional claims obtained +from the userinfo endpoint apart from the ones that are already present in the +token, so it is not possible to create any mapping based on this (for example +group membership, email address, and so on). The tokens may include additional +claims, but this is not mandatory in the standard, being dependant on the OP +implementation. For example, Google's OAuth 2.0 introspection endpoint returns +these additional claims. + +Following the OpenID Connect terminology, the RP should be keystone, and not +the user client. If so, when a user wants to authenticate with OpenID Connect +as an IdP the client should contact the federation URL that should be protected +with OpenID Connect. Then the authentication flow should be the same as in the +horizon+websso case, all handled by ``mod_auth_openidc`` and the keystone app +will get all the OpenID Connect claims (i.e from the id token and userinfo). +This way keystoneauth should not implement any openid logic apart from +intercepting the redirect request to the login endpoint and popping out a +browser (as in [2]). + +[2] https://review.openstack.org/#/c/330006/ + +However, there are several disadvantages in doing this: + +* Only one grant type can be configured per provider, therefore if a grant type + of authz code is configured in the keystone server (the RP) the user won't be + able to use the client credentials grant, even if the OP allows to do so + +* All the code in keystoneauth regarding OpenID connect (that has been + released) becomes useless and should be deprecated, as it should not handle + any oidc grant type anymore. + +* If the configuread grant type is the Authorization Code, the specification + states that interaction with the user agent (e.g. a browser) is needed, + therefore this cannot be used (per design) in a service library. + +But it has a big advantage: + +* The user does not need to create and manage OpenID Clients in the OP + (thus it is not needed to handle the client id, secrets, etc.). + +Nevertheless, CLI users may be expecting a similar experience to the one +obtained in other cloud providers (like Google Cloud Engine) where the +behaviour is like the one we have in place right now (i.e. the user needs to +create an OpenID Connect client and user the obtained client id and secret). + +However, if we continue with this design, we can leave everything as it is +right now, but we need a specific OpenID Connect plugin in keystone that is +able to fetch the additional claims from the userinfo endpoint when it only +receives an id token. This way keystone will get all these additional claims +and the mapping set by the administrator can be based on them. If we do so, +operators should configure this plugin, instead of the current mapped plugin +(``keystone.auth.plugins.mapped.Mapped``). + +Proposed Change +=============== + +The proposed change is to implement a specific and native OpenID Connect plugin +for Keystone. When this plugin is used, it will handle all the OpenID +Connection actions and flows, obtaining all the user claims. Afterwards the +plugin will continue as the vanilla mapped plugin, but the additional claims +will be present. + +Alternatives +------------ + +The other alternative would be that all the OpenID Connect flow is done by +the Apache server where Keystone is running. The advantages and disadvantages +of this are described in the "Problem Description" section. + +Security Impact +--------------- + +This code will be managing the negotiation between keystone and the OpenID +Connect provider. However, there are Python modules (like `pyoidc`_) that are +`OpenID Connect certified implementations`_. We would implement only the logic +needed on top of these plugins. + +.. _OpenID Connect certified implementations: https://openid.net/developers/certified/ +.. _pyoidc: https://github.com/OpenIDC/pyoidc + +Notifications Impact +-------------------- + +None. + +Other End User Impact +--------------------- + +None, with the proposed solution. + +Performance Impact +------------------ + +Additional calls need to be made to the external endpoints, that may introduce +a delay when responding to the user. This is already happening at the Apache +module. + +Other Deployer Impact +--------------------- + +* There is an additional dependency on an external module (but this will remove + a dependency on the Apache module). + +* It would require additional configuration options and sections (one per + provider). + +Developer Impact +---------------- + +None. + +Implementation +============== + +Assignee(s) +----------- + +Primary assignee: +* Alvaro Lopez (aloga) + +Other contributors: +* None + +Work Items +---------- + +1. Create an additional mapped plugin, implementing the described logic. + +Dependencies +============ + +None. + +Documentation Impact +==================== + +New documentation needs to be added on how to configure an OpenID Connect +provider. + + +References +========== + +* `OpenID Connect Specifications ` +* `OAuth 2.0 specification ` +* `Using Google OAuth 2.0 ` +* `Using Google OpenID Connect ` +* `Using the Python client for GCE ` +* `OpenID Connect certified implementations ` +* `pyoidc `