Application Credentials for application authn

Implement Applicataion Credentials as a new credential type in order to
improve security by limiting access and not embedding user account
credentials in configuration files. Application Credentials are intended
to empower consumers of clouds to have a mechanism to safely use the API
without compromising their credentials, and to allow cloud-native
applications that need to make API calls the ability to do so without
needing to have the complete account credentials embedded.

Partially-implements: blueprint application-credentials
Co-Authored-By: Jamie Lennox <jamielennox@gmail.com>
Co-Authored-By: Monty Taylor <mordred@inaugust.com>
Co-Authored-By: Colleen Murphy <colleen.murphy@suse.com>
Change-Id: I58a345513dadbacd4166f7d4a4b8d11775ccc736
This commit is contained in:
Ronald De Rose 2017-03-27 18:39:05 +00:00 committed by Monty Taylor
parent e9f4da1961
commit 99ac820fc8
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
1 changed files with 608 additions and 0 deletions

View File

@ -0,0 +1,608 @@
..
This work is licensed under a Creative Commons Attribution 3.0 Unported
License.
http://creativecommons.org/licenses/by/3.0/legalcode
=======================
Application Credentials
=======================
`bp application-credentials <https://blueprints.launchpad.net/keystone/+spec/application-credentials>`_
Implement application-specific credentials as a new credential type in order to
improve security by limiting access and not embedding User credentials in
configuration files.
Problem Description
===================
Currently, automated API interactions -- such as service-to-service
communication, deployment tooling, and cloud-native applications -- must
authenticate with keystone as keystone Users in order to be granted access to
the resources they need to automate. This model, while convenient for keystone,
increases the risk of account compromise by requiring the distribution of
unencrypted passwords. It also exacerbates the potential damage an account
compromise can have since an application using a User's credentials has, as a
result, all the User's permissions. The model hinders mitigation of a
compromise because changing the compromised password necessitates downtime for
distributed applications using the password. The following use cases
demonstrate the situation faced by operators and end users today and the need
for an alternate model for authentication of applications.
.. note:: To avoid confusion, in this document User will be used to refer
to the Keystone User Resource, Consumer will be used to refer
to a Human who is consuming resources from an OpenStack Cloud,
Deployer to a Human who manages an OpenStack Deployment and
Application as non-Human consumer of OpenStack APIs. While the
word "application" may carry connotations, it should be understood
here to mean any amount of code or automation expected to interact
with the OpenStack APIs in the absence of direct human interaction.
Use Cases
---------
* As an OpenStack Consumer, I want to create Applications that programmatically
request a token and make API calls for resources in my project.
To accomplish this, one will typically store User credentials in a
configuration file, such as `clouds.yaml`_ so that scripts can access the
OpenStack API without Human interaction. Having User credentials that are
potentially used for other systems, such as with federated identity, in
configuration files and/or embedding them into a virtual machine, creates a
potential security risk.
* As an OpenStack Consumer, I want to limit the actions that an Application
can make to a pre-defined subset of what my User otherwise has access to do.
By using a User's credentials, the Application will have all of that User's
access. This is likely more access than needed. For instance, a regular
Consumer cannot set up an application that can only upload images to Glance but
cannot create or delete servers in Nova.
* As an OpenStack Consumer and an OpenStack Deployer, I need to be able to
gracefully rotate application credentials.
The current system of Users and Passwords requires simultaneously changing the
Password and updating any Application configuration. For a Deployer this means
updating all of the config files for all of the OpenStack services at the same
time. During the period between the Password being changed and config being
updated, the Application will not be able to talk to the OpenStack API and will
be down. The Application Credential should allow for the creation of a new
credential, rolling the new credential out, then deleting the old credential
for credential rotation with no downtime.
* As a team of OpenStack Consumers, I want to be able to make Applications
that will not stop working when one of my Team Members leaves.
As the use of Automation increases in the world, more and more teams have
problems with automation that was tied to a specific Consumer's User
credentials. The application credentials should be managed as Project level
resources so that all Consumers who have Users with write-allowed roles on the
Project can rotate or delete them as they see fit if membership in their Team
changes.
* As an OpenStack Deployer, I need to configure some OpenStack Services to be
able to make API calls to other OpenStack Services.
Information about Service Users are currently stored in configuration files.
For example, the nova Service User is currently in the configuration of every
nova-api, nova-compute and neutron configuration file. At run time, those
services use those User Credentials to perform service-to-service actions.
From a security perspective, it can be considered unfavorable to have User
credentials written to a file on disk, especially if the User has elevated
privileges on a Project. It should be possible to apply the principle of least
privilege (POLP) and limit access to the minimum level required to complete the
task. For a Consumer this means being able to choose to not use the full User
credentials for automation. For a Deployer this means being able to easily make
service-specific authorizations to go into the config files.
.. _clouds.yaml: https://docs.openstack.org/developer/os-client-config/#config-files
Proposed Change
===============
Implement a new restricted credential type (Application Credentials) to be used
by Applications. Application Credentials will be assigned the same roles the
Creating User has at creation time.
.. note:: This idea was originally going to be called "API Keys" because the
idea is similar to other industry usage around generating a secret
for applications to use with an API. This plan was discontinued
because the term, while not well-defined in the industry, carries
specific connotations about its usage and implementation that this
specification does not match. The name 'Application Credentials' is
chosen instead to be a generic term describing vaguely what this is
intended to be, which is identification information intended for use
only by applications.
.. _`on the mailing list`: http://lists.openstack.org/pipermail/openstack-dev/2017-May/116596.html
Application Credentials will have the following characteristics:
* Immutable.
* Allow for optionally setting limits, e.g. 5 Application Credentials per User
or Project, to prevent abuse of the resource.
* Assigned the curent roles the creating User has on the Project at creation
time, or optionally a list of roles that is a subset of the creating User's
roles on the Project.
* Secret exposed only once at creation time in the create API response.
* Limited ability to manipulate identity objects (see `Limitations Imposed`_)
* Support expiration.
* Support deletion by any user with a write-allowed role in the project.
* Will be deleted when the associated User is deleted.
Application Credentials will be treated as credentials and not authorization
tokens, as this fits within the keystone model and is consistent with others
APIs providing application authentication. It also avoids the security and
performance implications of creating a new token type that would potentially
never expire and have custom validation.
.. note:: In the future, it will be possible for a Consumer to further limit,
at creation time, the operations the Application Credential can
perform. The intent is that such a further limitation will be
expressed at a granularity of REST calls. That functionality is not
described or implemented here, but is required to fully meet the Use
Case associated with pre-defined subset of User actions.
.. note:: In the future, it may be possible to create an Application Credential
that can persist past the lifecycle of the User. That functionality
is not described or implemented here, but is required to fully meet
the Use Case associated with automation not ceasing to work when
a human leaves a team.
Application Credential Management
---------------------------------
By default, any User with at least a member role on a Project should be able to
list, add, and delete Application Credentials for that project. For example,
adding an Application Credentials:
::
POST /v3/projects/{project_id}/application_credentials
.. code-block:: json
{
"application_credential": {
"name:" "backup",
"description": "Backup job...",
"expires_at": "2017-11-06T15:32:17.000000",
"roles": ["Member"]
}
}
`name` must be unique within a given Project, but `name` is only guaranteed to
be unique within its Project. This is consistent with how the other keystone
resources operate. `name` may be useful for Consumers who want human readable
config files.
`description` is a long description for storing information about the purpose
of the Application Credential. It is mostly useful in reports or listings of
Application Credential.
`expires_at` is when the Application Credential expires. 'null' means that the
Application Credential does not automatically expire. `expires_at` is in `ISO
Date Time Format`_ and is assumed to be in UTC if an explicit timezone offset
is not included.
`roles` is an optional list of role names that is a subset of the roles
the Creating User has on the Project. Roles that the Creating User does not
have on a project are an error.
In the initial implementation, the Application Credential will assume the roles
of the Creating User or the given subset and we will not implement
fine-grained access controls beyond that.
Response example:
.. code-block:: json
{
"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",
"user_id": "9ac4bbe2-36c7-49ee-b296-59ce7a4d3edf",
"roles": ["Member"]
}
}
The `id` in the response is the Application Credential identifier and would be
returned in get or list API calls. An `id` is globally unique to the cloud.
`secret` is a random string and only returned via the create API call.
Keystone will only store a hash of the `secret` and not the `secret` itself,
so a lost `secret` is unrecoverable. Subsequent queries of an Application
Credential will not return the secret field.
.. note:: Identifying the correct way to generate an acceptable secret needs
to be done. Nova generates admin passwords on server creation, which
is probably a good place to start. If that approach is taken, use of
`random.choice` should be replaced for Python 3 with
`secrets.choice`.
`roles` is a list of role names. It is informational and can be used by the
Consumer to verify that the Application Credential inherited the roles from
the User that the Consumer expected. This is not a policy enforcement, it is
simply for human validation.
`user_id` contains the `id` of the Creating User. It can be used by other
Consumers who have a User with access to the Project in question to manage and
audit Application Credentials that were created by other Team members.
If the Consumer prefers to generate their own `secret`, they can do so and
provide it in the create call. Keystone will store a hash of the given
`secret`. Keystone will return the secret once upon creation in the same way it
would if it was generated, but will not store the secret itself nor return it
after the initial creation.
A Consumer can list the existing Application Credentials for a Project:
::
GET /v3/projects/{project_id}/application_credentials
.. code-block:: json
{
"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",
"user_id": "9ac4bbe2-36c7-49ee-b296-59ce7a4d3edf",
"roles": ["Member"]
}
]
}
A Consumer can get information about a specific existing Application
Credential:
::
GET /v3/projects/{project_id}/application_credentials/{application_credential_id}
.. code-block:: json
{
"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",
"user_id": "9ac4bbe2-36c7-49ee-b296-59ce7a4d3edf",
"roles": ["Member"]
}
]
}
A Consumer can delete an existing Application Credential to invalidate it:
::
DELETE /v3/projects/{project_id}/application_credentials/{credential_id}
.. note:: Application Credentials that expire will be deleted. The alternative
would be to allow them to accumulate for forever in the hopes that
keeping them around will make investigation as to why an Application
is not working harder, but the only real benefit to this is providing
a different error message. More thought and feedback on this are
needed, but are not essential for the first round of work.
When the Creating User for an Application Credential is deleted, that
Application Credential is also deleted.
.. note:: In the future there may be a way to create a persistent credential,
but that mechanism is not defined.
Aside from deletion, Application Credentials are immutable and may not be
modified.
.. _ISO Date Time Format: https://tools.ietf.org/html/rfc3339#section-5.6
Using an Application Credential to Obtain a Token
-------------------------------------------------
An Application Credential can be used for authentication to request a scoped
token following Keystone's normal authorization flow. For example:
::
POST /v3/auth/tokens
.. code-block:: json
{
"auth": {
"identity": {
"methods": [
"application_credential"
],
"application_credential": {
"id": "aa4541d9-0bc0-44f5-b02d-a9d922df7cbd",
"secret": "a49670c3c18b9e079b9cfaf51634f563dc8ae3070db2..."
}
}
}
}
Keystone will validate the Application Credential by matching a hash of the key
secret associated with the id using `passlib`_ similar to how Keystone does
Password authentication currently.
This improves security as the Application Credentials can have
Consumer-controlled limited lifespan and can be rotated and safely stored in
configuration files. For Consumers that do not currently have a User with
privileges to create Users, this will provide both security and additional
flexibility in how they can manage Applications.
If the Application Credential is referred to by `name`, it will be necessary to
provide either `project_id` or the combination of `project_name` and
`project_domain_name` so that Keystone can look up the Application Credential
in the appropriate Project.
::
POST /v3/auth/tokens
.. code-block:: json
{
"auth": {
"identity": {
"methods": [
"application_credential"
],
"application_credential": {
"name": "backup",
"project": {
"id": "1a6f968a-cebe-4265-9b36-f3ca2801296c"
},
"secret": "a49670c3c18b9e079b9cfaf51634f563dc8ae3070db2..."
}
}
}
}
As an alternative to the current use of Service Users, a Deployer could
create a single Service User and an Application Credential for each service. Or
even create a Nova user and then give each nova instance it's own Application
Credential. Although at this point the Application Credential does not have
the ability to further limit API use, the ability to start assigning
Application Credentials per-service and performing expiration and rotation may
be a desirable step forward that can be further enhanced with the addition of
restricting an Application Credential's API Access.
.. _passlib: https://pypi.python.org/pypi/passlib
Limitations Imposed
-------------------
Since API Access Lists are not implemented at this stage, Keystone
will explicitly block tokens generated from an Application Credential from
doing the following:
* POST /projects/{}/application_credentials
* DELETE /projects/{}/application_credentials/{}
* POST /users
* PATCH /users/{}
* DELETE /users/{}
* POST /projects
* PATCH /projects/{}
* DELETE /projects/{}
* PUT /projects/{}/users/{}/roles/{}
* PUT /domains/{}/users/{}/roles/{}
* PUT /projects/{}/groups/{}/roles/{}
* PUT /domains/{}/groups/{}/roles/{}
.. note:: It might be simpler and safer for now to just block a token
generated from an Application Credential from doing any action in
Keystone altogether. This would check for an "application_credential"
value in the "methods" key of a token object. This would be a
temporary measure until the followup API action exclusion support is
implemented.
In the future, when the Consumer can expess additional REST API limitations
at Application Credential creation time, the built-in identity blacklist should
be migrated to being content in that system. It is entirely reasonable that
a Consumer desire to grant the ability to automate Identity operations.
However, until the the additional system is in place, it is safer to block
the operations altogether.
Design Justifications
---------------------
Implementing Application Credential management as normal CRUD with default
policy that normal Users have access to adds the ability without requiring the
Deployer to perform additional setup or manage additional external services.
Implementing Application Credentials consumption as an auth-type plugin means
that any Client code that supports pluggable auth in any way should be able to
easily consume the new feature. Client code that doesn't yet implement support
for pluggable authentication should have a compelling motivation to add it.
Alternatives
============
`Enhance tokens`_ by allowing delegation of subsets of roles to a token. This
solves the problem of granting too much access to an application, but it still
necessitates tying an application to a User, and token expiry makes it
insufficient for use by applications.
`Enhance users`_ by adding new credential types and then allowing role
assignments to be assigned to credential types instead of users. This doesn't
solve the problem of applications needing to continue running after a User has
been decommissioned. Additionally, regular Consumers frequently do not have
permission to create Users, especially in places that use an identity backend
like LDAP or AD.
Just use OAuth. Keystone already supports OAuth-based authentication. However,
adding OAuth on top of an existing Auth system is a deployer opt-in task that
involves considerable deployer effort. If the goal is to add support for a
concept that will always dependably exist, OAuth represents too high of a
burden to be reasonable. Moreover, OAuth tokens have the same flaws as regular
keystone tokens, namely that they are tied to a User, rather than a project,
and that they are required to expire, rather than optionally expiring.
`Enhance trusts`_ by detaching them from Users. Trusts still require role
assignments on projects to be created by administrators. Adding the ability to
allow users to delegate their own roles to trusts would require a much more
significant rework of the trusts model.
On naming: GCE uses the term "Service Account" and discusses the concept of
"Application Access". Both could be useful alternate terms to Application
Credential. Drawbacks are that "Service Account" uses the word "account" which
may run afoul of understanding. "Application Access" is a good description of
what we wish to provide but "Access" does not well-denote the unique unit of
information that can be requested and submitted, where "Credential" does.
Github exposes a similar concept as `GitHub App`_ which are special accounts
intended to be used for non-human API access, such as bots or other automation.
They are similarly intended to be a project-level resource rather than a
user-level resource. "App" could be a name for this. However, the Github
concept goes hand-in-hand with a catalog of services that people can
register to do things on their accounts, which is well out of scope for this
proposal. Additionally the term "App" carries connotations that may not be
appropriate for people who would use this construct as part of a general
automation system. Using that term is likely to cause more confusion than good.
.. _`Enhance tokens`: https://review.openstack.org/#/c/186979
.. _`Enhance users`: https://review.openstack.org/#/c/389870
.. _`Enhance trusts`: https://review.openstack.org/#/c/396634
.. _`GitHub App`: https://developer.github.com/apps/
Security Impact
---------------
This would have a positive security impact:
* Instead of having a Service User for each service, all services can use a
single Service User and multiple Application Credentials. This decreases the
attack vector of gaining access to privileged operations by reducing the
number of accounts to attack.
* User names and passwords are kept out of configuration files. While
Application Credentials are still extremely sensitive, if compromised they do
not allow attackers to glean service user password conventions from
configuration.
* Application Credentials will grow the ability to have limited access, so a
move to them is a step towards limited access credentials.
* Application Credentials can be gracefully rotated out of use and deleted
periodically, allowing Consumers and Deployers a mechanism to prevent
compromised Users without requiring swapping credentials in short amounts of
time that might cause service interruption or downtime.
There is an inherent risk with adding a new credential type and changing
authentication details. One such risk would be the allowing of many credentials
for the same User account.
Notifications Impact
--------------------
None
Other End User Impact
---------------------
* Consumers who have Applications that monitor or interact with OpenStack
Services should be able to leverage this feature to improve the overall
security and manageability of their Applications.
* Consumers can gracefully rotate Application Credentials for an
Application with no downtime by creating a new Application Credential,
updating config files to use the new Application Credential, and finally
deleting the old Application Credential.
* Consumers who do not start using Application Credentials should experience no
impact.
Performance Impact
------------------
This should have the same performance impact as username/password
authentication does today, since the same mechanism will be used to compare
hashes to obtain an answer.
Other Deployer Impact
---------------------
Deployers should notice the following maintenance improvements:
* Deployers only need to enforce security on a single Service User instead
of multiple.
* Password rotation policies for Service Users no longer require immediately
redeploying service configuration files. A User password change does not
affect the existing Application Credential in the various service
configuration files.
* Deployers can gracefully rotate Application Credentials through a deployment
with no downtime.
Developer Impact
----------------
None
Implementation
==============
Assignee(s)
-----------
Primary assignee:
- Monty Taylor
Other contributors:
- Ron De Rose
- Anthony Washington
- TBD
Work Items
----------
#. Develop backend to support Application Credentials
#. Implement API, business logic, and validation for CRUD operations
#. Add Credential deletion to User deletion
#. Add new Application Credential authentication plugin
#. Block access to Keystone CRUD
#. Add consumption support to keystoneauth
#. Documentation
#. Notify deployment projects about using Application Credentials
(Devstack, OpenStack-Ansible, Tripleo, OpenStack Puppet, etc)
#. Notify non-Python SDKs about using Application Credentials (Fog,
gophercloud, JClouds)
Dependencies
============
None
Documentation Impact
====================
The documentation team, along with the documentation liaison for keystone
should update the installation guide to account for Application Credentials and
Service Users. The user guides should also be updated to reflect the ability
for Users to use Application Credentials for application authentication.
References
==========
None