- Part 1 (deploying & initializing Vault)
- Part 3 (auth methods, policies, and tokens)
- Part 4 (Vault agent & auto-auth)
- Part 5 (dynamic secrets)
- Part 6 (Vault HA cluster, backup & recovery)
Transit secrets engine
The transit secrets engine is your “encryption as a service”, so let us enable it first:
vault secrets enable -path tokenizer transit
The -path
is optional, but is a good way to keep your secrets engines organized. If you do not provide a path, the default is to use the same name as the secrets engine.
I will then create my encryption key:
vault write -f tokenizer/keys/mykey
Before data can be encrypted, it needs to first be base64 encoded and passed in as “plaintext”:
vault write tokenizer/encrypt/mykey plaintext=$(base64 <<< 'encryptme!!')
The output look similar to the following:
Key Value
--- -----
ciphertext vault:v1:QDbZXSsPb/Iie6Y4snrw2tGE+gyAXax3lMLkyFZuT3jkxAjy90yQjw==
key_version 1
Decryption work similarly to encryption, except you call the /decrypt
endpoint and pass in the ciphertext instead:
vault write tokenizer/decrypt/mykey ciphertext='vault:v1:QDbZXSsPb/Iie6Y4snrw2tGE+gyAXax3lMLkyFZuT3jkxAjy90yQjw=='
The plaintext returned to you will have to be base64 decoded to get your original data back.
**IMPORTANT**: it is important to note that Vault does NOT store your encrypted ciphertexts. It is your responsibility to provide store for them so that you may retrieve and decrypt at a later time.
Rotate
You can rotate encryption keys by simply calling the /rotate
endpoint for the key:
vault write -f tokenizer/keys/mykey/rotate
If you were to encrypt the same data again, you will get back a ciphertext which will have a prefix of vault:v2
now (instead of vault:v1
). This identifies that the message was encrypted by Vault and more importantly, the key version that was used.
Rewrap
Instead of re-encrypting the original data with the new, rotated key, you can just rewrap your previous version’s ciphertext with the new key instead:
vault write tokenizer/rewrap/mykey ciphertext='vault:v1:QDbZXSsPb/Iie6Y4snrw2tGE+gyAXax3lMLkyFZuT3jkxAjy90yQjw=='
NOTE: rewrapping a previous version’s ciphertext may not yield the same ciphertext as encrypting the orignal data with the new key. However, they both will decrypt to the same plaintext.
Invalidate
When you rotate a key in the transit secrets engine, you are not truly rotating it in the way you might expect. The older versions of the keys still exists and are actually still active until you tell it otherwise! If you think about it, it makes sense. What would happen to your existing secrets wrapped with the old key if it becomes invalidated when you rotate keys?
Once you have rewrapped your old ciphertexts and are ready to remove your old keys, you can update your key’s config to specify the min_available_version
, min_decryption_version
, and min_encryption_version
accordingly. I will not be showing you an example of this here, but instead posing it to you as a challenge (see challenge #2 below) to search the documentation and figure it out on your own.
Auto-unseal
Auto-unsealer Vault instance
I will be making the Vault server created in part 1 of this guide the auto-unsealer. To do this we will enable a transit secrets engine and create a key for auto-unseal purposes:
vault secrets enable transit
vault write -f transit/keys/autounseal
I will then create a Vault policy that gives the permissions to encrypt and decrypt with said key by applying the policy defined below:
vault policy write autounseal ./autounseal-policy.hcl
TIP: it is a good idea to name your policy after the policy definition file
Finally, we will create ourselves a token that has the permissions to use this key:
vault token create -orphan -policy="autounseal"
Do not worry if you do not fully understand this part regarding policies and tokens. I will dive deeper into this topic in part 3 of my guide. For now, keep the token this command generated handy as you will need it for the next step…
Vault server instance
Spin yourself up a new Vault instance because *this* is going to be the actual Vault server you will be interfacing with for the rest of the guide. However, do NOT start or initialize this server just yet as we still need to modify the configuration file so that it knows what server (or service) to look towards for auto-unsealing. My config is below:
In the seal
stanza, we defined that it will be using the transit secrets engine and the address of that Vault server instance. The path to the encryption key and the token which we generated with the appropriate permissions is also provided here.
NOTE: my auto-unsealer Vault instance is accessed with an HTTPS endpoint because it was enabled in part 1’s challenge
You can now start and initialize Vault:
export VAULT_ADDR='http://127.0.0.1:8200'
vault operator init -recovery-shares=1 -recovery-threshold=1
Which produces the following output:
Recovery Key 1: KRIBbiPw86+8POMu+WklaZHowFrwMkMUXk5eM2CKrW0=
Initial Root Token: hvs.maMFrnbioy3ywtGrUeJw6K85
Success! Vault is initialized
Did you notice that I specified -recovery-shares
and -recovery-threshold
instead of -key-shares
and -key-threshold
, respectively? Also, initialization returns a Recovery Key instead of an Unseal Key. I am not going to tell you the difference between them. That is for you to read up on.
CHALLENGE #1: create a 4096-bit RSA key called “myrsa” under the tokenizer/
path
CHALLENGE #2: rotate key, “mykey” once more but keep only the last 2 versions of the keys active and ensure key v1 is deleted
CHALLENGE #3: using v2 of “mykey”, encrypt a message, rewrap it with v3 (newest) and decrypt it again
CHALLENGE #4: configure auto-unseal using a KMS from a cloud provider
>> SPOILER ALERT!!! SOLUTION GUIDE BELOW!!! <<
Challenge solutions guide
SOLUTION #1
vault write -f tokenizer/keys/myrsakey type=rsa-4096
The default key type is AES256-GCM96, but many types are supported, including the popular RSA, ECDSA and HMAC.
Different key types also have different features. For example: AES-GCM keys do not support signing, but RSA keys do (but in turn, they do not support key derivation).
SOLUTION #2
If you have been following along with the example I provided in the guide, you should have 2 versions of mykey (you can confirm this with vault read tokenizer/keys/mykey
). To rotate it once more, simply run vault write -f tokenizer/keys/mykey/rotate
(your latest key version should be v3). To keep the last 2 versions the key active, write to the key’s config and set the min_decryption_version
and min_encryption_version
to 2 thus allowing v2 and v3 to be used:
vault write -f tokenizer/keys/mykey/config \
min_decryption_version=2 \
min_encryption_version=2
If you tried to decrypt any ciphertexts that uses the v1 key now, you will get an error telling you that this is “disallowed by policy (too old)”.
The next ask is for the the v1 key to be deleted. For this, you need to set the min_available_version
. Any key lower than what is specified for this field gets deleted. As long as a key is available, you can always update the min_decryption_version
and min_encryption_version
and still be able to use the key, but once it is deleted, it is gone.
SOLUTION #3
As long as it is available and allowed, you can still specify a previous version of a key to be used for encrypting messages (you do not need to specify for decrypting because this information is already embedded in the prefx of your ciphertext):
vault write tokenizer/encrypt/mykey key_version=2 plaintext=$(base64 <<< 'my secret msg')
vault write tokenizer/rewrap/mykey ciphertext='[CIPHERTEXT_v2]'
vault write tokenizer/decrypt/mykey ciphertext='[CIPHERTEXT_V3]'
SOLUTION #4
I will not be posting a solution for this challenge per se. I have multiple GitHub repos that deploy Vault on the various cloud provider’s serverless container service and they all use the provider’s native KMS for auto-unseal, so you can check those out and adapt it for the VM solution. You just have to make sure the VM’s default service account has the correct IAM permissions and scopes.
- Vault on Cloud Run (GCP)
- Vault on Fargate (AWS)
- Vault on Azure Container Instances
- Vault on Azure Container Apps
NOTE: you will see that I use a fairly blank Vault config in these repos as many of the settings are passed at run time via environment variables. This is actually the recommended way to run Vault.