Add Fernet token spec

Fernet tokens are the successor to UUID tokens, and are the default
in rocky and beyond.  This changeset add the specification for the
implementation of Fernet token support into the keystone charm.

Change-Id: I190b6bccba71ea4afac2cc733b0628c270ca34e6
This commit is contained in:
Alex Kavanagh 2018-07-13 09:16:30 +02:00
parent 988efac25d
commit 9beaeab888
1 changed files with 264 additions and 0 deletions

View File

@ -0,0 +1,264 @@
..
Copyright 2017 Canonical LTD
This work is licensed under a Creative Commons Attribution 3.0
Unported License.
http://creativecommons.org/licenses/by/3.0/legalcode
..
This template should be in ReSTructured text. Please do not delete
any of the sections in this template. If you have nothing to say
for a whole section, just write: "None". For help with syntax, see
http://sphinx-doc.org/rest.html To test out your formatting, see
http://www.tele3.cz/jbar/rest/rest.html
====================================
Keystone Fernet Token Implementation
====================================
Fernet tokens were added to OpenStack to solve the problem of keystone being
required to persist tokens to the a common database (cluster) like UUID tokens,
and solve the problem of size for PKI or PKIZ tokens.
From `Fernet - Frequently Asked Questions
<https://docs.openstack.org/keystone/pike/admin/identity-fernet-token-faq.html>`__
A fernet token is a bearer token that represents user authentication. Fernet
tokens contain a limited amount of identity and authorization data in a
`MessagePacked`__ payload. The payload is then wrapped as a Fernet message for
transport, where `Fernet`__ provides the required web safe characteristics for
use in URLs and headers. The data inside the fernet token is protected using
symmetric encryption keys, or fernet keys.
.. _Fernet: https://github.com/fernet/spec
.. _MessagePacked: http://msgpack.org/
Task Description
================
Keystone has had support for Fernet tokens since Kilo, so all services support
fernet token authorization. In the upcoming Rocky release the sql token driver
and uuid token provider will be removed. The main part of the work on this
task is changing the keystone charm to enable configuration of fernet tokens,
provide the initialisation of fernet tokens, provide rotation of fernet keys
and their subsequent synchronisation to other keystone units.
There are also some issues around what aspects should be configurable vs
controlled by the charm. The :ref:`proposed-change-label` section provides
more details about this.
Finally, upgrading an existing OpenStack implementation from another token
system to Fernet tokens is discussed, and the support the charm(s) will need to
implement to support this, along with documentation.
.. _proposed-change-label:
Proposed Change
===============
Theory of Operation
-------------------
The keystone-charm will, with the appropriate configuration options, configure
keystone to generate fernet tokens.
In order to generate tokens, fernet keys are used. These are generated by
keystone and have an expiry date. The key repository is a directory, and each
key is an integer number, with the highest number being the primary key. Key
'0' is the staged key, that will be the next primary. Other keys are secondary
keys.
New tokens are only ever generated from the primary key, whilst they secondary
keys are used to validate existing tokens. The staging key is not used to
generate tokens, but can be used to validate tokens as the staging key might be
the new primary key on the master due to a rotation and the keys have not yet
been synchronised across all the units.
Fernet keys need to be rotated at periodic intervals, and the keys need to be
synchronised to each of the other keystone units. Keys should only be rotated
on the master keystone unit, and must be synchronised *before* they are rotated
again. "*Over rotation*" occurs if a unit rotates its keys such that there is
no suitable decoding key on another unit that can decode a token that has been
generated on the master. This happens if two key rotations are done on the
master before a synchronisation has been successfully performed. This should
be avoided. Over rotations can also cause validation keys to be removed
*before* a token's expiration which would result in failed validations.
There are 3 parts to the **Key Rotation Strategy**:
1. The rotation frequency
2. The token lifespan (set with ``[token] expiration``)
3. The number of active keys: (set with ``[fernet_tokens] max_active_keys``)
There needs to be at least 3 keys as a minumum. The actual number of keys is
determined by the *token lifespan* and the *rotation frequency*. The
*max_active_keys* must be one greater than the *token lifespan* / *rotation
frequency*
To quote from the `FAQ <https://docs.openstack.org/keystone/queens/admin/identity-fernet-token-faq.html>`__:
The number of max_active_keys for a deployment can be determined by
dividing the token lifetime, in hours, by the frequency of rotation in
hours and adding two. Better illustrated as:
.. code:: python
token_expiration = 24
rotation_frequency = 6
max_active_keys = (token_expiration / rotation_frequency) + 2
In the keystone charm, there is already the config parameter
``token-expiration`` in seconds, and there will be a proposed config item of
``fernet-max-active-keys``. Thus, the *rotation frequence* will be calucated
as:
.. code:: python
token_expiration = 24 # actually 3600, as it's in seconds
max_active_keys = 6
rotation_frequency = token_expiration / (max_active_keys - 2)
Thus, the fernet-max-active-keys can never be less than 3 (which will be
enforced in the charm), which would make the rotation frequency the *same* as
the token expiration time.
Upgrading an Existing System
----------------------------
To upgrade an existing system to use Fernet tokens, the keystone charm should
be upgraded first. The (new) configuration option ``token-provider`` has a
*no* default value set, which means on a pre-rocky install the token type will
remain as ``UUID``. In order to move a pre-rocky install to Fernet, the
``token-provider`` option should be set to ``fernet``.
Note that if a pre-rocky system is upgraded to rocky, then the default token
type will be ``fernet``. The rocky cycle removes support for ``UUID`` tokens.
Thus upgrading a system to rocky will automatically use fernet as the only
token type. The ``token-provider`` option is only valid for pre-rocky systems.
Note that althought the charms enable token cachinng with memcache by default,
this is only for the default of 300 seconds as the ``token_cache_time`` is not
being set (see `(Keystone) Middleware Architecture: Improving response time
<https://github.com/openstack/keystonemiddleware/blob/master/doc/source/middlewarearchitecture.rst#improving-response-time>`__
for further details. The impact of this is that some services may try to
re-authenticate during the upgrade, and depending on which keystone unit they
"pick", and what stage the upgrade is at, will determine whether the action
succeeds. As different services may be part of the same action, this might
lead to *odd* failure modes. However, once the system is upgraded, after the
default of 300 seconds, *all* services will continue to operate normally.
Therefore, there *may* be some percieved outage of the openstack control plane.
Already running instances will not be affected and all services will continue
to operate normally as soon as they request new tokens from keystone.
Additional Configuration Items
------------------------------
The following configuration items will be needed in the keystone charm.
* **token-provider** - the token system to use: Either 'uuid' or 'fernet'. The
default will not be set. Pre-rocky systems will have a default of ``uuid``.
On rocky systems, the configuration option has no effect. As the default is
``uuid`` for pre-rocky systems, the token-provider won't change on an upgrade
unless the operator sets the configuration value to ``fernet``.
* **fernet-max-active-keys** - the maximum active keys configured in keystone.
This controls the key rotation trigger times based on this config item and
the config item *token-expiration*.
Keystone Actions
----------------
The following action will be required:
* **purge-tokens** -- purge existing tokens from the database. This is used
after upgrading from ``UUID`` to ``Fernet`` tokens,
Internal Cron Jobs
------------------
The charm will set up a cron job to rotate the keys and then synchronise them
to the other peered units. The cron job will call ``juju run`` from within the
charm to rotate the keys and then synchronise the keys to the other peered
units. It will also only perform this action if it is the leader. The cron
job will run on *all* peered units, but only have an effect on the leader.
Synchronisation of the Fernet keys will be via Juju leader settings. The keys
are small, and "leader settings" provides a convenient and secure mechanism to
synchronise the keys between units without having to explicitly provide
networking for all keystone peered units. The delay in transferring the keys
using hooks is not an issue as the synchronisation does not need to be
immediate; indeed, it could be just before the next key rotation in the worst
case, although, this is extremely unlikely to be the case.
Alternatives
============
In the Openstack rocky release, *fernet* is the only token provider available.
Therefore, there is no alternative.
Implementation
==============
Assignee(s)
-----------
Primary assignee:
ajkavanagh
Secondary assignees:
fnordahl
Gerrit Topic
------------
Use Gerrit topic "fernet-keystone-charm" for all patches related to this spec.
.. code-block:: bash
git-review -t fernet-keystone-charm
Work Items
----------
* Add fernet token functionality to the keystone-charm. This includes:
* setup
* upgrade
* rotate / sync actions
* cron job for automatic rotate / sync.
* Add Fernet token information to the documentation:
* charm-store text for keystone charm
* Notes in the charm guide re: uuid vs Fernet.
* Update tests:
* Amulet/bundle for actions / verify installation.
* Update other bundles to ensure defaults
* Upgrade from uuid to Fernet tokens.
Repositories
------------
No new git repositories required.
Documentation
-------------
Documentation will be provided as part of the keystone charm and notes in charm
guide.
Security
--------
A change of token provider does have security implications and well tested and
proved best practices for using the fernet token provider will be implemented.
Testing
-------
Unit tests will be developed along with new code. Functional tests will be
implemented. A scenario test for change of token provider will also be
written.
Dependencies
============
No external dependencies.