Fernet tokens and key rotation
Keystone recently merged a new token provider that gives deployers the ability to use non-persistent tokens. These non-persistent tokens have been known by a few different names during the landing process (Authenticated Encryption tokens, Keystone Lightweight tokens, etc). For the duration of this post, they’ll be referred to as Fernet tokens. For those unfamiliar with the concept (and/or specification) of Fernet tokens and how they apply to Keystone, I encourage you to peruse the specification, as well as some non-persistent token benefits.
This particular post is intended to walk through some of the tooling in place for Fernet key rotation, as well as basic usage. I’d like to do a post on the built-in benefits Fernet key rotation has for key replication in multi-node environments, but this will serve as an introduction.
Key Rotation Tooling
The tooling considers keys to have one of two purposes and one of three states. Each key in the repository can either encrypt and decrypt information, or strictly decrypt (these could *almost* be thought of as active and passive). These two “purposes” translate nicely to key “states”. The key repository’s primary key has the ability to encrypt and decrypt information, while secondary keys only decrypt information. The third state is a special case of a secondary key called a staged key. Like a secondary key, a staged key is one that has the ability to decrypt information but being the staged key means that it is next-in-line to be the primary key (this is important for validating tokens in multi-node deployments).
The following output details the behavior of setting up a new key repository.
What we have after running keystone-manage fernet_setup is a key repository with two new keys. The key named with the highest index, 1 in this case, is considered the primary key. The file named 0 is always reserved as the staged key. Given the above state of our key repository, let’s rotate the keys.
Now, we will be encrypting information with key 2, since it is our new primary, and keys 0 and 1 will be strictly for decrypting. This gives us the ability to verify information that was encrypted with key 1 since there was a period of time where it was the primary. Information that was encrypted with key 2 can still be verified since being the primary key means it can encrypt and decrypt for as long as it is the primary key. Let’s say that we have an active key limit of three, where we only want a maximum of three keys in our key repository. Knowing that, let’s do another rotation:
As expected, we have a new primary key named 3, but we’ve removed key 1. Because the current configuration has a limit of three active keys, the tooling removed the key with the lowest index, excluding 0. This means that we can still verify anything that has been encrypted with keys 2 and 3, since each was at one point a primary key, but anything encrypted with key 1 will not be verified since the key used to encrypt that information has been purged. The number of active keys in a key repository can be configured through Keystone’s max_active_keys configuration option. It is important to note that it is possible to prematurely revoke a valid token if key rotation happens too often, or the number of max_active_keysis set to an integer too low. For example, if you plan to rotate your primary key every 30 minutes and each Keystone token has a lifespan of six hours, max_active_keys should be set to 12 or more.
A possible improvement would be to determine how many active keys are needed based on the token expiration and how often keys are rotated. This would push more responsibility into Keystone and for the first iteration of Fernet tokens, we’ll probably stick to documentation.
Photo Credit: LEEROY.ca via PEXELS