Add JWS token provider documentation
Add documentation that advertise support for JWS tokens. bp json-web-tokens Change-Id: If1700c53674ad98b54f572a73b5d4350c7837ab6
This commit is contained in:
committed by
Morgan Fainberg
parent
96adccd0ec
commit
950e7d1f6d
72
doc/source/admin/jws-key-rotation.rst
Normal file
72
doc/source/admin/jws-key-rotation.rst
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
================
|
||||||
|
JWS key rotation
|
||||||
|
================
|
||||||
|
|
||||||
|
The JWS token provider issues tokens using asymmetric signing. This document
|
||||||
|
attempts to describe how to manage key pairs in a deployment of keystone nodes
|
||||||
|
that need to validate tokens issued by one another.
|
||||||
|
|
||||||
|
The inherent benefit of using asymmetric keys is that each keystone server
|
||||||
|
generates it's own key pair. The private key is used to sign tokens. Anyone
|
||||||
|
with access to the public key has the ability to verify the token signature.
|
||||||
|
This is a critical step in validating tokens across a cluster of keystone
|
||||||
|
nodes.
|
||||||
|
|
||||||
|
It is necessary for operators to sync public keys across all keystone nodes in
|
||||||
|
the deployment. Each keystone server will need a corresponding public key for
|
||||||
|
every node. This only applies to public keys. Private keys should never leave
|
||||||
|
the server they are generated from.
|
||||||
|
|
||||||
|
Initial setup
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Before a deployment of keystone servers can issue JWT tokens, each server must
|
||||||
|
set ``keystone.conf [token] provider = jws``. Additionally, each API server
|
||||||
|
must have its own asymmetric key pair either generated manually or using
|
||||||
|
``keystone-manage create_jws_keypair``. If you're generating the key pairs
|
||||||
|
manually, they must be usable with the ``ES256`` JSON Web Algorithm (`JWA`_). It
|
||||||
|
is worth noting that the ``keystone-manage create_jws_keypair`` command line
|
||||||
|
utility will create an appropriate key pair, but it will not automatically
|
||||||
|
deploy it to the key repository locations defined in ``keystone.conf
|
||||||
|
[jwt_tokens]``. It is up to operators to move these files accordingly and
|
||||||
|
resolve possible file name conflicts.
|
||||||
|
|
||||||
|
After generating a key pair, the public key from each API server must be shared
|
||||||
|
with every other API server in the deployment. Ensure the private key used to
|
||||||
|
sign JWS tokens is readable by the process running keystone and available in
|
||||||
|
the ``keystone.conf [jwt_tokens] jws_private_key_repository`` location.
|
||||||
|
Keystone will automatically use a key named ``private.pem`` to sign tokens and
|
||||||
|
ignore all other keys in the repository. To validate tokens, keystone will
|
||||||
|
iterate all available public keys in ``keystone.conf [jwt_tokens]
|
||||||
|
jws_public_key_repository``. At a minimum, this repository needs to have the
|
||||||
|
corresponding public key to the ``private.pem`` key found in ``keystone.conf
|
||||||
|
[jwt_tokens] jws_private_key_repository``.
|
||||||
|
|
||||||
|
.. _`JWA`: https://tools.ietf.org/html/rfc7518
|
||||||
|
|
||||||
|
Continued operations
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Depending on the security requirements for your deployment, you might need to
|
||||||
|
rotate out an existing key pair. To do so without prematurely invalidating
|
||||||
|
tokens, follow these steps:
|
||||||
|
|
||||||
|
1. Generate a new asymmetric key pair for a given keystone API server (see
|
||||||
|
``keystone-manage create_jws_keypair`` for more details)
|
||||||
|
2. Copy or sync the newly generated public key to the public key repositories
|
||||||
|
of all other keystone API servers, the public key should be placed in
|
||||||
|
``keystone.conf [jwt_tokens] jws_public_key_repository``
|
||||||
|
3. Copy the new private key to the private key repository on the API server
|
||||||
|
you're performing the rotation on and make sure it's named ``private.pem``,
|
||||||
|
at this point the server will start signing tokens with the new private key
|
||||||
|
and all other keystone API servers will be able to validate those tokens
|
||||||
|
since they already have a copy of the public key from step #2
|
||||||
|
4. At this point, you must wait until the last tokens signed with the old
|
||||||
|
private key have expired before you can remove the old corresponding public
|
||||||
|
keys from each keystone API server, note this should be a minimum of
|
||||||
|
``keystone.conf [token] expiration``
|
||||||
|
5. Once you're confident all tokens signed with the old private key are
|
||||||
|
expired, it is safe to remove the old corresponding public key from each API
|
||||||
|
server in the deployment, which is important in case the original private
|
||||||
|
key was compromised and prevents attackers from using it craft their own
|
||||||
|
tokens
|
||||||
@@ -31,3 +31,16 @@ Fernet
|
|||||||
|
|
||||||
Fernet tokens are bearer tokens. They must be protected from unnecessary
|
Fernet tokens are bearer tokens. They must be protected from unnecessary
|
||||||
disclosure to prevent unauthorized access.
|
disclosure to prevent unauthorized access.
|
||||||
|
|
||||||
|
JWS
|
||||||
|
``jws`` tokens do not need to be persisted at all, but require that you
|
||||||
|
configure an asymmetric key pair to sign and validate tokens. The key pair can
|
||||||
|
be generated using ``keystone-manage create_jws_keypair`` or it can be
|
||||||
|
generated out-of-band manually so long as it is compatible with the JWT
|
||||||
|
``ES256`` Elliptic Curve Digital Signature Algorithm (ECDSA) using a P-256
|
||||||
|
curve and a SHA-256 hash algorithm.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
JWS tokens are bearer tokens. They must be protected from unnecessary
|
||||||
|
disclosure to prevent unauthorized access.
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
# drivers should maintain their own equivalent document, and merge it with this
|
# drivers should maintain their own equivalent document, and merge it with this
|
||||||
# when their code merges into core.
|
# when their code merges into core.
|
||||||
driver-impl-fernet=Fernet tokens
|
driver-impl-fernet=Fernet tokens
|
||||||
|
driver-impl-jws=JWS tokens
|
||||||
|
|
||||||
[operation.create_unscoped_token]
|
[operation.create_unscoped_token]
|
||||||
title=Create unscoped token
|
title=Create unscoped token
|
||||||
@@ -65,6 +66,7 @@ notes=All token providers must be capable of issuing tokens without an explicit
|
|||||||
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
||||||
--os-password=<password> token issue
|
--os-password=<password> token issue
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|
||||||
[operation.create_system_token]
|
[operation.create_system_token]
|
||||||
title=Create system-scoped token
|
title=Create system-scoped token
|
||||||
@@ -73,6 +75,7 @@ notes=All token providers must be capable of issuing system-scoped tokens.
|
|||||||
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
||||||
--os-system-scope all token issue
|
--os-system-scope all token issue
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|
||||||
[operation.create_project_scoped_token]
|
[operation.create_project_scoped_token]
|
||||||
title=Create project-scoped token
|
title=Create project-scoped token
|
||||||
@@ -82,6 +85,7 @@ cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
|||||||
--os-password=<password> --os-project-name=<project>
|
--os-password=<password> --os-project-name=<project>
|
||||||
--os-project-domain-name=<domain> token issue
|
--os-project-domain-name=<domain> token issue
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|
||||||
[operation.create_domain_scoped_token]
|
[operation.create_domain_scoped_token]
|
||||||
title=Create domain-scoped token
|
title=Create domain-scoped token
|
||||||
@@ -91,6 +95,7 @@ notes=Domain-scoped tokens are not required for all use cases, and for some use
|
|||||||
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
||||||
--os-password=<password> --os-domain-name=<domain> token issue
|
--os-password=<password> --os-domain-name=<domain> token issue
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|
||||||
[operation.create_trust_scoped_token]
|
[operation.create_trust_scoped_token]
|
||||||
title=Create trust-scoped token
|
title=Create trust-scoped token
|
||||||
@@ -100,6 +105,7 @@ notes=Tokens scoped to a trust convey only the user impersonation and
|
|||||||
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
cli=openstack --os-username=<username> --os-user-domain-name=<domain>
|
||||||
--os-password=<password> --os-trust-id=<trust> token issue
|
--os-password=<password> --os-trust-id=<trust> token issue
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|
||||||
[operation.create_token_using_oauth]
|
[operation.create_token_using_oauth]
|
||||||
title=Create a token given an OAuth access token
|
title=Create a token given an OAuth access token
|
||||||
@@ -107,6 +113,7 @@ status=optional
|
|||||||
notes=OAuth access tokens can be exchanged for keystone tokens.
|
notes=OAuth access tokens can be exchanged for keystone tokens.
|
||||||
cli=
|
cli=
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|
||||||
[operation.revoke_token]
|
[operation.revoke_token]
|
||||||
title=Revoke a token
|
title=Revoke a token
|
||||||
@@ -117,6 +124,7 @@ notes=Tokens may be individually revoked, such as when a user logs out of
|
|||||||
revoked token was previously used to create additional tokens).
|
revoked token was previously used to create additional tokens).
|
||||||
cli=openstack token revoke
|
cli=openstack token revoke
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|
||||||
[feature.online_validation]
|
[feature.online_validation]
|
||||||
title=Online validation
|
title=Online validation
|
||||||
@@ -125,6 +133,7 @@ notes=Keystone must be able to validate the tokens that it issues when
|
|||||||
presented with a token that it previously issued.
|
presented with a token that it previously issued.
|
||||||
cli=
|
cli=
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|
||||||
[feature.offline_validation]
|
[feature.offline_validation]
|
||||||
title=Offline validation
|
title=Offline validation
|
||||||
@@ -134,6 +143,7 @@ notes=Services using Keystone for authentication may want to validate tokens
|
|||||||
performance and scalability.
|
performance and scalability.
|
||||||
cli=
|
cli=
|
||||||
driver-impl-fernet=missing
|
driver-impl-fernet=missing
|
||||||
|
driver-impl-jws=missing
|
||||||
|
|
||||||
[feature.non_persistent]
|
[feature.non_persistent]
|
||||||
title=Non-persistent
|
title=Non-persistent
|
||||||
@@ -144,3 +154,4 @@ notes=If a token format does not require persistence (such as to a SQL
|
|||||||
operations such as `keystone-manage token_flush`.
|
operations such as `keystone-manage token_flush`.
|
||||||
cli=
|
cli=
|
||||||
driver-impl-fernet=complete
|
driver-impl-fernet=complete
|
||||||
|
driver-impl-jws=complete
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ Token providers
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
The token type issued by keystone is configurable through the
|
The token type issued by keystone is configurable through the
|
||||||
``/etc/keystone/keystone.conf`` file. Currently, the only supported token
|
``/etc/keystone/keystone.conf`` file. Currently, there are two supported token
|
||||||
provider is ``fernet``.
|
providers, ``fernet`` and ``jws``.
|
||||||
|
|
||||||
Fernet tokens
|
Fernet tokens
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@@ -93,4 +93,36 @@ fernet tokens. Like UUID tokens, fernet tokens must be passed back to the
|
|||||||
Identity service in order to validate them. For more information on the fernet
|
Identity service in order to validate them. For more information on the fernet
|
||||||
token type, see the :doc:`fernet-token-faq`.
|
token type, see the :doc:`fernet-token-faq`.
|
||||||
|
|
||||||
|
A deployment might consider using the fernet provider as opposed to JWS tokens
|
||||||
|
if they are concerned about public expose of the payload used to build tokens.
|
||||||
|
|
||||||
|
JWS tokens
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
The JSON Web Signature (JWS) token format is a type of JSON Web Token (JWT) and
|
||||||
|
it was implemented in the Stein release. JWS tokens are signed, meaning the
|
||||||
|
information used to build the token ID is not opaque to users and can it can be
|
||||||
|
decoded by anyone. JWS tokens are ephemeral, or non-persistent, which means
|
||||||
|
they won't bloat the database or require replication across nodes. Since the
|
||||||
|
JWS token provider uses asymmetric keys, the tokens are signed with private
|
||||||
|
keys and validated with public keys. The JWS token provider implementation
|
||||||
|
only supports the ``ES256`` JSON Web Algorithm (JWA), which is an Elliptic
|
||||||
|
Curve Digital Signature Algorithm (ECDSA) using the P-256 curve and a SHA-256
|
||||||
|
hash algorithm.
|
||||||
|
|
||||||
|
A deployment might consider using JWS tokens as opposed to fernet tokens if
|
||||||
|
there are security concerns about sharing symmetric encryption keys across
|
||||||
|
hosts. Note that a major difference between the two providers is that JWS
|
||||||
|
tokens are not opaque and can be decoded by anyone with the token ID. Fernet
|
||||||
|
tokens are opaque in that the token ID is ciphertext. Despite the JWS token
|
||||||
|
payload being readable by anyone, keystone reserves the right to make backwards
|
||||||
|
incompatible changes to the token payload itself, which is not an API contract.
|
||||||
|
We only recommend validating the token against keystone's authentication API to
|
||||||
|
inspect its associated metadata. We strongly discourage relying on decoded
|
||||||
|
payloads for information about tokens.
|
||||||
|
|
||||||
|
More information about JWTs can be found in the `specification`_.
|
||||||
|
|
||||||
|
.. _`specification`: https://tools.ietf.org/html/rfc7519
|
||||||
|
|
||||||
.. support_matrix:: token-support-matrix.ini
|
.. support_matrix:: token-support-matrix.ini
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ Everything you need to know about keystone tokens.
|
|||||||
|
|
||||||
tokens-overview
|
tokens-overview
|
||||||
fernet-token-faq
|
fernet-token-faq
|
||||||
|
jws-key-rotation
|
||||||
token-provider
|
token-provider
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
[`blueprint json-web-tokens <https://blueprints.launchpad.net/keystone/+spec/json-web-tokens>`_]
|
||||||
|
Keystone now supports a JSON Web Signature (JWS) token provider in addition
|
||||||
|
to fernet tokens. Fernet token remain the default token provider. Full
|
||||||
|
details can be found in the `specification
|
||||||
|
<http://specs.openstack.org/openstack/keystone-specs/specs/keystone/stein/json-web-tokens.html>`_.
|
||||||
Reference in New Issue
Block a user