open-vault/website/source/guides/authentication.html.md
2018-01-18 17:40:35 -08:00

16 KiB

layout page_title sidebar_current description
guides AppRole Pull Authentication - Guides guides-authentication Authentication is a process in Vault by which user or machine-supplied information is verified to create a token with pre-configured policy.

Authentication

Before a client can interact with Vault, it must authenticate against an auth backend to acquire a token. This token has policies attached so that the behavior of the client can be governed.

Since tokens are the core method for authentication within Vault, there is a token auth backend (often refer as token store). This is a special auth backend responsible for creating and storing tokens.

Auth Backends

Auth backends performs authentication to verify the user or machine-supplied information. Some of the supported auth backends are targeted towards users while others are targeted toward machines or apps. For example, LDAP auth backend enables user authentication using an existing LDAP server while AppRole auth backend is recommended for machines or apps.

Getting Started guide walks you through how to enable GitHub auth backend for user authentication.

This guide focuses on generating tokens for machines or apps by enabling AppRole auth backend.

Reference Material

Estimated Time to Complete

10 minutes

Personas

The end-to-end scenario described in this guide involves two personas:

  • admin with privileged permissions to configure an auth backend
  • app is the consumer of secrets stored in Vault

Challenge

Think of a scenario where a DevOps team wants to configure Jenkins to read secrets from Vault so that it can inject the secrets to app's environment variables (e.g. MYSQL_DB_HOST) at deployment time.

Instead of hardcoding secrets in each build script as a plaintext, Jenkins retrieves secrets from Vault.

Vault communication

As a user, you can authenticate with Vault using your LDAP credentials, and Vault generates a token. This token has policies granting you to perform appropriate operations.

How can a Jenkins server programmatically request a token so that it can read secrets from Vault?

Solution

Enable AppRole auth backend so that the Jenkins server can obtain a Vault token with appropriate policies attached. Since each AppRole has attached policies, you can write fine-grained policies limiting which app can access which path.

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.

Policy requirements

To perform all tasks demonstrated in this guide, you need to be able to authenticate with Vault as an admin user. The admin user's policy must include the following permissions:

# Mount the AppRole auth backend
path "sys/auth/approle/*" {
  capabilities = [ "create", "read", "update", "delete" ]
}

# Create and manage roles
path "auth/approle/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}

# Write ACL policies
path "sys/policy/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}

# Write test data
path "secret/mysql/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}

-> NOTE: For the purpose of this guide, you can use root token. However, Vault team recommends 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.

If you are not familiar with policies, complete the policies guide.

Steps

Vault supports a number of authentication backends, and most auth backends must be enabled first including AppRole.

The overall workflow is: AppRole auth backend workflow

In this guide, you are going to perform the following steps:

  1. Enable AppRole auth backend
  2. Create a role with policy attached
  3. Get Role ID and Secret ID
  4. Login with Role ID & Secret ID
  5. Read secrets using the AppRole token

Step 1 through 3 need to be performed by an admin user. Step 4 and 5 describe the commands that an app runs to get a token and read secrets from Vault.

Step 1: Enable AppRole auth backend

(Persona: admin)

AppRole is an authentication mechanism within Vault to allow machines or apps to acquire a token to interact with Vault. It uses Role ID and Secret ID for login.

CLI command

vault auth-enable approle

API call using cURL

Before begin, create the following environment variables for your convenience:

  • VAULT_ADDR is set to your Vault server address
  • VAULT_TOKEN is set to your Vault token

Example:

$ export VAULT_ADDR=http://127.0.0.1:8201

$ export VAULT_TOKEN=0c4d13ba-9f5b-475e-faf2-8f39b28263a5

Now, enable the AppRole auth backend via API:

curl -X POST -H "X-Vault-Token: $VAULT_TOKEN" --data '{"type": "approle"}' \
    $VAULT_ADDR/v1/sys/auth/approle

Step 2: Create a role with policy attached

(Persona: admin)

When you enabled AppRole auth backend, it gets mounted at the /auth/approle path. In this example, you are going to create a role for Jenkins server.

The scenario in this guide requires the app persona (Jenkins) to have the following policy (jenkins-pol.hcl):

# Login with AppRole
path "auth/approle/login" {
  capabilities = [ "create", "read" ]
}

# Read test data
path "secret/mysql/*" {
  capabilities = [ "read" ]
}

CLI command

Before creating a role, create jenkins policy:

vault policy-write jenkins jenkins-pol.hcl

The command to create a new AppRole:

vault write auth/approle/role/<ROLE_NAME> [args]

There are a number of parameters that you can set. Most importantly, you want to specify the policies when you create a role.

Example:

The following example creates a role named jenkins with jenkins policy attached. (NOTE: This example creates a role operates in pull mode.)

