b76a56d40c
* migrates nav data format and updates docs pages * removes sidebar_title from content files
616 lines
18 KiB
Plaintext
616 lines
18 KiB
Plaintext
---
|
|
layout: guides
|
|
page_title: Encryption as a Service - Guides
|
|
description: >-
|
|
HashiCorp Vault's transit secrets engine handles cryptographic functions on
|
|
data in-transit. It can also viewed as _encryption as a service_.
|
|
---
|
|
|
|
# Encryption as a Service: Transit Secrets Engine
|
|
|
|
Vault's `transit` secrets engine handles cryptographic functions on
|
|
data-in-transit. Vault doesn't store the data sent to the secrets engine, so it
|
|
can also be viewed as **_encryption as a service_**.
|
|
|
|
Although the `transit` secrets engine provides additional features (sign and
|
|
verify data, generate hashes and HMACs of data, and act as a source of random
|
|
bytes), its primary use case is to encrypt data. This relieves the burden of
|
|
proper encryption/decryption from application developers and pushes the burden
|
|
onto the operators of Vault.
|
|
|
|
## Reference Materials
|
|
|
|
- [Transit Secret Engine](/docs/secrets/transit)
|
|
- [Transit Secret Engine API](/api/secret/transit)
|
|
- [Transparent Data Encryption in the Modern Datacenter](https://www.hashicorp.com/blog/transparent-data-encryption-in-the-modern-datacenter)
|
|
|
|
~> **NOTE:** An [interactive
|
|
tutorial](https://www.katacoda.com/hashicorp/scenarios/vault-transit) is
|
|
also available if you do not have a Vault environment to perform the steps
|
|
described in this guide.
|
|
|
|
## Estimated Time to Complete
|
|
|
|
10 minutes
|
|
|
|
## Personas
|
|
|
|
The end-to-end scenario described in this guide involves two personas:
|
|
|
|
- **operator** with privileged permissions to manage the encryption keys
|
|
- **app** with un-privileged permissions encrypt/decrypt secrets via API
|
|
|
|
## Challenge
|
|
|
|
Think of the following scenario:
|
|
|
|
_Example Inc._ recently made headlines for a massive data breach which exposed
|
|
millions of their users' payment card accounts online. When they tracked down the
|
|
problem they found that a new HVAC system with management software had been put
|
|
into their data centers and had created vulnerabilities in their networks and
|
|
exposed ports and IPs to the databases publicly.
|
|
|
|
## Solutions
|
|
|
|
The `transit` secrets engine enables security teams to fortify data during
|
|
transit and at rest. So even if an intrusion occurs, your data is encrypted with
|
|
AES 256-bit CBC encryption (TLS in transit). Even if an attacker were able to
|
|
access the raw data, they would only have encrypted bits. This means attackers
|
|
would need to compromise multiple systems before exfiltrating data.
|
|
|
|
![Encryption as a Service](/img/vault-encryption.png)
|
|
|
|
This guide demonstrates the basics of the `transit` secrets engine.
|
|
|
|
## Prerequisites
|
|
|
|
To perform the tasks described in this guide, you need to have a Vault
|
|
environment. Refer to the [Getting
|
|
Started](/intro/getting-started/install) guide to install Vault. Make sure
|
|
that your Vault server has been [initialized and
|
|
unsealed](/intro/getting-started/deploy).
|
|
|
|
### Policy requirements
|
|
|
|
-> **NOTE:** For the purpose of this guide, you can use **`root`** token to work
|
|
with Vault. However, it is recommended that root tokens are only used for just
|
|
enough initial setup or in emergencies. As a best practice, use tokens with
|
|
appropriate set of policies based on your role in the organization.
|
|
|
|
To perform all tasks demonstrated in this guide, your policy must include the
|
|
following permissions:
|
|
|
|
```shell
|
|
# Enable transit secrets engine
|
|
path "sys/mounts/transit" {
|
|
capabilities = [ "create", "read", "update", "delete", "list" ]
|
|
}
|
|
|
|
# To read enabled secrets engines
|
|
path "sys/mounts" {
|
|
capabilities = [ "read" ]
|
|
}
|
|
|
|
# Manage the transit secrets engine
|
|
path "transit/*" {
|
|
capabilities = [ "create", "read", "update", "delete", "list" ]
|
|
}
|
|
```
|
|
|
|
If you are not familiar with policies, complete the
|
|
[policies](/guides/identity/policies) guide.
|
|
|
|
## Steps
|
|
|
|
You will perform the following:
|
|
|
|
1. [Configure Transit Secret Engine](#step1)
|
|
1. [Encrypt Secrets](#step2)
|
|
1. [Decrypt a cipher-text](#step3)
|
|
1. [Rotate the Encryption Key](#step4)
|
|
1. [Update Key Configuration](#step5)
|
|
|
|
### Step 1: Configure Transit Secret Engine ((#step1))
|
|
|
|
(**Persona:** operator)
|
|
|
|
The `transit` secrets engine must be configured before it can perform its
|
|
operations. This step is usually done by an **operator** or configuration
|
|
management tool.
|
|
|
|
#### CLI command
|
|
|
|
Enable the `transit` secret engine by executing the following command:
|
|
|
|
```shell-session
|
|
$ vault secrets enable transit
|
|
```
|
|
|
|
> By default, the secrets engine will mount at the name of the engine. If you
|
|
> wish to enable it at a different path, use the `-path` argument.
|
|
|
|
> **Example:** `vault secrets enable -path=encryption transit`
|
|
|
|
Now, create an encryption key ring named, `orders` by executing the following
|
|
command:
|
|
|
|
```shell-session
|
|
$ vault write -f transit/keys/orders
|
|
```
|
|
|
|
#### API call using cURL
|
|
|
|
Enable `transit` secret engine using `/sys/mounts` endpoint:
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: <TOKEN>" \
|
|
--request POST \
|
|
--data <PARAMETERS> \
|
|
<VAULT_ADDRESS>/v1/sys/mounts/<PATH>
|
|
```
|
|
|
|
Where `<TOKEN>` is your valid token, and `<PARAMETERS>` holds [configuration
|
|
parameters](/api/system/mounts#enable-secrets-engine) of the secret engine.
|
|
|
|
**Example:**
|
|
|
|
The following example enables transit secret engine at `sys/mounts/transit`
|
|
path, and passed the secret engine type (`transit`) in the request payload.
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST \
|
|
--data '{"type":"transit"}' \
|
|
https://127.0.0.1:8200/v1/sys/mounts/transit
|
|
```
|
|
|
|
Now, create an encryption key ring named, `orders` using the `transit/keys`
|
|
endpoint:
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST \
|
|
https://127.0.0.1:8200/v1/transit/keys/orders
|
|
```
|
|
|
|
#### Web UI
|
|
|
|
Open a web browser and launch the Vault UI (e.g. http://127.0.0.1:8200/ui) and then login.
|
|
|
|
1. Select **Enable new engine** and select **Transit** from **Secrets engine type**
|
|
drop-down list.
|
|
![Enable new engine](/img/vault-secrets-enable.png)
|
|
|
|
1. Click **Enable Engine**.
|
|
|
|
1. Select **Create encryption key** and enter `orders` in the **Name** field.
|
|
![Create a key](/img/vault-transit-1.png)
|
|
|
|
1. Click **Create encryption key** to complete.
|
|
|
|
~> **NOTE:** Typically, you want to create an encryption key ring for each
|
|
application.
|
|
|
|
### Step 2: Encrypt Secrets ((#step2))
|
|
|
|
(**Persona:** operator)
|
|
|
|
Once the `transit` secrets engine has been configured, any client with a valid
|
|
token with proper permission can send data to encrypt.
|
|
|
|
Here, you are going to encrypt a plaintext, _"credit-card-number"_.
|
|
|
|
-> **NOTE:** You can pass non-text binary file such as a PDF or image.
|
|
When you encrypt a plaintext, it must be base64-encoded.
|
|
|
|
#### CLI command
|
|
|
|
To encrypt your secret, use the `transit/encrypt` endpoint:
|
|
|
|
```shell-session
|
|
$ vault write transit/encrypt/<key_ring_name>
|
|
```
|
|
|
|
Execute the following command to encrypt a plaintext:
|
|
|
|
```shell-session
|
|
$ vault write transit/encrypt/orders plaintext=$(base64 <<< "credit-card-number")
|
|
|
|
Key Value
|
|
--- -----
|
|
ciphertext vault:v1:cZNHVx+sxdMErXRSuDa1q/pz49fXTn1PScKfhf+PIZPvy8xKfkytpwKcbC0fF2U=
|
|
```
|
|
|
|
Vault does _NOT_ store any of this data. The output you received is the
|
|
ciphertext. You can store this ciphertext at the desired location (e.g. MySQL
|
|
database) or pass it to another application.
|
|
|
|
#### API call using cURL
|
|
|
|
To encrypt your secret, use the [`transit/encrypt`
|
|
endpoint](/api/secret/transit#encrypt-data).
|
|
|
|
**Example:**
|
|
|
|
```shell
|
|
# Generate base64-encoded plaintext
|
|
$ base64 <<< "credit-card-number"
|
|
Y3JlZGl0LWNhcmQtbnVtYmVyCg==
|
|
|
|
# Pass the base64-encoded plaintext in the request payload
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST \
|
|
--data '{"plaintext": "Y3JlZGl0LWNhcmQtbnVtYmVyCg=="}' \
|
|
https://127.0.0.1:8200/v1/transit/encrypt/orders | jq
|
|
{
|
|
"request_id": "f483d9b6-8132-782e-1665-ad432c2461ab",
|
|
"lease_id": "",
|
|
"renewable": false,
|
|
"lease_duration": 0,
|
|
"data": {
|
|
"ciphertext": "vault:v1:/9hdQutaWpZR72s3+VSCLK1JNhV1wKM49hYVjh7RjmuxIy/OvshtgV4L4uVB+aQ="
|
|
},
|
|
...
|
|
}
|
|
```
|
|
|
|
Vault does _NOT_ store any of this data. The output you received is the
|
|
ciphertext. You can store this ciphertext at the desired location (e.g. MySQL
|
|
database) or pass it to another application.
|
|
|
|
#### Web UI
|
|
|
|
1. Select the **orders** encryption key.
|
|
|
|
1. Select **Key actions**.
|
|
![Key action](/img/vault-transit-2.png)
|
|
|
|
1. Make sure that **Encrypt** is selected under **TRANSIT ACTIONS**, and then
|
|
enter "credit-card-number" in the **Plaintext** field.
|
|
![Encrypt plaintext](/img/vault-transit-3.png)
|
|
|
|
1. Click **Encode to base64** to encode the plaintext.
|
|
|
|
1. Click **Encrypt**.
|
|
Vault does _NOT_ store any of this data. The output you received is the
|
|
ciphertext. You can click **Copy** to copy the resulting ciphertext and store it
|
|
at the desired location (e.g. MySQL database) or pass it to another application.
|
|
![Encrypt plaintext](/img/vault-transit-4.png)
|
|
|
|
### Step 3: Decrypt a cipher-text ((#step3))
|
|
|
|
(**Persona:** operator)
|
|
|
|
Any client with a valid token with proper permission can decrypt the ciphertext
|
|
generated by Vault. To decrypt the ciphertext, invoke the `transit/decrypt`
|
|
endpoint.
|
|
|
|
#### CLI command
|
|
|
|
Execute the following command to decrypt the ciphertext resulted in [Step
|
|
2](#step2):
|
|
|
|
```shell-session
|
|
$ vault write transit/decrypt/orders \
|
|
ciphertext="vault:v1:cZNHVx+sxdMErXRSuDa1q/pz49fXTn1PScKfhf+PIZPvy8xKfkytpwKcbC0fF2U=" \
|
|
|
|
Key Value
|
|
--- -----
|
|
plaintext Y3JlZGl0LWNhcmQtbnVtYmVyCg==
|
|
```
|
|
|
|
The resulting data is base64-encoded. To reveal the original plaintext, run the
|
|
following command:
|
|
|
|
```shell-session
|
|
$ base64 --decode <<< "Y3JlZGl0LWNhcmQtbnVtYmVyCg=="
|
|
credit-card-number
|
|
```
|
|
|
|
#### API call using cURL
|
|
|
|
Use the `transit/decrypt` endpoint to decrypt the ciphertext resulted in [Step
|
|
2](#step2):
|
|
|
|
**Example:**
|
|
|
|
```shell
|
|
# Pass the ciphertext in the request payload to decode
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST \
|
|
--data '{"ciphertext": "vault:v1:/9hdQutaWpZR72s3+VSCLK1JNhV1wKM49hYVjh7RjmuxIy/OvshtgV4L4uVB+aQ="}' \
|
|
https://127.0.0.1:8200/v1/transit/decrypt/orders | jq
|
|
{
|
|
"request_id": "062d7998-8932-76f2-f96c-5938a55ff005",
|
|
"lease_id": "",
|
|
"renewable": false,
|
|
"lease_duration": 0,
|
|
"data": {
|
|
"plaintext": "Y3JlZGl0LWNhcmQtbnVtYmVyCg=="
|
|
},
|
|
...
|
|
}
|
|
|
|
# The resulting data is base64-encoded that it must be decoded to reveal the plaintext
|
|
$ base64 --decode <<< "Y3JlZGl0LWNhcmQtbnVtYmVyCg=="
|
|
credit-card-number
|
|
```
|
|
|
|
#### Web UI
|
|
|
|
1. Select the **orders** encryption key.
|
|
|
|
1. Select **Key actions**.
|
|
|
|
1. Make sure that **Decrypt** is selected under **TRANSIT ACTIONS**, and then
|
|
enter the ciphertext you wish to decrypt.
|
|
![Decrypt ciphertext](/img/vault-transit-5.png)
|
|
|
|
1. Click **Decrypt**.
|
|
|
|
1. The resulting data is base64-encoded. Click **Decode from base64** to reveal
|
|
the plaintext.
|
|
|
|
### Step 4: Rotate the Encryption Key ((#step4))
|
|
|
|
(**Persona:** operator)
|
|
|
|
One of the benefits of using the Vault `transit` secrets engine is its ability
|
|
to easily 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.
|
|
|
|
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.
|
|
|
|
#### CLI command
|
|
|
|
To rotate the encryption key, invoke the `transit/keys/<key_ring_name>/rotate`
|
|
endpoint.
|
|
|
|
```shell-session
|
|
$ vault write -f transit/keys/orders/rotate
|
|
```
|
|
|
|
Let's encrypt another data:
|
|
|
|
```shell-session
|
|
$ vault write transit/encrypt/orders plaintext=$(base64 <<< "visa-card-number")
|
|
Key Value
|
|
--- -----
|
|
ciphertext vault:v2:45f9zW6cglbrzCjI0yCyC6DBYtSBSxnMgUn9B5aHcGEit71xefPEmmjMbrk3
|
|
```
|
|
|
|
Compare the ciphertexts from [Step 2](#step2).
|
|
|
|
```
|
|
ciphertext vault:v1:cZNHVx+sxdMErXRSuDa1q/pz49fXTn1PScKfhf+PIZPvy8xKfkytpwKcbC0fF2U=
|
|
```
|
|
|
|
Notice that the first ciphertext starts with "**`vault:v1:`**". After rotating
|
|
the encryption key, the ciphertext starts with "**`vault:v2:`**". This indicates
|
|
that the data gets encrypted using the latest version of the key after the
|
|
rotation.
|
|
|
|
Execute the following command to rewrap your ciphertext from [Step 2](#step2)
|
|
with the latest version of the encryption key:
|
|
|
|
```shell-session
|
|
$ vault write transit/rewrap/orders \
|
|
ciphertext="vault:v1:cZNHVx+sxdMErXRSuDa1q/pz49fXTn1PScKfhf+PIZPvy8xKfkytpwKcbC0fF2U="
|
|
Key Value
|
|
--- -----
|
|
ciphertext vault:v2:kChHZ9w4ILRfw+DzO53IZ8m5PyB2yp2/tKbub34uB+iDqtDRB+NLCPrpzTtJHJ4=
|
|
```
|
|
|
|
Notice that the resulting ciphertext now starts with "`vault:v2:`".
|
|
|
|
This operation does not reveal the plaintext data. But Vault will decrypt the
|
|
value using the appropriate key in the keyring and then encrypted the resulting
|
|
plaintext with the newest key in the keyring.
|
|
|
|
#### API call using cURL
|
|
|
|
To rotate the encryption key, invoke the `transit/keys/<key_ring_name>/rotate`
|
|
endpoint.
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST
|
|
https://127.0.0.1:8200/v1/transit/keys/orders/rotate
|
|
```
|
|
|
|
Let's encrypt another data:
|
|
|
|
```shell
|
|
# Generate base64-encoded plaintext
|
|
$ base64 <<< "visa-card-number"
|
|
dmlzYS1jYXJkLW51bWJlcgo=
|
|
|
|
# Pass the base64-encoded plaintext in the request payload
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST \
|
|
--data '{"plaintext": "dmlzYS1jYXJkLW51bWJlcgo="}' \
|
|
https://127.0.0.1:8200/v1/transit/encrypt/orders | jq
|
|
{
|
|
...
|
|
"data": {
|
|
"ciphertext": "vault:v2:et873RqkfLlS268LqYspVUnqhqZm0flNwhthe4ZzfcuZQab1TnirQ8/hMNYA"
|
|
},
|
|
...
|
|
}
|
|
```
|
|
|
|
Compare the ciphertexts from [Step 2](#step2).
|
|
|
|
```
|
|
ciphertext vault:v1:cZNHVx+sxdMErXRSuDa1q/pz49fXTn1PScKfhf+PIZPvy8xKfkytpwKcbC0fF2U=
|
|
```
|
|
|
|
Notice that the first ciphertext starts with "**`vault:v1:`**". After rotating
|
|
the encryption key, the ciphertext starts with "**`vault:v2:`**". This indicates
|
|
that the data gets encrypted using the latest version of the key after the
|
|
rotation.
|
|
|
|
Execute the `transit/rewrap` endpoint to rewrap your ciphertext from [Step 2](#step2)
|
|
with the latest version of the encryption key:
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST \
|
|
--data '{"ciphertext": "vault:v1:/9hdQutaWpZR72s3+VSCLK1JNhV1wKM49hYVjh7RjmuxIy/OvshtgV4L4uVB+aQ="}' \
|
|
https://127.0.0.1:8200/v1/transit/rewrap/orders | jq
|
|
{
|
|
...
|
|
"data": {
|
|
"ciphertext": "vault:v2:ykqXDP65tLVSrqxoNZh51gIobYQSwNGT+SbiD/2nl8rrhF2md+wplBGdXlhDzd4="
|
|
},
|
|
...
|
|
```
|
|
|
|
Notice that the resulting ciphertext now starts with "`vault:v2:`".
|
|
|
|
This operation does not reveal the plaintext data. But Vault will decrypt the
|
|
value using the appropriate key in the keyring and then encrypted the resulting
|
|
plaintext with the newest key in the keyring.
|
|
|
|
## Step 5: Update Key Configuration ((#step5))
|
|
|
|
(**Persona:** operator)
|
|
|
|
The operators can [update the encryption key
|
|
configuration](/api/secret/transit#update-key-configuration) to
|
|
specify the minimum version of ciphertext allowed to be decrypted, the minimum
|
|
version of the key that can be used to encrypt the plaintext, the key is allowed
|
|
to be deleted, etc.
|
|
|
|
This helps further tightening the data encryption rule.
|
|
|
|
#### CLI Command
|
|
|
|
Execute the key rotation command a few times to generate multiple versions of
|
|
the key:
|
|
|
|
```shell-session
|
|
$ vault write -f transit/keys/orders/rotate
|
|
```
|
|
|
|
Now, read the `orders` key information:
|
|
|
|
```shell-session
|
|
$ vault read transit/keys/orders
|
|
|
|
Key Value
|
|
--- -----
|
|
...
|
|
keys map[6:1531439714 1:1531439594 2:1531439667 3:1531439714 4:1531439714 5:1531439714]
|
|
latest_version 6
|
|
min_decryption_version 1
|
|
min_encryption_version 0
|
|
...
|
|
```
|
|
|
|
In the example, the current version of the key is **6**. However, there is no
|
|
restriction about the minimum encryption key version, and any of the key
|
|
versions can decrypt the data (`min_decryption_version`).
|
|
|
|
Run the following command to enforce the use of the encryption key at version
|
|
**5** or later to decrypt the data.
|
|
|
|
```shell-session
|
|
$ vault write transit/keys/orders/config min_decryption_version=5
|
|
```
|
|
|
|
Now, verify the `orders` key configuration:
|
|
|
|
```shell-session
|
|
$ vault read transit/keys/orders
|
|
|
|
Key Value
|
|
--- -----
|
|
allow_plaintext_backup false
|
|
deletion_allowed false
|
|
derived false
|
|
exportable false
|
|
keys map[5:1531811719 6:1531811721]
|
|
latest_version 6
|
|
min_decryption_version 5
|
|
min_encryption_version 0
|
|
...
|
|
```
|
|
|
|
#### API call using cURL
|
|
|
|
Execute the `transit/keys/<key_ring_name>/rotate` endpoint a few times key
|
|
rotation command a few times to generate multiple versions of the key:
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST
|
|
https://127.0.0.1:8200/v1/transit/keys/orders/rotate
|
|
```
|
|
|
|
Read the `transit/keys/orders` endpoint to review the `orders` key
|
|
detail:
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
https://127.0.0.1:8200/v1/transit/keys/orders | jq
|
|
{
|
|
...
|
|
"keys": {
|
|
"1": 1531804669,
|
|
"2": 1531810236,
|
|
"3": 1531811712,
|
|
"4": 1531811715,
|
|
"5": 1531811719,
|
|
"6": 1531811721
|
|
},
|
|
"latest_version": 6,
|
|
"min_decryption_version": 1,
|
|
"min_encryption_version": 0,
|
|
...
|
|
```
|
|
|
|
In the example, the current version of the key is **6**. However, there is no
|
|
restriction about the minimum encryption key version, and any of the key
|
|
versions can decrypt the data (`min_decryption_version`).
|
|
|
|
Run the following command to enforce the use of the encryption key at version
|
|
**5** or later to decrypt the data.
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
--request POST
|
|
--data '{"min_decryption_version": 5}'
|
|
https://127.0.0.1:8200/v1/transit/keys/orders/config
|
|
```
|
|
|
|
Now, verify the `orders` key configuration:
|
|
|
|
```shell-session
|
|
$ curl --header "X-Vault-Token: ..." \
|
|
https://127.0.0.1:8200/v1/transit/keys/orders | jq
|
|
{
|
|
...
|
|
"keys": {
|
|
"5": 1531811719,
|
|
"6": 1531811721
|
|
},
|
|
"latest_version": 6,
|
|
"min_decryption_version": 5,
|
|
"min_encryption_version": 0,
|
|
...
|
|
```
|
|
|
|
-> **NOTE:** Notice that the output only displays two valid encryption key
|
|
versions (`5` and `6`).
|
|
|
|
## Next steps
|
|
|
|
[Transit Secrets Re-wrapping](/guides/encryption/transit-rewrap) guide
|
|
introduces a sample application which re-wraps data after rotating an encryption
|
|
key in the transit engine in Vault.
|