Problem creating a shared policy for all users

Hi,

This one should be rather simple. So I am using a file storage backend with userpass authentication.

For some reason I can’t read from kv/data/shared/* even though my policy expressly permits it.

So I’ve logged in as my root token.

Enabled v2 kv secrets engine:

vault secrets enable -version=2 kv

Now I’ve created a policy 1000.hcl

path "kv/data/users/1000/*" {
  capabilities = ["create", "update", "read"]
}

path "kv/delete/users/1000/*" {
  capabilities = ["delete", "update"]
}

path "kv/undelete/users/1000/*" {
  capabilities = ["update"]
}

path "kv/destroy/users/1000/*" {
  capabilities = ["update"]
}

path "kv/metadata/users/1000/*" {
  capabilities = ["list", "read", "delete"]
}

path "kv/metadata/" {
  capabilities = ["list"]
}

path "kv/metadata/users/" {
  capabilities = ["list"]
}

# I would have thought this would have worked
path "kv/data/shared/*" {
  capabilities = ["read"]
}

path "kv/metadata/shared/" {
  capabilities = ["list"]
}

Write the policy:

vault policy write 1000 1000.hcl
Success! Uploaded policy: 1000

Enable userpass and create a user:

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

vault write auth/userpass/users/1000 \
    password='mypass' \
    policies=admins,1000

Success! Data written to: auth/userpass/users/1000

I wrote some values to kv/data/shared as the root user:

vault kv put kv/data/shared foo=bar

And able to read them:

vault kv get kv/data/shared
====== Metadata ======
Key              Value
---              -----
created_time     2021-11-09T14:05:06.80154004Z
deletion_time    n/a
destroyed        false
version          1

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

Then I login as the user:

vault login -method=userpass username=1000
Password (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                    Value
---                    -----
token                  s.Sgxvzg5iQ71xY4jh1oVWLnYK
token_accessor         l6PzfE9EDwMOGCGDivzjpPl0
token_duration         768h
token_renewable        true
token_policies         ["1000" "admins" "default"]
identity_policies      []
policies               ["1000" "admins" "default"]
token_meta_username    1000

Now the problem seems to be I can’t read from kv/data/shared

$ vault kv get kv/data/shared

Error reading kv/data/data/shared: Error making API request.

URL: GET https://vault.example.com/v1/kv/data/data/shared
Code: 403. Errors:

* 1 error occurred:
	* permission denied

What am I doing wrong? The intention is to have some vault secrets that all the users can read.

I can get reading and writing to kv/users/1000/foo to work though.

When reading/writing to KVv2 paths you don’t need to specify the “data” portion of the path unless you’re using the API.

Instead of $ vault kv get kv/data/shared try $ vault get kv/shared

However, your policy doesn’t allow reading a secret called “shared” but rather list the secrets in the folder named “shared”. If you want to allow reading the secret named “shared” you should add a path of kv/data/shared with read capabilities. (You may want to add the glob character to the end of path "kv/metadata/shared/ so that subfolders can be listed as well.)

You should create a secret within the folder such as $ vault kv put kv/shared/my_secret foo=bar and then read with $ vault kv get kv/shared/my_secret

1 Like

Ah yes, I did read about that somewhere…

In my original post I had this (it was at the bottom after the other user policies).

# I would have thought this would have worked
path "kv/data/shared/*" {
  capabilities = ["read"]
}

path "kv/metadata/shared/" {
  capabilities = ["list"]
}

I think that might have been the problem. Yes you’re right, it was the extra data causing the issue.

Using the above rules but the below calls worked.

As root

vault kv put kv/shared/key foo=bar

As a user

vault kv get kv/shared/key

There are a bunch of issues:

a) Your policies path for the secret doesn’t match your actual secret, nor your ‘get’ path. The paths must match.

b) Other than the first path, the others will never match anything, not sure what you’re trying to do there.

c) You’re confusing CLI paths and API paths. The policy gets the API path ("/data/" get added to the engine path. ‘kv/ → kv/data/’. However when you use the vault CLI, you just use “kv/”.

I did look at the documentation for policies and kv and then by chance I found this example: HashiCorp Simple Policy. Configure Simple Vault Policies | by Alex N | Medium

Normally I don’t put a whole lot of confidence into blog articles like this as they’re often out of date or teach incorrect practices. However, after reading it further it did fit my usecase perfectly, and I had already set Vault up in a docker container on my server so I didn’t see too much reason to muck around with the dev server.

My specific usecase is storing some secret data that I use in my dotfiles. I use chezmoi to manage that.

A friend of mine also uses my dotfile templates. We have basically the same setup except for the secrets we use, (we both use a lot of terminal programs which are all config driven). The idea is to use this feature: How-To: Use Vault.

So with that some data can be shared in kv/shared and some will be specific to our own personal configuration kv/data/users/user etc.

So my question is. Is the policy described there incorrect or can you think of any improvements?

# The `create`, `read`, and `update` capabilities on the paths
# "kv/data/users/<user>/*" allow the creation, reading, and writing of keys on
# the paths "kv/users/<user>/*"
path "kv/data/users/1000/*" {
  capabilities = ["create", "update", "read"]
}

# The `delete` capability on the "kv/delete/users/<user>/*" paths allows the
# deletion of the latest version of keys on the "kv/users/<user>/*" paths.
# Deletion is not necessarily permanent.

# The `update` capability on the "kv/delete/users/<user>/*" paths allows the
# deletion of any version of keys on the "kv/users/<user>/*" paths.
path "kv/delete/users/1000/*" {
  capabilities = ["delete", "update"]
}

# The `update` capability on the "kv/undelete/users/<user>/*" paths allows the
# undeletion of keys on the "kv/users/<user>/*" paths.
path "kv/undelete/users/1000/*" {
  capabilities = ["update"]
}

# The `update` capability on the "kv/destroy/users/<user>/*" paths allows the
# permanent destruction of keys on the "kv/users/<user>/*" paths.
path "kv/destroy/users/1000/*" {
  capabilities = ["update"]
}

# The `list`, `read`, and `delete` capabilities on the
# "kv/metadata/users/<user>/*" paths allows the listing of, viewing of, and
# permanent removal of metadata of keys on the "kv/users/<user>/*" paths.
path "kv/metadata/users/1000/*" {
  capabilities = ["list", "read", "delete"]
}

# We also include the `list` capability on the "kv/metadata/" and
# "kv/metadata/users/" paths so that users can navigate to their secrets in the
# Vault UI.
path "kv/metadata/" {
  capabilities = ["list"]
}

path "kv/metadata/users/" {
  capabilities = ["list"]
}

# We also include the `read` capability on the "kv/data/shared/*" path so that
# users can read secrets shared between team members.
path "kv/data/shared/*" {
  capabilities = ["read"]
}

path "kv/metadata/shared/" {
  capabilities = ["list"]
}

In this case I called the user “1000” and “1001” to match our UIDs on the server. This was just because I didn’t want to use our names like the example in the blog article.

Is there a unique identifier for accounts I can use besides the name in the template?

Not to be a pain, but reading someone else’s post doesn’t equal reading the documentation. That’s someone’s post which may or may not be correct.

I have been using and administering Vault for almost a decade now and have never seen anyone use those paths to implement access – it’s possible that’s how it was original implemented, but they’re certainly no longer in the documents.

Please read the product’s documentation.

Not at all, and it is as I suspected.

You’re right. I re-wrote the policy and tested it and found that this was sufficient for creating, updating, deleting and destroying secrets.

path "kv/data/users/1000/*" {
  capabilities = ["create", "update", "read"]
}

I did however find I needed these in order to use the web interface to navigate my secrets. I found I had to explicitly have delete permission or I couldn’t delete through the web interface, though I it seemed the above rule was sufficient for update and create.

path "kv/metadata/" {
  capabilities = ["list"]
}

path "kv/metadata/users/" {
  capabilities = ["list"]
}

path "kv/metadata/users/1000/*" {
  capabilities = ["list", "delete"]
}

I required the same for the shared secrets. These are expected to stay same hence why they are read-only.

path "kv/data/shared/*" {
  capabilities = ["read"]
}

path "kv/metadata/shared/*" {
  capabilities = ["list"]
}

Overall I am happier with how much more simple the policy is. What I did notice is this has come up in the past: Vault KVV2 requires policy paths to include "data" but vault kv put to exclude it · Issue #7161 · hashicorp/vault · GitHub

If there is anything else you spot there that is wrong don’t hesitate to say.