mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-07 07:07:05 +02: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:
parent
314874c2b1
commit
415d260995
@ -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
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 {
|
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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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`,
|
||||||
|
"",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
@ -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.
|
||||||
//
|
//
|
||||||
|
@ -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 {
|
||||||
|
@ -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 != "" {
|
||||||
|
@ -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.
|
||||||
|
@ -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).
|
||||||
|
@ -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.
|
||||||
|
@ -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).
|
||||||
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user