Use PGP encrypted key shares to unseal Vault
Vault encrypts data with an encryption key. That encryption key is further encrypted with a second key, known as the root key. At Vault initialization time, you can choose to split the root key into a number of key shares using Shamir's Secret Sharing algorithm. When using the Shamir seal, these key shares are called unseal keys, but when using an auto seal or HSM, they're known as recovery keys.
By default, Vault splits the root key into 5 key shares, and requires a quorum of 3 key shares for reconstruction of the root key to use in rekey or unseal operations. In the diagram, the key shares held by Alice, Carol, and Dan satisfy the quorum, and represent the users which participate in the examples for this tutorial.
Challenge
When you initialize Vault, it returns hexadecimal-encoded or base64-encoded representations of the key shares and initial root token value in plaintext, as shown in these examples.
Learn more about the init operation and its output using the Vault HTTP API, CLI, or web UI.
Example expected output:
Vault returns base64-encoded unseal keys, and a plaintext initial root token value when initialized through the vault
CLI.
This is acceptable in certain circumstances, such as development or evaluation clusters, but you could require more protection of the key shares and initial root token when operating in production.
Solution
Vault can encrypt the key shares and initial root token value at initialization time with user-supplied public keys generated from any RFC 4880 compliant PGP software, such as GNU Privacy Guard (GPG).
Tip
Vault also directly supports encrypting key shares with GPG public keys from Keybase usernames, but that is not specifically covered in this tutorial.
When you initialize Vault with the pgp-keys and root-token-pgp-key options, it encrypts the unseal keys and root token value with the specified GPG public keys, base64 encodes the encrypted values, and outputs those values instead of plaintext values.
Scenario introduction
Your goal in this scenario is to explore using Vault with the Shamir seal, and to encrypt the unseal key shares with GPG at initialization time. You'll then learn how to use the GPG encrypted key shares to unseal and rekey Vault.
First, you'll use a terminal session to configure and start a simplified Vault server Docker container.
You'll then use GPG to generate 5 distinct sets of keys to use for encrypting and decrypting the Vault key shares.
Then, it's time to initialize Vault. Acting as all 5 personas, you'll pass in the GPG public key from each persona to initialize Vault with the API, CLI, or UI. Once Vault initializes, it returns the key shares, each one encrypted with the GPG public keys which you passed in at initialization time.
The 5 key shares are distributed to Alice, Bob, Carol, Dan, and Frank, which for the purpose of this tutorial just means that the encrypted key share value gets assigned to an environment variable after initialization.
You'll then act as 3 of the 5 personas (Alice, Carol, and Dan), and use their encrypted key shares to unseal Vault.
After you unseal Vault, you can then act as the 3 personas, and use their encrypted key shares to rekey Vault.
If you are unfamiliar with initialization, rekeying, seals and keys, you can review the operator init, operator rekey, operator unseal, and Seal/Unseal concepts documentation to learn more.
Personas
The steps described in this tutorial are typically performed by a team of security engineers.
You'll assume 4 of these hypothetical named security engineer roles in the hands-on lab scenario:
Alice: Team lead; coordinates distribution of teams GPG public keys, starts the Vault initialization process, and takes part in unseal and rekeying operations.
Bob: Provides a GPG public key to Alice for use in initializing Vault, but does not take part in unseal or rekeying operations unless needed.
Carol: Provides a GPG public key to Alice for use in initializing Vault, and also takes part in unseal and rekeying operations.
Dan: Provides a GPG public key to Alice for use in initializing Vault, and also takes part in unseal and rekeying operations.
Frank: Provides a GPG public key to Alice for use in initializing Vault, but does not take part in unseal or rekeying operations unless needed.
Dave: Vault operator. Configure and start the Vault server.
Prerequisites
You need the following to perform the tasks described in this tutorial:
Docker Desktop installed (tutorial tested with Docker Desktop version 4.18.0 and Docker Engine version 20.10.24).
Working internet from the Docker host computer to download Docker images.
Familiarity with initializing, unsealing, and rekeying Vault is helpful, but not required to follow along with the hands-on lab. If you are unfamiliar with these concepts and their related tasks, reviewing the Seal/Unseal concepts documentation along with the operator init, operator unseal, and operator rekey can help you.
Tip
For a thorough understanding of the initialization, unseal, and rekey operations, you can also review the /sys/init, /sys/unseal, and /sys/rekey API documentation.
Lab setup
The lab for this scenario is a single Vault server Docker container with integrated storage.
You'll also deploy some community image based GPG Docker containers on which you generate 5 sets of GPG keys. You can then use these keys to encrypt and decrypt the Vault key shares for all operations in the hands-on lab.
Tip
The hands-on lab uses GPG Docker containers for ease of use, and to avoid conflicts with any GPG software already installed on your computer.
Create hands-on lab home
You can create a temporary directory to hold all the content needed for this hands-on lab, and then assign its path to an environment variable for later reference.
Open a terminal, and create the directory /tmp/learn-vault-pgp
.
Export the hands-on directory path as the value to the HC_LEARN_LAB
environment variable.
Vault server setup
The goal of this section is to deploy a simple Vault server container that you'll use for the hands-on lab.
Create directories for Vault configuration and data.
Pull the latest Vault Docker image.
Example abbreviated output:
Create a Docker network named
learn-vault
.Create the Vault server configuration file.
Note
The listener stanza disables TLS (
tls_disable = 1
) just for this tutorial. Vault should always be used with TLS in production deployments. This configuration requires a certificate file and key file on each Vault host.Start Vault server container.
Confirm that the Vault server container is up.
Example expected output:
Export an environment variable for the
vault
CLI to address the primary server.Verify Vault server status
Example expected output:
The Vault server is now ready for you to initialize and unseal with unseal keys.
Prepare GPG key sets
Your goal in this section is to create 5 sets of simple GPG keys, which you use later for encrypting and decrypting Vault key shares. An example CLI workflow diagram is shown for the Alice persona, and the workflow is similar for each persona.
You can use a community GnuPG Docker container to generate keys for demonstrating the features in this tutorial.
Note
The example GPG configuration and runtime environment is just for this tutorial. You are encouraged to use a configuration and runtime environment that is compliant with your organization's security policies for actual use cases.
You do these tasks as each of the 5 main personas.
You'll non-interactively generate the 5 GPG keys with a configuration like this example for Alice:
Each set of keys has these properties as defined in the configuration:
- RSA key and subkey
- 4096 bit key size for key and subkey
- Persona name
- Persona email
- Expires in 1 day
- Passphrase in configuration for tutorial simplicity
Create directories for each of the personas.
Change modes on the persona directories. This increases security for access to the directories. If you omit this step, GPG warns you that the directory permissions are insecure whenever you execute it.
Pull the community GnuPG Docker container image.
Example abbreviated output:
You're now prepared to use GPG containers to generate each persona's keys.
Note
When exporting the public key values, you do not need the common ASCII armored format (i.e., --armor
) parameter. This is because Vault expects a plain base64-encoded version of the binary key output without any extra characters like those present in ASCII armored public key output. Instead, pipe the command output to base64
for encoding prior to writing the file.
Alice's GPG key
Complete these steps as the persona Alice.
Define a shell alias
gpg
that interacts with the Docker GPG container for the Alice persona. The container is named, uses a volume mapping between the host and container for all GPG configuration and key material. The GPG console is specified with an environment variable, and the container removes itself after each execution.Create the GPG configuration for Alice.
Generate the key for Alice.
Example expected output:
Export the public key for Alice into the file
$HC_LEARN_LAB/alice/alice_key.pub
.This command is expected to produce no output.
Bob's GPG key
Complete these steps as the persona Bob.
Update the
gpg
alias so it interacts with the Docker GPG container for the Bob persona.Create the GPG configuration for bob.
Generate the key for bob.
Example expected output:
Export the public key for bob into the file
$HC_LEARN_LAB/bob/bob_key.pub
.This command is expected to produce no output.
Carol's GPG key
Complete these steps as the persona Carol.
Update the
gpg
alias so it interacts with the Docker GPG container for the Carol persona.Create the GPG configuration for Carol.
Generate the key for Carol.
Example expected output:
Export the public key for Carol into the file
$HC_LEARN_LAB/carol/carol_key.pub
.This command is expected to produce no output.
Dan' GPG key
Complete these steps as the persona Dan.
Update the
gpg
alias so it interacts with the Docker GPG container for the Dan persona.Create the GPG configuration for Dan.
Generate the key for Dan.
Example expected output:
Export the public key for Dan into the file
$HC_LEARN_LAB/dan/dan_key.pub
.This command is expected to produce no output.
Frank's GPG key
Complete these steps as the persona Frank.
Update the
gpg
alias so it interacts with the Docker GPG container for the Frank persona.Create the GPG configuration for Frank.
Generate the key for Frank.
Example expected output:
Export the public key for Frank into the file
$HC_LEARN_LAB/frank/frank_key.pub
.This command is expected to produce no output.
Some of the steps later require base64-encoded versions of the persona's GPG public keys. Before proceeding, create a base64-encoded version of each persona's GPG public key.
You can complete this step as the Alice persona.
Now that you've generated all of the GPG keys, you can use them to initialize, unseal, and rekey Vault.
Initialize Vault with public keys
Initialize Vault with the 5 GPG public keys specified to encrypt the key shares, and Carol's GPG public key to encrypt the initial root token value. Write the initialization output to the file "$HC_LEARN_LAB"/vault_init_output.txt
.
This command is expected to produce no output, but you can list the file contents if you want to verify.
Example expected output:
The initialization output shows base64-encoded and encrypted unseal key and initial root token values.
Unseal Vault with encrypted key shares
You initialized Vault, and it output the GPG encrypted unseal key shares and initial root token value.
Now you can use the encrypted key shares for Alice, Carol, and Dan to unseal Vault.
Your workflow for each encrypted unseal key is to use Base64 for decoding the Vault output value, then GPG to decrypt that decoded value. The result is an unseal key share that you can pass to Vault for rekeying or unsealing.
Tip
For the examples in this tutorial, the following passphrases are used. You need these passphrases to decrypt each of the key shares used in the following examples.
- Alice:
recede-yard-unwilling-shrouded
- Bob:
giggle-suing-starring-sugar
- Carol:
unnerving-appealing-primarily-overload
- Dan:
shawl-stem-elective-stoop
- Frank:
capitol-wasting-darwinism-surcharge
Vault returned Alice's encrypted key share as the value of
Unseal Key 1:
when you initialized it. To make the unseal key data available for decryption, you can base64 decode it and write it to the file$HC_LEARN_LAB/alice/alice_unseal_key.dat
, which gets mapped to/root/.gnupg/alice_unseal_key.dat
in the GPG Docker container.
Carol's encrypted key share is the value of
Unseal Key 3:
. Base64 decode it, and write it to the file$HC_LEARN_LAB/carol/carol_unseal_key.dat
, which gets mapped to/root/.gnupg/carol_unseal_key.dat
in the GPG Docker container.Dan's encrypted key share is the value of
Unseal Key 4:
. Base64 decode it, and write it to the file$HC_LEARN_LAB/dan/dan_unseal_key.dat
, which gets mapped to/root/.gnupg/dan_unseal_key.dat
in the GPG Docker container.Define a shell alias
gpg
that interacts with the Docker GPG container for the Alice persona. For the purpose of this tutorial, you can pass in Alice's GPG key passphrase with the--passphrase
flag.As the Alice persona, begin the unseal operation by decrypting the encrypted unseal key with GPG, and then passing it to Vault.
Example expected output:
Update the
gpg
alias so that it interacts with the Docker GPG container for the Carol persona. For the purpose of this tutorial, you can pass in Carol's GPG key passphrase with the--passphrase
flag.As the Carol persona, continue the unseal workflow by decrypting the encrypted unseal key with GPG, and then passing it to Vault.
Example expected output:
Update the
gpg
alias so that it interacts with the Docker GPG container for the Dan persona. For the purpose of this tutorial, you can pass in Dan's GPG key passphrase with the--passphrase
flag.As the Dan persona, continue the unseal workflow by decrypting the encrypted unseal key with GPG, and then passing it to Vault.
Example expected output:
You've unsealed the Vault server with GPG encrypted unseal key shares. You also decrypted the encrypted initial root token value and used it to sin into the Vault UI.
In the next section, you'll learn how to rekey Vault with encrypted key shares.
Rekey Vault with encrypted key shares
Now that you've experienced unsealing Vault with encrypted key shares, you're ready to try rekeying Vault with those same encrypted key shares.
Rekeying Vault requires a token with capabilities to POST against the /sys/rekey/
API endpoints. In this tutorial, you'll use the initial root token value for this token.
Perform these steps as the Alice persona.
Update the
gpg
alias so that it interacts with the Docker GPG container for the Alice persona.Base64 decode the initial root token value, and write it to the file
$HC_LEARN_LAB/alice/initial_root_token.dat
, which gets mapped to/root/.gnupg/initial_root_token.dat
in the GPG Docker container.Decrypt the encrypted initial root token value and export the result as the
VAULT_TOKEN
environment variable.Expected example output:
Start the rekey workflow. Use the
-backup
option to save the encrypted keys in Vault's core so if they are lost, you can still recover.Example expected output:
Store the returned nonce value in an environment variable.
Example:
Where
216100bc-fa97-6ce6-dacd-e774a2635175
is the nonce value returned to you.Each of the 3 personas holding a quorum of key shares must now submit their decrypted key share. As the Alice persona, execute the rekey command to submit Alice's unseal key share.
Example expected output:
The Rekey Progress now shows 1/3.
Perform these steps as the Carol persona.
Update the
gpg
alias so that it interacts with the Docker GPG container for the Carol persona.Execute the rekey command to submit Alice's unseal key share.
Example expected output:
The Rekey Progress now shows 2/3.
Perform these steps as the Dan persona.
Update the
gpg
alias so that it interacts with the Docker GPG container for the Dan persona.Execute the rekey command to submit Alice's unseal key share.
Example expected output:
You have rekeyed Vault with encrypted key shares using the CLI. Vault returns the new base64-encoded and encrypted key shares. It also makes a backup of the keys, and stores them in Vault storage.
Next steps
You can learn more about Vault seals in the Vault Architecture and Seal/Unseal documentation.
If you'd like to learn about automatically unsealing Vault with cloud based seals, a hardware security module (HSM), or the Vault Transit secrets engine, review the Vault seal configuration documentation.
Cleanup
Stop the Vault Docker container (Docker removes it automatically).
Remove the Docker network.
Remove the hands-on lab directory.
Unset the
gpg
alias.Unset the environment variables.