Access Management

AppRole Pull Authentication

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

Authentication Workflow

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

Auth Methods

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

The Getting Started guide walks you through how to enable the GitHub auth method for user authentication.

This introductory guide focuses on generating tokens for machines or apps by enabling the AppRole auth method.

Personas

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

  • admin with privileged permissions to configure an auth method
  • 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 an app's environment variables (e.g. MYSQL_DB_HOST) at deployment time.

Instead of hardcoding secrets in each build script as plain text, Jenkins retrieves secrets from Vault.

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

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

Solution

Enable AppRole auth method 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.

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

To perform all tasks demonstrated in this guide, your policy must include the following permissions:

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

# Configure the AppRole auth method
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/policies/acl/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}

# Write test data
# Set the path to "secret/data/mysql/*" if you are running `kv-v2`
path "secret/mysql/*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}

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

Scenario Introduction

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.

The basic workflow is: AppRole auth method workflow

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

  1. Enable AppRole auth method
  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 method

(Persona: admin)

Like many other auth methods, AppRole must be enabled before it can be used.

CLI command / API call using cURL / Web UI

CLI command

Enable approle auth method by executing the following command:

$ vault auth enable approle

API call using cURL

Enable approle auth method by mounting its endpoint at /sys/auth/approle:

$ curl --header "X-Vault-Token: <TOKEN>" \
       --request POST \
       --data <PARAMETERS> \
       <VAULT_ADDRESS>/v1/sys/auth/approle

Where <TOKEN> is your valid token, and <PARAMETERS> holds configuration parameters of the method.

Example:

$ curl --header "X-Vault-Token: ..." \
       --request POST \
       --data '{"type": "approle"}' \
       http://127.0.0.1:8200/v1/sys/auth/approle

The above example passes the type (approle) in the request payload at the sys/auth/approle endpoint.

