Vault can be used to store any secret in a secure manner. The secrets may be SSL certificates and keys for your organization's domain, credentials to connect to a corporate database server, etc. Storing such sensitive information in plaintext is not desirable.
NOTE: This guide demonstrates the use of Key/Value Secrets Engine Version 1. To learn about Key/Value Secrets Engine Version 2, refer to the Versioned Key/Value Secrets Engine guide.
Personas
The end-to-end scenario described in this guide involves two personas:
Challenge
Consider the following situations:
- Developers use a single admin account to access a third-party app (e.g. Splunk) and anyone who knows the user ID and password can log in as an admin
- SSH keys to connect to remote machines are shared and stored as a plaintext
- API keys to invoke external system APIs are stored as a plaintext
- An app integrates with LDAP, and its configuration information is in a plaintext
Organizations often seek an uniform workflow to securely store this sensitive information.
Solution
Use Vault as centralized secret storage to secure any sensitive information. Vault encrypts these secrets using 256-bit AES in GCM mode with a randomly generated nonce prior to writing them to its persistent storage. The storage backend never sees the unencrypted value, so even if an attacker gained access to the raw storage, they wouldn't be able to read your secrets.
Prerequisites
To perform the tasks described in this guide, you need to have a Vault environment. Refer to the Getting Started guide to install Vault. Make sure that your Vault server has been initialized and unsealed.
NOTE: An interactive tutorial is also available if you do not have a Vault environment to perform the steps described in this guide. Click the Show Tutorial button to launch the tutorial.
Policy requirements
NOTE: For the purpose of this guide, you can use the 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:
# Enable key/value secrets engine at the kv-v1 path
path "sys/mounts/kv-v1" {
capabilities = [ "update" ]
}
# To list the available secrets engines
path "sys/mounts" {
capabilities = [ "read" ]
}
# Write and manage secrets in key/value secrets engine
path "kv-v1/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# Create policies to permit apps to read secrets
path "sys/policies/acl/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# Create tokens for verification & test
path "auth/token/create" {
capabilities = [ "create", "update", "sudo" ]
}
If you are not familiar with policies, complete the policies guide.
Scenario Introduction
This guide demonstrates the basic steps to store secrets using Vault. The scenario here is to store the following secrets:
- API key (Google API)
- Root certificate of a production database (MySQL)
To store your API key within the configured physical storage for Vault, use the key/value secrets engine.
The Key/Value secrets engine passes any operation through to the configured storage backend for Vault. For example, if your Vault server is configured with Consul as its storage backend, a "read" operation turns into a read from Consul at the same path.
You will perform the following:
- Enable KV Secrets Engine
- Store the Google API key
- Store the root certificate for MySQL
- Generate a token for apps
- Retrieve the secrets
Step 1: Enable KV Secrets Engine
Currently, when you start the Vault server in dev
mode, it
automatically enables v2
of the KV secrets engine at secret/
.
This guide focuses on key/value v1 secrets engine. The Versioned Key/Value Secret Engin guide highlights features that are specific to the key/value v2 secrets engine.
CLI command / API call using cURL / Web UI
CLI command
Execute the following command to list enabled secrets engines:
$ vault secrets list -detailed
Under the Options, the version number is indicated.
Path Type Accessor ... Options Description
---- ---- -------- ------- -----------
cubbyhole/ cubbyhole cubbyhole_9d52aeac ... map[] per-token private secret storage
identity/ identity identity_acea5ba9 ... map[] identity store
secret/ kv kv_2226b7d3 ... map[version:2] key/value secret storage
...
Execute the following command to enable the key/value secrets engine v1 at kv-v1/
:
$ vault secrets enable -path="kv-v1" kv
API call using cURL
To check the KV secrets engine version, use the sys/mounts
endpoint:
$ curl --header "X-Vault-Token: <TOKEN>" \
<VAULT_ADDRESS>/v1/sys/mounts
Where <TOKEN>
is your valid token, and <VAULT_ADDRESS>
is where your vault
server is running.
Example:
$ curl --header "X-Vault-Token: ..." \
http://127.0.0.1:8200/v1/sys/mounts | jq
...
"secret/": {
"accessor": "kv_f05b8b9c",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0,
"plugin_name": ""
},
"description": "key/value secret storage",
"local": false,
"options": {
"version": "2"
},
"seal_wrap": false,
"type": "kv"
},
...
NOTE: This example uses jq tool to format the JSON output for readability.
Execute the sys/mounts
endpoint to enable the key/value secrets engine v1 at
kv-v1/
:
$ curl --header "X-Vault-Token: ..." \
--request POST \
--data '{ "type": "kv" }' \
https://127.0.0.1:8200/v1/sys/mounts/kv-v1
Web UI
Open a web browser and launch the Vault UI (e.g.
http://127.0.0.1:8200/ui
) and then login.If the key/value v2 is enabled at
secret
, the UI indicates withv2
tag.Select Enable new engine and click the KV radio button.
Click Next.
Enter
kv-v1
in the Path field, and select1
from the Version drop-down list.Click Enable Engine.
Step 2: Store the Google API key
Everything after the kv-v1
path is a key-value pair to write to the
secrets engine. You can specify multiple values. If the value has a space, you
need to surround it with quotes. Having keys with spaces is permitted, but
strongly discouraged because it can lead to unexpected client-side behavior.
Let's assume that the path convention in your organization is
kv-v1/<OWNER>/apikey/<APP>
for API keys. To store the Google API key used
by the engineering team, the path would be kv-v1/eng/apikey/Google
. If you
have an API key for New Relic owned by the DevOps team, the path would look like
kv-v1/devops/apikey/New_Relic
.
CLI command / API call using cURL / Web UI
CLI command
To set key/value secrets:
$ vault kv put kv-v1/<PATH> <KEY>=VALUE>
The <PATH>
can be anything you want it to be, and your organization should
decide on the naming convention that makes most sense.
Example:
$ vault kv put kv-v1/eng/apikey/Google key=AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI
Success! Data written to: kv-v1/eng/apikey/Google
The secret key is "key" and its value is "AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI" in this example.
Execute the following command to read back the secrets:
$ vault kv get kv-v1/eng/apikey/Google
=== Data ===
Key Value
--- -----
key AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI
API call using cURL
Use kv-v1/<PATH>
endpoint to set secrets:
curl --header "X-Vault-Token: <TOKEN>" \
--request POST \
--data @payload.json \
<VAULT_ADDRESS>/v1/kv-v1/<PATH>
Where <TOKEN>
is your valid token, and kv-v1/<PATH>
is the path to
your secrets. The payload.json
contains the parameters to invoke the endpoint.
Example:
# Create the API request payload
$ tee payload.json <<EOF
{
"key": "AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI"
}
EOF
$ curl --header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
http://127.0.0.1:8200/v1/kv-v1/eng/apikey/Google
The secret key is "key" and its value is "AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI" in this example.
Read back the secret.
$ curl --header "X-Vault-Token: ..." \
http://127.0.0.1:8200/v1/kv-v1/eng/apikey/Google | jq
{
"request_id": "e4091d6a-759b-e629-afc5-1d9d15f4fbae",
"lease_id": "",
"renewable": false,
"lease_duration": 2764800,
"data": {
"key": "AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
Web UI
In the Web UI, select
kv-v1
and then Create secret.Enter
eng/apikey/Google
in the Path for this secret field,key
in the key field, and some value in the key value field.Click Save.
Clicking on the sensitive information toggle let you show or hide the secret value.
Step 3: Store the root certificate for MySQL
For the purpose of this guide, generate a mock certificate using OpenSSL.
$ openssl req -x509 -sha256 -nodes -newkey rsa:2048 -keyout selfsigned.key -out cert.pem
The generated cert.pem
file will look something like this:
-----BEGIN CERTIFICATE-----
MIICyjCCAbICCQDrpZYh8et7yTANBgkqhkiG9w0BAQsFADAnMQswCQYDVQQGEwJV
UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMB4XDTE4MTExMjIwNDEwNVoXDTE4
MTIxMjIwNDEwNVowJzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQH
DAJTRjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnIdgpml8+xk+Oj
1RGMCyJ1P15RiM6rdtszT+DFBg893Lqsjoyd5YgwELLz0Ux8nviG4L5OXOujEpAP
2cQBxTSLQjBELBZY9q0Qky3+2ewqV6lSfcXrcf/JuDJGR5K8HSqwNG35R3WGnZ+O
JhY0Dmx06IAs/FF8gP88zTQ8M7zuaThkF8MaF4sWPf6+texQwjzk4rewknGBFzar
9wFxVwNCyDD6ewIYPtgDxdJ1bwBVoX3KKKXm8GStl/Zva0aEtbSq/161J4VbTro2
dxArMPKzxjD6NLyF59UNs7vbzyfiw/Wq7BJzU7Kued5KdGt0bEiyWZYO+EvvxGmE
1pHfqysCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAavj4CA+7XFVHbwYMbK3c9tN/
73hkMvkAZWix5bfmOo0cNRuCeJnRIX+o6DmusIc8eXJJJV/20+zoSvUwlsLDPXoN
+c41GfIiEUSaSdSBtETMy8oPga718nIwAvNgYiUHXnV3B0nLYBUpYSnsD00/6VXG
xZUIEVBd7Ib5aRwmK8U5drxoWaBoG5qdvH9iapwTrCcPsRjsLBq7Iza2oBORGlfF
CjqiW2+KJzwRiTQj70yceniGVHM+VSpFYCLJ0mXeyLfITy7joqxr4AGYz+EhpLuf
iDpYDNYlr0JDVQqogskWjrnWOh0YcIJKgVtiTh2HDM5TdQgeXg4wv5IqLok0Tw==
-----END CERTIFICATE-----
NOTE: If you don't have OpenSSL, copy the above certificate and
save it as cert.pem
.
CLI command / API call using cURL / Web UI
CLI command
The command is basically the same as the Google API key example. The path
convention for certificates is kv-v1/<ENVIRONMENT>/cert/<SYSTEM>
. To
store the root certificate for production MySQL, the path becomes
kv-v1/prod/cert/mysql
.
$ vault kv put kv-v1/prod/cert/mysql cert=@cert.pem
This example reads the root certificate from a PEM file from the disk, and store
it under the kv-v1/prod/cert/mysql
path.
NOTE: Any value that begins with @
indicates a file name. Data will be
loaded from this file.
API call using cURL
To perform the same task using the Vault API, pass the certificate in the request payload.
# Create the API request payload
$ tee payload-cert.json <<EOF
{
"cert": "-----BEGIN CERTIFICATE-----\nMIICyjCCAbICCQDrpZYh8et7yTANBgkqhkiG9w0BAQsFADAnMQswCQYDVQQGEwJV\nUzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMB4XDTE4MTExMjIwNDEwNVoXDTE4\nMTIxMjIwNDEwNVowJzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQH\nDAJTRjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnIdgpml8+xk+Oj\n1RGMCyJ1P15RiM6rdtszT+DFBg893Lqsjoyd5YgwELLz0Ux8nviG4L5OXOujEpAP\n2cQBxTSLQjBELBZY9q0Qky3+2ewqV6lSfcXrcf/JuDJGR5K8HSqwNG35R3WGnZ+O\nJhY0Dmx06IAs/FF8gP88zTQ8M7zuaThkF8MaF4sWPf6+texQwjzk4rewknGBFzar\n9wFxVwNCyDD6ewIYPtgDxdJ1bwBVoX3KKKXm8GStl/Zva0aEtbSq/161J4VbTro2\ndxArMPKzxjD6NLyF59UNs7vbzyfiw/Wq7BJzU7Kued5KdGt0bEiyWZYO+EvvxGmE\n1pHfqysCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAavj4CA+7XFVHbwYMbK3c9tN/\n73hkMvkAZWix5bfmOo0cNRuCeJnRIX+o6DmusIc8eXJJJV/20+zoSvUwlsLDPXoN\n+c41GfIiEUSaSdSBtETMy8oPga718nIwAvNgYiUHXnV3B0nLYBUpYSnsD00/6VXG\nxZUIEVBd7Ib5aRwmK8U5drxoWaBoG5qdvH9iapwTrCcPsRjsLBq7Iza2oBORGlfF\nCjqiW2+KJzwRiTQj70yceniGVHM+VSpFYCLJ0mXeyLfITy7joqxr4AGYz+EhpLuf\niDpYDNYlr0JDVQqogskWjrnWOh0YcIJKgVtiTh2HDM5TdQgeXg4wv5IqLok0Tw==\n-----END CERTIFICATE-----\n"
}
EOF
$ curl --header "X-Vault-Token: ..." \
--request POST \
--data @payload-cert.json \
http://127.0.0.1:8200/v1/kv-v1/prod/cert/mysql
Web UI
In the Web UI, select
kv-v1
under Secrets Engines list and then Create secret.Enter
prod/cert/mysql
in the Path for secret field, andcert
in the key name field. Paste in thecert.pem
contents.Click Save.
Step 4: Generate a token for apps
To read the secrets, apps
persona needs "read" ability on those secrets engine
paths. In this scenario, the apps
policy must include the following:
Example: apps-policy.hcl
# Read-only permit
path "kv-v1/eng/apikey/Google" {
capabilities = [ "read" ]
}
# Read-only permit
path "kv-v1/prod/cert/mysql" {
capabilities = [ "read" ]
}
CLI command / API call using cURL / Web UI
CLI command
First create apps
policy, and generate a token so that you can authenticate
as an apps
persona, and read secrets.
# Create "apps" policy
$ vault policy write apps apps-policy.hcl
Policy 'apps' written.
# Create a new token with app policy
$ vault token create -policy="apps"
Key Value
--- -----
token e4bdf7dc-cbbf-1bb1-c06c-6a4f9a826cf2
token_accessor 54700b7e--data828-a6c4-6141-96e71e002bd7
token_duration 768h0m0s
token_renewable true
token_policies [apps default]
Now apps
can use this token to read the secrets.
API call using cURL
First create an apps
policy, and generate a token so that you can authenticate
as an app
persona.
Example:
# Payload to pass in the API call
$ tee payload-policy.json <<EOF
{
"policy": "# Read-only permit\npath \"kv-v1/eng/apikey/Google\" {\n capabilities = [ \"read\" ]\n}\n\n# Read-only permit\npath \"kv-v1/prod/cert/mysql\" {\n capabilities = [ \"read\" ]\n}"
}
EOF
# Create "apps" policy
$ curl --header "X-Vault-Token: ..." \
--request PUT \
--data @payload-policy.json \
http://127.0.0.1:8200/v1/sys/policies/acl/apps
# Generate a new token with apps policy
$ curl --header "X-Vault-Token: ..." \
--request POST \
--data '{"policies": ["apps"]}' \
http://127.0.0.1:8200/v1/auth/token/create | jq
{
"request_id": "e1737bc8-7e51-3943-42a0-2dbd6cb40e3e",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": null,
"warnings": null,
"auth": {
"client_token": "1c97b03a-6098-31cf-9d8b-b404e52dcb4a",
"accessor": "b10a3eb7-15fe-1924-600e-403cfda34c28",
"policies": [
"apps",
"default"
],
"metadata": null,
"lease_duration": 2764800,
"renewable": true,
"entity_id": ""
}
}
Now apps
can use this token to read the secrets.
Web UI
Open a web browser and click the Policies tab, and then select Create ACL policy.
Toggle the Upload file sliding switch, and click Choose a file to select your
apps-policy.hcl
file you authored. Set the Name toapps
.Click Create Policy to complete.
Click the Vault CLI shell icon (
>_
) to open a command shell. Execute the following command to create a new token:vault write auth/token/create policies=apps
Now apps
can use this token to read the secrets.
NOTE: For the purposes of this guide, you created a policy for the apps
persona, and generated a token for it. However, in the real world, you may have
a dedicated policy author
, or admin
to write policies. Also, the
consumer of the API key may be different from the consumer of the root
certificate. Then each persona would have a policy based on what it needs to
access.
Step 5: Retrieve the secrets
Using the token from Step 4, read the Google API key and the root certificate for MySQL.
CLI command / API call using cURL
CLI command
The command to read a secret is:
$ vault kv get kv-v1/<PATH>
Example:
# Authenticate with Vault using the generated token first
$ vault login e4bdf7dc-cbbf-1bb1-c06c-6a4f9a826cf2
Successfully authenticated! You are now logged in.
token: e4bdf7dc-cbbf-1bb1-c06c-6a4f9a826cf2
token_duration: 2764277
token_policies: [apps default]
# Read the API key
$ vault kv get kv-v1/eng/apikey/Google
Key Value
--- -----
refresh_interval 768h0m0s
key AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI
To return the key's value and nothing else, pass -field=key
as an argument.
$ vault kv get -field=key kv-v1/eng/apikey/Google
AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI
Root certificate example:
The command is basically the same:
$ vault kv get -field=cert kv-v1/prod/cert/mysql
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA6E2Uq0XqreZISgVMUu9pnoMsq+OoK1PI54rsA9vtDE6wiRk0GWhf5vD4DGf1
...
API call using cURL
Read the Google API key.
$ curl --header "X-Vault-Token: 1c97b03a-6098-31cf-9d8b-b404e52dcb4a" \
--request GET \
http://127.0.0.1:8200/v1/kv-v1/eng/apikey/Google | jq
{
"request_id": "5a2005ac-1149-2275-cab3-76cee71bf524",
"lease_id": "",
"renewable": false,
"lease_duration": 2764800,
"data": {
"key": "AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
Retrieve the key value using jq:
$ curl --header "X-Vault-Token: 1c97b03a-6098-31cf-9d8b-b404e52dcb4a" \
--request GET \
http://127.0.0.1:8200/v1/kv-v1/eng/apikey/Google | jq ".data.key"
Root certificate example:
$ curl --header "X-Vault-Token: 1c97b03a-6098-31cf-9d8b-b404e52dcb4a" \
--request GET \
http://127.0.0.1:8200/v1/kv-v1/prod/cert/mysql | jq ".data.cert"
Additional Discussion
Q: How do I enter my secrets without exposing the secret in my shell's history?
As a precaution, you may wish to avoid passing your secret as a part of the CLI command so that the secret won't appear in the history file. Here are a few techniques you can use.
Option 1: Use a dash "-"
An easy technique is to use a dash "-" and then press Enter. This allows you to
enter the secret on a new line. After entering the secret, press Ctrl+d
to
end the pipe which will write the secret to the Vault.
$ vault kv put kv-v1/eng/apikey/Google key=-
AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI
<Ctrl+d>
Option 2: Read the secret from a file
Using the Google API key example, you can create a file containing the key (apikey.txt
):
{
"key": "AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI"
}
The CLI command would look like:
$ vault kv put kv-v1/eng/apikey/Google @apikey.txt
Option 3: Disable all vault command history
Sometimes, you may not even want the vault
command itself to appear in history
at all. Option 1 and Option 2 prevent the secret from appearing in your shell history. The vault
command itself (vault kv put kv-v1/eng/apikey/Google
) will still appear in your history.
You can configure your shell to avoid logging any vault
commands to your history.
In bash
:
$ export HISTIGNORE="&:vault*"
NOTE: This prevents vault
commands from appearing when using the Up arrow or when searching command history with <Ctrl-r>
.
Q: How do I save multiple values at once?
The two examples introduced in this guide only had a single key/value pair. You can pass multiple values in a single command.
$ vault kv put kv-v1/dev/config/mongodb url=foo.example.com:35533 db_name=users \
username=admin password=passw0rd
Or, read several secrets from a file:
$ tee mongodb.json <<EOF
{
"url": "foo.example.com:35533",
"db_name": "users",
"username": "admin",
"password": "pa$$w0rd"
}
EOF
$ vault kv put kv-v1/dev/config/mongodb @mongodb.json
Help and Reference
- Key/Value Secrets Engine
- Key/Value Secrets Engine API
- Client libraries for Vault API for commonly used languages