mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	Support trimming trailing slashes via a mount tuneable to support CMPv2 (#28752)
* Support trimming trailing slashes via a mount tuneable to support CMPv2 * changelog/ * Perform trimming in handleLoginRequest too * Eagerly fetch the mount entry so we only test this once * Add a mount match function that gets path and entry * Update vault/request_handling.go Co-authored-by: Steven Clark <steven.clark@hashicorp.com> * more docs * Some patches (from ENT) didnt apply * patch fail * Update vault/router.go Co-authored-by: Steven Clark <steven.clark@hashicorp.com> * PR feedback * dupe * another dupe * Add support for enabling trim_request_trailing_slashes on mount creation * Fix read mount api returning configuration for trim_request_trailing_slashes * Fix test assertion * Switch enable and tune arguments to BoolPtrVal to allow end-users to specify false flag * Add trim-request-trailing-slashes to the auth enable API and CLI --------- Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
This commit is contained in:
		| @@ -290,23 +290,23 @@ type MountInput struct { | ||||
| } | ||||
|  | ||||
| type MountConfigInput struct { | ||||
| 	Options                   map[string]string       `json:"options" mapstructure:"options"` | ||||
| 	DefaultLeaseTTL           string                  `json:"default_lease_ttl" mapstructure:"default_lease_ttl"` | ||||
| 	Description               *string                 `json:"description,omitempty" mapstructure:"description"` | ||||
| 	MaxLeaseTTL               string                  `json:"max_lease_ttl" mapstructure:"max_lease_ttl"` | ||||
| 	ForceNoCache              bool                    `json:"force_no_cache" mapstructure:"force_no_cache"` | ||||
| 	AuditNonHMACRequestKeys   []string                `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"` | ||||
| 	AuditNonHMACResponseKeys  []string                `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"` | ||||
| 	ListingVisibility         string                  `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"` | ||||
| 	PassthroughRequestHeaders []string                `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"` | ||||
| 	AllowedResponseHeaders    []string                `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"` | ||||
| 	TokenType                 string                  `json:"token_type,omitempty" mapstructure:"token_type"` | ||||
| 	AllowedManagedKeys        []string                `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` | ||||
| 	PluginVersion             string                  `json:"plugin_version,omitempty"` | ||||
| 	UserLockoutConfig         *UserLockoutConfigInput `json:"user_lockout_config,omitempty"` | ||||
| 	DelegatedAuthAccessors    []string                `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` | ||||
| 	IdentityTokenKey          string                  `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` | ||||
|  | ||||
| 	Options                    map[string]string       `json:"options" mapstructure:"options"` | ||||
| 	DefaultLeaseTTL            string                  `json:"default_lease_ttl" mapstructure:"default_lease_ttl"` | ||||
| 	Description                *string                 `json:"description,omitempty" mapstructure:"description"` | ||||
| 	MaxLeaseTTL                string                  `json:"max_lease_ttl" mapstructure:"max_lease_ttl"` | ||||
| 	ForceNoCache               bool                    `json:"force_no_cache" mapstructure:"force_no_cache"` | ||||
| 	AuditNonHMACRequestKeys    []string                `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"` | ||||
| 	AuditNonHMACResponseKeys   []string                `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"` | ||||
| 	ListingVisibility          string                  `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"` | ||||
| 	PassthroughRequestHeaders  []string                `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"` | ||||
| 	AllowedResponseHeaders     []string                `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"` | ||||
| 	TokenType                  string                  `json:"token_type,omitempty" mapstructure:"token_type"` | ||||
| 	AllowedManagedKeys         []string                `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` | ||||
| 	PluginVersion              string                  `json:"plugin_version,omitempty"` | ||||
| 	UserLockoutConfig          *UserLockoutConfigInput `json:"user_lockout_config,omitempty"` | ||||
| 	DelegatedAuthAccessors     []string                `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` | ||||
| 	IdentityTokenKey           string                  `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` | ||||
| 	TrimRequestTrailingSlashes *bool                   `json:"trim_request_trailing_slashes,omitempty" mapstructure:"trim_request_trailing_slashes"` | ||||
| 	// Deprecated: This field will always be blank for newer server responses. | ||||
| 	PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"` | ||||
| } | ||||
| @@ -328,19 +328,20 @@ type MountOutput struct { | ||||
| } | ||||
|  | ||||
| type MountConfigOutput struct { | ||||
| 	DefaultLeaseTTL           int                      `json:"default_lease_ttl" mapstructure:"default_lease_ttl"` | ||||
| 	MaxLeaseTTL               int                      `json:"max_lease_ttl" mapstructure:"max_lease_ttl"` | ||||
| 	ForceNoCache              bool                     `json:"force_no_cache" mapstructure:"force_no_cache"` | ||||
| 	AuditNonHMACRequestKeys   []string                 `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"` | ||||
| 	AuditNonHMACResponseKeys  []string                 `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"` | ||||
| 	ListingVisibility         string                   `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"` | ||||
| 	PassthroughRequestHeaders []string                 `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"` | ||||
| 	AllowedResponseHeaders    []string                 `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"` | ||||
| 	TokenType                 string                   `json:"token_type,omitempty" mapstructure:"token_type"` | ||||
| 	AllowedManagedKeys        []string                 `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` | ||||
| 	UserLockoutConfig         *UserLockoutConfigOutput `json:"user_lockout_config,omitempty"` | ||||
| 	DelegatedAuthAccessors    []string                 `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` | ||||
| 	IdentityTokenKey          string                   `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` | ||||
| 	DefaultLeaseTTL            int                      `json:"default_lease_ttl" mapstructure:"default_lease_ttl"` | ||||
| 	MaxLeaseTTL                int                      `json:"max_lease_ttl" mapstructure:"max_lease_ttl"` | ||||
| 	ForceNoCache               bool                     `json:"force_no_cache" mapstructure:"force_no_cache"` | ||||
| 	AuditNonHMACRequestKeys    []string                 `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"` | ||||
| 	AuditNonHMACResponseKeys   []string                 `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"` | ||||
| 	ListingVisibility          string                   `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"` | ||||
| 	PassthroughRequestHeaders  []string                 `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"` | ||||
| 	AllowedResponseHeaders     []string                 `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"` | ||||
| 	TokenType                  string                   `json:"token_type,omitempty" mapstructure:"token_type"` | ||||
| 	AllowedManagedKeys         []string                 `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` | ||||
| 	UserLockoutConfig          *UserLockoutConfigOutput `json:"user_lockout_config,omitempty"` | ||||
| 	DelegatedAuthAccessors     []string                 `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` | ||||
| 	IdentityTokenKey           string                   `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` | ||||
| 	TrimRequestTrailingSlashes bool                     `json:"trim_request_trailing_slashes,omitempty" mapstructure:"trim_request_trailing_slashes"` | ||||
|  | ||||
| 	// Deprecated: This field will always be blank for newer server responses. | ||||
| 	PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"` | ||||
|   | ||||
							
								
								
									
										3
									
								
								changelog/28752.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/28752.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| ```release-note:improvement | ||||
| core: Add a mount tuneable that trims trailing slashes of request paths during POST.  Needed to support CMPv2 in PKI. | ||||
| ``` | ||||
| @@ -23,24 +23,25 @@ var ( | ||||
| type AuthEnableCommand struct { | ||||
| 	*BaseCommand | ||||
|  | ||||
| 	flagDescription               string | ||||
| 	flagPath                      string | ||||
| 	flagDefaultLeaseTTL           time.Duration | ||||
| 	flagMaxLeaseTTL               time.Duration | ||||
| 	flagAuditNonHMACRequestKeys   []string | ||||
| 	flagAuditNonHMACResponseKeys  []string | ||||
| 	flagListingVisibility         string | ||||
| 	flagPluginName                string | ||||
| 	flagPassthroughRequestHeaders []string | ||||
| 	flagAllowedResponseHeaders    []string | ||||
| 	flagOptions                   map[string]string | ||||
| 	flagLocal                     bool | ||||
| 	flagSealWrap                  bool | ||||
| 	flagExternalEntropyAccess     bool | ||||
| 	flagTokenType                 string | ||||
| 	flagVersion                   int | ||||
| 	flagPluginVersion             string | ||||
| 	flagIdentityTokenKey          string | ||||
| 	flagDescription                string | ||||
| 	flagPath                       string | ||||
| 	flagDefaultLeaseTTL            time.Duration | ||||
| 	flagMaxLeaseTTL                time.Duration | ||||
| 	flagAuditNonHMACRequestKeys    []string | ||||
| 	flagAuditNonHMACResponseKeys   []string | ||||
| 	flagListingVisibility          string | ||||
| 	flagPluginName                 string | ||||
| 	flagPassthroughRequestHeaders  []string | ||||
| 	flagAllowedResponseHeaders     []string | ||||
| 	flagOptions                    map[string]string | ||||
| 	flagLocal                      bool | ||||
| 	flagSealWrap                   bool | ||||
| 	flagExternalEntropyAccess      bool | ||||
| 	flagTokenType                  string | ||||
| 	flagVersion                    int | ||||
| 	flagPluginVersion              string | ||||
| 	flagIdentityTokenKey           string | ||||
| 	flagTrimRequestTrailingSlashes BoolPtr | ||||
| } | ||||
|  | ||||
| func (c *AuthEnableCommand) Synopsis() string { | ||||
| @@ -217,6 +218,12 @@ func (c *AuthEnableCommand) Flags() *FlagSets { | ||||
| 		Usage:   "Select the key used to sign plugin identity tokens.", | ||||
| 	}) | ||||
|  | ||||
| 	f.BoolPtrVar(&BoolPtrVar{ | ||||
| 		Name:   flagNameTrimRequestTrailingSlashes, | ||||
| 		Target: &c.flagTrimRequestTrailingSlashes, | ||||
| 		Usage:  "Whether to trim trailing slashes for incoming requests to this mount", | ||||
| 	}) | ||||
|  | ||||
| 	return set | ||||
| } | ||||
|  | ||||
| @@ -324,6 +331,11 @@ func (c *AuthEnableCommand) Run(args []string) int { | ||||
| 		if fl.Name == flagNameIdentityTokenKey { | ||||
| 			authOpts.Config.IdentityTokenKey = c.flagIdentityTokenKey | ||||
| 		} | ||||
|  | ||||
| 		if fl.Name == flagNameTrimRequestTrailingSlashes && c.flagTrimRequestTrailingSlashes.IsSet() { | ||||
| 			val := c.flagTrimRequestTrailingSlashes.Get() | ||||
| 			authOpts.Config.TrimRequestTrailingSlashes = &val | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil { | ||||
|   | ||||
| @@ -100,6 +100,7 @@ func TestAuthEnableCommand_Run(t *testing.T) { | ||||
| 			"-allowed-response-headers", "authorization", | ||||
| 			"-listing-visibility", "unauth", | ||||
| 			"-identity-token-key", "default", | ||||
| 			"-trim-request-trailing-slashes=true", | ||||
| 			"userpass", | ||||
| 		}) | ||||
| 		if exp := 0; code != exp { | ||||
| @@ -127,6 +128,9 @@ func TestAuthEnableCommand_Run(t *testing.T) { | ||||
| 		if exp := "The best kind of test"; authInfo.Description != exp { | ||||
| 			t.Errorf("expected %q to be %q", authInfo.Description, exp) | ||||
| 		} | ||||
| 		if !authInfo.Config.TrimRequestTrailingSlashes { | ||||
| 			t.Errorf("expected trim_request_trailing_slashes to be enabled") | ||||
| 		} | ||||
| 		if diff := deep.Equal([]string{"authorization,authentication", "www-authentication"}, authInfo.Config.PassthroughRequestHeaders); len(diff) > 0 { | ||||
| 			t.Errorf("Failed to find expected values in PassthroughRequestHeaders. Difference is: %v", diff) | ||||
| 		} | ||||
|   | ||||
| @@ -40,6 +40,7 @@ type AuthTuneCommand struct { | ||||
| 	flagUserLockoutCounterResetDuration time.Duration | ||||
| 	flagUserLockoutDisable              bool | ||||
| 	flagIdentityTokenKey                string | ||||
| 	flagTrimRequestTrailingSlashes      BoolPtr | ||||
| } | ||||
|  | ||||
| func (c *AuthTuneCommand) Synopsis() string { | ||||
| @@ -195,6 +196,11 @@ func (c *AuthTuneCommand) Flags() *FlagSets { | ||||
| 		Usage: "Select the semantic version of the plugin to run. The new version must be registered in " + | ||||
| 			"the plugin catalog, and will not start running until the plugin is reloaded.", | ||||
| 	}) | ||||
| 	f.BoolPtrVar(&BoolPtrVar{ | ||||
| 		Name:   flagNameTrimRequestTrailingSlashes, | ||||
| 		Target: &c.flagTrimRequestTrailingSlashes, | ||||
| 		Usage:  "Whether to trim trailing slashes for incoming requests to this mount", | ||||
| 	}) | ||||
|  | ||||
| 	f.StringVar(&StringVar{ | ||||
| 		Name:    flagNameIdentityTokenKey, | ||||
| @@ -306,6 +312,11 @@ func (c *AuthTuneCommand) Run(args []string) int { | ||||
| 		if fl.Name == flagNameIdentityTokenKey { | ||||
| 			mountConfigInput.IdentityTokenKey = c.flagIdentityTokenKey | ||||
| 		} | ||||
|  | ||||
| 		if fl.Name == flagNameTrimRequestTrailingSlashes && c.flagTrimRequestTrailingSlashes.IsSet() { | ||||
| 			val := c.flagTrimRequestTrailingSlashes.Get() | ||||
| 			mountConfigInput.TrimRequestTrailingSlashes = &val | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	// Append /auth (since that's where auths live) and a trailing slash to | ||||
|   | ||||
| @@ -120,6 +120,7 @@ func TestAuthTuneCommand_Run(t *testing.T) { | ||||
| 				"-listing-visibility", "unauth", | ||||
| 				"-plugin-version", version, | ||||
| 				"-identity-token-key", "default", | ||||
| 				"-trim-request-trailing-slashes=true", | ||||
| 				"my-auth/", | ||||
| 			}) | ||||
| 			if exp := 0; code != exp { | ||||
| @@ -156,6 +157,9 @@ func TestAuthTuneCommand_Run(t *testing.T) { | ||||
| 			if exp := 3600; mountInfo.Config.MaxLeaseTTL != exp { | ||||
| 				t.Errorf("expected %d to be %d", mountInfo.Config.MaxLeaseTTL, exp) | ||||
| 			} | ||||
| 			if !mountInfo.Config.TrimRequestTrailingSlashes { | ||||
| 				t.Errorf("expected trim_request_trailing_slashes to be enabled") | ||||
| 			} | ||||
| 			if diff := deep.Equal([]string{"authorization", "www-authentication"}, mountInfo.Config.PassthroughRequestHeaders); len(diff) > 0 { | ||||
| 				t.Errorf("Failed to find expected values in PassthroughRequestHeaders. Difference is: %v", diff) | ||||
| 			} | ||||
|   | ||||
| @@ -97,6 +97,8 @@ const ( | ||||
| 	flagNamePluginVersion = "plugin-version" | ||||
| 	// flagNameIdentityTokenKey selects the key used to sign plugin identity tokens | ||||
| 	flagNameIdentityTokenKey = "identity-token-key" | ||||
| 	// flagNameTrimRequestTrailingSlashes selects the key used to determine whether to trim trailing slashes | ||||
| 	flagNameTrimRequestTrailingSlashes = "trim-request-trailing-slashes" | ||||
| 	// flagNameUserLockoutThreshold is the flag name used for tuning the auth mount lockout threshold parameter | ||||
| 	flagNameUserLockoutThreshold = "user-lockout-threshold" | ||||
| 	// flagNameUserLockoutDuration is the flag name used for tuning the auth mount lockout duration parameter | ||||
|   | ||||
| @@ -23,26 +23,27 @@ var ( | ||||
| type SecretsEnableCommand struct { | ||||
| 	*BaseCommand | ||||
|  | ||||
| 	flagDescription               string | ||||
| 	flagPath                      string | ||||
| 	flagDefaultLeaseTTL           time.Duration | ||||
| 	flagMaxLeaseTTL               time.Duration | ||||
| 	flagAuditNonHMACRequestKeys   []string | ||||
| 	flagAuditNonHMACResponseKeys  []string | ||||
| 	flagListingVisibility         string | ||||
| 	flagPassthroughRequestHeaders []string | ||||
| 	flagAllowedResponseHeaders    []string | ||||
| 	flagForceNoCache              bool | ||||
| 	flagPluginName                string | ||||
| 	flagPluginVersion             string | ||||
| 	flagOptions                   map[string]string | ||||
| 	flagLocal                     bool | ||||
| 	flagSealWrap                  bool | ||||
| 	flagExternalEntropyAccess     bool | ||||
| 	flagVersion                   int | ||||
| 	flagAllowedManagedKeys        []string | ||||
| 	flagDelegatedAuthAccessors    []string | ||||
| 	flagIdentityTokenKey          string | ||||
| 	flagDescription                string | ||||
| 	flagPath                       string | ||||
| 	flagDefaultLeaseTTL            time.Duration | ||||
| 	flagMaxLeaseTTL                time.Duration | ||||
| 	flagAuditNonHMACRequestKeys    []string | ||||
| 	flagAuditNonHMACResponseKeys   []string | ||||
| 	flagListingVisibility          string | ||||
| 	flagPassthroughRequestHeaders  []string | ||||
| 	flagAllowedResponseHeaders     []string | ||||
| 	flagForceNoCache               bool | ||||
| 	flagPluginName                 string | ||||
| 	flagPluginVersion              string | ||||
| 	flagOptions                    map[string]string | ||||
| 	flagLocal                      bool | ||||
| 	flagSealWrap                   bool | ||||
| 	flagExternalEntropyAccess      bool | ||||
| 	flagVersion                    int | ||||
| 	flagAllowedManagedKeys         []string | ||||
| 	flagDelegatedAuthAccessors     []string | ||||
| 	flagIdentityTokenKey           string | ||||
| 	flagTrimRequestTrailingSlashes BoolPtr | ||||
| } | ||||
|  | ||||
| func (c *SecretsEnableCommand) Synopsis() string { | ||||
| @@ -245,6 +246,12 @@ func (c *SecretsEnableCommand) Flags() *FlagSets { | ||||
| 		Usage:   "Select the key used to sign plugin identity tokens.", | ||||
| 	}) | ||||
|  | ||||
| 	f.BoolPtrVar(&BoolPtrVar{ | ||||
| 		Name:   flagNameTrimRequestTrailingSlashes, | ||||
| 		Target: &c.flagTrimRequestTrailingSlashes, | ||||
| 		Usage:  "Whether to trim trailing slashes for incoming requests to this mount", | ||||
| 	}) | ||||
|  | ||||
| 	return set | ||||
| } | ||||
|  | ||||
| @@ -359,6 +366,11 @@ func (c *SecretsEnableCommand) Run(args []string) int { | ||||
| 		if fl.Name == flagNameIdentityTokenKey { | ||||
| 			mountInput.Config.IdentityTokenKey = c.flagIdentityTokenKey | ||||
| 		} | ||||
|  | ||||
| 		if fl.Name == flagNameTrimRequestTrailingSlashes && c.flagTrimRequestTrailingSlashes.IsSet() { | ||||
| 			val := c.flagTrimRequestTrailingSlashes.Get() | ||||
| 			mountInput.Config.TrimRequestTrailingSlashes = &val | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if err := client.Sys().Mount(mountPath, mountInput); err != nil { | ||||
|   | ||||
| @@ -120,6 +120,7 @@ func TestSecretsEnableCommand_Run(t *testing.T) { | ||||
| 			"-allowed-managed-keys", "key1,key2", | ||||
| 			"-identity-token-key", "default", | ||||
| 			"-delegated-auth-accessors", "authAcc1,authAcc2", | ||||
| 			"-trim-request-trailing-slashes=true", | ||||
| 			"-force-no-cache", | ||||
| 			"pki", | ||||
| 		}) | ||||
| @@ -157,6 +158,9 @@ func TestSecretsEnableCommand_Run(t *testing.T) { | ||||
| 		if exp := true; mountInfo.Config.ForceNoCache != exp { | ||||
| 			t.Errorf("expected %t to be %t", mountInfo.Config.ForceNoCache, exp) | ||||
| 		} | ||||
| 		if !mountInfo.Config.TrimRequestTrailingSlashes { | ||||
| 			t.Errorf("expected trim_request_trailing_slashes to be enabled") | ||||
| 		} | ||||
| 		if diff := deep.Equal([]string{"authorization,authentication", "www-authentication"}, mountInfo.Config.PassthroughRequestHeaders); len(diff) > 0 { | ||||
| 			t.Errorf("Failed to find expected values in PassthroughRequestHeaders. Difference is: %v", diff) | ||||
| 		} | ||||
|   | ||||
| @@ -23,20 +23,21 @@ var ( | ||||
| type SecretsTuneCommand struct { | ||||
| 	*BaseCommand | ||||
|  | ||||
| 	flagAuditNonHMACRequestKeys   []string | ||||
| 	flagAuditNonHMACResponseKeys  []string | ||||
| 	flagDefaultLeaseTTL           time.Duration | ||||
| 	flagDescription               string | ||||
| 	flagListingVisibility         string | ||||
| 	flagMaxLeaseTTL               time.Duration | ||||
| 	flagPassthroughRequestHeaders []string | ||||
| 	flagAllowedResponseHeaders    []string | ||||
| 	flagOptions                   map[string]string | ||||
| 	flagVersion                   int | ||||
| 	flagPluginVersion             string | ||||
| 	flagAllowedManagedKeys        []string | ||||
| 	flagDelegatedAuthAccessors    []string | ||||
| 	flagIdentityTokenKey          string | ||||
| 	flagAuditNonHMACRequestKeys    []string | ||||
| 	flagAuditNonHMACResponseKeys   []string | ||||
| 	flagDefaultLeaseTTL            time.Duration | ||||
| 	flagDescription                string | ||||
| 	flagListingVisibility          string | ||||
| 	flagMaxLeaseTTL                time.Duration | ||||
| 	flagPassthroughRequestHeaders  []string | ||||
| 	flagAllowedResponseHeaders     []string | ||||
| 	flagOptions                    map[string]string | ||||
| 	flagVersion                    int | ||||
| 	flagPluginVersion              string | ||||
| 	flagAllowedManagedKeys         []string | ||||
| 	flagDelegatedAuthAccessors     []string | ||||
| 	flagIdentityTokenKey           string | ||||
| 	flagTrimRequestTrailingSlashes BoolPtr | ||||
| } | ||||
|  | ||||
| func (c *SecretsTuneCommand) Synopsis() string { | ||||
| @@ -175,6 +176,12 @@ func (c *SecretsTuneCommand) Flags() *FlagSets { | ||||
| 		Usage:   "Select the key used to sign plugin identity tokens.", | ||||
| 	}) | ||||
|  | ||||
| 	f.BoolPtrVar(&BoolPtrVar{ | ||||
| 		Name:   flagNameTrimRequestTrailingSlashes, | ||||
| 		Target: &c.flagTrimRequestTrailingSlashes, | ||||
| 		Usage:  "Whether to trim trailing slashes for incoming requests to this mount", | ||||
| 	}) | ||||
|  | ||||
| 	return set | ||||
| } | ||||
|  | ||||
| @@ -267,6 +274,10 @@ func (c *SecretsTuneCommand) Run(args []string) int { | ||||
| 		if fl.Name == flagNameIdentityTokenKey { | ||||
| 			mountConfigInput.IdentityTokenKey = c.flagIdentityTokenKey | ||||
| 		} | ||||
| 		if fl.Name == flagNameTrimRequestTrailingSlashes && c.flagTrimRequestTrailingSlashes.IsSet() { | ||||
| 			val := c.flagTrimRequestTrailingSlashes.Get() | ||||
| 			mountConfigInput.TrimRequestTrailingSlashes = &val | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if err := client.Sys().TuneMount(mountPath, mountConfigInput); err != nil { | ||||
|   | ||||
| @@ -196,6 +196,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { | ||||
| 				"-listing-visibility", "unauth", | ||||
| 				"-plugin-version", version, | ||||
| 				"-delegated-auth-accessors", "authAcc1,authAcc2", | ||||
| 				"-trim-request-trailing-slashes=true", | ||||
| 				"mount_tune_integration/", | ||||
| 			}) | ||||
| 			if exp := 0; code != exp { | ||||
| @@ -232,6 +233,9 @@ func TestSecretsTuneCommand_Run(t *testing.T) { | ||||
| 			if exp := 3600; mountInfo.Config.MaxLeaseTTL != exp { | ||||
| 				t.Errorf("expected %d to be %d", mountInfo.Config.MaxLeaseTTL, exp) | ||||
| 			} | ||||
| 			if !mountInfo.Config.TrimRequestTrailingSlashes { | ||||
| 				t.Errorf("expected trim_request_trailing_slashes to be enabled") | ||||
| 			} | ||||
| 			if diff := deep.Equal([]string{"authorization", "www-authentication"}, mountInfo.Config.PassthroughRequestHeaders); len(diff) > 0 { | ||||
| 				t.Errorf("Failed to find expected values for PassthroughRequestHeaders. Difference is: %v", diff) | ||||
| 			} | ||||
|   | ||||
| @@ -1389,7 +1389,9 @@ func (b *SystemBackend) mountInfo(ctx context.Context, entry *MountEntry, legacy | ||||
| 			entryConfig["max_lease_ttl"] = coreMaxTTL | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if entry.Config.TrimRequestTrailingSlashes { | ||||
| 		entryConfig["trim_request_trailing_slashes"] = true | ||||
| 	} | ||||
| 	if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { | ||||
| 		entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string) | ||||
| 	} | ||||
| @@ -1613,6 +1615,10 @@ func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, d | ||||
| 		config.ForceNoCache = true | ||||
| 	} | ||||
|  | ||||
| 	if apiConfig.TrimRequestTrailingSlashes { | ||||
| 		config.TrimRequestTrailingSlashes = true | ||||
| 	} | ||||
|  | ||||
| 	if err := checkListingVisibility(apiConfig.ListingVisibility); err != nil { | ||||
| 		return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", apiConfig.ListingVisibility)), nil | ||||
| 	} | ||||
| @@ -2149,6 +2155,10 @@ func (b *SystemBackend) handleTuneReadCommon(ctx context.Context, path string) ( | ||||
| 		resp.Data["identity_token_key"] = rawVal.(string) | ||||
| 	} | ||||
|  | ||||
| 	if mountEntry.Config.TrimRequestTrailingSlashes { | ||||
| 		resp.Data["trim_request_trailing_slashes"] = mountEntry.Config.TrimRequestTrailingSlashes | ||||
| 	} | ||||
|  | ||||
| 	if mountEntry.Config.UserLockoutConfig != nil { | ||||
| 		resp.Data["user_lockout_counter_reset_duration"] = int64(mountEntry.Config.UserLockoutConfig.LockoutCounterReset.Seconds()) | ||||
| 		resp.Data["user_lockout_threshold"] = mountEntry.Config.UserLockoutConfig.LockoutThreshold | ||||
| @@ -2753,6 +2763,30 @@ func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if rawVal, ok := data.GetOk("trim_request_trailing_slashes"); ok { | ||||
| 		trimRequestTrailingSlashes := rawVal.(bool) | ||||
|  | ||||
| 		oldVal := mountEntry.Config.TrimRequestTrailingSlashes | ||||
| 		mountEntry.Config.TrimRequestTrailingSlashes = trimRequestTrailingSlashes | ||||
|  | ||||
| 		// Update the mount table | ||||
| 		var err error | ||||
| 		switch { | ||||
| 		case strings.HasPrefix(path, "auth/"): | ||||
| 			err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local) | ||||
| 		default: | ||||
| 			err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			mountEntry.Config.TrimRequestTrailingSlashes = oldVal | ||||
| 			return handleError(err) | ||||
| 		} | ||||
|  | ||||
| 		if b.Core.logger.IsInfo() { | ||||
| 			b.Core.logger.Info("mount tuning of trim_request_trailing_slashes successful", "path", path) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var err error | ||||
| 	var resp *logical.Response | ||||
| 	var options map[string]string | ||||
| @@ -3269,6 +3303,7 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque | ||||
| 		return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", apiConfig.ListingVisibility)), nil | ||||
| 	} | ||||
| 	config.ListingVisibility = apiConfig.ListingVisibility | ||||
| 	config.TrimRequestTrailingSlashes = apiConfig.TrimRequestTrailingSlashes | ||||
|  | ||||
| 	if len(apiConfig.AuditNonHMACRequestKeys) > 0 { | ||||
| 		config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys | ||||
| @@ -7156,4 +7191,8 @@ This path responds to the following HTTP methods. | ||||
| 		`The label representing a path-prefix within the /.well-known/ path`, | ||||
| 		"", | ||||
| 	}, | ||||
| 	"trim_request_trailing_slashes": { | ||||
| 		`Whether to trim a trailing slash on incoming requests to this mount`, | ||||
| 		"", | ||||
| 	}, | ||||
| } | ||||
|   | ||||
| @@ -3826,6 +3826,10 @@ func (b *SystemBackend) authPaths() []*framework.Path { | ||||
| 					Description: strings.TrimSpace(sysHelp["identity_token_key"][0]), | ||||
| 					Required:    false, | ||||
| 				}, | ||||
| 				"trim_request_trailing_slashes": { | ||||
| 					Type:     framework.TypeBool, | ||||
| 					Required: false, | ||||
| 				}, | ||||
| 			}, | ||||
| 			Operations: map[logical.Operation]framework.OperationHandler{ | ||||
| 				logical.ReadOperation: &framework.PathOperation{ | ||||
| @@ -3916,6 +3920,10 @@ func (b *SystemBackend) authPaths() []*framework.Path { | ||||
| 									Type:     framework.TypeString, | ||||
| 									Required: false, | ||||
| 								}, | ||||
| 								"trim_request_trailing_slashes": { | ||||
| 									Type:     framework.TypeBool, | ||||
| 									Required: false, | ||||
| 								}, | ||||
| 							}, | ||||
| 						}}, | ||||
| 					}, | ||||
| @@ -4686,6 +4694,10 @@ func (b *SystemBackend) mountPaths() []*framework.Path { | ||||
| 					Type:        framework.TypeString, | ||||
| 					Description: strings.TrimSpace(sysHelp["identity_token_key"][0]), | ||||
| 				}, | ||||
| 				"trim_request_trailing_slashes": { | ||||
| 					Type:        framework.TypeBool, | ||||
| 					Description: strings.TrimSpace(sysHelp["trim_request_trailing_slashes"][0]), | ||||
| 				}, | ||||
| 			}, | ||||
|  | ||||
| 			Operations: map[logical.Operation]framework.OperationHandler{ | ||||
| @@ -4788,6 +4800,10 @@ func (b *SystemBackend) mountPaths() []*framework.Path { | ||||
| 									Type:     framework.TypeString, | ||||
| 									Required: false, | ||||
| 								}, | ||||
| 								"trim_request_trailing_slashes": { | ||||
| 									Type:     framework.TypeBool, | ||||
| 									Required: false, | ||||
| 								}, | ||||
| 							}, | ||||
| 						}}, | ||||
| 					}, | ||||
|   | ||||
| @@ -349,19 +349,20 @@ type MountEntry struct { | ||||
|  | ||||
| // MountConfig is used to hold settable options | ||||
| type MountConfig struct { | ||||
| 	DefaultLeaseTTL           time.Duration         `json:"default_lease_ttl,omitempty" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default | ||||
| 	MaxLeaseTTL               time.Duration         `json:"max_lease_ttl,omitempty" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`             // Override for global default | ||||
| 	ForceNoCache              bool                  `json:"force_no_cache,omitempty" structs:"force_no_cache" mapstructure:"force_no_cache"`          // Override for global default | ||||
| 	AuditNonHMACRequestKeys   []string              `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"` | ||||
| 	AuditNonHMACResponseKeys  []string              `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"` | ||||
| 	ListingVisibility         ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"` | ||||
| 	PassthroughRequestHeaders []string              `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"` | ||||
| 	AllowedResponseHeaders    []string              `json:"allowed_response_headers,omitempty" structs:"allowed_response_headers" mapstructure:"allowed_response_headers"` | ||||
| 	TokenType                 logical.TokenType     `json:"token_type,omitempty" structs:"token_type" mapstructure:"token_type"` | ||||
| 	AllowedManagedKeys        []string              `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` | ||||
| 	UserLockoutConfig         *UserLockoutConfig    `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"` | ||||
| 	DelegatedAuthAccessors    []string              `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` | ||||
| 	IdentityTokenKey          string                `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` | ||||
| 	DefaultLeaseTTL            time.Duration         `json:"default_lease_ttl,omitempty" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default | ||||
| 	MaxLeaseTTL                time.Duration         `json:"max_lease_ttl,omitempty" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`             // Override for global default | ||||
| 	ForceNoCache               bool                  `json:"force_no_cache,omitempty" structs:"force_no_cache" mapstructure:"force_no_cache"`          // Override for global default | ||||
| 	AuditNonHMACRequestKeys    []string              `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"` | ||||
| 	AuditNonHMACResponseKeys   []string              `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"` | ||||
| 	ListingVisibility          ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"` | ||||
| 	PassthroughRequestHeaders  []string              `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"` | ||||
| 	AllowedResponseHeaders     []string              `json:"allowed_response_headers,omitempty" structs:"allowed_response_headers" mapstructure:"allowed_response_headers"` | ||||
| 	TokenType                  logical.TokenType     `json:"token_type,omitempty" structs:"token_type" mapstructure:"token_type"` | ||||
| 	AllowedManagedKeys         []string              `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` | ||||
| 	UserLockoutConfig          *UserLockoutConfig    `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"` | ||||
| 	DelegatedAuthAccessors     []string              `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` | ||||
| 	IdentityTokenKey           string                `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` | ||||
| 	TrimRequestTrailingSlashes bool                  `json:"trim_request_trailing_slashes,omitempty" mapstructure:"trim_request_trailing_slashes"` // If requests to this mount should have trailing slashes trimmed | ||||
|  | ||||
| 	// PluginName is the name of the plugin registered in the catalog. | ||||
| 	// | ||||
| @@ -389,20 +390,21 @@ type APIUserLockoutConfig struct { | ||||
|  | ||||
| // APIMountConfig is an embedded struct of api.MountConfigInput | ||||
| type APIMountConfig struct { | ||||
| 	DefaultLeaseTTL           string                `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` | ||||
| 	MaxLeaseTTL               string                `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` | ||||
| 	ForceNoCache              bool                  `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` | ||||
| 	AuditNonHMACRequestKeys   []string              `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"` | ||||
| 	AuditNonHMACResponseKeys  []string              `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"` | ||||
| 	ListingVisibility         ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"` | ||||
| 	PassthroughRequestHeaders []string              `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"` | ||||
| 	AllowedResponseHeaders    []string              `json:"allowed_response_headers,omitempty" structs:"allowed_response_headers" mapstructure:"allowed_response_headers"` | ||||
| 	TokenType                 string                `json:"token_type" structs:"token_type" mapstructure:"token_type"` | ||||
| 	AllowedManagedKeys        []string              `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` | ||||
| 	UserLockoutConfig         *UserLockoutConfig    `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"` | ||||
| 	PluginVersion             string                `json:"plugin_version,omitempty" mapstructure:"plugin_version"` | ||||
| 	DelegatedAuthAccessors    []string              `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` | ||||
| 	IdentityTokenKey          string                `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` | ||||
| 	DefaultLeaseTTL            string                `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` | ||||
| 	MaxLeaseTTL                string                `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` | ||||
| 	ForceNoCache               bool                  `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` | ||||
| 	AuditNonHMACRequestKeys    []string              `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"` | ||||
| 	AuditNonHMACResponseKeys   []string              `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"` | ||||
| 	ListingVisibility          ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"` | ||||
| 	PassthroughRequestHeaders  []string              `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"` | ||||
| 	AllowedResponseHeaders     []string              `json:"allowed_response_headers,omitempty" structs:"allowed_response_headers" mapstructure:"allowed_response_headers"` | ||||
| 	TokenType                  string                `json:"token_type" structs:"token_type" mapstructure:"token_type"` | ||||
| 	AllowedManagedKeys         []string              `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` | ||||
| 	UserLockoutConfig          *UserLockoutConfig    `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"` | ||||
| 	PluginVersion              string                `json:"plugin_version,omitempty" mapstructure:"plugin_version"` | ||||
| 	DelegatedAuthAccessors     []string              `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` | ||||
| 	IdentityTokenKey           string                `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` | ||||
| 	TrimRequestTrailingSlashes bool                  `json:"trim_request_trailing_slashes,omitempty" mapstructure:"trim_request_trailing_slashes"` // If requests to this mount should have trailing slashes trimmed | ||||
|  | ||||
| 	// PluginName is the name of the plugin registered in the catalog. | ||||
| 	// | ||||
|   | ||||
| @@ -612,6 +612,25 @@ func (c *Core) switchedLockHandleRequest(httpCtx context.Context, req *logical.R | ||||
| } | ||||
|  | ||||
| func (c *Core) handleCancelableRequest(ctx context.Context, req *logical.Request) (resp *logical.Response, err error) { | ||||
| 	waitGroup, err := waitForReplicationState(ctx, c, req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// Decrement the wait group when our request is done | ||||
| 	if waitGroup != nil { | ||||
| 		defer waitGroup.Done() | ||||
| 	} | ||||
|  | ||||
| 	if c.MissingRequiredState(req.RequiredState(), c.perfStandby) { | ||||
| 		return nil, logical.ErrMissingRequiredState | ||||
| 	} | ||||
|  | ||||
| 	// Ensure the req contains a MountPoint as it is depended on by some | ||||
| 	// functionality (e.g. quotas) | ||||
| 	var entry *MountEntry | ||||
| 	req.MountPoint, entry = c.router.MatchingMountAndEntry(ctx, req.Path) | ||||
|  | ||||
| 	// Allowing writing to a path ending in / makes it extremely difficult to | ||||
| 	// understand user intent for the filesystem-like backends (kv, | ||||
| 	// cubbyhole) -- did they want a key named foo/ or did they want to write | ||||
| @@ -622,24 +641,11 @@ func (c *Core) handleCancelableRequest(ctx context.Context, req *logical.Request | ||||
| 		(req.Operation == logical.UpdateOperation || | ||||
| 			req.Operation == logical.CreateOperation || | ||||
| 			req.Operation == logical.PatchOperation) { | ||||
| 		return logical.ErrorResponse("cannot write to a path ending in '/'"), nil | ||||
| 	} | ||||
| 	waitGroup, err := waitForReplicationState(ctx, c, req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// MountPoint will not always be set at this point, so we ensure the req contains it | ||||
| 	// as it is depended on by some functionality (e.g. quotas) | ||||
| 	req.MountPoint = c.router.MatchingMount(ctx, req.Path) | ||||
|  | ||||
| 	// Decrement the wait group when our request is done | ||||
| 	if waitGroup != nil { | ||||
| 		defer waitGroup.Done() | ||||
| 	} | ||||
|  | ||||
| 	if c.MissingRequiredState(req.RequiredState(), c.perfStandby) { | ||||
| 		return nil, logical.ErrMissingRequiredState | ||||
| 		if entry == nil || !entry.Config.TrimRequestTrailingSlashes { | ||||
| 			return logical.ErrorResponse("cannot write to a path ending in '/'"), nil | ||||
| 		} else { | ||||
| 			req.Path = strings.TrimSuffix(req.Path, "/") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	err = c.PopulateTokenEntry(ctx, req) | ||||
| @@ -892,7 +898,6 @@ func (c *Core) handleCancelableRequest(ctx context.Context, req *logical.Request | ||||
|  | ||||
| 	var nonHMACReqDataKeys []string | ||||
| 	var nonHMACRespDataKeys []string | ||||
| 	entry := c.router.MatchingMountEntry(ctx, req.Path) | ||||
| 	if entry != nil { | ||||
| 		// Get and set ignored HMAC'd value. Reset those back to empty afterwards. | ||||
| 		if rawVals, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { | ||||
|   | ||||
| @@ -373,23 +373,28 @@ func (r *Router) MatchingMountByAccessor(mountAccessor string) *MountEntry { | ||||
| // MatchingMount returns the mount prefix that would be used for a path | ||||
| func (r *Router) MatchingMount(ctx context.Context, path string) string { | ||||
| 	r.l.RLock() | ||||
| 	mount := r.matchingMountInternal(ctx, path) | ||||
| 	mount, _ := r.matchingMountInternal(ctx, path) | ||||
| 	r.l.RUnlock() | ||||
| 	return mount | ||||
| } | ||||
|  | ||||
| func (r *Router) matchingMountInternal(ctx context.Context, path string) string { | ||||
| // MatchingMountAndEntry returns the MountEntry used for a path and it's router path | ||||
| func (r *Router) MatchingMountAndEntry(ctx context.Context, path string) (string, *MountEntry) { | ||||
| 	return r.matchingMountInternal(ctx, path) | ||||
| } | ||||
|  | ||||
| func (r *Router) matchingMountInternal(ctx context.Context, path string) (string, *MountEntry) { | ||||
| 	ns, err := namespace.FromContext(ctx) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 		return "", nil | ||||
| 	} | ||||
| 	path = ns.Path + path | ||||
|  | ||||
| 	mount, _, ok := r.root.LongestPrefix(path) | ||||
| 	mount, raw, ok := r.root.LongestPrefix(path) | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 		return "", nil | ||||
| 	} | ||||
| 	return mount | ||||
| 	return mount, raw.(*routeEntry).mountEntry | ||||
| } | ||||
|  | ||||
| // matchingPrefixInternal returns a mount prefix that a path may be a part of | ||||
| @@ -416,7 +421,7 @@ func (r *Router) matchingPrefixInternal(ctx context.Context, path string) string | ||||
| func (r *Router) MountConflict(ctx context.Context, path string) string { | ||||
| 	r.l.RLock() | ||||
| 	defer r.l.RUnlock() | ||||
| 	if exactMatch := r.matchingMountInternal(ctx, path); exactMatch != "" { | ||||
| 	if exactMatch, _ := r.matchingMountInternal(ctx, path); exactMatch != "" { | ||||
| 		return exactMatch | ||||
| 	} | ||||
| 	if prefixMatch := r.matchingPrefixInternal(ctx, path); prefixMatch != "" { | ||||
|   | ||||
| @@ -89,6 +89,10 @@ flags](/vault/docs/commands) included on all commands. | ||||
| - `-token-type` `(string: "")` - Specifies the type of tokens that should be | ||||
|   returned by the auth method. | ||||
|  | ||||
| - `-trim-request-trailing-slashes` `(bool: false)` - If true, requests to | ||||
|   this mount with trailing slashes will have those slashes trimmed. | ||||
|   Necessary for some standards based APIs handled by Vault. | ||||
|  | ||||
| - `-plugin-version` `(string: "")` - Configures the semantic version of the plugin | ||||
|   to use. If unspecified, implies the built-in or any matching unversioned plugin | ||||
|   that may have been registered. | ||||
|   | ||||
| @@ -168,6 +168,10 @@ flags](/vault/docs/commands) included on all commands. | ||||
| - `-token-type` `(string: "")` - Specifies the type of tokens that should be | ||||
|   returned by the auth method. | ||||
|  | ||||
| - `-trim-request-trailing-slashes` `(bool: false)` - If true, requests to | ||||
|   this mount with trailing slashes will have those slashes trimmed.  | ||||
|   Necessary for some standards based APIs handled by Vault. | ||||
|  | ||||
| - `-plugin-version` `(string: "")` - Configures the semantic version of the plugin | ||||
|   to use. The new version will not start running until the mount is | ||||
|   [reloaded](/vault/docs/commands/plugin/reload). | ||||
|   | ||||
| @@ -111,6 +111,10 @@ flags](/vault/docs/commands) included on all commands. | ||||
|   backend can delegate authentication to. To allow multiple accessors, provide | ||||
|   the `delegated-auth-accessors` multiple times, each time with 1 accessor. | ||||
|  | ||||
| - `-trim-request-trailing-slashes` `(bool: false)` - If true, requests to | ||||
|   this mount with trailing slashes will have those slashes trimmed. | ||||
|   Necessary for some standards based APIs handled by Vault. | ||||
|  | ||||
| - `-plugin-version` `(string: "")` - Configures the semantic version of the plugin | ||||
|   to use. If unspecified, implies the built-in or any matching unversioned plugin | ||||
|   that may have been registered. | ||||
|   | ||||
| @@ -97,6 +97,10 @@ flags](/vault/docs/commands) included on all commands. | ||||
|   backend can delegate authentication to. To allow multiple accessors, provide | ||||
|   the `delegated-auth-accessors` multiple times, each time with 1 accessor. | ||||
|  | ||||
| - `-trim-request-trailing-slashes` `(bool: false)` - If true, requests to | ||||
|   this mount with trailing slashes will have those slashes trimmed.  | ||||
|   Necessary for some standards based APIs handled by Vault. | ||||
|  | ||||
| - `-plugin-version` `(string: "")` - Configures the semantic version of the plugin | ||||
|   to use. The new version will not start running until the mount is | ||||
|   [reloaded](/vault/docs/commands/plugin/reload). | ||||
|   | ||||
| @@ -87,10 +87,10 @@ To get an authentication mount's accessor field, the following command can be us | ||||
| $ vault read -field=accessor sys/auth/auth/cert | ||||
| ``` | ||||
|  | ||||
| For CMP to work within certain clients, a few response headers need to be explicitly allowed | ||||
| along with configuring the list of accessors the mount can delegate authentication towards. | ||||
| The following will grant the required response headers, you will need to replace the values for the `delegated-auth-accessors` | ||||
| to match your values. | ||||
| For CMP to work within certain clients, a few response headers need to be explicitly | ||||
| allowed, trailing slashes must be trimmed, and the list of accessors the mount can delegate authentication towards | ||||
| must be configured. The following will grant the required response headers, you will need to replace the values for  | ||||
| the `delegated-auth-accessors` to match your values. | ||||
|  | ||||
| ```shell-session | ||||
| $ vault secrets tune \ | ||||
| @@ -98,6 +98,7 @@ $ vault secrets tune \ | ||||
|   -allowed-response-headers="Content-Length" \ | ||||
|   -allowed-response-headers="WWW-Authenticate" \ | ||||
|   -delegated-auth-accessors="auth_cert_4088ac2d" \ | ||||
|   -trim-request-trailing-slashes="true" \ | ||||
|   pki | ||||
| ``` | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Scott Miller
					Scott Miller