Identity and Access Management

ACL Policy Path Templating

Vault operates on a secure by default standard, and as such, an empty policy grants no permissions in the system. Therefore, policies must be created to govern the behavior of clients and instrument Role-Based Access Control (RBAC) by specifying access privileges (authorization).

Since everything in Vault is path based, policy authors must be aware of all existing paths as well as paths to be created.

The Policies guide walks you through the creation of ACL policies in Vault.

» Challenge

The only way to specify non-static paths in ACL policies was to use globs (*) at the end of paths.

path "transit/keys/*" {
  capabilities = [ "read" ]
}

path "secret/webapp_*" {
  capabilities = [ "create", "read", "update", "delete", "list" ]
}

This makes many management and delegation tasks challenging. For example, allowing a user to change their own password by invoking the auth/userpass/users/<user_name>/password endpoint can require either a policy for every user or requires the use of Sentinel which is a part of Vault Enterprise.

» Solution

As of Vault 0.11, ACL templating capability is available to allow a subset of user information to be used within ACL policy paths.

» Prerequisites

To perform the tasks described in this guide, you need to have an environment with Vault 0.11 or later. Refer to the Getting Started guide to install Vault.

» Policy requirements

Since this guide demonstrates the creation of an admin policy, log in with the root token if possible. Otherwise, refer to the policy requirement in the Policies guide.

» Steps

Assume that the following policy requirements were given:

  • Each user can perform all operations on their allocated key/value secret path (user-kv/data/<user_name>)

  • The education group has a dedicated key/value secret store for each region where all operations can be performed by the group members (group-kv/data/education/<region>)

  • The group members can update the group information such as metadata about the group (identity/group/id/<group_id>)

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

  1. Write templated ACL policies
  2. Deploy your policy
  3. Setup an entity and a group
  4. Test the ACL templating

» Step 1: Write templated ACL policies

Policy authors can pass in a policy path containing double curly braces as templating delimiters: {{<parameter>}}.

» Available Templating Parameters

Name Description
identity.entity.id The entity's ID
identity.entity.name The entity's name
identity.entity.metadata.<<metadata key>> Metadata associated with the entity for the given key
identity.entity.aliases.<<mount accessor>>.id Entity alias ID for the given mount
identity.entity.aliases.<<mount accessor>>.name Entity alias name for the given mount
identity.entity.aliases.<<mount accessor>>.metadata.<<metadata key>> Metadata associated with the alias for the given mount and metadata key
identity.groups.ids.<<group id>>.name The group name for the given group ID
identity.groups.names.<<group name>>.id The group ID for the given group name
identity.groups.names.<<group id>>.metadata.<<metadata key>> Metadata associated with the group for the given key
identity.groups.names.<<group name>>.metadata.<<metadata key>> Metadata associated with the group for the given key

Example:

This policy allows users to change their own password given that the username and password are defined in the userpass auth method.

path "auth/userpass/users/{{identity.entity.aliases.auth_userpass_6671d643.name}}/password" {
  capabilities = [ "update" ]
}

» Write the following policies:

User template (user-tmpl.hcl)

# Grant permissions on user specific path
path "user-kv/data/{{identity.entity.name}}/*" {
    capabilities = [ "create", "update", "read", "delete", "list" ]
}

# For Web UI usage
path "user-kv/metadata" {
  capabilities = ["list"]
}

Group template (group-tmpl.hcl)

# Grant permissions on the group specific path
# The region is specified in the group metadata
path "group-kv/data/education/{{identity.groups.names.education.metadata.region}}/*" {
    capabilities = [ "create", "update", "read", "delete", "list" ]
}

# Group member can update the group information
path "identity/group/id/{{identity.groups.names.education.id}}" {
  capabilities = [ "update", "read" ]
}

# For Web UI usage
path "group-kv/metadata" {
  capabilities = ["list"]
}

path "identity/group/id" {
  capabilities = [ "list" ]
}

» Step 2: Deploy your policy

CLI command / API call using cURL / Web UI

» CLI command

# Create the user-tmpl policy
$ vault policy write user-tmpl user-tmpl.hcl

# Create the group-tmpl policy
$ vault policy write group-tmpl group-tmpl.hcl

» API call using cURL

To create a policy, use the /sys/policies/acl endpoint:

$ curl --header "X-Vault-Token: <TOKEN>" \
       --request PUT \
       --data <PAYLOAD> \
       <VAULT_ADDRESS>/v1/sys/policies/acl/<POLICY_NAME>

Where <TOKEN> is your valid token, and <PAYLOAD> includes the policy name and stringified policy.

Example:

