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" |     address = "%s" | ||||||
|     tls_disable = true |     tls_disable = true | ||||||
|     require_request_header = false |     require_request_header = false | ||||||
|  |     disable_request_limiter = false | ||||||
| } | } | ||||||
| listener "tcp" { | listener "tcp" { | ||||||
|     address = "%s" |     address = "%s" | ||||||
|     tls_disable = true |     tls_disable = true | ||||||
|     require_request_header = true |     require_request_header = true | ||||||
|  | 	disable_request_limiter = true | ||||||
| } | } | ||||||
| ` | ` | ||||||
| 	listenAddr1 := generateListenerAddress(t) | 	listenAddr1 := generateListenerAddress(t) | ||||||
|   | |||||||
| @@ -901,6 +901,8 @@ func (c *ServerCommand) InitListeners(config *server.Config, disableClustering b | |||||||
| 		} | 		} | ||||||
| 		props["max_request_duration"] = lnConfig.MaxRequestDuration.String() | 		props["max_request_duration"] = lnConfig.MaxRequestDuration.String() | ||||||
|  |  | ||||||
|  | 		props["disable_request_limiter"] = strconv.FormatBool(lnConfig.DisableRequestLimiter) | ||||||
|  |  | ||||||
| 		if lnConfig.ChrootNamespace != "" { | 		if lnConfig.ChrootNamespace != "" { | ||||||
| 			props["chroot_namespace"] = lnConfig.ChrootNamespace | 			props["chroot_namespace"] = lnConfig.ChrootNamespace | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -611,6 +611,7 @@ func testLoadConfigFile_json(t *testing.T) { | |||||||
| 					Type:                  "tcp", | 					Type:                  "tcp", | ||||||
| 					Address:               "127.0.0.1:443", | 					Address:               "127.0.0.1:443", | ||||||
| 					CustomResponseHeaders: DefaultCustomHeaders, | 					CustomResponseHeaders: DefaultCustomHeaders, | ||||||
|  | 					DisableRequestLimiter: false, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
|  |  | ||||||
| @@ -789,8 +790,9 @@ func testConfig_Sanitized(t *testing.T) { | |||||||
| 		"listeners": []interface{}{ | 		"listeners": []interface{}{ | ||||||
| 			map[string]interface{}{ | 			map[string]interface{}{ | ||||||
| 				"config": map[string]interface{}{ | 				"config": map[string]interface{}{ | ||||||
| 					"address":          "127.0.0.1:443", | 					"address":                 "127.0.0.1:443", | ||||||
| 					"chroot_namespace": "admin/", | 					"chroot_namespace":        "admin/", | ||||||
|  | 					"disable_request_limiter": false, | ||||||
| 				}, | 				}, | ||||||
| 				"type": configutil.TCP, | 				"type": configutil.TCP, | ||||||
| 			}, | 			}, | ||||||
| @@ -889,6 +891,7 @@ listener "tcp" { | |||||||
|   redact_addresses = true |   redact_addresses = true | ||||||
|   redact_cluster_name = true |   redact_cluster_name = true | ||||||
|   redact_version = true |   redact_version = true | ||||||
|  |   disable_request_limiter = true | ||||||
| } | } | ||||||
| listener "unix" { | listener "unix" { | ||||||
|   address = "/var/run/vault.sock" |   address = "/var/run/vault.sock" | ||||||
| @@ -951,6 +954,7 @@ listener "unix" { | |||||||
| 					RedactAddresses:       true, | 					RedactAddresses:       true, | ||||||
| 					RedactClusterName:     true, | 					RedactClusterName:     true, | ||||||
| 					RedactVersion:         true, | 					RedactVersion:         true, | ||||||
|  | 					DisableRequestLimiter: true, | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					Type:              "unix", | 					Type:              "unix", | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ cluster_addr = "top_level_cluster_addr" | |||||||
| listener "tcp" { | listener "tcp" { | ||||||
|   address = "127.0.0.1:443" |   address = "127.0.0.1:443" | ||||||
|   chroot_namespace="admin/" |   chroot_namespace="admin/" | ||||||
|  |   disable_request_limiter = false | ||||||
| } | } | ||||||
|  |  | ||||||
| backend "consul" { | backend "consul" { | ||||||
|   | |||||||
| @@ -263,6 +263,10 @@ func handler(props *vault.HandlerProperties) http.Handler { | |||||||
| 		wrappedHandler = disableReplicationStatusEndpointWrapping(wrappedHandler) | 		wrappedHandler = disableReplicationStatusEndpointWrapping(wrappedHandler) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if props.ListenerConfig != nil && props.ListenerConfig.DisableRequestLimiter { | ||||||
|  | 		wrappedHandler = wrapRequestLimiterHandler(wrappedHandler, props) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return wrappedHandler | 	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) { | 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{} | 	lim := &limits.RequestLimiter{} | ||||||
| 	if r.PathLimited { | 	if r.PathLimited { | ||||||
| 		lim = core.GetRequestLimiter(limits.SpecialPathLimiter) | 		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 { | func rateLimitQuotaWrapping(handler http.Handler, core *vault.Core) http.Handler { | ||||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
| 		ns, err := namespace.FromContext(r.Context()) | 		ns, err := namespace.FromContext(r.Context()) | ||||||
|   | |||||||
| @@ -143,6 +143,10 @@ type Listener struct { | |||||||
| 	// DisableReplicationStatusEndpoint disables the unauthenticated replication status endpoints | 	// DisableReplicationStatusEndpoint disables the unauthenticated replication status endpoints | ||||||
| 	DisableReplicationStatusEndpointsRaw interface{} `hcl:"disable_replication_status_endpoints"` | 	DisableReplicationStatusEndpointsRaw interface{} `hcl:"disable_replication_status_endpoints"` | ||||||
| 	DisableReplicationStatusEndpoints    bool        `hcl:"-"` | 	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. | // 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.parseChrootNamespaceSettings, | ||||||
| 		l.parseRedactionSettings, | 		l.parseRedactionSettings, | ||||||
| 		l.parseDisableReplicationStatusEndpointSettings, | 		l.parseDisableReplicationStatusEndpointSettings, | ||||||
|  | 		l.parseDisableRequestLimiter, | ||||||
| 	} { | 	} { | ||||||
| 		err := parser() | 		err := parser() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -370,6 +375,17 @@ func (l *Listener) parseDisableReplicationStatusEndpointSettings() error { | |||||||
| 	return nil | 	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. | // parseChrootNamespace attempts to parse the raw listener chroot namespace settings. | ||||||
| // The state of the listener will be modified, raw data will be cleared upon | // The state of the listener will be modified, raw data will be cleared upon | ||||||
| // successful parsing. | // successful parsing. | ||||||
| @@ -446,6 +462,10 @@ func (l *Listener) parseRequestSettings() error { | |||||||
| 		return fmt.Errorf("invalid value for require_request_header: %w", err) | 		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 | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -181,14 +181,16 @@ func TestListener_parseRequestSettings(t *testing.T) { | |||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
|  |  | ||||||
| 	tests := map[string]struct { | 	tests := map[string]struct { | ||||||
| 		rawMaxRequestSize            any | 		rawMaxRequestSize             any | ||||||
| 		expectedMaxRequestSize       int64 | 		expectedMaxRequestSize        int64 | ||||||
| 		rawMaxRequestDuration        any | 		rawMaxRequestDuration         any | ||||||
| 		expectedDuration             time.Duration | 		expectedDuration              time.Duration | ||||||
| 		rawRequireRequestHeader      any | 		rawRequireRequestHeader       any | ||||||
| 		expectedRequireRequestHeader bool | 		expectedRequireRequestHeader  bool | ||||||
| 		isErrorExpected              bool | 		rawDisableRequestLimiter      any | ||||||
| 		errorMessage                 string | 		expectedDisableRequestLimiter bool | ||||||
|  | 		isErrorExpected               bool | ||||||
|  | 		errorMessage                  string | ||||||
| 	}{ | 	}{ | ||||||
| 		"nil": { | 		"nil": { | ||||||
| 			isErrorExpected: false, | 			isErrorExpected: false, | ||||||
| @@ -224,6 +226,17 @@ func TestListener_parseRequestSettings(t *testing.T) { | |||||||
| 			expectedRequireRequestHeader: true, | 			expectedRequireRequestHeader: true, | ||||||
| 			isErrorExpected:              false, | 			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 { | 	for name, tc := range tests { | ||||||
| @@ -234,9 +247,10 @@ func TestListener_parseRequestSettings(t *testing.T) { | |||||||
|  |  | ||||||
| 			// Configure listener with raw values | 			// Configure listener with raw values | ||||||
| 			l := &Listener{ | 			l := &Listener{ | ||||||
| 				MaxRequestSizeRaw:       tc.rawMaxRequestSize, | 				MaxRequestSizeRaw:        tc.rawMaxRequestSize, | ||||||
| 				MaxRequestDurationRaw:   tc.rawMaxRequestDuration, | 				MaxRequestDurationRaw:    tc.rawMaxRequestDuration, | ||||||
| 				RequireRequestHeaderRaw: tc.rawRequireRequestHeader, | 				RequireRequestHeaderRaw:  tc.rawRequireRequestHeader, | ||||||
|  | 				DisableRequestLimiterRaw: tc.rawDisableRequestLimiter, | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			err := l.parseRequestSettings() | 			err := l.parseRequestSettings() | ||||||
| @@ -251,11 +265,13 @@ func TestListener_parseRequestSettings(t *testing.T) { | |||||||
| 				require.Equal(t, tc.expectedMaxRequestSize, l.MaxRequestSize) | 				require.Equal(t, tc.expectedMaxRequestSize, l.MaxRequestSize) | ||||||
| 				require.Equal(t, tc.expectedDuration, l.MaxRequestDuration) | 				require.Equal(t, tc.expectedDuration, l.MaxRequestDuration) | ||||||
| 				require.Equal(t, tc.expectedRequireRequestHeader, l.RequireRequestHeader) | 				require.Equal(t, tc.expectedRequireRequestHeader, l.RequireRequestHeader) | ||||||
|  | 				require.Equal(t, tc.expectedDisableRequestLimiter, l.DisableRequestLimiter) | ||||||
|  |  | ||||||
| 				// Ensure the state was modified for the raw values. | 				// Ensure the state was modified for the raw values. | ||||||
| 				require.Nil(t, l.MaxRequestSizeRaw) | 				require.Nil(t, l.MaxRequestSizeRaw) | ||||||
| 				require.Nil(t, l.MaxRequestDurationRaw) | 				require.Nil(t, l.MaxRequestDurationRaw) | ||||||
| 				require.Nil(t, l.RequireRequestHeaderRaw) | 				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 { | func CreateContextOriginalBody(parent context.Context, body io.ReadCloser) context.Context { | ||||||
| 	return context.WithValue(parent, ctxKeyOriginalBody{}, body) | 	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
	 Mike Palmiotto
					Mike Palmiotto