Re-wrapping data after encryption key rotation
In addition to being able to store secrets, Vault can encrypt/decrypt data stored elsewhere. The primary use of this is to allow applications to encrypt their data while still storing it in their primary data store. Vault does not store the data.
The transit secrets engine handles cryptographic functions on data-in-transit, and often referred to as Encryption as a Service (EaaS). Both small amounts of arbitrary data, and large files such as images, can be protected with the transit engine. This EaaS function can augment or eliminate the need for Transparent Data Encryption (TDE) with databases to encrypt the contents of a bucket, volume, and disk, etc.
Encryption Key Rotation
One of the benefits of using the Vault EaaS is its ability to rotate the
encryption keys. Keys can be rotated manually by a human, or an automated
process which invokes the key rotation API endpoint through cron
, a CI
pipeline, a periodic Nomad batch job, Kubernetes Job, etc.
The goal of this tutorial is to demonstrate an example for re-wrapping data after rotating an encryption key in the transit engine in Vault.
Personas
The end-to-end scenario described in this tutorial involves two personas:
- security engineer with privileged permissions to manage the encryption keys
- app with un-privileged permissions rewraps secrets via API
Challenge
Vault maintains the versioned keyring and the operator can decide the minimum version allowed for decryption operations. When data is encrypted using Vault, the resulting ciphertext is prepended with the version of the key used to encrypt it.
The following example shows data that was encrypted using the fourth version of a particular encryption key:
For example, an organization could decide that a key should be rotated once a week, and that the minimum version allowed to decrypt records is the current version as well as the previous two versions. If the current version is five, then Vault would decrypt records that were sent to it with the following prefixes:
- vault:v5:lkjasfdlkjafdlkjsdflajsdf==
- vault:v4:asdfas9pirapirteradr33vvv==
- vault:v3:ouoiujarontoiue8987sdjf^1==
In this example, what would happen if you send Vault data that was encrypted
with the first or second version of the key (vault:v1:...
or vault:v2:...
)?
Vault would refuse to decrypt the data as the key used is less than the minimum key version allowed.
Solution
Luckily, Vault provides an easy way of re-wrapping encrypted data when a key is rotated. Using the rewrap API endpoint, a non-privileged Vault entity can send data encrypted with an older version of the key to have it re-encrypted with the latest version. The application performing the re-wrapping never interacts with the decrypted data. The process of rotating the encryption key and rewrapping records could (and should) be completely automated. Records could be updated slowly over time to lessen database load, or all at once at the time of rotation. The exact implementation will depend heavily on the needs of each particular organization or application.
Prerequisites
This lab was tested on macOS using an x86_64 based processor. If you are running macOS on an Apple silicon-based processor, use a x86_64 based Linux virtual machine in your preferred cloud provider.
Policy requirements
Each persona require a different set of capabilities. These are expressed in policies. If you are not familiar with policies, complete the policies tutorial.
The security engineer tasks require these capabilities.
Note
For these tasks, you can use Vault's root
token. However, it is
recommended that root tokens are only used for enough initial setup or in
emergencies. As a best practice, use tokens with an appropriate set of policies
based on your role in the organization.
The app tasks require these capabilities.
Lab setup
Clone GitHub repository
Download the sample application code from learn-vault-eaas-transit-rewrap repository to perform the steps described in this tutorial.
Clone the
learn-vault-eaas-transit-rewrap
repository.Or download the repository:
This repository contains supporting content for this Vault tutorial.
Go into the
learn-vault-eaas-transit-rewrap
directory.Working directory
This tutorial assumes that the remainder of commands are executed within this directory.
Start Vault
In another terminal, start a Vault dev server with
root
as the root token.The Vault dev server defaults to running at
127.0.0.1:8200
. The server is initialized and unsealed.Insecure operation
Do not run a Vault dev server in production. This approach starts a Vault server with an in-memory database and runs in an insecure way.
Switch back to the terminal you cloned the GitHub repository in. Export an environment variable for the
vault
CLI to address the Vault server.Export an environment variable for the
vault
CLI to authenticate with the Vault server.Note
For these tasks, you can use Vault's root token. However, it is recommended that root tokens are only used for enough initial setup or in emergencies. As a best practice, use an authentication method or token that meets the policy requirements.
The Vault server is ready.
Start MySQL database in Docker
The application requires a MySQL database. Docker provides a MySQL server image that satisfies the application's requirements
Note
For the demonstration, a MySQL database runs locally using Docker. However, these steps would work for an existing MySQL database by supplying the proper network information to your environment.
Pull a MySQL server image with
docker
.Create a directory for the demo data.
Create a database named
my_app
that sets the root user password toroot
and adds a user namedvault
.
The database is available.
Enable the transit secrets engine
(Persona: security engineer)
Enable the
transit
secrets engine.Create an encryption key to use for transit named
my_app_key
.The transit key
my_app_key
is created.
Generate a new token for the sample app
(Persona: security engineer)
Before generating a token, create a limited scope policy named rewrap_example
for the sample application.
Review the limited scope policy stored in rewrap_example.hcl
.
Create the
rewrap_example
policy.The policy is created.
Create a token with the
rewrap_example
policy.The output displays a token capable of using the transit key
my_app_key
.Create another token and store the token in the variable
APP_TOKEN
.Display the
APP_TOKEN
.The application uses this token to seed the database.
Run the sample application
(Persona: app)
The application stores data within the database. The data contains a field that require encryption. Vault provides that encryption through the transit secrets engine.
File | Description |
---|---|
Program.cs | Starting point of this sample app (the Main() method) is in this file. It reads the environment variable values, connects to Vault and the MySQL database. If the user_data table does not exist, it creates it. |
DBHelper.cs | Defines a method to create the user_data table if it does not exist. Finds and updates records that need to be rewrapped with the new key. |
AppDb.cs | Connects to the MySQL database. |
Record.cs | Sample data record template. |
VaultClient.cs | Defines methods necessary to rewrap transit data. |
WebHelper.cs | Helper code to seed the initial table schema. |
rewrap_example.csproj | Project file for this sample app. |
Run the sample application with the Vault server address, the transit key, and the generated token.
Example output:
The application finishes after it generates several database entries.
Connect to the database with the root credentials.
Within the mysql shell, connect to the
my_app
table.Within the mysql shell, display 10 rows from the
user_data
table where the city starts with a Vault transit key.The results display 10 rows that match this query. The city field is encrypted with the Vault transit key.
Drop the connection to the database.
The application used the Vault server through the application token to encrypt these fields.
Rotate the encryption key
(Persona: security engineer)
The encryption key my_app_key
can be rotated.
Rotate the
my_app_key
transit key.Display information about the
my_app_key
transit key.The output displays that this transit key has two versions.
Programmatically re-wrap the data
(Persona: app)
The application's database contains fields encrypted with the first version of the transit key.
Run the application again to re-wrap the encrypted fields.
Example output:
The application finishes after it re-wraps the encrypted fields in the existing entries.
Connect to the database with the root credentials.
Within the mysql shell, connect to the
my_app
table.Within the mysql shell, display 10 rows from the
user_data
table where the city starts with a Vault transit key versionv1
.The results of this query are an empty set.
Within the mysql shell, display 10 rows from the
user_data
table where the city starts with a Vault transit key versionv2
.The results display 10 rows that match this query. The city field is encrypted with the second version of the Vault transit key.
Drop the connection to the database.
Clean up
Stop and remove the MySQL container.
Remove the
rewrap-data
directoryRemove the local git repository
Conclusion
An application similar to this could be scheduled via cron, run periodically as a Nomad batch job, or executed in a variety of other ways. You could also modify it to re-wrap a limited number of records at a time so as to not put undue strain on the database. The final implementation should be based upon the needs and design goals specific to each organization or application.