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:
Scott Miller 2024-10-24 10:47:17 -05:00 committed by GitHub
parent 314874c2b1
commit 415d260995
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 292 additions and 140 deletions

View File

@ -290,23 +290,23 @@ type MountInput struct {
} }
type MountConfigInput struct { type MountConfigInput struct {
Options map[string]string `json:"options" mapstructure:"options"` Options map[string]string `json:"options" mapstructure:"options"`
DefaultLeaseTTL string `json:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL string `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
Description *string `json:"description,omitempty" mapstructure:"description"` Description *string `json:"description,omitempty" mapstructure:"description"`
MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" mapstructure:"force_no_cache"` 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"` 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"` AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"` ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"` PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
AllowedResponseHeaders []string `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"` AllowedResponseHeaders []string `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"` TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"`
PluginVersion string `json:"plugin_version,omitempty"` PluginVersion string `json:"plugin_version,omitempty"`
UserLockoutConfig *UserLockoutConfigInput `json:"user_lockout_config,omitempty"` UserLockoutConfig *UserLockoutConfigInput `json:"user_lockout_config,omitempty"`
DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"`
IdentityTokenKey string `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` 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. // Deprecated: This field will always be blank for newer server responses.
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"` PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
} }
@ -328,19 +328,20 @@ type MountOutput struct {
} }
type MountConfigOutput struct { type MountConfigOutput struct {
DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" mapstructure:"force_no_cache"` 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"` 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"` AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"` ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"` PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
AllowedResponseHeaders []string `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"` AllowedResponseHeaders []string `json:"allowed_response_headers,omitempty" mapstructure:"allowed_response_headers"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"` TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"`
UserLockoutConfig *UserLockoutConfigOutput `json:"user_lockout_config,omitempty"` UserLockoutConfig *UserLockoutConfigOutput `json:"user_lockout_config,omitempty"`
DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"`
IdentityTokenKey string `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` 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. // Deprecated: This field will always be blank for newer server responses.
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"` PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`

3
changelog/28752.txt Normal file
View 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.
```

View File

@ -23,24 +23,25 @@ var (
type AuthEnableCommand struct { type AuthEnableCommand struct {
*BaseCommand *BaseCommand
flagDescription string flagDescription string
flagPath string flagPath string
flagDefaultLeaseTTL time.Duration flagDefaultLeaseTTL time.Duration
flagMaxLeaseTTL time.Duration flagMaxLeaseTTL time.Duration
flagAuditNonHMACRequestKeys []string flagAuditNonHMACRequestKeys []string
flagAuditNonHMACResponseKeys []string flagAuditNonHMACResponseKeys []string
flagListingVisibility string flagListingVisibility string
flagPluginName string flagPluginName string
flagPassthroughRequestHeaders []string flagPassthroughRequestHeaders []string
flagAllowedResponseHeaders []string flagAllowedResponseHeaders []string
flagOptions map[string]string flagOptions map[string]string
flagLocal bool flagLocal bool
flagSealWrap bool flagSealWrap bool
flagExternalEntropyAccess bool flagExternalEntropyAccess bool
flagTokenType string flagTokenType string
flagVersion int flagVersion int
flagPluginVersion string flagPluginVersion string
flagIdentityTokenKey string flagIdentityTokenKey string
flagTrimRequestTrailingSlashes BoolPtr
} }
func (c *AuthEnableCommand) Synopsis() string { func (c *AuthEnableCommand) Synopsis() string {
@ -217,6 +218,12 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
Usage: "Select the key used to sign plugin identity tokens.", 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 return set
} }
@ -324,6 +331,11 @@ func (c *AuthEnableCommand) Run(args []string) int {
if fl.Name == flagNameIdentityTokenKey { if fl.Name == flagNameIdentityTokenKey {
authOpts.Config.IdentityTokenKey = c.flagIdentityTokenKey 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 { if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {

View File

@ -100,6 +100,7 @@ func TestAuthEnableCommand_Run(t *testing.T) {
"-allowed-response-headers", "authorization", "-allowed-response-headers", "authorization",
"-listing-visibility", "unauth", "-listing-visibility", "unauth",
"-identity-token-key", "default", "-identity-token-key", "default",
"-trim-request-trailing-slashes=true",
"userpass", "userpass",
}) })
if exp := 0; code != exp { 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 { if exp := "The best kind of test"; authInfo.Description != exp {
t.Errorf("expected %q to be %q", 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 { 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) t.Errorf("Failed to find expected values in PassthroughRequestHeaders. Difference is: %v", diff)
} }

View File

@ -40,6 +40,7 @@ type AuthTuneCommand struct {
flagUserLockoutCounterResetDuration time.Duration flagUserLockoutCounterResetDuration time.Duration
flagUserLockoutDisable bool flagUserLockoutDisable bool
flagIdentityTokenKey string flagIdentityTokenKey string
flagTrimRequestTrailingSlashes BoolPtr
} }
func (c *AuthTuneCommand) Synopsis() string { 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 " + 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.", "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{ f.StringVar(&StringVar{
Name: flagNameIdentityTokenKey, Name: flagNameIdentityTokenKey,
@ -306,6 +312,11 @@ func (c *AuthTuneCommand) Run(args []string) int {
if fl.Name == flagNameIdentityTokenKey { if fl.Name == flagNameIdentityTokenKey {
mountConfigInput.IdentityTokenKey = c.flagIdentityTokenKey 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 // Append /auth (since that's where auths live) and a trailing slash to

View File

@ -120,6 +120,7 @@ func TestAuthTuneCommand_Run(t *testing.T) {
"-listing-visibility", "unauth", "-listing-visibility", "unauth",
"-plugin-version", version, "-plugin-version", version,
"-identity-token-key", "default", "-identity-token-key", "default",
"-trim-request-trailing-slashes=true",
"my-auth/", "my-auth/",
}) })
if exp := 0; code != exp { if exp := 0; code != exp {
@ -156,6 +157,9 @@ func TestAuthTuneCommand_Run(t *testing.T) {
if exp := 3600; mountInfo.Config.MaxLeaseTTL != exp { if exp := 3600; mountInfo.Config.MaxLeaseTTL != exp {
t.Errorf("expected %d to be %d", 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 { 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) t.Errorf("Failed to find expected values in PassthroughRequestHeaders. Difference is: %v", diff)
} }

View File

@ -97,6 +97,8 @@ const (
flagNamePluginVersion = "plugin-version" flagNamePluginVersion = "plugin-version"
// flagNameIdentityTokenKey selects the key used to sign plugin identity tokens // flagNameIdentityTokenKey selects the key used to sign plugin identity tokens
flagNameIdentityTokenKey = "identity-token-key" 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 is the flag name used for tuning the auth mount lockout threshold parameter
flagNameUserLockoutThreshold = "user-lockout-threshold" flagNameUserLockoutThreshold = "user-lockout-threshold"
// flagNameUserLockoutDuration is the flag name used for tuning the auth mount lockout duration parameter // flagNameUserLockoutDuration is the flag name used for tuning the auth mount lockout duration parameter

View File

@ -23,26 +23,27 @@ var (
type SecretsEnableCommand struct { type SecretsEnableCommand struct {
*BaseCommand *BaseCommand
flagDescription string flagDescription string
flagPath string flagPath string
flagDefaultLeaseTTL time.Duration flagDefaultLeaseTTL time.Duration
flagMaxLeaseTTL time.Duration flagMaxLeaseTTL time.Duration
flagAuditNonHMACRequestKeys []string flagAuditNonHMACRequestKeys []string
flagAuditNonHMACResponseKeys []string flagAuditNonHMACResponseKeys []string
flagListingVisibility string flagListingVisibility string
flagPassthroughRequestHeaders []string flagPassthroughRequestHeaders []string
flagAllowedResponseHeaders []string flagAllowedResponseHeaders []string
flagForceNoCache bool flagForceNoCache bool
flagPluginName string flagPluginName string
flagPluginVersion string flagPluginVersion string
flagOptions map[string]string flagOptions map[string]string
flagLocal bool flagLocal bool
flagSealWrap bool flagSealWrap bool
flagExternalEntropyAccess bool flagExternalEntropyAccess bool
flagVersion int flagVersion int
flagAllowedManagedKeys []string flagAllowedManagedKeys []string
flagDelegatedAuthAccessors []string flagDelegatedAuthAccessors []string
flagIdentityTokenKey string flagIdentityTokenKey string
flagTrimRequestTrailingSlashes BoolPtr
} }
func (c *SecretsEnableCommand) Synopsis() string { func (c *SecretsEnableCommand) Synopsis() string {
@ -245,6 +246,12 @@ func (c *SecretsEnableCommand) Flags() *FlagSets {
Usage: "Select the key used to sign plugin identity tokens.", 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 return set
} }
@ -359,6 +366,11 @@ func (c *SecretsEnableCommand) Run(args []string) int {
if fl.Name == flagNameIdentityTokenKey { if fl.Name == flagNameIdentityTokenKey {
mountInput.Config.IdentityTokenKey = c.flagIdentityTokenKey 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 { if err := client.Sys().Mount(mountPath, mountInput); err != nil {

View File

@ -120,6 +120,7 @@ func TestSecretsEnableCommand_Run(t *testing.T) {
"-allowed-managed-keys", "key1,key2", "-allowed-managed-keys", "key1,key2",
"-identity-token-key", "default", "-identity-token-key", "default",
"-delegated-auth-accessors", "authAcc1,authAcc2", "-delegated-auth-accessors", "authAcc1,authAcc2",
"-trim-request-trailing-slashes=true",
"-force-no-cache", "-force-no-cache",
"pki", "pki",
}) })
@ -157,6 +158,9 @@ func TestSecretsEnableCommand_Run(t *testing.T) {
if exp := true; mountInfo.Config.ForceNoCache != exp { if exp := true; mountInfo.Config.ForceNoCache != exp {
t.Errorf("expected %t to be %t", 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 { 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) t.Errorf("Failed to find expected values in PassthroughRequestHeaders. Difference is: %v", diff)
} }

View File

@ -23,20 +23,21 @@ var (
type SecretsTuneCommand struct { type SecretsTuneCommand struct {
*BaseCommand *BaseCommand
flagAuditNonHMACRequestKeys []string flagAuditNonHMACRequestKeys []string
flagAuditNonHMACResponseKeys []string flagAuditNonHMACResponseKeys []string
flagDefaultLeaseTTL time.Duration flagDefaultLeaseTTL time.Duration
flagDescription string flagDescription string
flagListingVisibility string flagListingVisibility string
flagMaxLeaseTTL time.Duration flagMaxLeaseTTL time.Duration
flagPassthroughRequestHeaders []string flagPassthroughRequestHeaders []string
flagAllowedResponseHeaders []string flagAllowedResponseHeaders []string
flagOptions map[string]string flagOptions map[string]string
flagVersion int flagVersion int
flagPluginVersion string flagPluginVersion string
flagAllowedManagedKeys []string flagAllowedManagedKeys []string
flagDelegatedAuthAccessors []string flagDelegatedAuthAccessors []string
flagIdentityTokenKey string flagIdentityTokenKey string
flagTrimRequestTrailingSlashes BoolPtr
} }
func (c *SecretsTuneCommand) Synopsis() string { func (c *SecretsTuneCommand) Synopsis() string {
@ -175,6 +176,12 @@ func (c *SecretsTuneCommand) Flags() *FlagSets {
Usage: "Select the key used to sign plugin identity tokens.", 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 return set
} }
@ -267,6 +274,10 @@ func (c *SecretsTuneCommand) Run(args []string) int {
if fl.Name == flagNameIdentityTokenKey { if fl.Name == flagNameIdentityTokenKey {
mountConfigInput.IdentityTokenKey = c.flagIdentityTokenKey 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 { if err := client.Sys().TuneMount(mountPath, mountConfigInput); err != nil {

View File

@ -196,6 +196,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) {
"-listing-visibility", "unauth", "-listing-visibility", "unauth",
"-plugin-version", version, "-plugin-version", version,
"-delegated-auth-accessors", "authAcc1,authAcc2", "-delegated-auth-accessors", "authAcc1,authAcc2",
"-trim-request-trailing-slashes=true",
"mount_tune_integration/", "mount_tune_integration/",
}) })
if exp := 0; code != exp { if exp := 0; code != exp {
@ -232,6 +233,9 @@ func TestSecretsTuneCommand_Run(t *testing.T) {
if exp := 3600; mountInfo.Config.MaxLeaseTTL != exp { if exp := 3600; mountInfo.Config.MaxLeaseTTL != exp {
t.Errorf("expected %d to be %d", 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 { 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) t.Errorf("Failed to find expected values for PassthroughRequestHeaders. Difference is: %v", diff)
} }

View File

@ -1389,7 +1389,9 @@ func (b *SystemBackend) mountInfo(ctx context.Context, entry *MountEntry, legacy
entryConfig["max_lease_ttl"] = coreMaxTTL 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 { if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string) 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 config.ForceNoCache = true
} }
if apiConfig.TrimRequestTrailingSlashes {
config.TrimRequestTrailingSlashes = true
}
if err := checkListingVisibility(apiConfig.ListingVisibility); err != nil { if err := checkListingVisibility(apiConfig.ListingVisibility); err != nil {
return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", apiConfig.ListingVisibility)), 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) 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 { if mountEntry.Config.UserLockoutConfig != nil {
resp.Data["user_lockout_counter_reset_duration"] = int64(mountEntry.Config.UserLockoutConfig.LockoutCounterReset.Seconds()) resp.Data["user_lockout_counter_reset_duration"] = int64(mountEntry.Config.UserLockoutConfig.LockoutCounterReset.Seconds())
resp.Data["user_lockout_threshold"] = mountEntry.Config.UserLockoutConfig.LockoutThreshold 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 err error
var resp *logical.Response var resp *logical.Response
var options map[string]string 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 return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", apiConfig.ListingVisibility)), nil
} }
config.ListingVisibility = apiConfig.ListingVisibility config.ListingVisibility = apiConfig.ListingVisibility
config.TrimRequestTrailingSlashes = apiConfig.TrimRequestTrailingSlashes
if len(apiConfig.AuditNonHMACRequestKeys) > 0 { if len(apiConfig.AuditNonHMACRequestKeys) > 0 {
config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys 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`, `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`,
"",
},
} }

