open-vault/website/content/guides/secret-mgmt/cubbyhole.mdx

554 lines
16 KiB
Plaintext
Raw Normal View History

2018-01-10 19:14:59 +00:00
---
layout: guides
page_title: Cubbyhole Response Wrapping - Guides
sidebar_title: Cubbyhole Response Wrapping
2018-01-10 19:14:59 +00:00
description: |-
Vault provides a capability to wrap Vault response and store it in a
"cubbyhole" where the holder of the one-time use wrapping token can unwrap to
uncover the secret.
---
# Cubbyhole
2018-02-01 17:50:59 +00:00
The term _cubbyhole_ comes from an Americanism where you get a "locker" or "safe
2018-01-10 19:14:59 +00:00
place" to store your belongings or valuables. In Vault, cubbyhole is your
"locker". All secrets are namespaced under **your token**. If that token
2018-01-10 19:14:59 +00:00
expires or is revoked, all the secrets in its cubbyhole are revoked as well.
It is not possible to reach into another token's cubbyhole even as the root
user. This is the key difference between the cubbyhole and the key/value secret
engine. The secrets in the key/value secret engine are accessible to any token for as
2018-01-10 19:14:59 +00:00
long as its policy allows it.
## Reference Material
- [Cubbyhole](/docs/secrets/cubbyhole)
- [Response Wrapping](/docs/concepts/response-wrapping)
2018-01-10 19:14:59 +00:00
~> **NOTE:** An [interactive
tutorial](https://www.katacoda.com/hashicorp/scenarios/vault-cubbyhole) is
also available if you do not have a Vault environment to perform the steps
described in this guide.
2018-01-10 19:14:59 +00:00
## Estimated Time to Complete
10 minutes
2018-01-24 00:22:17 +00:00
## Personas
The end-to-end scenario described in this guide involves two personas:
- **`admin`** with privileged permissions to create tokens
- **`apps`** trusted entity retrieving secrets from Vault
2018-01-24 00:22:17 +00:00
2018-01-10 19:14:59 +00:00
## Challenge
In order to tightly manage the secrets, you set the scope of who can do what
using the [Vault policy](/docs/concepts/policies) and attach that to
2018-01-10 19:14:59 +00:00
tokens, roles, entities, etc.
Think of a case where you have a trusted entity (Chef, Jenkins, etc.) which
reads secrets from Vault. This trusted entity must obtain a token. If the
trusted entity or its host machine was rebooted, it must re-authenticate with
Vault using a valid token.
How can you securely distribute the initial token to the trusted entity?
2018-01-10 19:14:59 +00:00
## Solution
Use Vault's **cubbyhole response wrapping** where the initial token is stored in
the cubbyhole secret engine. The wrapped secret can be unwrapped using the
single-use wrapping token. Even the user or the system created the initial token
won't see the original value. The wrapping token is short-lived and can be
revoked just like any other tokens so that the risk of unauthorized access can
be minimized.
2018-01-10 19:14:59 +00:00
## 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
2018-01-24 00:22:17 +00:00
that your Vault server has been [initialized and
unsealed](/intro/getting-started/deploy).
2018-01-10 19:14:59 +00:00
### Policy requirements ((#policy))
2018-01-24 00:22:17 +00:00
-> **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:
2018-01-24 00:22:17 +00:00
```shell
# Manage tokens
2018-01-25 05:21:23 +00:00
path "auth/token/*" {
capabilities = [ "create", "read", "update", "delete", "sudo" ]
2018-01-24 00:22:17 +00:00
}
# Write ACL policies
path "sys/policy/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# Manage secret/dev secret engine - for Verification test
path "secret/dev" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
2018-01-24 00:22:17 +00:00
```
2018-01-10 19:14:59 +00:00
If you are not familiar with policies, complete the
[policies](/guides/identity/policies) guide.
2018-01-10 19:14:59 +00:00
## Steps
Think of a scenario where apps read secrets from Vault. The `apps` need:
- Policy granting "read" permission on the specific path (`secret/dev`)
- Valid tokens to interact with Vault
New Docs Website (#5535) * conversion stage 1 * correct image paths * add sidebar title to frontmatter * docs/concepts and docs/internals * configuration docs and multi-level nav corrections * commands docs, index file corrections, small item nav correction * secrets converted * auth * add enterprise and agent docs * add extra dividers * secret section, wip * correct sidebar nav title in front matter for apu section, start working on api items * auth and backend, a couple directory structure fixes * remove old docs * intro side nav converted * reset sidebar styles, add hashi-global-styles * basic styling for nav sidebar * folder collapse functionality * patch up border length on last list item * wip restructure for content component * taking middleman hacking to the extreme, but its working * small css fix * add new mega nav * fix a small mistake from the rebase * fix a content resolution issue with middleman * title a couple missing docs pages * update deps, remove temporary markup * community page * footer to layout, community page css adjustments * wip downloads page * deps updated, downloads page ready * fix community page * homepage progress * add components, adjust spacing * docs and api landing pages * a bunch of fixes, add docs and api landing pages * update deps, add deploy scripts * add readme note * update deploy command * overview page, index title * Update doc fields Note this still requires the link fields to be populated -- this is solely related to copy on the description fields * Update api_basic_categories.yml Updated API category descriptions. Like the document descriptions you'll still need to update the link headers to the proper target pages. * Add bottom hero, adjust CSS, responsive friendly * Add mega nav title * homepage adjustments, asset boosts * small fixes * docs page styling fixes * meganav title * some category link corrections * Update API categories page updated to reflect the second level headings for api categories * Update docs_detailed_categories.yml Updated to represent the existing docs structure * Update docs_detailed_categories.yml * docs page data fix, extra operator page remove * api data fix * fix makefile * update deps, add product subnav to docs and api landing pages * Rearrange non-hands-on guides to _docs_ Since there is no place for these on learn.hashicorp, we'll put them under _docs_. * WIP Redirects for guides to docs * content and component updates * font weight hotfix, redirects * fix guides and intro sidenavs * fix some redirects * small style tweaks * Redirects to learn and internally to docs * Remove redirect to `/vault` * Remove `.html` from destination on redirects * fix incorrect index redirect * final touchups * address feedback from michell for makefile and product downloads
2018-10-19 15:40:11 +00:00
![Response Wrapping Scenario](/img/vault-cubbyhole.png)
Setting the appropriate policies and token generation are done by the `admin`
persona. For the `admin` to distribute the initial token to the app securely, it
uses cubbyhole response wrapping. In this guide, you perform the following:
2018-01-10 19:14:59 +00:00
2018-01-24 00:22:17 +00:00
1. [Create and wrap a token](#step1)
2. [Unwrap the secret](#step2)
2018-01-10 19:14:59 +00:00
**NOTE:** This guide demonstrates how the response wrapping works. To learn more
about reading and writing secrets in Vault, refer to the [Static
Secret](/guides/secret-mgmt/static-secrets) guide.
### Step 1: Create and wrap a token ((#step1))
2018-01-24 00:22:17 +00:00
(**Persona:** admin)
2018-01-10 19:14:59 +00:00
To solve the [challenge](#challenge) addressed in this guide:
1. More privileged token (`admin`) wraps a secret only the expecting client can
read
2. The receiving client (`app`) unwraps the secret to obtain the token
When the response to `vault token create` request is wrapped, Vault inserts the
generated token into the cubbyhole of a single-use token, returning that
2018-01-10 19:14:59 +00:00
single-use wrapping token. Retrieving the secret requires an unwrap operation
against this wrapping token.
In this scenario, an [admin user](#personas) creates a token using response
wrapping. To perform the steps in this guide, first create a policy for the app.
2018-01-24 00:22:17 +00:00
`apps-policy.hcl`:
2018-01-24 00:22:17 +00:00
```shell
# For testing, read-only on secret/dev path
path "secret/dev" {
capabilities = [ "read" ]
}
```
2018-01-10 19:14:59 +00:00
#### CLI command
2018-01-24 00:22:17 +00:00
First create an `apps` policy:
```shell-session
$ vault policy write apps apps-policy.hcl
2018-01-24 00:22:17 +00:00
Policy 'apps' written.
```
To create a token using response wrapping:
```shell-session
$ vault token create -policy=<POLICY_NAME> -wrap-ttl=<WRAP_TTL>
2018-01-10 19:14:59 +00:00
```
Where the `<WRAP_TTL>` can be either an integer number of seconds or a string
duration of seconds (15s), minutes (20m), or hours (25h).
2018-01-10 19:14:59 +00:00
**Example:**
Generate a token for `apps` persona using response wrapping with TTL of 120
2018-01-24 00:22:17 +00:00
seconds.
```shell-session
$ vault token create -policy=apps -wrap-ttl=120
2018-01-10 19:14:59 +00:00
Key Value
--- -----
wrapping_token: 9ac59985-094f-a2de-aed8-bf688e436fbc
wrapping_token_ttl: 2m0s
2018-01-10 19:14:59 +00:00
wrapping_token_creation_time: 2018-01-10 00:47:54.970185208 +0000 UTC
wrapping_token_creation_path: auth/token/create
wrapped_accessor: 195763a9-3f26-1fcf-6a1a-ee0a11e76cb1
```
2018-01-24 00:22:17 +00:00
The response is the wrapping token; therefore, the admin user does not even see
the generated token from the `token create` command.
2018-01-18 01:39:21 +00:00
2018-01-24 00:22:17 +00:00
#### API call using cURL
2018-01-18 01:39:21 +00:00
First create an `apps` policy using `sys/policy` endpoint:
2018-01-18 01:39:21 +00:00
```shell-session
$ curl --header "X-Vault-Token: <TOKEN>" \
--request PUT \
--data <PAYLOAD> \
<VAULT_ADDRESS>/v1/sys/policy/<POLICY_NAME>
```
Where `<TOKEN>` is your valid token, and `<PAYLOAD>` includes policy name and
2018-03-20 18:54:10 +00:00
stringified policy.
2018-01-18 01:39:21 +00:00
**Example:**
```shell
# Request payload
2018-01-24 00:22:17 +00:00
$ cat payload.json
{
"policy": "path \"secret/dev\" { capabilities = [ \"read\" ] }"
2018-01-24 00:22:17 +00:00
}
# API call to create a policy named, "apps"
$ curl --header "X-Vault-Token: ..." --request PUT --data @payload.json \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/sys/policy/apps
2018-01-18 01:39:21 +00:00
```
2018-01-10 19:14:59 +00:00
Response wrapping is per-request and is triggered by providing to Vault the
desired TTL for a response-wrapping token for that request. This is set using
the **`X-Vault-Wrap-TTL`** header in the request and can be either an integer
number of seconds or a string duration.
```shell-session
2018-01-24 00:22:17 +00:00
$ curl --header "X-Vault-Wrap-TTL: <TTL>" \
--header "X-Vault-Token: <TOKEN>" \
--request <HTTP_VERB> \
--data '<PARAMETERS>' \
<VAULT_ADDRESS>/v1/<ENDPOINT>
```
Where `<TTL>` can be either an integer number of seconds or a string duration of
seconds (15s), minutes (20m), or hours (25h).
2018-01-10 19:14:59 +00:00
**Example:**
To wrap the response of token create request:
2018-01-10 19:14:59 +00:00
```shell-session
$ curl --header "X-Vault-Wrap-TTL: 120" \
2018-01-24 00:22:17 +00:00
--header "X-Vault-Token: ..." \
--request POST \
--data '{"policies":["apps"]}' \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/auth/token/create | jq
2018-01-10 19:14:59 +00:00
{
"request_id": "",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": {
"token": "e095129f-123a-4fef-c007-1f6a487cfa78",
"ttl": 120,
2018-01-10 19:14:59 +00:00
"creation_time": "2018-01-10T01:43:38.025351336Z",
"creation_path": "auth/token/create",
"wrapped_accessor": "44e8253c-65b4-1690-1bf1-7902a7a6b2aa"
},
"warnings": null,
"auth": null
}
2018-01-10 19:14:59 +00:00
```
This API call generates a token for `apps` persona using response wrapping with
TTL of 60 seconds. The admin user does not even see the generated token.
2018-01-10 19:14:59 +00:00
### Step 2: Unwrap the secret ((#step2))
(**Persona:** apps)
2018-01-24 00:22:17 +00:00
The `apps` persona receives a wrapping token from the `admin`. In order for the
`apps` to acquire a valid token to read secrets from `secret/dev` path, it must
run the unwrap operation using this token.
2018-01-24 00:22:17 +00:00
-> **NOTE:** If a client has been expecting delivery of a response-wrapping
token and none arrives, this may be due to an attacker intercepting the token
and then preventing it from traveling further. This should cause an alert to
trigger an immediate investigation.
2018-01-10 19:14:59 +00:00
The following tasks will be performed to demonstrate the client operations:
1. Create a token with **`default`** policy
2. Authenticate with Vault using this `default` token (less privileged token)
3. Unwrap the secret to obtain more privileged token (**`apps`** persona token)
4. Verify that you can read `secret/dev` using the `apps`token
2018-01-10 19:14:59 +00:00
#### CLI command
First, create a token with `default` policy:
```shell
# Create a token with `default` policy
$ vault token create -policy=default
Key Value
--- -----
token 4522b2e8-27fe-bdc5-b932-d982f3166c6c
token_accessor 96108f48-7475-6190-b058-769a2e5ebc8e
token_duration 768h0m0s
token_renewable true
token_policies [default]
# Authenticate using the generated token
2018-01-31 17:27:14 +00:00
$ vault login 4522b2e8-27fe-bdc5-b932-d982f3166c6c
Successfully authenticated! You are now logged in.
token: 4522b2e8-27fe-bdc5-b932-d982f3166c6c
token_duration: 2764729
token_policies: [default]
# Verify that you do NOT have a permission to read secret/dev
$ vault read secret/dev
Error reading secret/dev: Error making API request.
URL: GET http://<VAULT_ADDRESS>/v1/secret/dev
Code: 403. Errors:
* permission denied
```
The command to unwrap the wrapped secret is:
2018-01-24 00:22:17 +00:00
```shell-session
2018-01-24 00:22:17 +00:00
$ vault unwrap <WRAPPING_TOKEN>
2018-01-10 19:14:59 +00:00
```
2018-01-10 19:14:59 +00:00
Or
```shell-session
2018-01-24 00:22:17 +00:00
$ VAULT_TOKEN=<WRAPPING_TOKEN> vault unwrap
2018-01-10 19:14:59 +00:00
```
**Example:**
```shell-session
2018-01-18 01:39:21 +00:00
$ VAULT_TOKEN=9ac59985-094f-a2de-aed8-bf688e436fbc vault unwrap
2018-01-10 19:14:59 +00:00
Key Value
--- -----
token 7bb915b2-8a44-48b0-a71d-72b590252016
token_accessor 195763a9-3f26-1fcf-6a1a-ee0a11e76cb1
token_duration 768h0m0s
token_renewable true
2018-01-24 00:22:17 +00:00
token_policies [apps default]
```
Verify that this token has `apps` policy attached.
2018-01-24 00:22:17 +00:00
Once the client acquired the token, future requests can be made using this
token.
```shell-session
2018-01-31 17:27:14 +00:00
$ vault login 7bb915b2-8a44-48b0-a71d-72b590252016
2018-01-24 00:22:17 +00:00
$ vault read secret/dev
2018-01-25 05:21:23 +00:00
No value found at secret/dev
2018-01-10 19:14:59 +00:00
```
#### API call using cURL
First, create a token with `default` policy:
```shell
# Create a new token default policy
$ curl --header "X-Vault-Token: ..." --request POST \
--data '{"policies": "default"}' \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/auth/token/create | jq
{
...
"auth": {
"client_token": "5fe14760-b0fd-22dc-403c-14a05003b67f",
"accessor": "e709610e-916e-f7e3-b93b-41f4dfdca7a0",
"policies": [
"default"
],
...
}
}
# Verify that you can NOT read secret/dev using default token
$ curl --header "X-Vault-Token: 5fe14760-b0fd-22dc-403c-14a05003b67f" \
--request GET \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/secret/dev | jq
{
"errors": [
"permission denied"
]
}
```
Now, unwrap the secret using `/sys/wrapping/unwrap` endpoint:
2018-01-10 19:14:59 +00:00
```shell-session
2018-01-24 00:22:17 +00:00
$ curl --header "X-Vault-Token: <WRAPPING_TOKEN>" \
--request POST \
<VAULT_ADDRESS>/v1/sys/wrapping/unwrap
```
2018-01-10 19:14:59 +00:00
2018-01-24 00:22:17 +00:00
**Example:**
```shell-session
2018-01-24 00:22:17 +00:00
$ curl --header "X-Vault-Token: e095129f-123a-4fef-c007-1f6a487cfa78" \
--request POST \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/sys/wrapping/unwrap | jq
2018-01-10 19:14:59 +00:00
{
"request_id": "d704435d-c1cf-b8a3-52f6-ec50bc8246c4",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": null,
"warnings": null,
"auth": {
"client_token": "af5f7682-aa55-fa37-5039-ee116df56600",
"accessor": "19b5407e-b304-7cde-e946-54942325d3c1",
"policies": [
2018-01-24 00:22:17 +00:00
"apps",
2018-01-10 19:14:59 +00:00
"default"
],
"metadata": null,
"lease_duration": 2764800,
"renewable": true
}
}
```
2018-01-24 00:22:17 +00:00
Once the client acquired the token, future requests can be made using this
token.
```shell-session
2018-01-24 00:22:17 +00:00
$ curl --header "X-Vault-Token: af5f7682-aa55-fa37-5039-ee116df56600" \
--request GET \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/secret/dev | jq
2018-01-24 00:22:17 +00:00
{
"errors": []
}
```
2018-01-10 19:14:59 +00:00
Since there is no data in `secret/dev`, it returns an empty array.
2018-01-18 01:39:21 +00:00
## Additional Discussion
2018-01-10 19:14:59 +00:00
The `cubbyhole` secret engine provides your own private secret storage space
where no one else can read (including `root`). This comes handy when you want to
store a password tied to your username that should not be shared with anyone.
2018-01-10 19:14:59 +00:00
The cubbyhole secret engine is mounted at the **`cubbyhole/`** prefix by
default. The secrets you store in the `cubbyhole/` path are tied to your token
and all tokens are permitted to read and write to the `cubbyhole` secret engine
by the [`default`](/docs/concepts/policies#default-policy) policy.
2018-01-10 19:14:59 +00:00
2018-01-24 00:22:17 +00:00
```shell
...
# Allow a token to manage its own cubbyhole
path "cubbyhole/*" {
capabilities = ["create", "read", "update", "delete", "list"]
2018-01-10 19:14:59 +00:00
}
...
2018-01-10 19:14:59 +00:00
```
To test the cubbyhole secret engine, perform the following steps. (NOTE: Keep
using the `apps` token from [Step 2](#step2) to ensure that you are logged in with
non-root token.)
2018-01-10 19:14:59 +00:00
#### CLI command
Commands to write and read secrets to the `cubbyhole` secret engine:
2018-01-10 19:14:59 +00:00
```shell
# Write key-value pair(s) in your cubbyhole
$ vault write cubbyhole/<PATH> <KEY>=<VALUE>
2018-01-10 19:14:59 +00:00
# Read values from your cubbyhole
$ vault write cubbyhole/<PATH>
2018-01-10 19:14:59 +00:00
```
**Example:**
Write secrets under `cubbyhole/private/` path, and read it back.
2018-01-10 19:14:59 +00:00
2018-01-24 00:22:17 +00:00
```shell
# Write "token" to cubbyhole/private/access-token path
2018-01-10 19:14:59 +00:00
$ vault write cubbyhole/private/access-token token="123456789abcdefg87654321"
Success! Data written to: cubbyhole/private/access-token
# Read value from cubbyhole/private/access-token path
2018-01-10 19:14:59 +00:00
$ vault read cubbyhole/private/access-token
Key Value
--- -----
token 123456789abcdefg87654321
```
Now, try to access the secret using the `root` token. It should NOT return the
secret.
2018-01-10 19:14:59 +00:00
```shell-session
2018-01-24 00:22:17 +00:00
$ VAULT_TOKEN=<ROOT_TOKEN> vault read cubbyhole/private/access-token
2018-01-10 19:14:59 +00:00
No value found at cubbyhole/private/access-token
```
#### API call using cURL
The API to work with the `cubbyhole` secret engine is very similar to `secret` secret engine:
```shell-session
$ curl --header "X-Vault-Token: <TOKEN>" \
--request POST \
--data <SECRETS> \
<VAULT_ADDRESS>/v1/cubbyhole/<PATH>
```
**Example:**
Write secrets under `cubbyhole/private/` path, and read it back.
```shell
# Write "token" to cubbyhole/private/access-token path
$ curl --header "X-Vault-Token: e095129f-123a-4fef-c007-1f6a487cfa78" --request POST \
--data '{"token": "123456789abcdefg87654321"}' \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/cubbyhole/private/access-token
# Read value from cubbyhole/private/access-token path
$ curl --header "X-Vault-Token: e095129f-123a-4fef-c007-1f6a487cfa78" --request GET \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/cubbyhole/private/access-token | jq
{
"request_id": "b2ff9f04-7a72-7eb0-672f-225b5eb652df",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"token": "123456789abcdefg87654321"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
```
Now, try to access the secret using the `root` token. It should NOT return the
secret.
```shell-session
$ curl --header "X-Vault-Token: root" --request GET \
2018-03-23 15:41:51 +00:00
http://127.0.0.1:8200/v1/cubbyhole/private/access-token | jq
{
"errors": []
}
```
Also, refer to [Cubbyhole Secret Engine (API)](/api/secret/cubbyhole).
2018-01-10 19:14:59 +00:00
## Next steps
The use of [AppRole Pull Authentication](/guides/identity/authentication) is a good
use case to leverage the response wrapping. Go through the guide if you have not
done so. To better understand the lifecycle of Vault tokens, proceed to [Tokens
and Leases](/guides/identity/lease) guide.