open-vault/website/pages/guides/identity/policy-templating.mdx
Jeff Escalante 8eed94b072
🌷 Docs Website Maintenance (#8985)
* website maintenance round
* improve docs, revert bug workaround as it was fixed
* boost memory
* remove unnecessary code
2020-05-21 13:18:17 -04:00

611 lines
21 KiB
Plaintext

---
layout: guides
page_title: ACL Policy Path Templating - Guides
description: 'As of 0.11, ACL policies support templating to allow non-static policy paths.'
---
# 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](/guides/identity/policies) guide walks you through the
creation of ACL policies in Vault.
-> This guide highlights the use of ACL templating which was introduced in
**Vault 0.11**.
## Reference Material
- [Templated Policies](/docs/concepts/policies#templated-policies)
- [Policy API](/api/system/policy)
- [Identity: Entities and Groups](/guides/identity/identity)
- [Streamline Secrets Management with Vault Agent and Vault 0.11](https://youtu.be/zDnIqSB4tyA?t=24m37s)
~> **NOTE:** An [interactive
tutorial](https://www.katacoda.com/hashicorp/scenarios/vault-policy-templating)
is also available if you do not have a Vault environment to perform the steps
described in this guide.
## Estimated Time to Complete
10 minutes
## Challenge
The only way to specify non-static paths in ACL policies was to use globs (`*`)
at the end of paths.
```hcl
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](/docs/enterprise/sentinel).
## 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.
-> **NOTE:** This feature leverages [Vault
Identities](/docs/secrets/identity) to inject values into 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](/intro/getting-started/install) guide to install Vault.
Alternately, you can use the [Vault
Playground](https://www.katacoda.com/hashicorp/scenarios/vault-playground)
environment.
~> This guide assumes that you know how to create ACL policies. If you don't,
go through the interactive [Policy
tutorial](https://www.katacoda.com/hashicorp/scenarios/vault-policies) or
[Policies](/guides/identity/policies) guide first.
### 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](/guides/identity/policies#policy-requirements) 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](#step1)
1. [Deploy your policy](#step2)
1. [Setup an entity and a group](#step3)
1. [Test the ACL templating](#step4)
### Step 1: Write templated ACL policies ((#step1))
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.ids.<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 |
-> **NOTE:** Identity groups are not directly attached to a token and an entity
can be associated with multiple groups. Therefore, in order to reference a
group, the **group ID** or **group name** must be provided (e.g.
`identity.groups.ids.59f001d5-dd49-6d63-51e4-357c1e7a4d44.name`).
Example:
This policy allows users to change their own password given that the username
and password are defined in the `userpass` auth method. The mount accessor
value (`auth_userpass_6671d643` in this example) can be read from the `sys/auth` endpoint.
```hcl
path "auth/userpass/users/{{identity.entity.aliases.auth_userpass_6671d643.name}}" {
capabilities = [ "update" ]
allowed_parameters = {
"password" = []
}
}
```
#### Write the following policies:
User template (`user-tmpl.hcl`)
```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`)
```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 ((#step2))
- [CLI command](#step2-cli)
- [API call using cURL](#step2-api)
- [Web UI](#step2-ui)
#### CLI command ((#step2-cli))
```shell
# 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 ((#step2-api))
To create a policy, use the `/sys/policies/acl` endpoint:
```shell-session
$ 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:
```shell
# 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 ((#step2-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**.
1. Toggle **Upload file**, and click **Choose a file** to select the
`user-tmpl.hcl` file you wrote at [Step 1](#step1).
![Create Policy](/img/vault-ctrl-grp-2.png)
This loads the policy and sets the **Name** to `user-tmpl`.
1. Click the **Create Policy** button.
1. Repeat the steps to create the `group-tmpl` policy.
### Step 3: Setup an entity and a group ((#step3))
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](/img/vault-acl-templating.png)
-> This step only demonstrates CLI commands and Web UI to create
entities and groups. Refer to the [Identity - Entities and
Groups](/guides/identity/identity) guide if you need the full details.
- [CLI command](#step3-cli)
- [Web UI](#step3-ui)
#### CLI command ((#step3-cli))
The following command uses [`jq`](https://stedolan.github.io/jq/download/) tool
to parse JSON output.
```shell
# 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 ((#step3-ui))
1. Click the **Access** tab, and select **Enable new method**.
1. Select **Username & Password** from the **Type** drop-down menu.
1. Click **Enable Method**.
1. Click the Vault CLI shell icon (**`>_`**) to open a command shell. Enter the
following command to create a new user, **`bob`**.
```plaintext
$ vault write auth/userpass/users/bob password="training"
```
![Create Policy](/img/vault-ctrl-grp-3.png)
1. Click the icon (**`>_`**) again to hide the shell.
1. From the **Access** tab, select **Entities** and then **Create entity**.
1. Enter **`bob_smith`** in the **Name** field and enter **`user-tmpl`** in the
**Policies** filed.
1. Click **Create**.
1. Select **Add alias**. Enter **`bob`** in the **Name** field and select
**`userpass/ (userpass)`** from the **Auth Backend** drop-down list.
1. Select the **`bob_smith`** entity and copy its **ID** displayed under the
**Details** tab.
1. Click **Groups** from the left navigation, and select **Create group**.
1. 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](/img/vault-acl-templating-2.png)
1. Click **Create**.
### Step 4: Test the ACL templating ((#step4))
- [CLI command](#step4-cli)
- [API call using cURL](#step4-api)
- [Web UI](#step4-ui)
#### CLI Command ((#step4-cli))
1. Enable key/value v2 secrets engine at `user-kv` and `group-kv` paths.
```plaintext
$ vault secrets enable -path=user-kv kv-v2
$ vault secrets enable -path=group-kv kv-v2
```
1. Log in as **`bob`**.
```plaintext
$ 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
```
1. 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!
```plaintext
$ 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
```
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.
```plaintext
$ 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
```
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](#step2), you saved the `education` group ID in the `group_id.txt` file.
```plaintext
$ 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.
```plaintext
$ 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 ((#step4-api))
1. Enable key/value v2 secrets engine at `user-kv` and `group-kv` paths.
```plaintext
$ 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
```
1. Log in as **`bob`**.
```plaintext
$ 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`.
1. 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!
```plaintext
$ 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
```
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.
```plaintext
$ 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
```
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.
```plaintext
$ 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](#step2). (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.
```plaintext
$ curl --header "X-Vault-Token: <bob_client_token>" \
http://127.0.0.1:8200/v1/identity/group/id/<education_group_id>
```
#### Web UI ((#step4-ui))
1. In **Secrets** tab, select **Enable new engine**.
1. Select the radio-button for **KV**, and then click **Next**.
1. Enter **`user-kv`** in the path field, and then select **2** for KV
version.
1. Click **Enable Engine**.
1. Return to **Secrets** and then select **Enable new engine** again.
1. Select the radio-button for **KV**, and then click **Next**.
1. Enter **`group-kv`** in the path field, and then select **2** for KV
version.
1. Click **Enable Engine**.
1. Now, sign out as the current user so that you can log in as `bob`. ![Sign
off](/img/vault-acl-templating-3.png)
1. In the Vault sign in page, select **Username** and then enter **`bob`** in
the **Username** field, and **`training`** in the **Password** field.
1. Click **Sign in**.
1. 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**.
1. Enter **`bob_smith/apikey`** in the **PATH FOR THIS SECRET** field,
**`webapp`** in the key field, and **`12344567890`** in its value field.
1. Click **Save**. You should be able to perform this successfully.
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/*`**". From the **Secrets** tab, select
**`group-kv`** secrets engine, and then select **Create secret**.
1. 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.
1. Click **Save**. You should be able to perform this successfully.
1. 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.
1. Select **Groups**, and then **`education`**.
1. Select **Edit group**. Add a new metadata where the key is
**`contact_email`** and its value is **`james@example.com`**.
1. Click **Save**. The group metadata should be successfully updated.
## Next steps
To learn about Sentinel policies to implement finer-grained policies, refer to
the [Sentinel Policies](/guides/identity/sentinel) guide.