Simplify management of policies with ACL policy 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 tutorial walks you through the creation of ACL policies in Vault.
This tutorial highlights the use of ACL templating which was introduced in Vault 0.11.
Challenge
The only way to specify non-static paths in ACL policies was to use globs (*
)
at the end of paths. Or, use plus-sign (+
) for a single directory wildcard
matching.
path "transit/keys/*" {
capabilities = [ "read" ]
}
path "secret/+/apikey" {
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.
Note
This feature leverages Vault Identities to inject values into ACL policy paths.
Prerequisites
To perform the tasks described in this tutorial, you need to have an environment with Vault 0.11 or later. Refer to the Vault install guide to install Vault.
Launch Terminal
This tutorial includes a free interactive command-line lab that lets you follow along on actual cloud infrastructure.
Scenario Introduction
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 tutorial, you are going to perform the following steps:
Lab setup
Open a terminal and start a Vault dev server with
root
as the root token.$ vault server -dev -dev-root-token-id=root
The Vault dev server defaults to running at
127.0.0.1:8200
. The server is also initialized and unsealed.Insecure operation
Do not run a Vault dev server in production. This approach is only used here to simplify the unsealing process for this demonstration.
Export an environment variable for the
vault
CLI to address the Vault server.$ export VAULT_ADDR=http://127.0.0.1:8200
Export an environment variable for the
vault
CLI to authenticate with the Vault server.$ export VAULT_TOKEN=root
Note
For these tasks, you can use Vault's root token. However, it is recommended that root tokens are only used for enough initial setup or in emergencies. As a best practice, use tokens with an appropriate set of policies based on your role in the organization.
The Vault server is ready.
Policy requirements
Since this tutorial demonstrates the creation of an admin
policy, log in with the
root
token if possible. Otherwise, refer to the policy requirement in the
Policies tutorial.
Create 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.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 |
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.
path "auth/userpass/users/{{identity.entity.aliases.auth_userpass_6671d643.name}}" {
capabilities = [ "update" ]
allowed_parameters = {
"password" = []
}
}
Write user template policy (
user-tmpl.hcl
).user-tmpl.hcl
$ 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"] } EOF
Write group template policy (
group-tmpl.hcl
).group-tmpl.hcl
$ 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" ] } EOF
Create a new policy called
user-tmpl
.$ vault policy write user-tmpl user-tmpl.hcl
Create a new policy called
group-tmpl
.$ vault policy write group-tmpl group-tmpl.hcl
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.
This step only demonstrates CLI commands and Web UI to create entities and groups. Refer to the Identity - Entities and Groups tutorial if you need the full details.
The following command uses jq
tool
to parse JSON output.
Enable the
userpass
auth method.$ vault auth enable userpass
Create a new user,
bob
with 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.txt
Create a
bob_smith
entity 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.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 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-v2
Enable key/value v2 secrets engine at
group-kv
.$ vault secrets enable -path=group-kv kv-v2
Unset the
VAULT_TOKEN
environment variable so you can log in as a different user.$ unset VAULT_TOKEN
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 thebob_smith
entity; therefore, the "user-kv/data/{{identity.entity.name}}/*
" expression in theuser-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
NOTE: Using control loops such as for loops for dynamic path templating may increase response times.
The region was set to
us-west
for theeducation
group that thebob_smith
belongs to. Therefore, the "group-kv/data/education/{{identity.groups.names.education.metadata.region}}/*
" expression in thegroup-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 theeducation
group ID in thegroup_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