'vault templating paths in policies results in permission denied

I'm trying to create a policy that allows for users to access a portion of the secret hierarchy based on their usernames. Rather than having a different policy for each user, I want to have one templated policy. I think this should work, but I keep getting permission denied errors. If I remove the templating and just hard-code the username in the policy path, secret retrieval works just fine, so it doesn't seem like it's any other part of the policy definition.

This is all with Vault 1.3.1, against a dev server, but the problem first came up on a non-dev server, with GCP/GCE authentication and database secrets, so it doesn't seem to be specific to any of those things, either.

Enable username/password authentication, and create a user that points to a new policy (to be defined later).

$ vault auth enable userpass
Success! Enabled userpass auth method at: userpass/

$ vault write auth/userpass/users/duvall policies=default,p2 password=duvall
Success! Data written to: auth/userpass/users/duvall

Login as this user and take a look at the token metadata.

$ vault login -method userpass username=duvall password=duvall

$ vault token lookup
Key                 Value
---                 -----
accessor            9ga3alRqZ6E3aSCEBNFWJY1X
creation_time       1581468214
creation_ttl        768h
display_name        userpass-duvall
entity_id           7513dc68-785b-d151-0efb-71315fc026dc
expire_time         2020-03-15T00:43:34.707416501Z
explicit_max_ttl    0s
id                  s.YZRQ3uclh2rg2H7gh3qH84P3
issue_time          2020-02-12T00:43:34.707423899Z
meta                map[username:duvall]
num_uses            0
orphan              true
path                auth/userpass/login/duvall
policies            [default p2]
renewable           true
ttl                 767h50m35s
type                service

Create the aforementioned policy with a path templated based on the metadata key username.

$ export VAULT_TOKEN=root

$ echo 'path "secret/data/role-secrets/{{identity.entity.metadata.username}}/*" {capabilities = ["read"]}' | vault policy write p2 -
Success! Uploaded policy: p2

Create a secret that matches the path in the policy.

$ vault kv put secret/role-secrets/duvall/s1 foo=bar
Key              Value
---              -----
created_time     2020-02-12T00:44:36.509412834Z
deletion_time    n/a
destroyed        false
version          1

As the user, reading the secret results in failure.

$ export VAULT_TOKEN=s.YZRQ3uclh2rg2H7gh3qH84P3

$ vault kv get secret/role-secrets/duvall/s1
Error making API request.

URL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/role-secrets/duvall/s1
Code: 403. Errors:

* preflight capability check returned 403, please ensure client's policies grant access to path "secret/role-secrets/duvall/s1/"

Rewrite the policy to remove the templating.

$ export VAULT_TOKEN=root

$ echo 'path "secret/data/role-secrets/duvall/*" {capabilities = ["read"]}' | vault policy write p2 -
Success! Uploaded policy: p2

This time, reading the secret succeeds.

$ export VAULT_TOKEN=s.YZRQ3uclh2rg2H7gh3qH84P3

$ vault kv get secret/role-secrets/duvall/s1
====== Metadata ======
Key              Value
---              -----
created_time     2020-02-12T00:44:36.509412834Z
deletion_time    n/a
destroyed        false
version          1

=== Data ===
Key    Value
---    -----
foo    bar

I'm not sure how relevant this is, but ... adding a metadata list capability to the policy changes the read error from a "preflight capability check" to a more normal "permission denied".

$ echo 'path "secret/metadata/*" {capabilities = ["list"]}\npath "secret/data/role-secrets/{{identity.entity.metadata.username}}/*" {capabilities = ["read"]}' | VAULT_TOKEN=root vault policy write p2 -
Success! Uploaded policy: p2

$ vault kv get secret/role-secrets/duvall/s1
Error reading secret/data/role-secrets/duvall/s1: Error making API request.

URL: GET http://127.0.0.1:8200/v1/secret/data/role-secrets/duvall/s1
Code: 403. Errors:

* 1 error occurred:
        * permission denied


Solution 1:[1]

You are missing a point that if you want to give access of secrets/database/rdb/ then you have to give read and list capabilities for path secrets, databse, rdb.

Now if you have multiple secrets stored in secrets/ path that you don't want to share then you have to give deny for that paths.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Bhargav Akhani