$ vault write auth/approle/role/jenkins policies="jenkins"

$ vault read auth/approle/role/jenkins

  Key               	Value
  ---               	-----
  bind_secret_id    	true
  bound_cidr_list
  period            	0
  policies          	[jenkins]
  secret_id_num_uses	0
  secret_id_ttl     	0
  token_max_ttl     	0
  token_num_uses    	0
  token_ttl         	0

NOTE: To attach multiple policies, pass the policy names as a comma separated string.

vault write auth/approle/role/jenkins policies="jenkins,anotherpolicy"

API call using cURL

Before creating a role, create jenkins policy:

$ curl -X PUT -H "X-Vault-Token: $VAULT_TOKEN" -d @payload.json \
    $VAULT_ADDR/v1/sys/policy/jenkins

$ cat payload.json
{
  "policy": "path \"auth/approle/login\" {  capabilities = [ \"create\", \"read\" ] } ... }"
}

There are a number of parameters that you can set. Most importantly, you want to specify the policies when you create a role.

You are going to create a role operates in pull mode in this example.

Example:

The following example creates a role named jenkins with jenkins policy attached. (NOTE: This example creates a role operates in pull mode.)

$ curl -X POST -H "X-Vault-Token:$VAULT_TOKEN" -d '{"policies":"jenkins"}' \
    $VAULT_ADDR/v1/auth/approle/role/jenkins

$ curl -X GET -H "X-Vault-Token: $VAULT_TOKEN" $VAULT_ADDR/v1/auth/approle/role/jenkins | jq

