Open kkredit opened 2 years ago
(Note: I don't work for HashiCorp, I just came across this whilst browsing for some of my own issues. Everything said below is things I've learned whilst working with Vault.)
Vault accepts LIST
as a HTTP verb. I'd actually assumed it was a standard verb, and was surprised when I checked https://www.iana.org/assignments/http-methods/http-methods.xhtml and found it isn't!
Vault also accepts GET
with query-params ?list=true
as an alternative form of a list operation, for use when the HTTP client software doesn't allow sending a LIST
verb.
list
and read
capabilities are not equivalent at all - in Vault, an operation is either a list
or a read
, and requires the appropriate capability.
There are some rather complex and completely undocumented subtleties involved with how Vault matches list requests to ACL policies, which I ended up having to teach myself by reading the source code:
Whether the user includes it or not, all list
operations are canonicalised by Vault to end with a trailing /
, and this has an effect on what policy paths such requests match. (https://github.com/hashicorp/vault/blob/ec7c50a5035a3573154c4e89c7a5629b3dc6e156/http/logical.go#L75-L77 and similar code later in the file)
When processing a list
operation, there's an extra check for matching against a policy with the trailing /
chopped off again - BUT, and here's a tricky exception - it doesn't apply if there's a /+/ wildcard in the policy path - my guess is someone overlooked coding that case when /+/ wildcard support was added to Vault. (https://github.com/hashicorp/vault/blob/ec7c50a5035a3573154c4e89c7a5629b3dc6e156/vault/acl.go#L365-L372)
Put these together, and you get a variety of behaviours, some useful, some infuriating.
Useful: a policy like this:
path "auth/approle/role/*" {
capabilities = ["read", "list"]
}
turns out to be sufficient to allow vault list auth/approle/role
even though an understanding based on basic shell globbing would make you think it didn't match. You don't have to write this repetitive style of policy every time you want to manage a collection:
path "auth/approle/role" {
capabilities = ["list"]
}
path "auth/approle/role/*" {
capabilities = ["read"]
}
Useful: a policy like this:
path "auth/approle/role/some-role/secret-id" {
capabilities = ["update"]
control_group { ... all the usual control group stuff ... }
}
will apply a control group to the path for all operations. But, if you additionally define a policy with a trailing slash, and no control group:
path "auth/approle/role/some-role/secret-id/" {
capabilities = ["list"]
}
then list operations will match the second policy and not be subject to the control group.
(OK, this is somewhat less vital since support for controlled_capabilities
was added, but I haven't been able to upgrade to a version with that yet.)
Infuriating: a policy like this:
path "some-specific-namespace/auth/token/accessors" {
capabilities = ["list", "sudo"]
}
works as intended, but if you try to set up a policy covering wildcarded namespaces:
path "+/auth/token/accessors" {
capabilities = ["list", "sudo"]
}
it fails to allow the access! You have to write this policy with a trailing slash for it to take effect:
path "+/auth/token/accessors/" {
capabilities = ["list", "sudo"]
}
@taoism4504 - another issue where we may want to think about how we're describing capabilities in our docs. Thanks! :)
@maxb I cannot express how much your comprehensive description of this behavior rescued my sanity. Thank you so much.
Thank you - it's always nice to know my words had a positive effect on someone!
I guess the closest the documentation gets to what was described here is https://github.com/hashicorp/vault/blob/1361932d9151194ea06e9677394244729873d31c/website/content/docs/concepts/policies.mdx?plain=1#L197-L199
@solutiongeek any way this could be better?
Documentation issue
The "Policies > Capabilities" documentation (here) is somewhat misleading about the
list
capability. The docs say,And then include
This suggests that
LIST
is an HTTP verb, which it is not. My understanding is that alist
capability on/things/*
is equivalent to aread
capability (which isGET
) on/things
. Please clarify the description of thelist
capability.