mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-09 16:17:01 +02:00
* Fix regexes for `sys/raw/` and `sys/leases/lookup/` to match prevailing conventions There are several endpoints in Vault which take an arbitrary path as the last parameter. Many of these are defined in terms of the `framework.MatchAllRegex` helper. Some were not, and were defined using custom regexes which gave rise to multiple OpenAPI endpoints - one with the path parameter, and one without. We need to fix these definitions, because they give rise to a very unnatural result when used to generate a client API - for example, you end up with `LeasesLookUp()` which is only capable of being used to list the very top level of the hierarchical collection of leases, and `LeasesLookUpWithPrefix(prefix)` which must be used for all deeper levels. This PR changes the regexes used for `sys/raw/` and `sys/leases/lookup/` to be consistent with the approach used for other well-known similar endpoints, such as `cubbyhole/`, `kv-v1/` and `kv-v2/metadata/`. This PR does have a very small compatibility issue, which I think is tolerable: prior to this change, `sys/raw` with no trailing slash was considered a valid endpoint, and now it will no longer be. One way to observe this is to try `vault path-help sys/raw` - before this change, it would work, after, it will not. You would have to instead use `vault path-help sys/raw/foobar` to see the help. I also considered whether losing the ability to read/write/delete `sys/raw` would be an issue. In each case, the precise HTTP result code will change, but each of these were meaningless operations that make no sense - you cannot read/write/delete a "file" at the "root directory" of the underlying Vault storage. In fact, during testing, I discovered that currently, `vault write sys/raw x=y` when using Raft storage, will permanently break the Vault instance - it causes a panic within the Raft FSM, which re-occurs immediately on restarting the server! This PR also closes off that footgun / DoS vector. None of these issues apply to `sys/leases/lookup/`, as the existing regex in that case was already not matching the path without the trailing slash. * changelog * Realign hardcoded sudo paths with updated OpenAPI spec
86 lines
5.1 KiB
Go
86 lines
5.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package api
|
|
|
|
import (
|
|
"regexp"
|
|
)
|
|
|
|
// sudoPaths is a map containing the paths that require a token's policy
|
|
// to have the "sudo" capability. The keys are the paths as strings, in
|
|
// the same format as they are returned by the OpenAPI spec. The values
|
|
// are the regular expressions that can be used to test whether a given
|
|
// path matches that path or not (useful specifically for the paths that
|
|
// contain templated fields.)
|
|
var sudoPaths = map[string]*regexp.Regexp{
|
|
"/auth/token/accessors": regexp.MustCompile(`^/auth/token/accessors/?$`),
|
|
"/auth/token/revoke-orphan": regexp.MustCompile(`^/auth/token/revoke-orphan$`),
|
|
"/pki/root": regexp.MustCompile(`^/pki/root$`),
|
|
"/pki/root/sign-self-issued": regexp.MustCompile(`^/pki/root/sign-self-issued$`),
|
|
"/sys/audit": regexp.MustCompile(`^/sys/audit$`),
|
|
"/sys/audit/{path}": regexp.MustCompile(`^/sys/audit/.+$`),
|
|
"/sys/auth/{path}": regexp.MustCompile(`^/sys/auth/.+$`),
|
|
"/sys/auth/{path}/tune": regexp.MustCompile(`^/sys/auth/.+/tune$`),
|
|
"/sys/config/auditing/request-headers": regexp.MustCompile(`^/sys/config/auditing/request-headers$`),
|
|
"/sys/config/auditing/request-headers/{header}": regexp.MustCompile(`^/sys/config/auditing/request-headers/.+$`),
|
|
"/sys/config/cors": regexp.MustCompile(`^/sys/config/cors$`),
|
|
"/sys/config/ui/headers": regexp.MustCompile(`^/sys/config/ui/headers/?$`),
|
|
"/sys/config/ui/headers/{header}": regexp.MustCompile(`^/sys/config/ui/headers/.+$`),
|
|
"/sys/internal/inspect/router/{tag}": regexp.MustCompile(`^/sys/internal/inspect/router/.+$`),
|
|
"/sys/leases": regexp.MustCompile(`^/sys/leases$`),
|
|
// This entry is a bit wrong... sys/leases/lookup does NOT require sudo. But sys/leases/lookup/ with a trailing
|
|
// slash DOES require sudo. But the part of the Vault CLI that uses this logic doesn't pass operation-appropriate
|
|
// trailing slashes, it always strips them off, so we end up giving the wrong answer for one of these.
|
|
"/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup(?:/.+)?$`),
|
|
"/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`),
|
|
"/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`),
|
|
"/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`),
|
|
"/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`),
|
|
"/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`),
|
|
"/sys/raw/{path}": regexp.MustCompile(`^/sys/raw(?:/.+)?$`),
|
|
"/sys/remount": regexp.MustCompile(`^/sys/remount$`),
|
|
"/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`),
|
|
"/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`),
|
|
"/sys/rotate": regexp.MustCompile(`^/sys/rotate$`),
|
|
"/sys/seal": regexp.MustCompile(`^/sys/seal$`),
|
|
"/sys/step-down": regexp.MustCompile(`^/sys/step-down$`),
|
|
|
|
// enterprise-only paths
|
|
"/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`),
|
|
"/sys/replication/performance/primary/secondary-token": regexp.MustCompile(`^/sys/replication/performance/primary/secondary-token$`),
|
|
"/sys/replication/primary/secondary-token": regexp.MustCompile(`^/sys/replication/primary/secondary-token$`),
|
|
"/sys/replication/reindex": regexp.MustCompile(`^/sys/replication/reindex$`),
|
|
"/sys/storage/raft/snapshot-auto/config": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/?$`),
|
|
"/sys/storage/raft/snapshot-auto/config/{name}": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/[^/]+$`),
|
|
}
|
|
|
|
func SudoPaths() map[string]*regexp.Regexp {
|
|
return sudoPaths
|
|
}
|
|
|
|
// Determine whether the given path requires the sudo capability.
|
|
// Note that this uses hardcoded static path information, so will return incorrect results for paths in namespaces,
|
|
// or for secret engines mounted at non-default paths.
|
|
// Expects to receive a path with an initial slash, but no trailing slashes, as the Vault CLI (the only known and
|
|
// expected user of this function) sanitizes its paths that way.
|
|
func IsSudoPath(path string) bool {
|
|
// Return early if the path is any of the non-templated sudo paths.
|
|
if _, ok := sudoPaths[path]; ok {
|
|
return true
|
|
}
|
|
|
|
// Some sudo paths have templated fields in them.
|
|
// (e.g. /sys/revoke-prefix/{prefix})
|
|
// The values in the sudoPaths map are actually regular expressions,
|
|
// so we can check if our path matches against them.
|
|
for _, sudoPathRegexp := range sudoPaths {
|
|
match := sudoPathRegexp.MatchString(path)
|
|
if match {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|