keystone-specs/specs/keystone/stein/capabilities-app-creds.rst

497 lines
22 KiB
ReStructuredText

..
This work is licensed under a Creative Commons Attribution 3.0 Unported
License.
http://creativecommons.org/licenses/by/3.0/legalcode
========================================================
Add Fine Grained Restrictions to Application Credentials
========================================================
`bp <https://blueprints.launchpad.net/keystone/+spec/whitelist-extension-for-app-creds>`_
Currently Keystone application credentials are mostly unrestricted.
Restrictions can only be imposed on creation of follow-up application
credentials and trusts. Other than that they allow unfettered use of the roles
being delegated in the project the application credential is created for. This
renders application credentials questionable anywhere a least-privilege
delegation is desired. Technically it would be possible to store a white list
style list of access rules for an application credential which other OpenStack
services would then enforce. This spec outlines an approach for storing and
handling such restrictions.
Problem Description
===================
This section uses the predecessor of application credentials, Keystone trusts,
to outline a few example where unrestricted role delegation is a problem. The
same problem applies to application credentials since both delegate a subset of
users' roles. Only the authentication method is different (a secret for
application credentials, a trustee user's username/password for trusts).
Keystone trusts, have been around for quite a while now (they were introduced
in the Grizzly release). Right now, trusts are used by the following projects
among others (list may not be complete):
* Heat: operations on behalf of the user at times when a token may have expired
already.
* Magnum: access to Magnum's certificate signing API and other OpenStack APIs
from inside a cluster's instances where the container orchestration engine
requires it (e.g. Glance as backend for docker-registry or cinder as backend
for docker-volume)
Other projects (neutron-lbaas, Barbican) hesitate to employ trusts and
application credentials since they are an all-or-nothing approach: they grant
full access to all OpenStack APIs in the scope (roles in a project) they are
created for. In order to provide least-privilege access, these services
implement ACLs of their own (Barbican, Swift) or rely on other services' ACLs
to grant limited access to resources (neutron-lbaas uses Barbican's ACLs to
grant its service user access to secret containers holding SSL keys). Monasca
suffers from slightly different problems: it uses Keystone to authenticate
metric submission which requires Keystone credentials or an application
credential. This can potentially be abused for out-of-band access to other
OpenStack APIs from inside any VMs running a Monasca agent.
In summary, there is a real need for fine-grained delegation of access. The
implementation of Keystone application credentials as it exists right now
cannot serve this need, though: an application credential can only be used to
grant full access within the application credential's project/roles scope, but
it cannot be used to give access limited to just one particular resource or
access that only allows the creation of specific new resources, but not the
modification/deletion of existing resources.
With fine-grained restrictions in place, application credentials can be used to
safely grant the least privilege required in the scenarios described above.
This is not possible with the current role based access control solution some
services use, where a special-purpose role such as Heat`s `heat_stack_user`
role is merely explicitly blacklisted for all operations other than the
specific one it is intended for. For this blacklisting usually only extends to
the service owning this particular role and putting these restrictions in place
(other services usually do not know this role is supposed to have blacklist
entries for any and all of _their_ operations and thus allow unrestricted
access).
Proposed Change
===============
The approach to implementing fine-grained permissions for application
credentials is two-pronged. Permission data is stored in Keystone and enforced
by keystonemiddleware as follows:
1) Alongside an application credential, a list of access rules with zero or
more access rules can be stored. An entry in this list consists of:
(a) A URL path (e.g. `/v2.1/servers`, `/v2.1/servers/*` or
`/v2.1/servers/{server_id}`). This URL path must be explicitly permitted
according to an operator-configured list of access rules (see `Access Rules
Config`_ below).
(b) A request method (e.g. `GET`)
(c) A service type (ideally matching the `published Service Types Authority`_)
from the Keystone service catalog.
This list is a whitelist, i.e. any request not explicitly allowed by an
access rule is rejected. Keystone itself does not validate the content of
access rules because that would require domain knowledge of each service in
the catalog. Every access rule must match a permitted access rule as
described in the `Access Rules Config`_ section below. If one or more
access rule entries fail this test, application credential creation will
fail.
2) A future iteration of this feature will create a toggle to control whether a
service can use one of these token to make background requests on behalf of
the user, for example to allow the compute service to make requests to the
block storage service even though the block storage API wasn't explicitly
whitelisted in the application credential access rules. For the time being,
chained service requests like this will be unrestricted and will rely on
operator-configured policies to prevent abuse.
3) `keystonemiddleware` on the service's side receives the access rule list
during token validation. It then checks
(a) The service type (e.g. `compute`)
(b) The URL path (e.g. `/v2.1/servers/*` or `/v2.1/servers/{server_id}`
or `/v2.1/servers/b2088298-50e5-4c81-8a50-66bfd1d8943b`)
(c) The request method (e.g. `GET`)
Against every entry in the access rule list retrieved from the token. If an
access rule matches the request, checking stops and the request is handed over
to `oslo.policy` for the regular role based checking. If no access rules
match, the request is rejected right away.
There are three special cases to access rule list processing:
(a) If no list is provided (i.e. if the `access_rules` attribute is
`None`), no access rule checking is performed and the request is passed
to `oslo.policy` right away.
(b) If an empty list is provided (i.e. `[]`), all requests are rejected
(even if the request would otherwise pass the test in (c).
(c) If there is a valid service token in the request, `keystonemiddleware`
passes the request to `oslo.policy` right away, though a future iteration
of this feature will enable a toggle to control this behavior.
.. _published Service Types Authority: https://service-types.openstack.org/
Access Rules Config
-------------------
Every access rule must be validated against an operator-configured list upon
application credential upon creation, unless the operator has explicitly
configured a permissive mode that does no validation. This section describes how
an operator defines a list and how they are used by Keystone.
The allowed access rules are operator configured as a JSON config file on disk,
with the idea that perhaps such a catalog might be exposed on service endpoints
someday. Keystone will document a curated list of URL templates for those APIs
where such a thing can be generated automatically. The operator can then use
this list as-is in the simplest case, or modify it for their local setup as they
chose. For every access rule the following information is stored:
1) A service type that matches one of the services in the Keystone catalog.
2) A URL path pattern, such as `/v2.1/servers/{server_id}`. The combination
of this string and the service type from (1) must be unique. It is anchored at
the beginning of a path, i.e. access rules' path attributes must fully match
this pattern and may not be preceded or followed by extra characters. The
template string may contain the following special wildcard templates:
* `{named_variable}`: allows arbitrary strings (excluding the `/` character).
Named placeholders in the access rule path pattern are there for
readability and direct comparison to API references and policy files, they
do not correlate to string formatting substitutions. Examples include
`{project_id}`, `{user_id}`, or `{server_id}`.
* `*`: allows arbitrary strings (excluding the `/` character)
* `**`: allows arbitrary strings (including the `/` character)
A user using a path pattern containing wild cards for validating one of
their access rules may substitute the wild card by any string fulfilling the
constraint imposed by the wild card. This allows the operator to be
permissive in their URL templates (to the point of only having one "**"
pattern in the most extreme case) and the user to be more restrictive than a
wild card template in their access rules.
Preventing Regressions
----------------------
If a Keystone API which supports this feature encounters a `keystonemiddleware`
version (or 3rd party software authenticating against Keystone) that dates to
before implementation of this feature there is potential for regression: while
Keystone would provide the access rule list upon token validation, the other
side would simply ignore it - giving the requests all the permissions granted
by the delegated roles. This can be prevented by treating application
credentials with access rules (i.e. a `access_rules` attribute that is not
`None`) as follows):
1) When requesting token validation, `keystonemiddleware` (or any 3rd party
application that supports access rule enforcement) sets an
`Openstack-Identity-Access-Rules` header with a version string as its value.
Token validation for an application credential with a access rule list will
only succeed if this header is present. The version string will allow us to
safely extend this feature by invalidating tokens using the extended version
in situations where `keystonemiddleware` only supports an older version
of this feature.
2) If there is no `Openstack-Identity-Access-Rules` header in the token
validation request, token validation fails.
This way we ensure that nobody erroneously assumes access rules are being
enforced in environments where outdated `keystonemiddleware` (or its equivalent
in 3rd party software) cannot enforce them because it is not aware of them. For
any application credentials that do not have access rules, validation proceeds
as it would have before the introduction of access rules (regardless of whether
there is an `Openstack-Identity-Access-Rules` or not).
Discoverability for Access Rules Config
---------------------------------------
Any user with a valid auth token can list the operator maintained access rules
through the Keystone API::
GET /v3/access_rules_config
.. code-block:: json
{
"compute": [
{
"path": "/v2.1/servers",
"method": "GET"
}
]
}
This allows them to discover the URL path templates they can use for creating
access rules in application credentials.
Access Rules and Roles
----------------------
Configured access rules will have an optional ROLE_ID value. If this value is
set, it indicates the role that the user needs to provide in the application
credential in order for the call to proceed. In addition, if the role_id value
is set, the user will only be able to use the access rule if the user has that
role assigned, either directly, or as a result of an implied role.
Chained API Calls
-----------------
One thing the access rules make rather tough is chained API calls: if an API
call is permitted by an access rule, but the service uses the same access rule
restricted token to call other services' APIs, these will fail. While it would
be possible to circumvent this problem with additional access rules to cover
the chained calls, that would be very poor ergonomics, especially for
operations with a large amount of chained API calls such as creating a Heat
stack.
A future optimization of this feature will implement a toggle for access
rules to give services blanket permission to perform chained API calls with the
token resulting from the Application credential. This is implemented as follows:
1) If `keystonemiddleware` receives a request that is permitted due to an
application credential with this toggle set, it requests a service token and
adds it to the request's object's headers.
2) Follow-up requests issued by the service will then send this service token
along with the regular token resulting from the application credential.
3) If `keystonemiddleware` encounters an application credential generated token
with this toggle plus a valid service token it will ignore any
non-empty access rulelists and pass the request to the service as-is.
API Examples
------------
An example creation request for an application credential might look as
follows:
::
POST /v3/users/{user_id}/application_credentials
.. code-block:: json
{
"application_credential": {
"name": "allow-metrics-logs",
"description": "Allow submitting metrics and logs to Monasca",
"roles": [
{"name": "monasca-agent"}
]
"access_rules": [
{
"path": "/v2.0/metrics",
"method": "POST"
},
{
"path": "/v3.0/logs",
"method": "POST"
}
]
}
}
Alternatives
------------
1) One alternative to this exists already: internal ACL implementations by
various OpenStack services. This situation is undesirable for several
reasons, some of which are:
(a) Auditability: authorization information is stored in multiple
locations, all of which need to be checked to find out
who is authorized to perform what operation. From an
auditability perspective it would be preferable to have
a central source of truth.
(b) Maintenance: when there are multiple independent implementations a lot
of code is duplicated and bugs may be duplicated as well
as new projects implement their own ACL system.
(c) Consistency: with multiple sources of truth, an individual service's
ACLs may well end up overriding a cloud-wide policy
permitting or denying an operation.
2) `391624 <https://review.openstack.org/#/c/391624/>`_ proposes a
superficially similar role check in `keystonemiddleware`. There are several
key differences, though:
(a) Application credential access rules do not require a `Cambrian
explosion <https://en.wikipedia.org/wiki/Cambrian_explosion>`_ of
fine-grained roles (one for every API operation of every OpenStack
service) that must be managed by an administrator.
(b) Application credential access rules does not require any changes to
existing policy enforcement. Instead, they add an additional check
that takes place before policy enforcement even comes into play and
rejects requests early. Not being entangled with policy enforcement
gives us the freedom to start out with a very basic implementation and
add features as required later (as opposed to having to be feature
complete immediately).
(c) The role check in `keystonemiddleware` targets administrators who want
to create role profiles for their users, such as "give this user
read-only access to any services' resources but without letting them
create new ones". Application credential access rules on the other
hand, target OpenStack services and third party applications that only
need access to a select handful of operations such as "submit SSL
certificates to the Magnum API for signing".
(d) Application credential access rules do not require keystone to be the
guardian of access control rules, since all the information needed to
validate access is contained in the token.
(e) Unlike a policy based check, an access rule based check will also work
for services that do not use `oslo.policy` such as Swift.
3) One implementation detail from the previous section was discussed at length
at the Rocky PTG: one could have chosen to match for `oslo.policy` targets
rather than URL paths in the access rules, which would have been easier in
some ways. In the end we opted for url paths for the following reasons:
(a) This is user facing and unlike API paths, policy targets are not
easily discoverable by the user since there is no documentation on
them. Moreover, policy targets are not as formalized as APIs and may
easily change over time, thus breaking existing access rules.
(b) URL paths can be rejected in keystonemiddleware, without involving
`oslo.policy`, leading to a faster failure for unauthorized requests.
Limitations
-----------
This proposal does not restrict the body of requests in any sort of way.
Security Impact
---------------
This change tightens security by providing a means to restrict the permissions
granted by application credentials. That being said, its implementation does
have various security critical aspects:
* This change adds additional information to the token data retrieved by
keystonemiddleware upon token validation.
* URLs in access rules are user-supplied strings. Care must be taken to
guard against format string attacks in these if anything beyond character by
character comparison takes place.
* It might be a good idea to limit the length/number of access rules per
API credential to prevent denial of service against the Keystone database (by
filling it with bogus rules) or the Keystone API (via large validation
payloads). Another reason to introduce such a limit is the possibility to
slow down a service by creating application credentials with a large number
of non-matching access rules, which can be used to slow down a particular
service.
* This change is unlikely to allow privilege escalation since it only adds
additional failing criteria to token validation and policy enforcement. These
failing criteria need to be carefully tested for false positives, though.
Notifications Impact
--------------------
No new notifications will be added from this API.
Other End User Impact
---------------------
Since this changes adds extra information to application credentials, both
python-keystoneclient and python-openstackclient need to be extended to handle
that extra information.
Performance Impact
------------------
The performance impact upon application credential creation is probably
neglible, since all that happens is that a small amount of data is stored along
with the application credential.
That small amount of data may not be so small during the token validation,
though, resulting in multiple/more packets being sent in response to a
validation request, causing congestion and/or increasing latency. This can be
mitigated by limiting the number of access rules allowed per application
credential.
Developer Impact
----------------
This change provides developers across all OpenStack services with a means to
create application credentials with fine-grained permissions, allowing them to
delegate access to a user's roles according to the principle of least
privilege.
As far as the application credentials API is concerned, it will be fully
backwards compatible, since specifying access rules when creating an
application credential is optional: if none are specified, the `access_rules`
attribute will be `None`, leading to no access rule checks being performed.
Implementation
==============
Assignee(s)
-----------
Primary assignee:
* Colleen Murphy <colleen@gazlene.net> cmurphy
Other contributors:
* Adam Young <ayoung@redhat.com> ayoung
* Johannes Grassler <jgr-launchpad@btw23.de> jgr-launchpad
Work Items
----------
1. Extend the application credential API and database schema in Keystone to
allow for receiving and storing access rule lists.
2. Implement handling for access rules in python-keystoneclient and
python-openstackclient.
3. Extend the Keystone token validation API to access rule lists upon
upon token validation.
4. Implement the endpoint list check in keystonemiddleware.
Dependencies
============
None
Documentation Impact
====================
* The access rule related settings for application credentials need to be
documented in the release notes and the admin guide.
* Documentation on access rules needs to be added to the *Application
Credentials* section of the Keystone user documentation.
References
==========
* Etherpad with original proposal from the Barcelona 2016 summit:
https://etherpad.openstack.org/p/ocata-keystone-authorization
* Etherpad with refined proposal from the Rocky PTG 2018:
https://etherpad.openstack.org/p/application-credentials-rocky-ptg
* Spec for securing Monasca metric submission from inside VMs
https://review.openstack.org/#/c/507110/ (would be greatly simplified by
having access rules in application credentials)
* Documentation on Barbican ACLs:
http://developer.openstack.org/api-guide/key-manager/acls.html
* Documentation on Swift ACLs:
https://www.swiftstack.com/docs/cookbooks/swift_usage/container_acl.html
* Generating a list of URL patterns for OpenStack services
http://adam.younglogic.com/2018/03/generating-url-patterns/
* Related concept for Istio:
https://istio.io/docs/reference/config/authorization/istio.rbac.v1alpha1/#AccessRule
* Updated design discussion:
http://lists.openstack.org/pipermail/openstack-discuss/2019-February/003031.html