Repropose JWT specification for Stein
This spec was something we agreed upon as a team during the Queens release. This commit reproposes it for Stein. Major differences between this specification the original proposal from Queens include: - the algorithm targeted for the implementation - the library being used to sign and validate JWTs - the payload data and claim information - asymmetric key rotation details bp json-web-tokens Change-Id: I598faca40a6d81dd58155165d4a323fb437f7a6c
This commit is contained in:
parent
e354333611
commit
c87a0230b3
|
@ -1,197 +0,0 @@
|
||||||
..
|
|
||||||
This work is licensed under a Creative Commons Attribution 3.0 Unported
|
|
||||||
License.
|
|
||||||
|
|
||||||
http://creativecommons.org/licenses/by/3.0/legalcode
|
|
||||||
|
|
||||||
======================================================
|
|
||||||
Add JSON Web Tokens as a Non-persistent Token Provider
|
|
||||||
======================================================
|
|
||||||
|
|
||||||
`bp json-web-tokens <https://blueprints.launchpad.net/keystone/+spec/json-web-tokens>`_
|
|
||||||
|
|
||||||
|
|
||||||
JSON Web Token is a type of non-persistent bearer token similar to the fernet
|
|
||||||
tokens we use today. JWT is an `open standard`_ with `actively maintained
|
|
||||||
libraries`_.
|
|
||||||
|
|
||||||
.. _`open standard`: https://tools.ietf.org/html/rfc7519
|
|
||||||
.. _`actively maintained libraries`: https://jwt.io/#libraries
|
|
||||||
|
|
||||||
Problem Description
|
|
||||||
===================
|
|
||||||
|
|
||||||
We currently support two types of tokens. The UUID persistent token format is
|
|
||||||
the original format. The fernet token format is a non-persistent format based
|
|
||||||
on a spec by Heroku and was made the default token format for keystone.
|
|
||||||
|
|
||||||
The UUID token format suffers a number of performance and maintainability
|
|
||||||
problems. It is already deprecated and we intend to eventually remove it, in
|
|
||||||
favor of non-persistent token formats.
|
|
||||||
|
|
||||||
However, the fernet format has its own problems that make it non-ideal. The
|
|
||||||
`fernet spec`_ is largely abandoned, making it hard to `get changes into it`_
|
|
||||||
and thereby into the `cryptography` implementation of it. Moreover, the fernet
|
|
||||||
spec is not recognized by any standards body and therefore not as closely
|
|
||||||
audited as an IETF standard, making it more susceptable to zero-day
|
|
||||||
vulnerabilities. Addressing these vulnerabilities falls solely on the
|
|
||||||
OpenStack, specifically the keystone, community.
|
|
||||||
|
|
||||||
It would be nice to offer a new type of token that is backed by a widely used
|
|
||||||
standard before we totally remove support for UUID tokens. This also increases
|
|
||||||
interoperability between the OpenStack ecosystem and other communities that
|
|
||||||
support JWT.
|
|
||||||
|
|
||||||
.. _`fernet spec`: https://github.com/fernet/spec/blob/master/Spec.md
|
|
||||||
.. _`get changes into it`: https://github.com/fernet/spec/pull/13
|
|
||||||
|
|
||||||
|
|
||||||
Proposed Change
|
|
||||||
===============
|
|
||||||
|
|
||||||
Create a new non-persistent keystone token backend based on the `JSON Web Token
|
|
||||||
standard`_. These will behave in much the same way as our current fernet tokens
|
|
||||||
do.
|
|
||||||
|
|
||||||
The token will be a nested JWT which is a signed JWT (`JWS`_) as the payload of
|
|
||||||
an encrypted JWT (`JWE`_), meaning the token data is signed and then encrypted.
|
|
||||||
This is the order specifically recommended in the JWT spec due to its use of
|
|
||||||
Authenticated Encryption which eliminates the need to sign after encryption.
|
|
||||||
|
|
||||||
Similar to the fernet, JWTs will require a key repository be set up to use for
|
|
||||||
signing tokens. A new ``keystone-manage`` command will be added to handle
|
|
||||||
secret generation and rotation which will likely re-use much of the utilities
|
|
||||||
in the fernet_setup and fernet_rotate commands. JWE can use symmetric keys for
|
|
||||||
encryption and signing the way fernet does but it is more typical to use an
|
|
||||||
asymmetric key pair to encrypt the Content Encryption Key (which is then used
|
|
||||||
as a symmetric key to encrypt the payload, but this is an implementation detail
|
|
||||||
that will be handled by the chosen library), so the JWT key repository should
|
|
||||||
have asymmetric key pairs which we can use for encryption and signing. The
|
|
||||||
specific algorithms used will depend on the support for them in the chosen
|
|
||||||
library.
|
|
||||||
|
|
||||||
The payload of the JWS will use the following `registered claims`_:
|
|
||||||
|
|
||||||
* the "sub" claim for the subject, i.e. the user. This claim will include the
|
|
||||||
same data that fernet tokens do, such as user information and scope.
|
|
||||||
* the "exp" claim for expiry
|
|
||||||
* the "iat" claim for "issued at", analogous to the timestamp field in the
|
|
||||||
fernet token.
|
|
||||||
|
|
||||||
The `PyJWT library`_ is already present in the requirements repository and so
|
|
||||||
would be a convenient choice to use for this implementation, but it
|
|
||||||
unfortunately `does not yet support JWE`_. We will need to evaluate the
|
|
||||||
`JWCrypto library`_ which does support encryption or consider contributing the
|
|
||||||
feature to PyJWT.
|
|
||||||
|
|
||||||
Users will request and present tokens in exactly the same way they currently do
|
|
||||||
with either UUID or Fernet tokens. There is no need to add or change any APIs.
|
|
||||||
|
|
||||||
.. _`JSON Web Token standard`: https://tools.ietf.org/html/rfc7519
|
|
||||||
.. _`JWS`: https://tools.ietf.org/html/rfc7515
|
|
||||||
.. _`JWE`: https://tools.ietf.org/html/rfc7516
|
|
||||||
.. _`registered claims`: https://tools.ietf.org/html/rfc7519#section-4.1
|
|
||||||
.. _`Python libraries`: https://jwt.io/#libraries
|
|
||||||
.. _`PyJWT library`: https://pyjwt.readthedocs.io/en/latest/
|
|
||||||
.. _`does not yet support JWE`: https://github.com/jpadilla/pyjwt/issues/143
|
|
||||||
.. _`JWCrypto library`: http://jwcrypto.readthedocs.org/
|
|
||||||
|
|
||||||
Alternatives
|
|
||||||
------------
|
|
||||||
|
|
||||||
Not applicable, this is an additive change. The alternative is not to add it or
|
|
||||||
to find a different token format.
|
|
||||||
|
|
||||||
Security Impact
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Since JWT is a widely used web standard, this will have a net positive impact
|
|
||||||
on security. The implementation will use JWE even though it is an optional
|
|
||||||
feature of the JWT spec. While this will not protect against an attacker using
|
|
||||||
a valid token to query keystone for information about the token, it protects
|
|
||||||
against an attacker gaining information from an expired or revoked token. This
|
|
||||||
will ensure that the data within the token is at least as secure as it is in
|
|
||||||
fernet tokens. These will still be bearer tokens and so interception of one
|
|
||||||
must still be guarded against.
|
|
||||||
|
|
||||||
Notifications Impact
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Notifications for JWTs will behave in the same way that they do for fernet
|
|
||||||
tokens, including for revocation events.
|
|
||||||
|
|
||||||
Other End User Impact
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
This will have no end user impact. They will request and use JWTs in exactly
|
|
||||||
the same way that they currently use fernet or UUID tokens.
|
|
||||||
|
|
||||||
Performance Impact
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Performance will be on-par with fernet tokens.
|
|
||||||
|
|
||||||
Other Deployer Impact
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
This is an optional, opt-in feature that will not be the default, so deployers
|
|
||||||
will not be affected unless they choose to deploy this feature. In that case,
|
|
||||||
deployers will need to set up a key repository before using JWTs. The key
|
|
||||||
repository will contain asymmetric key pairs rather than just secret keys. The
|
|
||||||
deployer will need to take care to sync and rotate keys the way they do with
|
|
||||||
fernet tokens.
|
|
||||||
|
|
||||||
Developer Impact
|
|
||||||
----------------
|
|
||||||
|
|
||||||
The new token type will reuse much of the work already done for fernet tokens
|
|
||||||
and will follow similar code paths, so this will be relatively easy to
|
|
||||||
maintain.
|
|
||||||
|
|
||||||
Implementation
|
|
||||||
==============
|
|
||||||
|
|
||||||
Assignee(s)
|
|
||||||
-----------
|
|
||||||
|
|
||||||
TBD: Please update this section when this spec is reproposed to a release.
|
|
||||||
|
|
||||||
Primary assignee:
|
|
||||||
<launchpad-id or None>
|
|
||||||
|
|
||||||
Other contributors:
|
|
||||||
<launchpad-id or None>
|
|
||||||
|
|
||||||
Work Items
|
|
||||||
----------
|
|
||||||
|
|
||||||
* Decide on an implementation library
|
|
||||||
* Refactor the fernet utilities modules to be generic enough to work with JWT
|
|
||||||
or inheritable
|
|
||||||
* Add a ``keystone-manage`` command to set up and rotate JWT signing/encryption
|
|
||||||
keys
|
|
||||||
* Generalize the ``TokenFormatter`` class to support JWT
|
|
||||||
* Refactor the fernet token provider module to be inheritable or generic
|
|
||||||
* Add a keystone doctor command to validate the setup in the same way that
|
|
||||||
fernet is validated
|
|
||||||
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
============
|
|
||||||
|
|
||||||
* A JWT library to be decided on
|
|
||||||
|
|
||||||
|
|
||||||
Documentation Impact
|
|
||||||
====================
|
|
||||||
|
|
||||||
The new ``[token]/provider`` configuration option will need to be documented,
|
|
||||||
as will the new ``keystone-manage`` commands.
|
|
||||||
|
|
||||||
|
|
||||||
References
|
|
||||||
==========
|
|
||||||
|
|
||||||
* `JSON Web Token RFC <https://tools.ietf.org/html/rfc7519>`_
|
|
||||||
* `JSON Web Token light introduction <https://jwt.io/introduction/>`_
|
|
||||||
* `History of cryptography's adoption of fernet <https://github.com/pyca/cryptography/issues/2900>`_
|
|
|
@ -0,0 +1,406 @@
|
||||||
|
..
|
||||||
|
This work is licensed under a Creative Commons Attribution 3.0 Unported
|
||||||
|
License.
|
||||||
|
|
||||||
|
http://creativecommons.org/licenses/by/3.0/legalcode
|
||||||
|
|
||||||
|
======================================================
|
||||||
|
Add JSON Web Tokens as a Non-persistent Token Provider
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
`bp json-web-tokens <https://blueprints.launchpad.net/keystone/+spec/json-web-tokens>`_
|
||||||
|
|
||||||
|
|
||||||
|
JSON Web Token is a type of non-persistent bearer token similar to the fernet
|
||||||
|
tokens we use today. JWT is an `open standard`_ with `actively maintained
|
||||||
|
libraries`_.
|
||||||
|
|
||||||
|
.. _`open standard`: https://tools.ietf.org/html/rfc7519
|
||||||
|
.. _`actively maintained libraries`: https://jwt.io/#libraries
|
||||||
|
|
||||||
|
Problem Description
|
||||||
|
===================
|
||||||
|
|
||||||
|
We currently support one token format called fernet. The fernet token format is
|
||||||
|
a non-persistent format based on a spec by Heroku and was made the default
|
||||||
|
token format for keystone.
|
||||||
|
|
||||||
|
However, the fernet format has its own problems that make it non-ideal. The
|
||||||
|
`fernet spec`_ is largely abandoned, making it hard to `get changes into it`_
|
||||||
|
and thereby into the `cryptography`_ implementation of it. Moreover, the fernet
|
||||||
|
spec is not recognized by any standards body and therefore not as closely
|
||||||
|
audited as an IETF standard, making it more susceptible to zero-day
|
||||||
|
vulnerabilities. Addressing these vulnerabilities falls solely on the
|
||||||
|
OpenStack, specifically the keystone, community.
|
||||||
|
|
||||||
|
It would be nice to offer a new type of token that is backed by a widely used
|
||||||
|
standard. This also increases the chances of interoperability between the
|
||||||
|
OpenStack ecosystem and other communities that support JWT.
|
||||||
|
|
||||||
|
.. _`get changes into it`: https://github.com/fernet/spec/pull/13
|
||||||
|
|
||||||
|
Use Cases
|
||||||
|
---------
|
||||||
|
|
||||||
|
* As a operator, I want to use a non-persistent token provider that isn't
|
||||||
|
coupled to a symmetric encryption or signing implementation. An
|
||||||
|
implementation built on asymmetric signing or encryption allows me to
|
||||||
|
distribute public keys from one node to another instead of syncing a
|
||||||
|
repository of symmetric keys. This makes it easier to deploy keystone nodes
|
||||||
|
with read-only capabilities strictly used for token validation. The
|
||||||
|
specific usecase for this allows me to deploy read-only regions keeping token
|
||||||
|
validation within the region, while having tokens issued from a central
|
||||||
|
identity management system in a separate region.
|
||||||
|
|
||||||
|
* As an operator, I want to be have a token provider to fall back on in the
|
||||||
|
event there is a security vulnerability in the `fernet spec`_ or the
|
||||||
|
`cryptography`_ implementation consumed by keystone.
|
||||||
|
|
||||||
|
* As a user, I want to be able to authenticate for a token that I can use with
|
||||||
|
other pieces of software outside of the OpenStack ecosystem that prove my
|
||||||
|
identity.
|
||||||
|
|
||||||
|
.. _`fernet spec`: https://github.com/fernet/spec/blob/master/Spec.md
|
||||||
|
.. _`cryptography`: https://github.com/pyca/cryptography
|
||||||
|
|
||||||
|
Proposed Change
|
||||||
|
===============
|
||||||
|
|
||||||
|
Create a new non-persistent keystone token backend based on the `JSON Web Token
|
||||||
|
standard`_. These will behave in much the same way as our current fernet tokens
|
||||||
|
do.
|
||||||
|
|
||||||
|
The token will be a signed JWT (`JWS`_) containing the authentication payload.
|
||||||
|
Note that signed tokens are web safe and integrity verified, but token payload
|
||||||
|
is not opaque to its holder. It is possible to decode a token and inspect the
|
||||||
|
payload with `JWS`_ tokens. Using nested `JWE`_ tokens are the JSON Web Token
|
||||||
|
equivalent to Fernet tokens and they are encrypted and signed.
|
||||||
|
|
||||||
|
This implementation reserves the right to change, modify, or remove items in
|
||||||
|
the payload at any point in time and for any reason. Decoding and relying on
|
||||||
|
attributes within the payload is not a supported API, nor should it be assumed
|
||||||
|
as one. Users should use formal APIs to request information from keystone. The
|
||||||
|
development team will not prevent or stall changes to token payloads, which are
|
||||||
|
internal implementation details of the token provider, due to users relying on
|
||||||
|
those attributes in ways they shouldn't. Likewise, deployment that consider
|
||||||
|
information in the token payload sensitive should rely on Fernet to prevent
|
||||||
|
that information from being exposed to users.
|
||||||
|
|
||||||
|
Similar to the Fernet, JWTs will require a key repository be set up to use for
|
||||||
|
signing tokens. A new ``keystone-manage`` command will be added to handle
|
||||||
|
secret generation and rotation which will likely re-use much of the utilities
|
||||||
|
in the ``fernet_setup`` and ``fernet_rotate`` commands. The recommended
|
||||||
|
algorithm to be used is ``ES256``. Keystone should not expose the ability to
|
||||||
|
end users to ask for a specific JWS algorithm. Support should be limited to
|
||||||
|
only supported or trusted algorithms that the end user cannot specify. JWS
|
||||||
|
tokens will be integrity verified with a private key and validated using a
|
||||||
|
corresponding public key. Since the ``ES256`` implementation only uses signing
|
||||||
|
(as opposed to signed encrypted payloads), this adheres to slightly better
|
||||||
|
security practices over fernet because private keys never have to be synced
|
||||||
|
across keystone API nodes. Only public keys need to be transferred to other
|
||||||
|
keystone API servers to validate tokens across a cluster.
|
||||||
|
|
||||||
|
The payload of the JWS will use the following `registered claims`_:
|
||||||
|
|
||||||
|
* the `sub` claim will be a **required** string containing the ID of the user
|
||||||
|
who authenticated for the token
|
||||||
|
* the `exp` claim will be a **required** numeric value for token expiration
|
||||||
|
* the `iat` claim will be a **required** numeric value for the time a token was
|
||||||
|
issued
|
||||||
|
|
||||||
|
The following private claims will be used to relay additional required
|
||||||
|
information and will be prefixed with `openstack` to avoid collisions with
|
||||||
|
future upstream claims:
|
||||||
|
|
||||||
|
* `openstack_methods` will be a **required** claim denoting the authentication
|
||||||
|
methods used to obtain the token
|
||||||
|
* `openstack_audit_ids` will be a **required** claim containing the audit
|
||||||
|
information associated to a token
|
||||||
|
* `openstack_system` will be an **optional** claim only present in
|
||||||
|
system-scoped tokens
|
||||||
|
* `openstack_domain_id` will be an **optional** claim only present in
|
||||||
|
domain-scoped tokens
|
||||||
|
* `openstack_project_id` will be an **optional** claim only present in
|
||||||
|
project-scoped tokens
|
||||||
|
* `openstack_trust_id` will be an **optional** claim only present in
|
||||||
|
trust-scoped tokens
|
||||||
|
* `openstack_app_cred_id` will be an **optional** claim only present in
|
||||||
|
application credential tokens
|
||||||
|
* `openstack_group_ids` will be an **optional** claim only present in federated
|
||||||
|
tokens to carry an ephemeral user's group assignments
|
||||||
|
* `openstack_idp_id` will be an **optional** claim only present in federated
|
||||||
|
tokens to carry the ID of a user's identity provider
|
||||||
|
* `openstack_protocol_id` will be an **optional** claim only present in
|
||||||
|
federated tokens to denote the protocol used by a federated user to
|
||||||
|
authenticate
|
||||||
|
* `openstack_access_token` will be an **optional** claim only present in OAuth
|
||||||
|
tokens
|
||||||
|
|
||||||
|
The `PyJWT library`_ is already present in the requirements repository and
|
||||||
|
would be a convenient choice to use for this implementation. Both the `PyJWT
|
||||||
|
library`_ and the `JWCrypto library`_ implement support for JWS. Since the
|
||||||
|
implementation detailed in this specification is unique to ``ES256``, a library
|
||||||
|
that supports JWE isn't necessary. If supporting another encrypted token type,
|
||||||
|
like fernet, is a requirement in the future, then finding or contributing JWE
|
||||||
|
support to the consuming library would be necessary.
|
||||||
|
|
||||||
|
Users will request and present tokens in exactly the same way they currently do
|
||||||
|
with Fernet tokens. There is no need to add or change any APIs.
|
||||||
|
|
||||||
|
.. _`JSON Web Token standard`: https://tools.ietf.org/html/rfc7519
|
||||||
|
.. _`JWS`: https://tools.ietf.org/html/rfc7515
|
||||||
|
.. _`JWE`: https://tools.ietf.org/html/rfc7516
|
||||||
|
.. _`registered claims`: https://tools.ietf.org/html/rfc7519#section-4.1
|
||||||
|
.. _`Python libraries`: https://jwt.io/#libraries
|
||||||
|
.. _`PyJWT library`: https://pyjwt.readthedocs.io/en/latest/
|
||||||
|
.. _`does not yet support JWE`: https://github.com/jpadilla/pyjwt/issues/143
|
||||||
|
.. _`JWCrypto library`: http://jwcrypto.readthedocs.org/
|
||||||
|
|
||||||
|
Key Setup & Rotation
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Much like the Fernet implementation, a JWT provider will require a key rotation
|
||||||
|
strategy. Since ``ES256`` relies on asymmetric signing, the suggested rotation
|
||||||
|
strategy will be slightly different than what is known with Fernet.
|
||||||
|
|
||||||
|
The Fernet implementation requires the usage of a staged key, which is just a
|
||||||
|
key with a special name, in order to ensure tokens can be validated during the
|
||||||
|
rotation process. This won't be required with JWT and the following steps
|
||||||
|
should be sufficient to perform key rotation without token invalidation due to
|
||||||
|
missing signing keys. Assume the following steps are being performed on three
|
||||||
|
different API servers, named `K1`, `K2`, and `K3`, that need to validate
|
||||||
|
tokens issued by each other.
|
||||||
|
|
||||||
|
1. A key pair is created for each API server. `K1.private`, `K1.pub` for
|
||||||
|
`K1`, `K2.private`, `K2.pub` for `K2`, and `K3.private`, `K3.pub` for `K3`.
|
||||||
|
2. A copy of each public key is transferred to each API server. `K1`, `K2`, and
|
||||||
|
`K3` all have copies of `K1.pub`, `K2.pub`, and `K3.pub`.
|
||||||
|
|
||||||
|
At this point, tokens issued from any API server can be validated anywhere. In
|
||||||
|
the event a single API server needs to rotate keypairs:
|
||||||
|
|
||||||
|
1. A new key pair is created for `K1` called `K1-new.private` and `K1-new.pub`.
|
||||||
|
`K1` is configured to start signing tokens with both `K1.private` and
|
||||||
|
`K1-new.private.`
|
||||||
|
2. `K1-new.pub` is copied to the public key repository of each API server. So
|
||||||
|
long as `K2` and `K3` have either `K1.pub` or `K1-new.pub` they can validate
|
||||||
|
tokens issued by `K1`.
|
||||||
|
3. After `K2` and `K3` have been updated with copies of `K1-new.pub`,
|
||||||
|
`K1.private` can be removed from `K1` and `K1.pub` can be removed from `K2`
|
||||||
|
and `K3`. Tokens that were signed with only `K1.private` are unable to be
|
||||||
|
verified and `K1.pub` should only be removed after those tokens have expired
|
||||||
|
anyway.
|
||||||
|
|
||||||
|
Traditional asymmetric keys can be revoked using revocation lists. At this time
|
||||||
|
we are not going to support a revocation list implementation for JWT key pairs.
|
||||||
|
The operator has the ability to sync public keys accordingly when they rotate
|
||||||
|
new keys in and out. Keystone will only use the public keys on disk to validate
|
||||||
|
tokens. Is could change in the future, but for now it keeps the key rotation
|
||||||
|
and key utilities with keystone simpler.
|
||||||
|
|
||||||
|
Crypto-Agility & Future Work
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
This specification is targeting a single algorithm for the initial JWT
|
||||||
|
implementation. If and when keystone decides to expand the implementation to
|
||||||
|
include additional algorithms, we should allow for flexibility between
|
||||||
|
configured algorithms, which will make it easier for operators to switch from
|
||||||
|
one algorithm to another if they need to.
|
||||||
|
|
||||||
|
For example, the validation process using a JWT token provider might support
|
||||||
|
validating multiple blessed algorithms, allowing multiple tokens signed with
|
||||||
|
different algorithms to be validated without require configuration changes
|
||||||
|
except on the signing node.
|
||||||
|
|
||||||
|
Alternatives
|
||||||
|
------------
|
||||||
|
|
||||||
|
Recently, there have been various efforts that help solve authenticated
|
||||||
|
encryption. One of these efforts was sparked by a `concern`_ with JWT, namely
|
||||||
|
the `JOSE`_ header. The issue detailed in the report was specific to users
|
||||||
|
being able to specify algorithms and exploit a validation weakness in various
|
||||||
|
JWT libraries. All python libraries have been patched, but keystone should
|
||||||
|
specifically rely on validating algorithm usage and never assuming algorithms
|
||||||
|
to be supplied by end users. Please see the full `report`_ for details on the
|
||||||
|
vulnerability and why we are going to strictly validate this information.
|
||||||
|
|
||||||
|
There is a proof-of-concept implementation for Platform Agnostic Security
|
||||||
|
Tokens, or `PASETO`_ that takes a more strict stance on algorithm validation
|
||||||
|
and the intended audience of the token. The strict stance of `versioned
|
||||||
|
protocols` with `PASETO`_ is certainly advantageous, but the implementation and
|
||||||
|
idea are still in the incipient stage. It's certainly worth noting that we
|
||||||
|
should keep out eye on this development and re-evaluate it if, or when, it gets
|
||||||
|
more adoption.
|
||||||
|
|
||||||
|
For now, if keystone supplies strict algorithm validation to the JWT
|
||||||
|
implementation, we should be able to offer a comparable backup option to
|
||||||
|
fernet.
|
||||||
|
|
||||||
|
.. _`concern`: https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
|
||||||
|
.. _`report`: https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
|
||||||
|
.. _`JOSE`: https://tools.ietf.org/html/rfc7519#section-5
|
||||||
|
.. _`PASETO`: https://github.com/paragonie/paseto
|
||||||
|
|
||||||
|
Security Impact
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Since JWT is a widely used web standard, this will have a net positive impact
|
||||||
|
on security. The implementation will use asymmetric signing, reducing risk of
|
||||||
|
having to replicate or transfer private keys from one host to another. Since
|
||||||
|
the token payloads are signed, data within the token will be readable to anyone
|
||||||
|
who has the token. The token can only be validated using the corresponding
|
||||||
|
public key of the private key used to sign the token originally. These will
|
||||||
|
still be bearer tokens and so interception of one must still be guarded
|
||||||
|
against.
|
||||||
|
|
||||||
|
Known Vulnerabilities
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
There is a documented `vulnerability`_ that affected several JWT libraries,
|
||||||
|
including one library written in Python.
|
||||||
|
|
||||||
|
In most cases, JSON Web Tokens will have a header, payload, and signature where
|
||||||
|
each section is delimited by a period (``.``). The header contains an important
|
||||||
|
piece of information, which is how the token's integrity is protected. This is
|
||||||
|
stored as the ``alg`` attribute of the header. The library verifying the token
|
||||||
|
uses the algorithm specified in the header to perform an integrity check and
|
||||||
|
compares its results to the signature portion of the token.
|
||||||
|
|
||||||
|
Security concerns have been documented and raised that describe the issues with
|
||||||
|
allowing clients to dictate algorithms used for token verification. This is a
|
||||||
|
concern specifically with applications that support asymmetric and symmetric
|
||||||
|
signing. An attacker could effectively bypass the verification check of a
|
||||||
|
token by using a published, or known, public key to generate a JWT with a
|
||||||
|
symmetric signing algorithm.
|
||||||
|
|
||||||
|
This would be applicable if keystone supported signed tokens and encrypted
|
||||||
|
tokens with the same token provider implementation. This vulnerability has been
|
||||||
|
addressed across various libraries after its discovery, but keystone should be
|
||||||
|
aware of the overall technique that lead to it in the first place. We can
|
||||||
|
mitigate this type of vulnerability in keystone by:
|
||||||
|
|
||||||
|
* Ensuring keystone doesn't blindly allow end users to specify which algorithm
|
||||||
|
is used to verify the integrity of a token (e.g., only implementing support
|
||||||
|
for ``ES256``)
|
||||||
|
* Ensure the ``alg`` supplied in the token header is only ever populated by
|
||||||
|
keystone
|
||||||
|
* Ensure keystone only issues tokens of a single encryption or signing strategy
|
||||||
|
(e.g., not allowing users to get signed token and encrypted tokens from the
|
||||||
|
same server, thus mixing asymmetric and symmetric key usage at runtime)
|
||||||
|
|
||||||
|
Specifics about the `vulnerability`_ can be found in the report.
|
||||||
|
|
||||||
|
.. _`vulnerability`: https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
|
||||||
|
|
||||||
|
Notifications Impact
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Notifications for JWTs will behave in the same way that they do for fernet
|
||||||
|
tokens, including for revocation events.
|
||||||
|
|
||||||
|
Other End User Impact
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
This will have no end user impact. They will request and use JWTs in exactly
|
||||||
|
the same way that they currently use fernet tokens.
|
||||||
|
|
||||||
|
Performance Impact
|
||||||
|
------------------
|
||||||
|
|
||||||
|
It will be worth investigating performance differences between token providers
|
||||||
|
that use asymmetric signing (JWT) and symmetric encryption (fernet). These
|
||||||
|
difference, if significant, should be published in documentation as it might be
|
||||||
|
useful for operators when choosing a token provider.
|
||||||
|
|
||||||
|
Other Deployer Impact
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
This is an optional, opt-in feature that will not be the default, so deployers
|
||||||
|
will not be affected unless they choose to use JWT. In that case, deployers
|
||||||
|
will need to set up a key repository before using JWTs. The key repository will
|
||||||
|
contain asymmetric key pairs rather than just secret keys. The deployer will
|
||||||
|
need to take care to sync and rotate keys the way they do with fernet tokens.
|
||||||
|
|
||||||
|
Developer Impact
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The new token type will reuse much of the work already done for fernet tokens
|
||||||
|
and will follow similar code paths, so this will be relatively easy to
|
||||||
|
maintain.
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
==============
|
||||||
|
|
||||||
|
Assignee(s)
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Primary assignee:
|
||||||
|
Gage Hugo (gagehugo)
|
||||||
|
Lance Bragstad (lbragstad)
|
||||||
|
XiYuan Wang (wxy)
|
||||||
|
|
||||||
|
Work Items
|
||||||
|
----------
|
||||||
|
|
||||||
|
* Refactor the fernet utilities modules to be generic enough to work with JWT
|
||||||
|
or inheritable
|
||||||
|
* Add a ``keystone-manage`` command to set up and rotate JWT signing keys
|
||||||
|
* Generalize the ``TokenFormatter`` class to support JWT
|
||||||
|
* Refactor the fernet token provider module to be inheritable or generic
|
||||||
|
* Add a keystone doctor command to validate the setup in the same way that
|
||||||
|
fernet is validated
|
||||||
|
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
============
|
||||||
|
|
||||||
|
There are three different libraries we can use to implement this functionality.
|
||||||
|
|
||||||
|
1. `PyJWT`_
|
||||||
|
|
||||||
|
This library only supports token signing, or JWS. It does not support JWE,
|
||||||
|
or authenticated encryption, yet. A minimum version of **1.0.1** is
|
||||||
|
`required`_, but this library is already included in OpenStack global
|
||||||
|
requirements repository.
|
||||||
|
|
||||||
|
2. `python-jose`_
|
||||||
|
|
||||||
|
This library only supports token signing, or JWS. It does not support JWE,
|
||||||
|
or authenticated encryption, yet. This library is not included in OpenStack
|
||||||
|
global requirements.
|
||||||
|
|
||||||
|
3. `JWCrypto`_
|
||||||
|
|
||||||
|
This library supports both JWS and JWE, but it is not included in OpenStack
|
||||||
|
global requirements.
|
||||||
|
|
||||||
|
3. `Authlib`_
|
||||||
|
|
||||||
|
This library supports both JWS and JWE, but its licensing is incompatible
|
||||||
|
with OpenStack as it is AGPL.
|
||||||
|
|
||||||
|
Given the fact that the initial implementation of JWT is not going to rely on
|
||||||
|
nested JWT tokens or encrypted payloads, it's safe to assume that signing
|
||||||
|
support will be sufficient. The PyJWT library is already included in global
|
||||||
|
requirements and we don't have a case to not use that specific library, which
|
||||||
|
is compatible with OpenStack licensing.
|
||||||
|
|
||||||
|
.. _`PyJWT`: https://pyjwt.readthedocs.io/en/latest/
|
||||||
|
.. _`required`: https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
|
||||||
|
.. _`python-jose`: https://python-jose.readthedocs.io/en/latest/
|
||||||
|
.. _`JWCrypto`: http://jwcrypto.readthedocs.io/en/latest/
|
||||||
|
.. _`Authlib`: https://docs.authlib.org/en/latest/
|
||||||
|
|
||||||
|
Documentation Impact
|
||||||
|
====================
|
||||||
|
|
||||||
|
The new ``[token]/provider`` configuration option will need to be documented,
|
||||||
|
as will the new ``keystone-manage`` commands.
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
* `JSON Web Token RFC <https://tools.ietf.org/html/rfc7519>`_
|
||||||
|
* `JSON Web Token light introduction <https://jwt.io/introduction/>`_
|
||||||
|
* `History of cryptography's adoption of fernet <https://github.com/pyca/cryptography/issues/2900>`_
|
Loading…
Reference in New Issue