View File

@ -3826,6 +3826,10 @@ func (b *SystemBackend) authPaths() []*framework.Path {
Description: strings.TrimSpace(sysHelp["identity_token_key"][0]), Description: strings.TrimSpace(sysHelp["identity_token_key"][0]),
Required: false, Required: false,
}, },
"trim_request_trailing_slashes": {
Type: framework.TypeBool,
Required: false,
},
}, },
Operations: map[logical.Operation]framework.OperationHandler{ Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{ logical.ReadOperation: &framework.PathOperation{
@ -3916,6 +3920,10 @@ func (b *SystemBackend) authPaths() []*framework.Path {
Type: framework.TypeString, Type: framework.TypeString,
Required: false, Required: false,
}, },
"trim_request_trailing_slashes": {
Type: framework.TypeBool,
Required: false,
},
}, },
}}, }},
}, },
@ -4686,6 +4694,10 @@ func (b *SystemBackend) mountPaths() []*framework.Path {
Type: framework.TypeString, Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["identity_token_key"][0]), 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{ Operations: map[logical.Operation]framework.OperationHandler{
@ -4788,6 +4800,10 @@ func (b *SystemBackend) mountPaths() []*framework.Path {
Type: framework.TypeString, Type: framework.TypeString,
Required: false, Required: false,
}, },
"trim_request_trailing_slashes": {
Type: framework.TypeBool,
Required: false,
},
}, },
}}, }},
}, },