Web UI

  1. Open a web browser and launch the Vault UI (e.g. http://127.0.0.1:8200/ui) and then login.

  2. Select the Access tab on top.

  3. Select Enable new method. Enable AppRole

  4. Select the AppRole radio button and click Next.

  5. Leave the path as the defaulted path and click Enable Method.

  6. Select approle without making any update. Enable AppRole

Step 2: Create a role with policy attached

(Persona: admin)

When you enabled the AppRole auth method, it gets mounted at the /auth/approle path. In this example, you are going to create a role for the app persona (jenkins in our scenario).

First, create a policy file named jenkins-pol.hcl with following policies to set appropriate permissions:

# Read-only permission on 'secret/data/mysql/*' path
path "secret/data/mysql/*" {
  capabilities = [ "read" ]
}

CLI command / API call using cURL / Web UI

CLI command

  1. Before creating a role, create a jenkins policy:

    $ vault policy write jenkins jenkins-pol.hcl
    
  2. The command to create a new AppRole:

    $ vault write auth/approle/role/<ROLE_NAME> [parameters]
    

    There are a number of parameters that you can set on a role. If you want to limit the use of the generated secret ID, set secret_id_num_uses or secret_id_ttl parameter values. Similarly, you can specify token_num_uses and token_ttl. You may never want the app token to expire. In such a case, specify the period so that the token generated by this AppRole is a periodic token. To learn more about periodic token, refer to the Tokens guide.

    The following command creates a role named jenkins with jenkins policy attached. The token's time-to-live (TTL) is set to 1 hour and can be renewed for up to 4 hours of its first creation. (NOTE: This example creates a role which operates in pull mode.)

    $ vault write auth/approle/role/jenkins token_policies="jenkins" \
            token_ttl=1h token_max_ttl=4h
    
    # Read the jenkins role
    $ vault read auth/approle/role/jenkins
    Key                        Value
    ---                        -----
    bind_secret_id             true
    local_secret_ids           false
    secret_id_bound_cidrs      <nil>
    secret_id_num_uses         0
    secret_id_ttl              0s
    token_bound_cidrs          []
    token_explicit_max_ttl     0s
    token_max_ttl              4h
    token_no_default_policy    false
    token_num_uses             0
    token_period               0s
    token_policies             [jenkins]
    token_ttl                  1h
    token_type                 default
    

API call using cURL

  1. Before creating a role, create jenkins policy:

    $ tee payload.json <<"EOF"
    {
      "policy": "# Read-only permission on 'secret/data/mysql/*' path\npath \"secret/data/mysql/*\" {\n  capabilities = [ \"read\" ]\n}"
    }
    EOF
    
    $ curl --header "X-Vault-Token: ..." --request PUT --data @payload.json \
         http://127.0.0.1:8200/v1/sys/policies/acl/jenkins
    
  2. Now, you are ready to create a role.

    The following example creates a role named jenkins with a jenkins policy attached. The token's time-to-live (TTL) is set to 1 hour and can be renewed for up to 4 hours of its first creation. (NOTE: This example creates a role which operates in pull mode.)

    $ tee payload.json <<EOF
    {
      "token_policies": "jenkins",
      "token_ttl": "1h",
      "token_max_ttl": "4h"
    }
    EOF
    
    $ curl --header "X-Vault-Token: ..." --request POST \
           --data @payload.json \
           http://127.0.0.1:8200/v1/auth/approle/role/jenkins
    

    There are a number of parameters that you can set on a role. If you want to limit the use of the generated secret ID, set secret_id_num_uses or secret_id_ttl parameter values. Similarly, you can specify token_num_uses and token_ttl. You may never want the app token to expire. In such a case, specify the period so that the token generated by this AppRole is a periodic token. To learn more about periodic tokens, refer to the Tokens guide.

  1. Review the Jenkins role you just created:

    $ curl --header "X-Vault-Token: ..." --request GET \
            http://127.0.0.1:8200/v1/auth/approle/role/jenkins | jq
    {
      ...
      "data": {
        "bind_secret_id": true,
        "local_secret_ids": false,
        "secret_id_bound_cidrs": null,
        "secret_id_num_uses": 0,
        "secret_id_ttl": 0,
        "token_bound_cidrs": [],
        "token_explicit_max_ttl": 0,
        "token_max_ttl": 14400,
        "token_no_default_policy": false,
        "token_num_uses": 0,
        "token_period": 0,
        "token_policies": [
          "jenkins"
        ],
        "token_ttl": 3600,
        "token_type": "default"
      },
      ...
    }
    

Web UI

  1. In the Web UI (e.g. http://127.0.0.1:8200/ui), select the Policies tab, and then select Create ACL policy.

  2. Toggle Upload file sliding switch, and click Choose a file to select your jenkins-pol.hcl file you authored. This loads the policy and sets the Name to be jenkins-pol. Modify the policy name to jenkins.

  3. Click Create Policy to complete.

  4. Click the Vault CLI shell icon (>_) to open a command shell. Execute the following command to create a new role, jenkins: vault write auth/approle/role/jenkins token_policies="jenkins" token_ttl=1h token_max_ttl=4h

    Create a role

Step 3: Get Role ID and Secret ID

The Role ID and Secret ID are like a username and password that a machine or app uses to authenticate.

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

To retrieve the Role ID, invoke the auth/approle/role/<ROLE_NAME>/role-id endpoint. To generate a new Secret ID, invoke the auth/approle/role/<ROLE_NAME>/secret-id endpoint.

CLI command / API call using cURL / Web UI

CLI command

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

  1. Execute the following command to retrieve the secret ID for the jenkins role.

    $ vault read auth/approle/role/jenkins/role-id
    
    Key     Value
    ---     -----
    role_id 675a50e7-cfe0-be76-e35f-49ec009731ea
    
  2. Execute the following command to generate a secret ID for the jenkins role.

    $ 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
    

    If you specified secret_id_ttl, secret_id_num_uses, or bound_cidr_list on the role in Step 2, the generated secret ID carries out the conditions.

API call using cURL

  1. To read the Role ID for the jenkins role:

    $ curl --header "X-Vault-Token:..." \
           http://127.0.0.1:8200/v1/auth/approle/role/jenkins/role-id | jq
    {
      ...
      "data": {
        "role_id": "ac78519a-125d-1bcd-2d60-4baec05876d2"
      },
      ...
    }
    
  2. To generate a new Secret ID for the jenkins role:

    $ curl --header "X-Vault-Token:..." \
           --request POST \
           http://127.0.0.1:8200/v1/auth/approle/role/jenkins/secret-id | jq
    {
      ...
      "data": {
        "secret_id": "985a85b2-4490-2d7c-fcde-411063eab7e4",
        "secret_id_accessor": "1980f81e-7ecc-6781-0c99-3a1765be8aaa"
      },
      ...
    }
    

    You can pass parameters in the request payload, or invoke the API with an empty payload.

    If you specified secret_id_ttl, secret_id_num_uses, or bound_cidr_list on the role in Step 2, the generated secret ID carries out the conditions.

Web UI

  1. In the Web UI, click the Vault CLI shell icon (>_) to open a command shell if it's not already opened.

  2. Execute the following command to read the Role ID:

    vault read auth/approle/role/jenkins/role-id
    
  3. Execute the following command to generate a new Secret ID:

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

    NOTE: The -force flag forces the write operation to continue without any data values specified.

    Credentials

Step 4: Login with Role ID & Secret ID

(Persona: app)

The client (in this case, Jenkins) uses the role ID and secret ID passed by the admin to authenticate with Vault. If Jenkins did not receive the role ID and/or secret ID, the admin needs to investigate.

CLI command / API call using cURL

CLI command

To login, use the auth/approle/login endpoint by passing the role ID and secret ID.

Example:

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

  Key                     Value
  ---                     -----
  token                   s.ncEw5bAZJqvGJgl8pBDM0C5h
  token_accessor          gIQFfVhUd8fDsZjC7gLBMnQu
  token_duration          1h
  token_renewable         true
  token_policies          ["default" "jenkins"]
  identity_policies       []
  policies                ["default" "jenkins"]
  token_meta_role_name    jenkins

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

API call using cURL

To login, use the auth/approle/login endpoint by passing the role ID and secret ID in the request payload.

Example:

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

$ curl --request POST --data @payload.json http://127.0.0.1:8200/v1/auth/approle/login | jq
{
  ...
  "auth": {
    "client_token": "s.Aiq8dZCsWhfVtmsZETuf3NW8",
    "accessor": "XSU8ns7ogKzoriQbVLzDdN3L",
    "policies": [
      "default",
      "jenkins"
    ],
    "token_policies": [
      "default",
      "jenkins"
    ],
    "metadata": {
      "role_name": "jenkins"
    },
    ...
  }
}

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

Step 5: Read secrets using the AppRole token

(Persona: app)

Once receiving a token from Vault, the client can make future requests using this token.

CLI command / API call using cURL

CLI command

Example:

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

$ VAULT_TOKEN=s.ncEw5bAZJqvGJgl8pBDM0C5h vault kv get secret/mysql/webapp
No value found at secret/mysql/webapp

Or you can first authenticate with Vault using the client_token.

$ vault login s.ncEw5bAZJqvGJgl8pBDM0C5h
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                     Value
---                     -----
token                   s.ncEw5bAZJqvGJgl8pBDM0C5h
token_accessor          gIQFfVhUd8fDsZjC7gLBMnQu
token_duration          55m17s
token_renewable         true
token_policies          ["default" "jenkins"]
identity_policies       []
policies                ["default" "jenkins"]
token_meta_role_name    jenkins

$ vault kv get secret/mysql/webapp
No value found at secret/mysql/webapp

Since there is no value at secret/mysql/webapp, it returns a "no value found" message.

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

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

$ vault kv put secret/mysql/webapp @mysqldb.txt

Now, try to read secrets from secret/mysql/webapp using the 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 --header "X-Vault-Token: 3e7dd0ac-8b3e-8f88-bb37-a2890455ca6e" \
       --request GET \
       http://127.0.0.1:8200/v1/secret/data/mysql/webapp | jq
{
  "errors": []
}

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

Optional: Using the admin user's token, create some secrets in the secret/mysql/webapp path.

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

$ curl --header "X-Vault-Token: ..." --request POST --data @mysqldb.txt \

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

Response Wrap the Secret ID

The Role ID is equivalent to a username, and Secret ID is the corresponding password. The app needs both to log in with Vault. Naturally, the next question becomes how to deliver those values to the client securely.

A common solution involves three personas instead of two: admin, app, and trusted entity. The trusted entity delivers the Role ID and Secret ID to the client by separate means.

For example, Terraform as a trusted entity can deliver the Role ID onto the virtual machine. When the app runs on the virtual machine, the Role ID already exists on the virtual machine.

AppRole auth method workflow

Secret ID is like a password. To keep the Secret ID confidential, use response wrapping so that only the expecting client can unwrap the Secret ID.

In Step 3, you executed the following command to retrieve the Secret ID:

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

Instead, use response wrapping by passing the -wrap-ttl parameter:

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

Key                              Value
---                              -----
wrapping_token:                  s.2kAzCgg1kN7vdpE1xxZxzpug
wrapping_accessor:               1N3YCs02iuZ75OCTi4eN0tuA
wrapping_token_ttl:              1m
wrapping_token_creation_time:    2019-12-16 17:17:13.956126 -0800 PST
wrapping_token_creation_path:    auth/approle/role/jenkins/secret-id

Send this wrapping_token to the client so that the response can be unwrapped and obtain the Secret ID.

$ VAULT_TOKEN=s.2kAzCgg1kN7vdpE1xxZxzpug vault unwrap

Key                   Value
---                   -----
secret_id             7673bcf6-bbba-0fa6-a54c-51a6a3219c92
secret_id_accessor    e0104ca1-0afd-5d90-3b99-646bbcb5c179

To learn more about the wrapping token, read the Cubbyhole Response Wrapping guide.

Limit the Secret ID Usages

Treat the Secret ID like a password and force it to be regenerated after a number of use.

To do so, update the role definition with secret_id_num_uses set.

CLI command

$ vault write auth/approle/role/jenkins token_policies="jenkins" \
        token_ttl=1h token_max_ttl=4h \
        secret_id_num_uses=10

API call using cURL

$ tee payload.json <<EOF
{
  "token_policies": "jenkins",
  "token_ttl": "1h",
  "token_max_ttl": "4h",
  "secret_id_num_uses": 10
}
EOF

$ curl --header "X-Vault-Token: ..." --request POST \
       --data @payload.json \
       http://127.0.0.1:8200/v1/auth/approle/role/jenkins

In this example, a Secret ID of the jenkins role can be used for up to 10 times to authenticate and fetch a client token. After the number of uses is reached, the Secret ID expires and you would need to generat a new one. This is similar to forcing a password rotation.

Help and Reference