mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
Request Limiter listener config opt-out (#25098)
This commit introduces a new listener config option to allow disabling the request limiter per-listener.
This commit is contained in:
4
changelog/25098.txt
Normal file
4
changelog/25098.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
```release-note:improvement
|
||||
limits: Add a listener configuration option `disable_request_limiter` to allow
|
||||
disabling the request limiter per-listener.
|
||||
```
|
||||
@@ -350,11 +350,13 @@ listener "tcp" {
|
||||
address = "%s"
|
||||
tls_disable = true
|
||||
require_request_header = false
|
||||
disable_request_limiter = false
|
||||
}
|
||||
listener "tcp" {
|
||||
address = "%s"
|
||||
tls_disable = true
|
||||
require_request_header = true
|
||||
disable_request_limiter = true
|
||||
}
|
||||
`
|
||||
listenAddr1 := generateListenerAddress(t)
|
||||
|
||||
@@ -901,6 +901,8 @@ func (c *ServerCommand) InitListeners(config *server.Config, disableClustering b
|
||||
}
|
||||
props["max_request_duration"] = lnConfig.MaxRequestDuration.String()
|
||||
|
||||
props["disable_request_limiter"] = strconv.FormatBool(lnConfig.DisableRequestLimiter)
|
||||
|
||||
if lnConfig.ChrootNamespace != "" {
|
||||
props["chroot_namespace"] = lnConfig.ChrootNamespace
|
||||
}
|
||||
|
||||
@@ -611,6 +611,7 @@ func testLoadConfigFile_json(t *testing.T) {
|
||||
Type: "tcp",
|
||||
Address: "127.0.0.1:443",
|
||||
CustomResponseHeaders: DefaultCustomHeaders,
|
||||
DisableRequestLimiter: false,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -789,8 +790,9 @@ func testConfig_Sanitized(t *testing.T) {
|
||||
"listeners": []interface{}{
|
||||
map[string]interface{}{
|
||||
"config": map[string]interface{}{
|
||||
"address": "127.0.0.1:443",
|
||||
"chroot_namespace": "admin/",
|
||||
"address": "127.0.0.1:443",
|
||||
"chroot_namespace": "admin/",
|
||||
"disable_request_limiter": false,
|
||||
},
|
||||
"type": configutil.TCP,
|
||||
},
|
||||
@@ -889,6 +891,7 @@ listener "tcp" {
|
||||
redact_addresses = true
|
||||
redact_cluster_name = true
|
||||
redact_version = true
|
||||
disable_request_limiter = true
|
||||
}
|
||||
listener "unix" {
|
||||
address = "/var/run/vault.sock"
|
||||
@@ -951,6 +954,7 @@ listener "unix" {
|
||||
RedactAddresses: true,
|
||||
RedactClusterName: true,
|
||||
RedactVersion: true,
|
||||
DisableRequestLimiter: true,
|
||||
},
|
||||
{
|
||||
Type: "unix",
|
||||
|
||||
@@ -13,6 +13,7 @@ cluster_addr = "top_level_cluster_addr"
|
||||
listener "tcp" {
|
||||
address = "127.0.0.1:443"
|
||||
chroot_namespace="admin/"
|
||||
disable_request_limiter = false
|
||||
}
|
||||
|
||||
backend "consul" {
|
||||
|
||||
@@ -263,6 +263,10 @@ func handler(props *vault.HandlerProperties) http.Handler {
|
||||
wrappedHandler = disableReplicationStatusEndpointWrapping(wrappedHandler)
|
||||
}
|
||||
|
||||
if props.ListenerConfig != nil && props.ListenerConfig.DisableRequestLimiter {
|
||||
wrappedHandler = wrapRequestLimiterHandler(wrappedHandler, props)
|
||||
}
|
||||
|
||||
return wrappedHandler
|
||||
}
|
||||
|
||||
@@ -910,6 +914,15 @@ func forwardRequest(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func acquireLimiterListener(core *vault.Core, rawReq *http.Request, r *logical.Request) (*limits.RequestListener, bool) {
|
||||
var disable bool
|
||||
disableRequestLimiter := rawReq.Context().Value(logical.CtxKeyDisableRequestLimiter{})
|
||||
if disableRequestLimiter != nil {
|
||||
disable = disableRequestLimiter.(bool)
|
||||
}
|
||||
if disable {
|
||||
return &limits.RequestListener{}, true
|
||||
}
|
||||
|
||||
lim := &limits.RequestLimiter{}
|
||||
if r.PathLimited {
|
||||
lim = core.GetRequestLimiter(limits.SpecialPathLimiter)
|
||||
|
||||
13
http/util.go
13
http/util.go
@@ -43,6 +43,19 @@ func wrapMaxRequestSizeHandler(handler http.Handler, props *vault.HandlerPropert
|
||||
})
|
||||
}
|
||||
|
||||
func wrapRequestLimiterHandler(handler http.Handler, props *vault.HandlerProperties) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
request := r.WithContext(
|
||||
context.WithValue(
|
||||
r.Context(),
|
||||
logical.CtxKeyDisableRequestLimiter{},
|
||||
props.ListenerConfig.DisableRequestLimiter,
|
||||
),
|
||||
)
|
||||
handler.ServeHTTP(w, request)
|
||||
})
|
||||
}
|
||||
|
||||
func rateLimitQuotaWrapping(handler http.Handler, core *vault.Core) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ns, err := namespace.FromContext(r.Context())
|
||||
|
||||
@@ -143,6 +143,10 @@ type Listener struct {
|
||||
// DisableReplicationStatusEndpoint disables the unauthenticated replication status endpoints
|
||||
DisableReplicationStatusEndpointsRaw interface{} `hcl:"disable_replication_status_endpoints"`
|
||||
DisableReplicationStatusEndpoints bool `hcl:"-"`
|
||||
|
||||
// DisableRequestLimiter allows per-listener disabling of the Request Limiter.
|
||||
DisableRequestLimiterRaw any `hcl:"disable_request_limiter"`
|
||||
DisableRequestLimiter bool `hcl:"-"`
|
||||
}
|
||||
|
||||
// AgentAPI allows users to select which parts of the Agent API they want enabled.
|
||||
@@ -257,6 +261,7 @@ func parseListener(item *ast.ObjectItem) (*Listener, error) {
|
||||
l.parseChrootNamespaceSettings,
|
||||
l.parseRedactionSettings,
|
||||
l.parseDisableReplicationStatusEndpointSettings,
|
||||
l.parseDisableRequestLimiter,
|
||||
} {
|
||||
err := parser()
|
||||
if err != nil {
|
||||
@@ -370,6 +375,17 @@ func (l *Listener) parseDisableReplicationStatusEndpointSettings() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseDisableRequestLimiter attempts to parse the raw disable_request_limiter
|
||||
// setting. The receiving Listener's DisableRequestLimiter field will be set
|
||||
// with the successfully parsed value or return an error
|
||||
func (l *Listener) parseDisableRequestLimiter() error {
|
||||
if err := parseAndClearBool(&l.DisableRequestLimiterRaw, &l.DisableRequestLimiter); err != nil {
|
||||
return fmt.Errorf("invalid value for disable_request_limiter: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseChrootNamespace attempts to parse the raw listener chroot namespace settings.
|
||||
// The state of the listener will be modified, raw data will be cleared upon
|
||||
// successful parsing.
|
||||
@@ -446,6 +462,10 @@ func (l *Listener) parseRequestSettings() error {
|
||||
return fmt.Errorf("invalid value for require_request_header: %w", err)
|
||||
}
|
||||
|
||||
if err := parseAndClearBool(&l.DisableRequestLimiterRaw, &l.DisableRequestLimiter); err != nil {
|
||||
return fmt.Errorf("invalid value for disable_request_limiter: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -181,14 +181,16 @@ func TestListener_parseRequestSettings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := map[string]struct {
|
||||
rawMaxRequestSize any
|
||||
expectedMaxRequestSize int64
|
||||
rawMaxRequestDuration any
|
||||
expectedDuration time.Duration
|
||||
rawRequireRequestHeader any
|
||||
expectedRequireRequestHeader bool
|
||||
isErrorExpected bool
|
||||
errorMessage string
|
||||
rawMaxRequestSize any
|
||||
expectedMaxRequestSize int64
|
||||
rawMaxRequestDuration any
|
||||
expectedDuration time.Duration
|
||||
rawRequireRequestHeader any
|
||||
expectedRequireRequestHeader bool
|
||||
rawDisableRequestLimiter any
|
||||
expectedDisableRequestLimiter bool
|
||||
isErrorExpected bool
|
||||
errorMessage string
|
||||
}{
|
||||
"nil": {
|
||||
isErrorExpected: false,
|
||||
@@ -224,6 +226,17 @@ func TestListener_parseRequestSettings(t *testing.T) {
|
||||
expectedRequireRequestHeader: true,
|
||||
isErrorExpected: false,
|
||||
},
|
||||
"disable-request-limiter-bad": {
|
||||
rawDisableRequestLimiter: "badvalue",
|
||||
expectedDisableRequestLimiter: false,
|
||||
isErrorExpected: true,
|
||||
errorMessage: "invalid value for disable_request_limiter",
|
||||
},
|
||||
"disable-request-limiter-good": {
|
||||
rawDisableRequestLimiter: "true",
|
||||
expectedDisableRequestLimiter: true,
|
||||
isErrorExpected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
@@ -234,9 +247,10 @@ func TestListener_parseRequestSettings(t *testing.T) {
|
||||
|
||||
// Configure listener with raw values
|
||||
l := &Listener{
|
||||
MaxRequestSizeRaw: tc.rawMaxRequestSize,
|
||||
MaxRequestDurationRaw: tc.rawMaxRequestDuration,
|
||||
RequireRequestHeaderRaw: tc.rawRequireRequestHeader,
|
||||
MaxRequestSizeRaw: tc.rawMaxRequestSize,
|
||||
MaxRequestDurationRaw: tc.rawMaxRequestDuration,
|
||||
RequireRequestHeaderRaw: tc.rawRequireRequestHeader,
|
||||
DisableRequestLimiterRaw: tc.rawDisableRequestLimiter,
|
||||
}
|
||||
|
||||
err := l.parseRequestSettings()
|
||||
@@ -251,11 +265,13 @@ func TestListener_parseRequestSettings(t *testing.T) {
|
||||
require.Equal(t, tc.expectedMaxRequestSize, l.MaxRequestSize)
|
||||
require.Equal(t, tc.expectedDuration, l.MaxRequestDuration)
|
||||
require.Equal(t, tc.expectedRequireRequestHeader, l.RequireRequestHeader)
|
||||
require.Equal(t, tc.expectedDisableRequestLimiter, l.DisableRequestLimiter)
|
||||
|
||||
// Ensure the state was modified for the raw values.
|
||||
require.Nil(t, l.MaxRequestSizeRaw)
|
||||
require.Nil(t, l.MaxRequestDurationRaw)
|
||||
require.Nil(t, l.RequireRequestHeaderRaw)
|
||||
require.Nil(t, l.DisableRequestLimiterRaw)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -543,3 +543,9 @@ func ContextOriginalBodyValue(ctx context.Context) (io.ReadCloser, bool) {
|
||||
func CreateContextOriginalBody(parent context.Context, body io.ReadCloser) context.Context {
|
||||
return context.WithValue(parent, ctxKeyOriginalBody{}, body)
|
||||
}
|
||||
|
||||
type CtxKeyDisableRequestLimiter struct{}
|
||||
|
||||
func (c CtxKeyDisableRequestLimiter) String() string {
|
||||
return "disable_request_limiter"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user