View File

@ -349,19 +349,20 @@ type MountEntry struct {
// MountConfig is used to hold settable options // MountConfig is used to hold settable options
type MountConfig struct { type MountConfig struct {
DefaultLeaseTTL time.Duration `json:"default_lease_ttl,omitempty" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default 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 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 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"` 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"` 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"` 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"` 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"` 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"` TokenType logical.TokenType `json:"token_type,omitempty" structs:"token_type" mapstructure:"token_type"`
AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"`
UserLockoutConfig *UserLockoutConfig `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"` UserLockoutConfig *UserLockoutConfig `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"`
DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"`
IdentityTokenKey string `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` 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. // 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 // APIMountConfig is an embedded struct of api.MountConfigInput
type APIMountConfig struct { type APIMountConfig struct {
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` TokenType string `json:"token_type" structs:"token_type" mapstructure:"token_type"`
AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"` AllowedManagedKeys []string `json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys"`
UserLockoutConfig *UserLockoutConfig `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"` UserLockoutConfig *UserLockoutConfig `json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config"`
PluginVersion string `json:"plugin_version,omitempty" mapstructure:"plugin_version"` PluginVersion string `json:"plugin_version,omitempty" mapstructure:"plugin_version"`
DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"` DelegatedAuthAccessors []string `json:"delegated_auth_accessors,omitempty" mapstructure:"delegated_auth_accessors"`
IdentityTokenKey string `json:"identity_token_key,omitempty" mapstructure:"identity_token_key"` 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. // PluginName is the name of the plugin registered in the catalog.
// //

View File

@ -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) { 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 // Allowing writing to a path ending in / makes it extremely difficult to
// understand user intent for the filesystem-like backends (kv, // understand user intent for the filesystem-like backends (kv,
// cubbyhole) -- did they want a key named foo/ or did they want to write // 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.UpdateOperation ||
req.Operation == logical.CreateOperation || req.Operation == logical.CreateOperation ||
req.Operation == logical.PatchOperation) { req.Operation == logical.PatchOperation) {
return logical.ErrorResponse("cannot write to a path ending in '/'"), nil if entry == nil || !entry.Config.TrimRequestTrailingSlashes {
} return logical.ErrorResponse("cannot write to a path ending in '/'"), nil
waitGroup, err := waitForReplicationState(ctx, c, req) } else {
if err != nil { req.Path = strings.TrimSuffix(req.Path, "/")
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
} }
err = c.PopulateTokenEntry(ctx, req) err = c.PopulateTokenEntry(ctx, req)
@ -892,7 +898,6 @@ func (c *Core) handleCancelableRequest(ctx context.Context, req *logical.Request
var nonHMACReqDataKeys []string var nonHMACReqDataKeys []string
var nonHMACRespDataKeys []string var nonHMACRespDataKeys []string
entry := c.router.MatchingMountEntry(ctx, req.Path)
if entry != nil { if entry != nil {
// Get and set ignored HMAC'd value. Reset those back to empty afterwards. // 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 { if rawVals, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {

View File

@ -373,23 +373,28 @@ func (r *Router) MatchingMountByAccessor(mountAccessor string) *MountEntry {
// MatchingMount returns the mount prefix that would be used for a path // MatchingMount returns the mount prefix that would be used for a path
func (r *Router) MatchingMount(ctx context.Context, path string) string { func (r *Router) MatchingMount(ctx context.Context, path string) string {
r.l.RLock() r.l.RLock()
mount := r.matchingMountInternal(ctx, path) mount, _ := r.matchingMountInternal(ctx, path)
r.l.RUnlock() r.l.RUnlock()
return mount 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) ns, err := namespace.FromContext(ctx)
if err != nil { if err != nil {
return "" return "", nil
} }
path = ns.Path + path path = ns.Path + path
mount, _, ok := r.root.LongestPrefix(path) mount, raw, ok := r.root.LongestPrefix(path)
if !ok { 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 // 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 { func (r *Router) MountConflict(ctx context.Context, path string) string {
r.l.RLock() r.l.RLock()
defer r.l.RUnlock() defer r.l.RUnlock()
if exactMatch := r.matchingMountInternal(ctx, path); exactMatch != "" { if exactMatch, _ := r.matchingMountInternal(ctx, path); exactMatch != "" {
return exactMatch return exactMatch
} }
if prefixMatch := r.matchingPrefixInternal(ctx, path); prefixMatch != "" { if prefixMatch := r.matchingPrefixInternal(ctx, path); prefixMatch != "" {

View File

@ -89,6 +89,10 @@ flags](/vault/docs/commands) included on all commands.
- `-token-type` `(string: "")` - Specifies the type of tokens that should be - `-token-type` `(string: "")` - Specifies the type of tokens that should be
returned by the auth method. 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 - `-plugin-version` `(string: "")` - Configures the semantic version of the plugin
to use. If unspecified, implies the built-in or any matching unversioned plugin to use. If unspecified, implies the built-in or any matching unversioned plugin
that may have been registered. that may have been registered.

View File

@ -168,6 +168,10 @@ flags](/vault/docs/commands) included on all commands.
- `-token-type` `(string: "")` - Specifies the type of tokens that should be - `-token-type` `(string: "")` - Specifies the type of tokens that should be
returned by the auth method. 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 - `-plugin-version` `(string: "")` - Configures the semantic version of the plugin
to use. The new version will not start running until the mount is to use. The new version will not start running until the mount is
[reloaded](/vault/docs/commands/plugin/reload). [reloaded](/vault/docs/commands/plugin/reload).

View File

@ -111,6 +111,10 @@ flags](/vault/docs/commands) included on all commands.
backend can delegate authentication to. To allow multiple accessors, provide backend can delegate authentication to. To allow multiple accessors, provide
the `delegated-auth-accessors` multiple times, each time with 1 accessor. 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 - `-plugin-version` `(string: "")` - Configures the semantic version of the plugin
to use. If unspecified, implies the built-in or any matching unversioned plugin to use. If unspecified, implies the built-in or any matching unversioned plugin
that may have been registered. that may have been registered.

View File

@ -97,6 +97,10 @@ flags](/vault/docs/commands) included on all commands.
backend can delegate authentication to. To allow multiple accessors, provide backend can delegate authentication to. To allow multiple accessors, provide
the `delegated-auth-accessors` multiple times, each time with 1 accessor. 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 - `-plugin-version` `(string: "")` - Configures the semantic version of the plugin
to use. The new version will not start running until the mount is to use. The new version will not start running until the mount is
[reloaded](/vault/docs/commands/plugin/reload). [reloaded](/vault/docs/commands/plugin/reload).

View File

@ -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 $ vault read -field=accessor sys/auth/auth/cert
``` ```
For CMP to work within certain clients, a few response headers need to be explicitly allowed For CMP to work within certain clients, a few response headers need to be explicitly
along with configuring the list of accessors the mount can delegate authentication towards. allowed, trailing slashes must be trimmed, and 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` must be configured. The following will grant the required response headers, you will need to replace the values for
to match your values. the `delegated-auth-accessors` to match your values.
```shell-session ```shell-session
$ vault secrets tune \ $ vault secrets tune \
@ -98,6 +98,7 @@ $ vault secrets tune \
-allowed-response-headers="Content-Length" \ -allowed-response-headers="Content-Length" \
-allowed-response-headers="WWW-Authenticate" \ -allowed-response-headers="WWW-Authenticate" \
-delegated-auth-accessors="auth_cert_4088ac2d" \ -delegated-auth-accessors="auth_cert_4088ac2d" \
-trim-request-trailing-slashes="true" \
pki pki
``` ```