Vault
Simplify management of Vault policies with ACL policy templating
Vault operates on a secure by default standard. An empty policy grants no permissions to Vault resources. You must create policies to define the behavior of clients and set up role-based access control (RBAC) by specifying capabilities (authorization) in Vault.
Since everything in Vault is path based, policy authors must be aware of existing paths as well as new paths that may be created.
If you are not familiar with Vault ACL policies, follow the access controls with Vault policies tutorial.
Challenge
You can specify non-static paths in ACL policies by using globs (*)
at the end of paths, or the plus-sign (+) for a single directory wildcard
match.
path "transit/keys/*" {
capabilities = [ "read" ]
}
path "secret/+/apikey" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
Using wildcards helps simplify policy management, but makes delegation tasks challenging. For example,
allowing a user to change their own password by invoking the
auth/userpass/users/<user_name>/password endpoint requires either a policy
for every user or requires the use of Sentinel, which is a part of Vault
Enterprise.
Solution
ACL templating allows you to use a subset of user information within ACL policy paths.
In this tutorial, you will write a policy with templated paths to allow access to Vault.
Prerequisites
Launch Terminal
This tutorial includes a free interactive command-line lab that lets you follow along on actual cloud infrastructure.
Scenario Introduction
Assume you have the following policy requirements:
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>).
Set up the lab
Open a terminal and start a Vault dev server with the literal string
rootas the root token value, and enable TLS.$ vault server -dev -dev-root-token-id root -dev-tlsThe dev server listens on the loopback interface at 127.0.0.1 on TCP port 8200 with TLS enabled. At runtime, the dev server also automatically unseals, and prints the unseal key and initial root token values to the standard output.
Root tokens
The dev mode server starts with an initial root token value set. Root token use should be extremely guarded in production environments because it provides full access to the Vault server. You can supply the root token value to start Vault in dev mode for convenience and to keep the steps here focused on the learning goals of this tutorial.In a new terminal, export the
VAULT_ADDRandVAULT_CACERTenvironment variables using the commands suggested in your Vault dev server output.Copy each command (without the
$) from the server output, and paste it into the new terminal session.Example:
export VAULT_ADDR='https://127.0.0.1:8200'Example:
export VAULT_CACERT='/var/folders/qr/zgztx0sj6n1dxy86sl36ntnw0000gn/T/vault-tls3037226588/vault-ca.pem'Remember to use your dev server's values, not the examples shown here.
Export an environment variable for the
vaultCLI to authenticate with the Vault server.$ export VAULT_TOKEN=rootThe Vault server is ready.
Policy requirements
Since this tutorial demonstrates the creation of an admin policy, log in with the
root token.
Create templated ACL policies
Policy authors can pass in a policy path containing double curly braces as
templating delimiters: {{<parameter>}}.
Identity groups are not directly attached to a token and you can associate an
entity with several groups. To reference a group, you must specify the group ID
or group name (for example, identity.groups.ids.59f001d5-dd49-6d63-51e4-357c1e7a4d44.name).
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.entity.aliases.<mount accessor>.custom_metadata.<custom metadata key> | Custom metadata associated with the entity alias |
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 |
Example policy:
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.
path "auth/userpass/users/{{identity.entity.aliases.auth_userpass_6671d643.name}}" {
capabilities = [ "update" ]
allowed_parameters = {
"password" = []
}
}
Write user template policy file (
user-tmpl.hcl) using the entity name.$ tee user-tmpl.hcl <<EOF # 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"] } EOFWrite group template policy file (
group-tmpl.hcl) using the group name.$ tee group-tmpl.hcl <<EOF # 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" ] } EOFA good practice is to manage your Vault policies as code in a version control system such as Git.
Create a new policy in Vault named
user-tmplusing theuser-tmpl.hclfile.$ vault policy write user-tmpl user-tmpl.hclCreate a new policy in Vault named
group-tmplusing thegroup-tmpl.hclfile.$ vault policy write group-tmpl group-tmpl.hcl
Set up an entity and a group
Now that you created a policy, create an entity - bob_smith with a user bob, as its entity alias, and a group, education with the bob_smith entity as its group member.

This workflow demonstrates CLI commands and Vault UI to create entities and groups. Refer to the Implement identity entities and groups tutorial if you are not familiar with identities.
Enable the
userpassauth method.$ vault auth enable userpassCreate a new user,
bobwith password, "training".$ 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.txtCreate a
bob_smithentity and save the identity ID in theentity_id.txt.$ vault write -format=json identity/entity name="bob_smith" \ policies="user-tmpl" \ metadata=team="Processor" \ | jq -r ".data.id" > entity_id.txtAdd an entity alias for the
bob_smithentity.$ vault write identity/entity-alias name="bob" \ canonical_id=$(cat entity_id.txt) \ mount_accessor=$(cat accessor.txt)Create education group and add
bob_smithentity as a member. Save the generated group ID in thegroup_id.txt.$ 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
Test the ACL templating
Enable key/value v2 secrets engine at
user-kv.$ vault secrets enable -path=user-kv kv-v2Enable key/value v2 secrets engine at
group-kv.$ vault secrets enable -path=group-kv kv-v2Unset the
VAULT_TOKENenvironment variable so you can log in as a different user.$ unset VAULT_TOKENLog in as
bob.$ vault login -method=userpass username="bob" Password (will be hidden):Enter the password
trainingwhen prompted, and pressReturn.Example output:
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 bobRemember that
bobis a member of thebob_smithentity; therefore, the "user-kv/data/{{identity.entity.name}}/*" expression in theuser-tmplpolicy 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 1Note
Using control loops such as for loops for dynamic path templating may increase response times.
You set the region to
us-westfor theeducationgroup that thebob_smithbelongs to. Therefore, the "group-kv/data/education/{{identity.groups.names.education.metadata.region}}/*" expression in thegroup-tmplpolicy translates to "group-kv/data/education/us-west/*".$ 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 1Verify that you can update the group information. The
group-tmplpolicy permits "update" and "read" on the "identity/group/id/{{identity.groups.names.education.id}}" path. In Step 2, you saved theeducationgroup ID in thegroup_id.txtfile.$ 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 the updated data.
$ 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






