From 3961d7b9cc583b93eaaaf0a5e1d91188762dfe32 Mon Sep 17 00:00:00 2001 From: Max Bowsher Date: Tue, 18 Jul 2023 12:20:34 +0100 Subject: [PATCH] api: Separate two distinct areas of code that were interleaved in one file (#21906) This is a code cleanup and addition of an explanatory comment. For some reason, the code related to the CLI guessing whether a path requires sudo, has been interleaved into plugin_helpers.go, which was previously purely code used on the server side in the implementation of Vault plugins. This remedies that by dividing the sudo paths code to a separate file, and adds a comment to plugin_helpers.go providing future readers with information about the overall theme of the file. No code has been changed - only moved and documented. --- api/plugin_helpers.go | 91 +++---------------- api/sudo_paths.go | 87 ++++++++++++++++++ ...gin_helpers_test.go => sudo_paths_test.go} | 0 3 files changed, 98 insertions(+), 80 deletions(-) create mode 100644 api/sudo_paths.go rename api/{plugin_helpers_test.go => sudo_paths_test.go} (100%) diff --git a/api/plugin_helpers.go b/api/plugin_helpers.go index b96cb9becf..a8d2325297 100644 --- a/api/plugin_helpers.go +++ b/api/plugin_helpers.go @@ -12,13 +12,23 @@ import ( "flag" "net/url" "os" - "regexp" "github.com/go-jose/go-jose/v3/jwt" "github.com/hashicorp/errwrap" ) +// This file contains helper code used when writing Vault auth method or secrets engine plugins. +// +// As such, it would be better located in the sdk module with the rest of the code which is only to support plugins, +// rather than api, but is here for historical reasons. (The api module used to depend on the sdk module, this code +// calls NewClient within the api package, so placing it in the sdk would have created a dependency cycle. This reason +// is now historical, as the dependency between sdk and api has since been reversed in direction.) +// Moving this code to the sdk would be appropriate if an api v2.0.0 release is ever planned. +// +// This helper code is used when a plugin is hosted by Vault 1.11 and earlier. Vault 1.12 and sdk v0.6.0 introduced +// version 5 of the backend plugin interface, which uses go-plugin's AutoMTLS feature instead of this code. + const ( // PluginAutoMTLSEnv is used to ensure AutoMTLS is used. This will override // setting a TLSProviderFunc for a plugin. @@ -33,56 +43,6 @@ const ( PluginUnwrapTokenEnv = "VAULT_UNWRAP_TOKEN" ) -// 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/?$`), - // TODO /auth/token/revoke-orphan requires sudo but isn't represented as such in the OpenAPI spec - "/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": regexp.MustCompile(`^/sys/leases/lookup/?$`), - "/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": regexp.MustCompile(`^/sys/raw$`), - "/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$`), - // TODO /sys/seal requires sudo but isn't represented as such in the OpenAPI spec - // TODO /sys/step-down requires sudo but isn't represented as such in the OpenAPI spec - - // 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/[^/]+$`), -} - // PluginAPIClientMeta is a helper that plugins can use to configure TLS connections // back to Vault. type PluginAPIClientMeta struct { @@ -250,32 +210,3 @@ func VaultPluginTLSProviderContext(ctx context.Context, apiTLSConfig *TLSConfig) return tlsConfig, nil } } - -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 -} diff --git a/api/sudo_paths.go b/api/sudo_paths.go new file mode 100644 index 0000000000..7ecb53454a --- /dev/null +++ b/api/sudo_paths.go @@ -0,0 +1,87 @@ +// 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/?$`), + // TODO /auth/token/revoke-orphan requires sudo but isn't represented as such in the OpenAPI spec + "/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": regexp.MustCompile(`^/sys/leases/lookup/?$`), + "/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": regexp.MustCompile(`^/sys/raw$`), + "/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$`), + // TODO /sys/seal requires sudo but isn't represented as such in the OpenAPI spec + // TODO /sys/step-down requires sudo but isn't represented as such in the OpenAPI spec + + // 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 +} diff --git a/api/plugin_helpers_test.go b/api/sudo_paths_test.go similarity index 100% rename from api/plugin_helpers_test.go rename to api/sudo_paths_test.go