Deny policy doesn't appear to work as expected

I am new to Vault. I was am trying out a policy that is applying permissions to a kv (version 2) secrets store at a path “webserver/”. The policy I have set up looks like this:

path “webserver/*” {
capabilities = [“list”,“read”]

path “webserver/database/base” {
capabilities = [“deny”]

While the “webserver/*” capabilities are being applied, the “webserver/database/base” policy appear to be being noticed, but not applied. I see the following behavior:

C:\tools\vaultAgent>set vault_token=s.DhjUheYi0YL0xryFZMNxIU04

C:\tools\vaultAgent>vault token capabilities s.DhjUheYi0YL0xryFZMNxIU04 webserver/database/base

C:\tools\vaultAgent>vault kv get webserver/database/base
====== Metadata ======
Key Value

created_time 2021-07-02T14:45:11.0355888Z
deletion_time n/a
destroyed false
version 2

====== Data ======
Key Value

password testpassword
username testuser

I did not expect that the data at the “webserver/database/base” location would be visible, given that the capabilities indicated for the token are “deny”.

I presume I am doing something wrong, but it’s not clear to me what that is. Any hints would be greatly appreciated.


PS. This is Vault 1.7.3, running on Windows 10.

The paths you are using for your policies are incorrect. They need to list the full API paths that you want to control, not the user visible paths you might see in the UI.

In particular you need to include the mount name at the start (often secrets/ for kv) and for kv2 you need to include the /data/ API piece (as well as any other ones you wish to allow access via the UI, version history or deletion).

Hi Stuart,

Thanks for the response, but I don’t think that’s the problem. When I enabled the Secrets Engine, I used the following command:

vault secrets enable -path=webserver -version=2 kv

So, I believe I’ve specified the full path. Unless there’s something else that would put something into the path without me doing something explicitly, I am guessing that the paths are ok.

The output from “vault secrets list” looks like this:

C:\tools\vault1>vault secrets list
Path Type Accessor Description

cubbyhole/ cubbyhole cubbyhole_64841b70 per-token private secret storage
database/ kv kv_36156121 n/a
identity/ identity identity_445a79b5 identity store
sys/ system system_96a983b1 system endpoints used for control, policy and debugging
totp/ totp totp_c5dab17d n/a
transit/ transit transit_cee13a8b n/a
webserver/ kv kv_93ccc8ed n/a

So it appears that Vault thinks it’s at webserver/, too.


Change this

to this

path “webserver/data/database/base” {
  capabilities = [“deny”]

That should get you what you’re after.

As @stuart-c mentioned you need to build ACLs around the API paths. KVv2 can be a bit confusing at first as there are some obfuscated paths when using the GUI and CLI. Checkout the API docs for full details for relevant pathing in your ACLs: KV - Secrets Engines - HTTP API | Vault by HashiCorp

Hi Jeff,

Thanks for that, it does now behave as I’d expected. It is still a bit confusing, though, since if I use the “vault token capabilities webserver/database/base”, it shows the top-level capabilities and not deny.

It’d really be nice if the CLI’s behavior was the same as the API’s in this regard.

Thanks again to both yourself and to Stuart for taking the time to respond.


When you use the vault token capabilities <path> you’re specifying the API path.

If you try vault token capabilities webserver/data/database/base you should see the deny.

But I agree the vault kv [options] abstractions does not do any favors for those trying to get an initial grasp on ACLs. With that said, if you were to start with a KVv1 secret engine things would make much more sense as the v1 pathing is very much in line with your expectations (note that you’d be using vault read <path> instead of vault kv get <path> for v1).

Thanks, I will give kv v.1 a try. We don’t really need versioned secrets just yet anyway.

I did do the capabilities thing on the full API path, and it did show me “deny”, but the confusing thing was that I was getting a “permission denied” error when I tried to do a “vault kv get …” using the path without the /data/ in it.

Thanks again.

Sounds good.

Just a note about the versioning - if you accidentally delete or change a KV secret version in v2 you have a way of getting it back. With KVv1 once it’s gone it’s gone without doing a full Vault restore from backup.

vault kv get <path> basically translates to vault read <mount>/data/<rest_of_path> so the result you got does make sense.