mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	 25540ad222
			
		
	
	25540ad222
	
	
	
		
			
			* 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
 | |
| }
 |