The purpose of using Vault's AppRole backend to to split up the values needed for an authentication and deliver them through two different channels to prevent any one system, other than the target client, to be in possession of the full set of credentials. In this way, we're able to provide narrowly-scoped tokens to these channels (e.g. Packer, Terraform, Chef, or other provisioning, orchestration, or configuration management tools. The narrowly-scoped tokens only have permissions to retrieve either the role_id
or secret_id
values from Vault (but not both).
This document outlines the steps to set up an AppRole authentication backend in Vault and demonstrates how to utilize the backend, both directly via the Vault CLI as well as through the HTTP API.
In addition, we'll go over a best-practice pattern for using the AppRole backend to enable integration between Vault and a configuration management s tool (Chef) making use of the Vault Ruby Gem.
We'll be using the application example-app
as the basis for this demonstration.
To speed through the below steps and create a functioning AppRole backend to use with other examples, we can simply run the following commands.
First, let's start vault in -dev
mode and push it into the background so we can use the same terminal:
VAULT_REDIRECT_ADDR=http://127.0.0.1:8200 && VAULT_UI=true && vault server -log-level=TRACE -dev -dev-root-token-id=root -dev-listen-address="0.0.0.0:8200" &>/dev/null &
We can use the jobs
command to view the status of background jobs and fg
to bring a job to the foreground.
Next, we'll login with the root
token and set up our AppRole:
# Login with our dev root token
vault auth root
# Dummy data
vault write secret/example-app foo=bar
# Example-app policy
tee example-app-policy.hcl <<EOF
path "secret/example-app/*" {
capabilities = ["list", "read"]
}
EOF
# Write the policy to Vault
vault write sys/policy/example-app-policy [email protected]
# Mount the AppRole auth backend
vault auth-enable approle
# Configure the example-app AppRole role
vault write auth/approle/role/example-app \
secret_id_ttl=10m \
token_num_uses=10 \
token_ttl=20m \
token_max_ttl=30m \
secret_id_num_uses=1 \
policies=example-app-policy
You can now continue with any AppRole examples. Read the below steps to understand the details behind this quick start.
Start by logging into Vault with the root token (or an administrative user). In order to make using the HTTP API a bit easier, make sure that the VAULT_ADDR
and VAULT_TOKEN
environment variables are set:
export VAULT_ADDR=http://127.0.0.1:8200
# Assuming you authenticated already via the Vault CLI
export VAULT_TOKEN=$(cat ~/.vault-token)
# Provide the authentication token directly
export VAULT_TOKEN=root
Also, the HTTP API responses are provided in raw JSON output, which isn't the easiest to read. In order to make this output more readable, we'll pipe output to the jq
tool where appropriate. For example:
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
$VAULT_ADDR/v1/auth/approle/role/example-app/role-id | jq
# Output
{
"request_id": "264231be-2109-f6bb-f317-402f2c742c0c",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"role_id": "b0559830-1fc9-a751-e866-ff04f9dbe58e"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
Before we start with the AppRole configuration, let's create some dummy data that will be scoped to our example-app
. We will also define a policy against that data.
The purpose of this policy is to restrict tokens generated by our example-app
AppRole configuration to only be able to retrieve secrets from this path.
CLI
vault write secret/example-app foo=bar
API
tee payload.json <<EOF
{
"foo": "bar"
}
EOF
curl \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/secret/example-app
Define our example-app
policy to read from this path and write the policy to Vault:
CLI
tee example-app-policy.hcl <<EOF
path "secret/example-app" {
capabilities = ["list", "read"]
}
EOF
vault write sys/policy/example-app-policy [email protected]
API
tee payload.json <<EOF
{
"rules": "path \"secret/example-app/*\" { capabilities = [\"list\", \"read\"] }"
}
EOF
curl \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/sys/policy/example-app-policy
With the initial setup complete, let's now mount an AppRole backend:
CLI
vault auth-enable approle
API
tee payload.json <<EOF
{
"type": "approle"
}
EOF
curl \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/sys/auth/approle
Next, we will create a role within the AppRole backend for our example-app
. A role defines a number of different parameters that are applied to the tokens and secret_id
values generated for that role. It also allows us to define the policies to attach (e.g. our example-app-policy
from above):
CLI
vault write auth/approle/role/example-app \
secret_id_ttl=10m \
token_num_uses=10 \
token_ttl=20m \
token_max_ttl=30m \
secret_id_num_uses=1 \
policies=example-app-policy
API
tee payload.json <<EOF
{
"secret_id_ttl": "10m",
"token_num_uses": "10",
"token_ttl": "20m",
"token_max_ttl": "30m",
"secret_id_num_uses": "1",
"policies": [ "example-app-policy" ]
}
EOF
curl \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/auth/approle/role/example-app
For a full list of available parameters and their use, refer to this documentation: AppRole Auth Backend - HTTP API - Vault by HashiCorp
Now that we have created our example-app
AppRole configuration, lets see how we can interact with it.
The two values required to perform an AppRole authentication are role_id
and secret_id
. These are similar to the username (or email address) and password approach for authentication.
The role_id
is not considered to be a secret value; rather, it's a static UUID that identifies a specific role configuration.
The secret_id
, on the other hand, should be handled as you would a sensitive value and will be unique to each instance of our example-app
.
Two common patterns is for the role_id
value to be embedded into a machine image or container as a text file or environment variable (e.g. using Packer or delivered during the provisioning process using Terraform. There are a number of different patterns through which this value can be delivered.
The important concept to remember is that reading the role_id
will return the a static UUID for a defined AppRole role; it does not change every time we retrieve the vault (whereas the secret_id
does). In our example, all example-app
instances would use the same role_id
to perform an AppRole authentication.
Let's read the role_id
for this AppRole:
CLI
vault read auth/approle/role/example-app/role-id
# Output
Key Value
--- -----
role_id b0559830-1fc9-a751-e866-ff04f9dbe58e
API
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
$VAULT_ADDR/v1/auth/approle/role/example-app/role-id | jq
# Output
{
"request_id": "264231be-2109-f6bb-f317-402f2c742c0c",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"role_id": "b0559830-1fc9-a751-e866-ff04f9dbe58e"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
The next value needed for an AppRole authentication is the secret_id
. There are two modes of operation when working with the SecretID: Pull and Push.
In the Pull mode, Vault generates a new secret_id
for every request. We can think of this mode as one where the client application "pulls" the value from Vault:
CLI
vault write -f /auth/approle/role/example-app/secret-id
# Output
Key Value
--- -----
secret_id d3cbe550-da0a-17e6-d2ec-bba911649e1f
secret_id_accessor 24975928-dcd8-4fb4-7805-76ff70c4af23
API
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
$VAULT_ADDR/v1/auth/approle/role/example-app/secret-id | jq
# Output
{
"request_id": "e1505c2c-e0e1-9d5f-c546-93689ef8156d",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"secret_id": "f0c5ba2d-1768-bd1a-ffa9-2313d02a7d9d",
"secret_id_accessor": "5ab186ca-02b1-b6bc-c09f-a06b649deaa5"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
This also returns a secret_id_accessor
that can be used to read the properties of the secret_id
without actually exposing the sensitive value:
CLI
vault write /auth/approle/role/example-app/secret-id-accessor/lookup secret_id_accessor=24975928-dcd8-4fb4-7805-76ff70c4af23
# Output
Key Value
--- -----
SecretIDNumUses 0
cidr_list []
creation_time 2017-10-20T22:01:37.232257501Z
expiration_time 2017-10-20T22:11:37.232257501Z
last_updated_time 2017-10-20T22:01:37.232257501Z
metadata map[]
secret_id_accessor 24975928-dcd8-4fb4-7805-76ff70c4af23
secret_id_num_uses 1
secret_id_ttl 600
The following warnings were returned from the Vault server:
* The field SecretIDNumUses is deprecated and will be removed in a future release; refer to secret_id_num_uses instead
API
tee payload.json <<EOF
{
"secret_id_accessor": "5ab186ca-02b1-b6bc-c09f-a06b649deaa5"
}
EOF
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/auth/approle/role/example-app/secret-id-accessor/lookup | jq
# Output
{
"request_id": "791934ff-d6e9-d74d-4dc2-954201acd2db",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"SecretIDNumUses": 0,
"cidr_list": [],
"creation_time": "2017-10-21T02:26:51.862283327Z",
"expiration_time": "2017-10-21T02:36:51.862283327Z",
"last_updated_time": "2017-10-21T02:26:51.862283327Z",
"metadata": {},
"secret_id_accessor": "5ab186ca-02b1-b6bc-c09f-a06b649deaa5",
"secret_id_num_uses": 1,
"secret_id_ttl": 600
},
"wrap_info": null,
"warnings": [
"The field SecretIDNumUses is deprecated and will be removed in a future release; refer to secret_id_num_uses instead"
],
"auth": null
}
Now that we have our role_id
and secret_id
, we can perform our AppRole authentication to obtain our final token:
CLI
vault write auth/approle/login \
role_id=b0559830-1fc9-a751-e866-ff04f9dbe58e \
secret_id=d3cbe550-da0a-17e6-d2ec-bba911649e1f
# Output
Key Value
--- -----
token fe7a46c2-5fdb-f373-21b3-78bdbd4f9261
token_accessor 0639fd93-9af8-cf8d-3e0f-9ba44c8dff65
token_duration 20m0s
token_renewable true
token_policies [default example-app-policy]
token_meta_role_name "example-app"
API
tee payload.json <<EOF
{
"role_id": "b0559830-1fc9-a751-e866-ff04f9dbe58e",
"secret_id": "f0c5ba2d-1768-bd1a-ffa9-2313d02a7d9d"
}
EOF
curl -s \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/auth/approle/login | jq
# Output
{
"request_id": "b1ddf818-b9e4-637c-8e3a-424bb84e8d47",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": null,
"warnings": null,
"auth": {
"client_token": "97088e4d-2bea-a1c6-a01d-b1282bb0061e",
"accessor": "69229837-03d9-adb5-5ae9-9c1b91c05858",
"policies": [
"default",
"example-app-policy"
],
"metadata": {
"role_name": "example-app"
},
"lease_duration": 1200,
"renewable": true,
"entity_id": "83e197b4-42b6-b88c-b94e-debc8336e96c"
}
}
For the Push mode, we can define a custom secret_id
value and set it for a specific AppRole. In this mode, we "push" the value into Vault:
CLI
vault write /auth/approle/role/example-app/custom-secret-id secret_id=test-secret-id-1
# Output
Key Value
--- -----
secret_id test-secret-id-1
secret_id_accessor 24afc56a-a507-7fc5-1c94-a4a3b0189d03
API
tee payload.json <<EOF
{
"secret_id": "test-secret-id-1"
}
EOF
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/auth/approle/role/example-app/custom-secret-id | jq
# Output
{
"request_id": "66570588-3c64-2ec3-c911-03936fc0db7d",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"secret_id": "test-secret-id-1",
"secret_id_accessor": "d3ace8d2-685a-21d9-5241-72c2ad64e0c9"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
We can define multiple custom secret_id
values for an AppRole role and list them out:
CLI
vault write /auth/approle/role/example-app/custom-secret-id secret_id=test-secret-id-2
# Output
Key Value
--- -----
secret_id test-secret-id-2
secret_id_accessor bc94f717-8c14-b653-dd4a-1d106b97e327
vault list /auth/approle/role/example-app/secret-id
# Output
Keys
----
24afc56a-a507-7fc5-1c94-a4a3b0189d03
bc94f717-8c14-b653-dd4a-1d106b97e327
API
tee payload.json <<EOF
{
"secret_id": "test-secret-id-2"
}
EOF
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/auth/approle/role/example-app/custom-secret-id | jq
# Output
{
"request_id": "6942d635-4d20-6c92-6182-caffe6a36ca8",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"secret_id": "test-secret-id-2",
"secret_id_accessor": "721cb1c3-b66c-6cba-893e-a3ca53f4d5c7"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
curl -s\
--header "X-Vault-Token: $VAULT_TOKEN" \
--request LIST \
$VAULT_ADDR/v1/auth/approle/role/example-app/secret-id | jq
# Output
{
"request_id": "ff73ea79-750b-1909-e7f4-59f1bdcc695f",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"keys": [
"d3ace8d2-685a-21d9-5241-72c2ad64e0c9",
"721cb1c3-b66c-6cba-893e-a3ca53f4d5c7"
]
},
"wrap_info": null,
"warnings": null,
"auth": null
}
The primary use case for the SecretID Push mode was to support compatibility with the deprecated App-ID backend.
However, this approach is useful in certain situations where an organization has a desire/need to pre-populate secret_id
values for an AppRole. For example, they might be based on known parameters of a server or application that are hashed together or a System UUID:
CLI
vault write /auth/approle/role/example-app/custom-secret-id secret_id=$(sudo dmidecode -s system-uuid)
# Output
Key Value
--- -----
secret_id E4B4399A-5711-432A-A7D7-6D5FBD6B3842
secret_id_accessor f34f9c84-7d27-d3c4-45a9-10d90c3a467c
vault list /auth/approle/role/example-app/secret-id
# Output
Keys
----
f34f9c84-7d27-d3c4-45a9-10d90c3a467c
API
tee payload.json <<EOF
{
"secret_id": "$(sudo dmidecode -s system-uuid)"
}
EOF
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/auth/approle/role/example-app/custom-secret-id | jq
# Output
{
"request_id": "0eff9d2b-bed5-515e-301a-59071c95b15d",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"secret_id": "E4B4399A-5711-432A-A7D7-6D5FBD6B3842",
"secret_id_accessor": "093396e5-cfdd-7064-3fa4-9523a362f106"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
The potential benefit to this approach is that there is no longer a need to use something like Chef or some other process to deliver the secret_id
to the example-app
instances, as they can be generated, on-the-fly, by the instance itself when logging in to Vault:
CLI
vault write auth/approle/login \
role_id=b0559830-1fc9-a751-e866-ff04f9dbe58e \
secret_id=$(sudo dmidecode -s system-uuid)
# Output
Key Value
--- -----
token 7fb0fbe4-1c4e-de7f-10be-3a16c852c12a
token_accessor 230061da-356b-5359-ed3f-c1f719cc2ee2
token_duration 20m0s
token_renewable true
token_policies [default example-app-policy]
token_meta_role_name "example-app"
API
tee payload.json <<EOF
{
"role_id": "b0559830-1fc9-a751-e866-ff04f9dbe58e",
"secret_id": "$(sudo dmidecode -s system-uuid)"
}
EOF
curl -s \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/auth/approle/login | jq
# Output
{
"request_id": "08a7eb72-c77b-0b7e-24a5-3bc4e6e41b00",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": null,
"warnings": null,
"auth": {
"client_token": "13a3c271-4bf3-99b4-747c-c31a2d493fa6",
"accessor": "d20e4626-d0d6-c816-b2f0-bf3955ea7325",
"policies": [
"default",
"example-app-policy"
],
"metadata": {
"role_name": "example-app"
},
"lease_duration": 1200,
"renewable": true,
"entity_id": "33daf519-acfa-f1f6-b826-5c647167c21e"
}
}
For more information regarding the caveats of the Push mode of operation, refer to the documentation here: Auth Backend: AppRole - Vault by HashiCorp.
Another level of protection that we can provide when interacting with an AppRole's secret_id
is to request Vault to wrap the response with a one-time use token. We use the -wrap-ttl
parameter (CLI) or X-Vault-Wrap-TTL
header (API):
CLI
vault write -wrap-ttl=60s -f /auth/approle/role/example-app/secret-id
# Output
Key Value
--- -----
wrapping_token: 17b64c34-3162-6848-8113-858517755ab3
wrapping_token_ttl: 1m0s
wrapping_token_creation_time: 2017-10-20 22:23:10.632900124 +0000 UTC
wrapping_token_creation_path: auth/approle/role/example-app/secret-id
API
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
--header "X-Vault-Wrap-TTL:60s" \
--request POST \
$VAULT_ADDR/v1/auth/approle/role/example-app/secret-id | jq
# Output
{
"request_id": "",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": {
"token": "ae084e63-c304-6230-9f39-d34db5527e2b",
"ttl": 60,
"creation_time": "2017-10-21T03:02:38.520841856Z",
"creation_path": "auth/approle/role/example-app/secret-id"
},
"warnings": null,
"auth": null
}
We can then deliver this token (via Chef, for example) to the client, which would then perform an unwrap
operation to obtain the secret_id
value:
CLI
vault unwrap 17b64c34-3162-6848-8113-858517755ab3
# Output
Key Value
--- -----
secret_id 321b854c-1200-ba4b-8445-80a84dd1603d
secret_id_accessor 4bbc0b58-f7bd-1b98-d81a-5868e390c5da
API
curl -s \
--header "X-Vault-Token: ae084e63-c304-6230-9f39-d34db5527e2b" \
--request POST \
$VAULT_ADDR/v1/sys/wrapping/unwrap | jq
# Output
{
"request_id": "85f52b37-a41a-6c2e-655a-54ae3313489f",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"secret_id": "f4b6ab38-ba3c-5856-dad8-7cb1c4acbd66",
"secret_id_accessor": "171a1915-336a-5de3-1025-336bab4534c7"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
This further reduces the potential for compromise by allowing access to the generated secret_id
value once, and only once.
Before setting things up on the Chef side, let's create a limited token that we'll provide to Chef that'll only have permissions to retrieve secret_id
values from the AppRole example-app
role that we created above. First, let's create the policy that we'll attach to the token:
CLI
tee chef-approle-policy.hcl <<EOF
path "auth/approle/role/example-app/secret-id" {
capabilities = ["update"]
}
EOF
vault write sys/policy/chef-approle-policy [email protected]
API
tee payload.json <<EOF
{
"rules": "path \"auth/approle/role/example-app/secret-id\" { capabilities = [\"update\"] }"
}
EOF
curl \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/sys/policy/chef-approle-policy
Now, let's generate a token with the above policy attached. This will typically be a longer-lived token. In the case of Chef, you might protect this token with an encrypted data bag or chef-vault, for example. Remember, this a very limited-scope token and can't be used to retrieve any secret other than the secret_id
for our AppRole example:
CLI
vault token-create -policy="chef-approle-policy"
# Output
Key Value
--- -----
token 4ba2f57a-2493-eeb9-b4af-f4b280b9ab8d
token_accessor bc2622b0-37ad-c137-2ffc-f0a58e93106e
token_duration 768h0m0s
token_renewable true
token_policies [chef-approle-policy default]
API
tee payload.json <<EOF
{
"policies": [
"chef-approle-policy"
]
}
EOF
curl -s \
--header "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data @payload.json \
$VAULT_ADDR/v1/auth/token/create | jq
# Output
{
"request_id": "1715b163-5197-e94c-83ff-1434b6708c5b",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": null,
"warnings": null,
"auth": {
"client_token": "e2ca6c70-6309-5fb7-7f91-8ac3e08b1d12",
"accessor": "8fdf3696-0b81-5d81-b95d-686d1d6e3e72",
"policies": [
"chef-approle-policy",
"default"
],
"metadata": null,
"lease_duration": 2764800,
"renewable": true,
"entity_id": ""
}
}
Now that we have our Chef AppRole token, let's create an example Chef recipe to demonstrate one way to provide an integration with Vault.
For the purpose of this demonstration, we'll set up Chef using the Quick Start — Chef Docs documentation, specifically running the chef-client in --local-mode
to keep things simple:
curl -O https://packages.chef.io/files/stable/chefdk/2.3.4/ubuntu/16.04/chefdk_2.3.4-1_amd64.deb
sudo apt install ./chefdk_2.3.4-1_amd64.deb
Install the Vault Ruby client: https://github.com/hashicorp/vault-ruby
chef gem install vault
Create our example cookbook:
chef generate app chef_vault_example
This will create a directory called chef_vault_example
using the standard Chef directory structure. Our recipes will be located in the chef_vault_example/cookbooks/chef_vault_example/recipes/
directory.
We'll use a custom resource (LWRP) for most of the logic needed to interact with Vault. Let's generate an empty vault.rb
file:
chef generate lwrp chef_vault_example/cookbooks/chef_vault_example/ vault
We can then edit the chef_vault_example/cookbooks/chef_vault_example/resources/vault.rb
file:
###
# Retrieve secrets from HashiCorp Vault
###
require 'vault'
resource_name :vault_secret
provides :vault_secret
property :path, String, name_property: true
property :destination, String
property :address, String, default: 'http://127.0.0.1:8200'
###
# Retrieve secret_id via AppRole
###
action :approle_read do
# run_state destination defaults to path
destination ||= new_resource.path
# Instantiate Vault client
vault = Vault::Client.new(address: new_resource.address)
# Token to generate secret_id from AppRole
# this should be moved to encrypted databag, chef-vault, or other mechanism
vault.token = '4ba2f57a-2493-eeb9-b4af-f4b280b9ab8d'
# Get our secret_id login
secret_id = vault.approle.create_secret_id('example-app').data[:secret_id]
# We can also response wrap our secret_id
#
# Get response-wrapped secret_id
# wrapped = vault.approle.create_secret_id('example-app', {
# wrap_ttl: "30s"
# })
#
# Unwrap the wrapped response to get the secret_id and other values
# unwrapped = vault.logical.unwrap(wrapped.wrap_info.token)
#
# Extract the secret_id from the response.
# secret_id = unwrapped.data[:secret_id]
# Do our AppRole login
vault.auth.approle( ENV['ROLE_ID'], secret_id )
# Secret retrieval
secret = vault.logical.read(destination)
# Retrieve data
node.run_state[destination] = secret.data
end
Now we'll update our default recipe (chef_vault_example/cookbooks/chef_vault_example/recipes/default.rb
) to make use of our new custom resource:
vault_secret '/secret/example-app' do
notifies :create, "template[/tmp/v_test]", :immediately
end
template '/tmp/v_test' do
source 'test.erb'
variables lazy {
{ :secret => node.run_state['/secret/example-app'].to_s }
}
end
In the above example, we're using the Vault Ruby client. We pass the Vault client our Chef token, which it then uses to generate to generate a secret_id
against our example-app
AppRole. We also have our role_id
saved as an environment variable on the client system and retrieve it in our recipe. Together with both these values, we then perform an AppRole authentication and retrieve secrets from our example-app
path. We then use a template resource to write out the secrets to the /tmp/v_test
file.
The last step here of our setup is to create our ERB file so we can output our secrets to a text file:
chef generate template chef_vault_example/cookbooks/chef_vault_example/ test
Edit the chef_vault_example/cookbooks/chef_vault_example/templates/test.erb
file:
Value retrieved from /secret/example-app: <%= @secret %>
Finally, we can run our cookbook. Change into your cookbook directory chef_vault_example/cookbooks/chef_vault_example/
and run the following:
chef-client --local-mode --override-runlist chef_vault_example
If all is successful, our output file /tmp/v_test
should output the following:
Value retrieved from /secret/example-app: {:foo=>"bar"}