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:
Mike Palmiotto
2024-01-26 15:24:32 -05:00
committed by GitHub
parent dc9d1e275d
commit 12f69a8ce5
10 changed files with 94 additions and 13 deletions

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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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",

View File

@@ -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" {

View File

@@ -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)

View File

@@ -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())

View File

@@ -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
}

View File

@@ -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)
}
})
}

View File

@@ -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"
}