{
  "request_id": "b18054ad-1ab5-8d83-eeed-193d97026ee7",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "bind_secret_id": true,
    "bound_cidr_list": "",
    "period": 0,
    "policies": [
      "jenkins"
    ],
    "secret_id_num_uses": 0,
    "secret_id_ttl": 0,
    "token_max_ttl": 0,
    "token_num_uses": 0,
    "token_ttl": 0
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

NOTE: To attach multiple policies, pass the policy names as a comma separated string.

$ curl -X POST -H "X-Vault-Token:$VAULT_TOKEN" -d '{"policies":"jenkins,anotherpolicy"}' \
    $VAULT_ADDR/v1/auth/approle/role/jenkins

Step 3: Get Role ID and Secret ID

(Persona: admin)

Since the example created a jenkins role which operates in pull mode, Vault will generate the Secret ID. Similarly to tokens, you can set properties such as usage-limit, TTLs, and expirations on the secret IDs.

CLI command

Now, you need to fetch the Role ID and Secret ID of a role.

Example:

$ vault read auth/approle/role/jenkins/role-id
  Key    	Value
  ---    	-----
  role_id	675a50e7-cfe0-be76-e35f-49ec009731ea

$ vault write -f auth/approle/role/jenkins/secret-id
  Key               	Value
  ---               	-----
  secret_id         	ed0a642f-2acf-c2da-232f-1b21300d5f29
  secret_id_accessor	a240a31f-270a-4765-64bd-94ba1f65703c

To list existing Secret IDs after creation:

vault list auth/approle/role/jenkins/secret-id

API call using cURL

Example:

$ curl -X GET -H "X-Vault-Token:$VAULT_TOKEN" $VAULT_ADDR/v1/auth/approle/role/jenkins/role-id | jq

$ curl -X POST -H "X-Vault-Token:$VAULT_TOKEN" $VAULT_ADDR/v1/auth/approle/role/jenkins/secret-id | jq

To list existing secret IDs after creation:

curl -X LIST -H "X-Vault-Token: $VAULT_TOKEN" $VAULT_ADDR/v1/auth/approle/role/jenkins/secret-id | jq

Step 4: Login with Role ID & Secret ID

(Persona: app)

To get a Vault token for the jenkins role, you need to pass the role ID and secret ID you obtained previously to the client (in this case, Jenkins).

-> Refer to the Advanced Features section for securely distributing the role ID and secret ID to the client app.

CLI command

Example:

vault write auth/approle/login role_id="675a50e7-cfe0-be76-e35f-49ec009731ea" \
  secret_id="ed0a642f-2acf-c2da-232f-1b21300d5f29"

  Key                 	Value
  ---                 	-----
  token               	eeaf890e-4b0f-a687-4190-c75b1d6d70bc
  token_accessor      	fcee5d4e-7281-8bb0-2901-e743c52e0502
  token_duration      	768h0m0s
  token_renewable     	true
  token_policies      	[jenkins]
  token_meta_role_name	"jenkins"

Now you have a client token with default and jenkins policies attached.

API call using cURL

Notice that the following API call passes the role ID and secret ID in the request payload.

Example:

$ cat jenkins.json
  {
    "role_id": "675a50e7-cfe0-be76-e35f-49ec009731ea",
    "secret_id": "ed0a642f-2acf-c2da-232f-1b21300d5f29"
  }

$ curl -X POST -d @jenkins.json $VAULT_ADDR/v1/auth/approle/login | jq

{
  "request_id": "fccae32b-1e6a-9a9c-7666-f5cb07805c1e",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": null,
  "wrap_info": null,
  "warnings": null,
  "auth": {
    "client_token": "3e7dd0ac-8b3e-8f88-bb37-a2890455ca6e",
    "accessor": "375c077e-bf02-a09b-c864-63d7f967e86b",
    "policies": [
      "default",
      "jenkins"
    ],
    "metadata": {
      "role_name": "jenkins"
    },
    "lease_duration": 2764800,
    "renewable": true,
    "entity_id": "54e0b765-6daf-0ff5-70b9-32c0d491f473"
  }
}

Now you have a client token with default and jenkins policies attached.

Step 5: Read secrets using the AppRole token

(Persona: app)

Once the client acquired the token, the future requests can be made using that token.

CLI command

Example:

You can pass the client_token returned in Step 4 as a part of the CLI command.

VAULT_TOKEN=3e7dd0ac-8b3e-8f88-bb37-a2890455ca6e vault read secret/mysql/webapp
No value found at secret/mysql/webapp

Alternatively, you can first authenticate with Vault using the client_token.

$ vault auth 3e7dd0ac-8b3e-8f88-bb37-a2890455ca6e
Successfully authenticated! You are now logged in.
token: 3e7dd0ac-8b3e-8f88-bb37-a2890455ca6e
token_duration: 2762013
token_policies: [default jenkins]

$ vault read secret/mysql/webapp
No value found at secret/mysql/webapp

Since there is no value in the secret/mysql/webapp, it returne "no value found" message.

Optional: Using the admin user's token, you can store some secrets in the secret/mysql/webapp backend.

$ vault write secret/dev/config/mongodb @mysqldb.txt

$ cat mysqldb.txt
{
  "url": "foo.example.com:35533",
  "db_name": "users",
  "username": "admin",
  "password": "pa$$w0rd"
}

Now, try to read secrets from secret/mysql/webapp using client_token again. This time, it should return the values you just created.

API call using cURL

You can now pass the client_token returned in Step 4 in the X-Vault-Token header.

Example:

curl -X GET -H "X-Vault-Token: 3e7dd0ac-8b3e-8f88-bb37-a2890455ca6e" \
    $VAULT_ADDR/v1/secret/mysql/webapp | jq

{
  "errors": []
}

Since there is no value in the secret/mysql/webapp, it returns an empty array.

Optional: Using the admin user's token, you can store some secrets in the secret/mysql/webapp backend.

$ curl -X POST -H "X-Vault-Token: $VAULT_TOKEN" --data @mysqldb.txt \
    $VAULT_ADDR/v1/secret/mysql/webapp

$ cat mysqldb.text
{
  "url": "foo.example.com:35533",
  "db_name": "users",
  "username": "admin",
  "password": "p@ssw0rd"
}

Now, try to read secrets from secret/mysql/webapp using client_token again. This time, it should return the values you just created.

Advanced Features

To keep the Secret ID confidential, use response wrapping so that the only expected client can unwrap the Secret ID.

In the previous step, you executed the following command to retrieve the Secret ID:

vault write -f auth/approle/role/jenkins/secret-id

Instead, use response wrapping:

vault write -wrap-ttl=60s -f auth/approle/role/jenkins/secret-id

Key                          	Value
---                          	-----
wrapping_token:              	9bbe23b7-5f8c-2aec-83dc-e97e94a2e632
wrapping_accessor:           	cb5bdc8f-0cdb-35ff-0e68-9de57a79c3bf
wrapping_token_ttl:          	1m0s
wrapping_token_creation_time:	2018-01-08 21:29:38.826611 -0800 PST
wrapping_token_creation_path:	auth/approle/role/jenkins/secret-id

The client app uses this wrapping_token to unwrap and obtain the Secret ID.

VAULT_TOKEN=9bbe23b7-5f8c-2aec-83dc-e97e94a2e632 vault unwrap

Key               	Value
---               	-----
secret_id         	575f23e4-01ad-25f7-2661-9c9bdbb1cf81
secret_id_accessor	7d8a40b7-a6fd-a634-579b-b7d673ff86fb

To retrieve the Secret ID alone, you can use jq as follow:

VAULT_TOKEN=2577044d-cf86-a065-e28f-e2a14ea6eaf7 vault unwrap -format=json | jq -r ".data.secret_id"

b07d7a47-1d0d-741d-20b4-ae0de7c6d964

Next steps

To learn more about response wrapping, go to Cubbyhole Response Wrapping guide.