# API request payload for user-tmpl
$ tee payload_user.json <<EOF
{
  "policy": "path "user-kv/data/{{identity.entity.name}}/*" {\n capabilities = [ "create", "update", "read", "delete", "list" ]\n } ..."
}
EOF

# Create user-tmpl policy
$ curl --header "X-Vault-Token: ..." \
       --request PUT
       --data @payload_user.json \
       http://127.0.0.1:8200/v1/sys/policies/acl/user-tmpl

# API request payload for group-tmpl
$ tee payload_group.json <<EOF
{
   "policy": "path "group-kv/data/{{identity.group.id}}/*" {\n capabilities = [ "create", "update", "read", "delete", "list" ]\n }"
}
EOF

# Create group-tmpl policy
$ curl --header "X-Vault-Token: ..." \
       --request PUT
       --data @payload_group.json \
       http://127.0.0.1:8200/v1/sys/policies/acl/group-tmpl

» Web UI

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

  1. Click the Policies tab, and then select Create ACL policy.

  2. Toggle Upload file, and click Choose a file to select the user-tmpl.hcl file you wrote at Step 1. This loads the policy and sets the Name to user-tmpl. Create Policy

  3. Click the Create Policy button.

  4. Repeat the steps to create the group-tmpl policy.

» Step 3: Setup an entity and a group

Let's create an entity, bob_smith with a user bob as its entity alias. Also, create a group, education and add the bob_smith entity as its group member.

Entity & Group

CLI command / Web UI

» CLI command

The following command uses jq tool to parse JSON output.

# Enable userpass
$ vault auth enable userpass

# Create a user, bob
$ vault write auth/userpass/users/bob password="training"

# Retrieve the userpass mount accessor and save it in a file named, accessor.txt
$ vault auth list -format=json | jq -r '.["userpass/"].accessor' > accessor.txt

# Create bob_smith entity and save the identity ID in the entity_id.txt
$ vault write -format=json identity/entity name="bob_smith" policies="user-tmpl" \
        | jq -r ".data.id" > entity_id.txt

# Add an entity alias for the bob_smith entity
$ vault write identity/entity-alias name="bob" \
       canonical_id=$(cat entity_id.txt) \
       mount_accessor=$(cat accessor.txt)

# Finally, create education group and add bob_smith entity as a member
# Save the generated group ID in the group_id.txt file
$ vault write -format=json identity/group name="education" \
      policies="group-tmpl" \
      metadata=region="us-west" \
      member_entity_ids=$(cat entity_id.txt)  \
      | jq -r ".data.id" > group_id.txt

» Web UI

  1. Click the Access tab, and select Enable new method.

  2. Select Username & Password from the Type drop-down menu.

  3. Click Enable Method.

  4. Click the Vault CLI shell icon (>_) to open a command shell. Enter the following command to create a new user, bob. Create Policy

  5. Click the icon (>_) again to hide the shell.

  6. From the Access tab, select Entities and then Create entity.

  7. Enter bob_smith in the Name field and enter user-tmpl in the Policies filed.

  8. Click Create.

  9. Select Add alias. Enter bob in the Name field and select userpass/ (userpass) from the Auth Backend drop-down list.

  10. Select the bob_smith entity and copy its ID displayed under the Details tab.

  11. Click Groups from the left navigation, and select Create group.

  12. Enter education in the Name, and enter group-tmpl in the Policies fields. Under Metadata, enter region as a key and us-west as the key value. Enter the bob_smith entity ID in the Member Entity IDs field. Group

  13. Click Create.

» Step 4: Test the ACL templating

CLI command / API call using cURL / Web UI

» CLI Command

Enable key/value v2 secrets engine at user-kv and group-kv paths.

$ vault secrets enable -path=user-kv kv-v2

$ vault secrets enable -path=group-kv kv-v2

Log in as bob.

$ vault login -method=userpass username="bob" password="training"

Key                    Value
---                    -----
token                  5f2b2594-f0b4-0a7b-6f51-767345091dcc
token_accessor         78b652dd-4320-f18f-b882-0732b7ae9ac9
token_duration         768h
token_renewable        true
token_policies         ["default"]
identity_policies      ["group-tmpl" "user-tmpl"]
policies               ["default" "group-tmpl" "user-tmpl"]
token_meta_username    bob

Remember that bob is a member of the bob_smith entity; therefore, the "user-kv/data/{{identity.entity.name}}/*" expression in the user-tmpl policy translates to "user-kv/data/bob_smith/*". Let's test!

$ vault kv put user-kv/bob_smith/apikey webapp="12344567890"
Key              Value
---              -----
created_time     2018-08-30T18:28:30.845345444Z
deletion_time    n/a
destroyed        false
version          1

The region was set to us-west for the education group that the bob_smith belongs to. Therefore, the "group-kv/data/education/{{identity.groups.names.education.metadata.region}}/*" expression in the group-tmpl policy translates to "group-kv/data/education/us-west/*". Let's verify.

$ vault kv put group-kv/education/us-west/db_cred password="ABCDEFGHIJKLMN"
Key              Value
---              -----
created_time     2018-08-30T18:29:02.023749491Z
deletion_time    n/a
destroyed        false
version          1

Verify that you can update the group information. The group-tmpl policy permits "update" and "read" on the "identity/group/id/{{identity.groups.names.education.id}}" path. In Step 2, you saved the education group ID in the group_id.txt file.

$ vault write identity/group/id/$(cat group_id.txt) \
        policies="group-tmpl" \
        metadata=region="us-west" \
        metadata=contact_email="james@example.com"

Read the group information to verify that the data has been updated.

$ vault read identity/group/id/$(cat group_id.txt)

Key                  Value
---                  -----
alias                map[]
creation_time        2018-08-29T20:38:49.383960564Z
id                   d6ee454e-915a-4bef-9e43-4ffd7762cd4c
last_update_time     2018-08-29T22:52:42.005544616Z
member_entity_ids    [1a272450-d147-c3fd-63ae-f16b65b5ee02]
member_group_ids     <nil>
metadata             map[contact_email:james@example.com region:us-west]
modify_index         3
name                 education
parent_group_ids     <nil>
policies             [group-tmpl]
type                 internal

» API call using cURL

Enable key/value v2 secrets engine at user-kv and group-kv paths.

$ tee payload.json <<EOF
{
  "type": "kv",
  "options": {
    "version": "2"
  }
}
EOF

$ curl --header "X-Vault-Token: ..." \
       --request POST \
       --data @payload.json \
       https://127.0.0.1:8200/v1/sys/mounts/user-kv

$ curl --header "X-Vault-Token: ..." \
       --request POST \
       --data @payload.json \
       https://127.0.0.1:8200/v1/sys/mounts/group-kv

Log in as bob.

$ curl --request POST \
       --data '{"password": "training"}' \
       http://127.0.0.1:8200/v1/auth/userpass/login/bob | jq

Copy the generated client_token value for bob.

Remember that bob is a member of the bob_smith entity; therefore, the "user-kv/data/{{identity.entity.name}}/*" expression in the user-tmpl policy translates to "user-kv/data/bob_smith/*". Let's test!

$ curl --header "X-Vault-Token: <bob_client_token>" \
       --request POST \
       --data '{ "data": {"webapp": "12344567890"} }' \
       http://127.0.0.1:8200/v1/user-kv/data/bob_smith/apikey

The region was set to us-west for the education group that the bob_smith belongs to. Therefore, the "group-kv/data/education/{{identity.groups.names.education.metadata.region}}/*" expression in the group-tmpl policy translates to "group-kv/data/education/us-west/*". Let's verify.

$ curl --header "X-Vault-Token: <bob_client_token>" \
       --request POST \
       --data '{ "data": {"password": "ABCDEFGHIJKLMN"} }' \
       http://127.0.0.1:8200/v1/group-kv/data/education/us-west/db_cred

Verify that you can update the group information. The group-tmpl policy permits "update" and "read" on the "identity/group/id/{{identity.groups.names.education.id}}" path.

$ tee group_info.json <<EOF
{
  "metadata": {
    "region": "us-west",
    "contact_email": "james@example.com"
  },
  "policies": "group-tmpl"
}
EOF

$ curl --header "X-Vault-Token: <bob_client_token>" \
       --request POST \
       --data @group_info.json \
       http://127.0.0.1:8200/v1/identity/group/id/<education_group_id>

Where the group ID is the ID returned in Step 2. (NOTE: If you performed Step 2 using the CLI commands, the group ID is stored in the group_id.txt file. If you performed the tasks via Web UI, copy the education group ID from UI.)

Read the group information to verify that the data has been updated.

$ curl --header "X-Vault-Token: <bob_client_token>" \
       http://127.0.0.1:8200/v1/identity/group/id/<education_group_id>

» Web UI

  1. In Secrets tab, select Enable new engine.

  2. Select the radio-button for KV, and then click Next.

  3. Enter user-kv in the path field, and then select 2 for KV version.

  4. Click Enable Engine.

  5. Return to Secrets and then select Enable new engine again.

  6. Select the radio-button for KV, and then click Next.

  7. Enter group-kv in the path field, and then select 2 for KV version.

  8. Click Enable Engine.

  9. Now, sign out as the current user so that you can log in as bob. Sign
off

  10. In the Vault sign in page, select Username and then enter bob in the Username field, and training in the Password field.

  11. Click Sign in.

  12. Remember that bob is a member of the bob_smith entity; therefore, the "user-kv/data/{{identity.entity.name}}/*" expression in the user-tmpl policy translates to "user-kv/data/bob_smith/*". Select user-kv secrets engine, and then select Create secret.

  13. Enter bob_smith/apikey in the PATH FOR THIS SECRET field, webapp in the key field, and 12344567890 in its value field.

  14. Click Save. You should be able to perform this successfully.

  15. The region was set to us-west for the education group that the bob_smith belongs to. Therefore, the "group-kv/data/education/{{identity.groups.names.education.metadata.region}}/*" expression in the group-tmpl policy translates to "group-kv/data/education/us-west/*". From the Secrets tab, select group-kv secrets engine, and then select Create secret.

  16. Enter education/us-west/db_cred in the PATH FOR THIS SECRET field. Enter password in the key field, and ABCDEFGHIJKLMN in its value field.

  17. Click Save. You should be able to perform this successfully.

  18. To verify that you can update the group information which is allowed by the "identity/group/id/{{identity.groups.names.education.id}}" expression in the group-tmpl policy, select the Access tab.

  19. Select Groups, and then education.

  20. Select Edit group. Add a new metadata where the key is contact_email and its value is james@example.com.

  21. Click Save. The group metadata should be successfully updated.

» Help and Reference