From d3747f8fc72d61bf827700b6eac69f865561d1ec Mon Sep 17 00:00:00 2001 From: Ruzil Mukhametov Date: Wed, 3 Apr 2024 11:39:20 +0300 Subject: [PATCH 1/4] feature: http buffer filter configuration (#6249) Signed-off-by: Ruzil Mukhametov --- apis/projectcontour/v1alpha1/contourconfig.go | 5 + cmd/contour/serve.go | 4 + cmd/contour/servecontext.go | 62 ++--- cmd/contour/servecontext_test.go | 12 + examples/contour/01-crds.yaml | 24 ++ examples/render/contour-deployment.yaml | 24 ++ .../render/contour-gateway-provisioner.yaml | 24 ++ examples/render/contour-gateway.yaml | 24 ++ examples/render/contour.yaml | 24 ++ .../contourconfig/contourconfiguration.go | 14 +- .../contourconfiguration_test.go | 14 +- internal/envoy/v3/buffer.go | 40 ++++ internal/xdscache/v3/listener.go | 8 + internal/xdscache/v3/listener_test.go | 215 ++++++++++++++++++ .../docs/main/config/api-reference.html | 14 ++ 15 files changed, 468 insertions(+), 40 deletions(-) create mode 100644 internal/envoy/v3/buffer.go diff --git a/apis/projectcontour/v1alpha1/contourconfig.go b/apis/projectcontour/v1alpha1/contourconfig.go index e967051d7dc..07663245a76 100644 --- a/apis/projectcontour/v1alpha1/contourconfig.go +++ b/apis/projectcontour/v1alpha1/contourconfig.go @@ -528,6 +528,11 @@ type EnvoyListener struct { // AccessLog defines where Envoy logs are outputted for this listener. // +optional AccessLog string `json:"accessLog,omitempty"` + + // Sets the max_request_bytes value for the http.buffer filter. + // If the value is 0, it skips adding a Buffer filter into the filter chain. + // +optional + BufferMaxRequestBytes uint32 `json:"bufferMaxRequestBytes,omitempty"` } // EnvoyLogging defines how Envoy's logs can be configured. diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go index 0697b45a2f8..978507e58a6 100644 --- a/cmd/contour/serve.go +++ b/cmd/contour/serve.go @@ -137,7 +137,9 @@ func registerServe(app *kingpin.Application) (*kingpin.CmdClause, *serveContext) serve.Flag("disable-leader-election", "Disable leader election mechanism.").BoolVar(&ctx.LeaderElection.Disable) serve.Flag("envoy-http-access-log", "Envoy HTTP access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpAccessLog) + serve.Flag("envoy-http-buffer-max-request-bytes", "Envoy HTTP buffer max request bytes.").PlaceHolder("10485760").Uint32Var(&ctx.httpBufferMaxRequestBytes) serve.Flag("envoy-https-access-log", "Envoy HTTPS access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpsAccessLog) + serve.Flag("envoy-https-buffer-max-request-bytesg", "Envoy HTTPS buffer max request bytes.").PlaceHolder("10485760").Uint32Var(&ctx.httpsBufferMaxRequestBytes) serve.Flag("envoy-service-http-address", "Kubernetes Service address for HTTP requests.").PlaceHolder("").StringVar(&ctx.httpAddr) serve.Flag("envoy-service-http-port", "Kubernetes Service port for HTTP requests.").PlaceHolder("").IntVar(&ctx.httpPort) serve.Flag("envoy-service-https-address", "Kubernetes Service address for HTTPS requests.").PlaceHolder("").StringVar(&ctx.httpsAddr) @@ -468,6 +470,8 @@ func (s *Server) doServe() error { HTTP2MaxConcurrentStreams: contourConfiguration.Envoy.Listener.HTTP2MaxConcurrentStreams, PerConnectionBufferLimitBytes: contourConfiguration.Envoy.Listener.PerConnectionBufferLimitBytes, SocketOptions: contourConfiguration.Envoy.Listener.SocketOptions, + HTTPBufferMaxRequestBytes: contourConfiguration.Envoy.HTTPListener.BufferMaxRequestBytes, + HTTPSBufferMaxRequestBytes: contourConfiguration.Envoy.HTTPSListener.BufferMaxRequestBytes, } if listenerConfig.TracingConfig, err = s.setupTracingService(contourConfiguration.Tracing); err != nil { diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go index 3a1057b6479..c2cca08aaea 100644 --- a/cmd/contour/servecontext.go +++ b/cmd/contour/servecontext.go @@ -77,14 +77,16 @@ type serveContext struct { useProxyProto bool // envoy's http listener parameters - httpAddr string - httpPort int - httpAccessLog string + httpAddr string + httpPort int + httpAccessLog string + httpBufferMaxRequestBytes uint32 // envoy's https listener parameters - httpsAddr string - httpsPort int - httpsAccessLog string + httpsAddr string + httpsPort int + httpsAccessLog string + httpsBufferMaxRequestBytes uint32 // PermitInsecureGRPC disables TLS on Contour's gRPC listener. PermitInsecureGRPC bool @@ -116,22 +118,24 @@ type LeaderElection struct { func newServeContext() *serveContext { // Set defaults for parameters which are then overridden via flags, ENV, or ConfigFile return &serveContext{ - Config: config.Defaults(), - statsAddr: "0.0.0.0", - statsPort: 8002, - debugAddr: "127.0.0.1", - debugPort: 6060, - healthAddr: "0.0.0.0", - healthPort: 8000, - metricsAddr: "0.0.0.0", - metricsPort: 8000, - httpAccessLog: xdscache_v3.DEFAULT_HTTP_ACCESS_LOG, - httpsAccessLog: xdscache_v3.DEFAULT_HTTPS_ACCESS_LOG, - httpAddr: "0.0.0.0", - httpsAddr: "0.0.0.0", - httpPort: 8080, - httpsPort: 8443, - PermitInsecureGRPC: false, + Config: config.Defaults(), + statsAddr: "0.0.0.0", + statsPort: 8002, + debugAddr: "127.0.0.1", + debugPort: 6060, + healthAddr: "0.0.0.0", + healthPort: 8000, + metricsAddr: "0.0.0.0", + metricsPort: 8000, + httpAccessLog: xdscache_v3.DEFAULT_HTTP_ACCESS_LOG, + httpsAccessLog: xdscache_v3.DEFAULT_HTTPS_ACCESS_LOG, + httpAddr: "0.0.0.0", + httpsAddr: "0.0.0.0", + httpBufferMaxRequestBytes: 0, + httpsBufferMaxRequestBytes: 0, + httpPort: 8080, + httpsPort: 8443, + PermitInsecureGRPC: false, ServerConfig: ServerConfig{ xdsAddr: "127.0.0.1", xdsPort: 8001, @@ -543,14 +547,16 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_v1alpha1.Co Namespace: ctx.Config.EnvoyServiceNamespace, }, HTTPListener: &contour_v1alpha1.EnvoyListener{ - Address: ctx.httpAddr, - Port: ctx.httpPort, - AccessLog: ctx.httpAccessLog, + Address: ctx.httpAddr, + Port: ctx.httpPort, + AccessLog: ctx.httpAccessLog, + BufferMaxRequestBytes: ctx.httpBufferMaxRequestBytes, }, HTTPSListener: &contour_v1alpha1.EnvoyListener{ - Address: ctx.httpsAddr, - Port: ctx.httpsPort, - AccessLog: ctx.httpsAccessLog, + Address: ctx.httpsAddr, + Port: ctx.httpsPort, + AccessLog: ctx.httpsAccessLog, + BufferMaxRequestBytes: ctx.httpsBufferMaxRequestBytes, }, Metrics: &envoyMetrics, Health: &contour_v1alpha1.HealthConfig{ diff --git a/cmd/contour/servecontext_test.go b/cmd/contour/servecontext_test.go index 766a2f0af83..ed3a3a94fcd 100644 --- a/cmd/contour/servecontext_test.go +++ b/cmd/contour/servecontext_test.go @@ -901,6 +901,18 @@ func TestConvertServeContext(t *testing.T) { return cfg }, }, + "envoy buffer filter": { + getServeContext: func(ctx *serveContext) *serveContext { + ctx.httpBufferMaxRequestBytes = 10 + ctx.httpsBufferMaxRequestBytes = 10 + return ctx + }, + getContourConfiguration: func(cfg contour_v1alpha1.ContourConfigurationSpec) contour_v1alpha1.ContourConfigurationSpec { + cfg.Envoy.HTTPListener.BufferMaxRequestBytes = 10 + cfg.Envoy.HTTPSListener.BufferMaxRequestBytes = 10 + return cfg + }, + }, } for name, tc := range cases { diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index 0666bd83e57..87dedc6b4ba 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -250,6 +250,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -267,6 +273,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -3935,6 +3947,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -3952,6 +3970,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index c5fe223017e..bfe6393a2eb 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -470,6 +470,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -487,6 +493,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -4155,6 +4167,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -4172,6 +4190,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index 396d8c87dc7..05d7ecb11d1 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -261,6 +261,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -278,6 +284,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -3946,6 +3958,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -3963,6 +3981,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index d68bc13728d..1375904e0ed 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -286,6 +286,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -303,6 +309,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -3971,6 +3983,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -3988,6 +4006,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index 1246f04d59a..ea5753a4d1f 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -470,6 +470,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -487,6 +493,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -4155,6 +4167,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer @@ -4172,6 +4190,12 @@ spec: description: Defines an Envoy Listener Address. minLength: 1 type: string + bufferMaxRequestBytes: + description: |- + Sets the max_request_bytes value for the http.buffer filter. + If the value is 0, it skips adding a Buffer filter into the filter chain. + format: int32 + type: integer port: description: Defines an Envoy listener Port. type: integer diff --git a/internal/contourconfig/contourconfiguration.go b/internal/contourconfig/contourconfiguration.go index 0c369f170cb..53745751f11 100644 --- a/internal/contourconfig/contourconfiguration.go +++ b/internal/contourconfig/contourconfiguration.go @@ -81,14 +81,16 @@ func Defaults() contour_v1alpha1.ContourConfigurationSpec { Name: "envoy", }, HTTPListener: &contour_v1alpha1.EnvoyListener{ - Address: "0.0.0.0", - Port: 8080, - AccessLog: "/dev/stdout", + Address: "0.0.0.0", + Port: 8080, + AccessLog: "/dev/stdout", + BufferMaxRequestBytes: 0, }, HTTPSListener: &contour_v1alpha1.EnvoyListener{ - Address: "0.0.0.0", - Port: 8443, - AccessLog: "/dev/stdout", + Address: "0.0.0.0", + Port: 8443, + AccessLog: "/dev/stdout", + BufferMaxRequestBytes: 0, }, Health: &contour_v1alpha1.HealthConfig{ Address: "0.0.0.0", diff --git a/internal/contourconfig/contourconfiguration_test.go b/internal/contourconfig/contourconfiguration_test.go index caaac35e8a2..f37bfd56170 100644 --- a/internal/contourconfig/contourconfiguration_test.go +++ b/internal/contourconfig/contourconfiguration_test.go @@ -75,14 +75,16 @@ func TestOverlayOnDefaults(t *testing.T) { Name: "coolname", }, HTTPListener: &contour_v1alpha1.EnvoyListener{ - Address: "3.4.5.6", - Port: 8989, - AccessLog: "/dev/oops", + Address: "3.4.5.6", + Port: 8989, + AccessLog: "/dev/oops", + BufferMaxRequestBytes: 10, }, HTTPSListener: &contour_v1alpha1.EnvoyListener{ - Address: "4.5.6.7", - Port: 8445, - AccessLog: "/dev/oops", + Address: "4.5.6.7", + Port: 8445, + AccessLog: "/dev/oops", + BufferMaxRequestBytes: 10, }, Health: &contour_v1alpha1.HealthConfig{ Address: "1.1.1.1", diff --git a/internal/envoy/v3/buffer.go b/internal/envoy/v3/buffer.go new file mode 100644 index 00000000000..ff0f47e06e7 --- /dev/null +++ b/internal/envoy/v3/buffer.go @@ -0,0 +1,40 @@ +// Copyright Project Contour Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v3 + +import ( + envoy_filter_http_buffer_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/buffer/v3" + envoy_filter_network_http_connection_manager_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "github.com/envoyproxy/go-control-plane/pkg/wellknown" + "google.golang.org/protobuf/types/known/wrapperspb" + + "github.com/projectcontour/contour/internal/protobuf" +) + +// BufferFilter returns a configured HTTP buffer filter, +// or nil if maxRequestBytes = 0. +func BufferFilter(maxRequestBytes uint32) *envoy_filter_network_http_connection_manager_v3.HttpFilter { + if maxRequestBytes == 0 { + return nil + } + + return &envoy_filter_network_http_connection_manager_v3.HttpFilter{ + Name: wellknown.Buffer, + ConfigType: &envoy_filter_network_http_connection_manager_v3.HttpFilter_TypedConfig{ + TypedConfig: protobuf.MustMarshalAny(&envoy_filter_http_buffer_v3.Buffer{ + MaxRequestBytes: wrapperspb.UInt32(maxRequestBytes), + }), + }, + } +} diff --git a/internal/xdscache/v3/listener.go b/internal/xdscache/v3/listener.go index 975c0654743..0e2685d1159 100644 --- a/internal/xdscache/v3/listener.go +++ b/internal/xdscache/v3/listener.go @@ -150,6 +150,12 @@ type ListenerConfig struct { // SocketOptions configures socket options HTTP and HTTPS listeners. SocketOptions *contour_v1alpha1.SocketOptions + + // HTTPBufferMaxRequestBytes configures the buffer http filter. + HTTPBufferMaxRequestBytes uint32 + + // HTTPSBufferMaxRequestBytes configures the buffer http filter. + HTTPSBufferMaxRequestBytes uint32 } type ExtensionServiceConfig struct { @@ -395,6 +401,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) { cm := envoy_v3.HTTPConnectionManagerBuilder(). Codec(envoy_v3.CodecForVersions(cfg.DefaultHTTPVersions...)). DefaultFilters(). + AddFilter(envoy_v3.BufferFilter(cfg.HTTPBufferMaxRequestBytes)). RouteConfigName(httpRouteConfigName(listener)). MetricsPrefix(listener.Name). AccessLoggers(cfg.newInsecureAccessLog()). @@ -468,6 +475,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) { Codec(envoy_v3.CodecForVersions(cfg.DefaultHTTPVersions...)). AddFilter(envoy_v3.FilterMisdirectedRequests(vh.VirtualHost.Name)). DefaultFilters(). + AddFilter(envoy_v3.BufferFilter(cfg.HTTPSBufferMaxRequestBytes)). AddFilter(envoy_v3.FilterJWTAuthN(vh.JWTProviders)). AddFilter(authzFilter). RouteConfigName(httpsRouteConfigName(listener, vh.VirtualHost.Name)). diff --git a/internal/xdscache/v3/listener_test.go b/internal/xdscache/v3/listener_test.go index 095f248210a..e422638f0a9 100644 --- a/internal/xdscache/v3/listener_test.go +++ b/internal/xdscache/v3/listener_test.go @@ -3232,6 +3232,221 @@ func TestListenerVisit(t *testing.T) { SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), }, + + "httpproxy with HTTPBufferMaxRequestBytes set in listener config": { + ListenerConfig: ListenerConfig{ + HTTPBufferMaxRequestBytes: 10, + }, + objs: []any{ + &contour_v1.HTTPProxy{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "simple", + Namespace: "default", + }, + Spec: contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "www.example.com", + }, + Routes: []contour_v1.Route{{ + Conditions: []contour_v1.MatchCondition{{ + Prefix: "/", + }}, + Services: []contour_v1.Service{{ + Name: "backend", + Port: 80, + }}, + }}, + }, + }, + service, + }, + want: listenermap(&envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTP_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8080), + FilterChains: envoy_v3.FilterChains( + envoy_v3.HTTPConnectionManagerBuilder(). + RouteConfigName(ENVOY_HTTP_LISTENER). + MetricsPrefix(ENVOY_HTTP_LISTENER). + AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)). + DefaultFilters(). + AddFilter(envoy_v3.BufferFilter(10)). + Get(), + ), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }), + }, + + "httpsproxy with HTTPSBufferMaxRequestBytes set in listener config": { + ListenerConfig: ListenerConfig{ + HTTPSBufferMaxRequestBytes: 10, + }, + objs: []any{ + &contour_v1.HTTPProxy{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "simple", + Namespace: "default", + }, + Spec: contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "www.example.com", + TLS: &contour_v1.TLS{ + SecretName: "secret", + }, + }, + Routes: []contour_v1.Route{{ + Conditions: []contour_v1.MatchCondition{{ + Prefix: "/", + }}, + Services: []contour_v1.Service{{ + Name: "backend", + Port: 80, + }}, + }}, + }, + }, + secret, + service, + }, + want: listenermap(&envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTP_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8080), + FilterChains: envoy_v3.FilterChains(envoy_v3.HTTPConnectionManagerBuilder(). + RouteConfigName(ENVOY_HTTP_LISTENER). + MetricsPrefix(ENVOY_HTTP_LISTENER). + AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)). + DefaultFilters(). + Get(), + ), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, &envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTPS_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + FilterChains: []*envoy_config_listener_v3.FilterChain{{ + FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ + ServerNames: []string{"www.example.com"}, + }, + TransportSocket: transportSocket("secret", envoy_transport_socket_tls_v3.TlsParameters_TLSv1_2, envoy_transport_socket_tls_v3.TlsParameters_TLSv1_3, nil, "h2", "http/1.1"), + Filters: envoy_v3.Filters(envoy_v3.HTTPConnectionManagerBuilder(). + AddFilter(envoy_v3.FilterMisdirectedRequests("www.example.com")). + DefaultFilters(). + AddFilter(envoy_v3.BufferFilter(10)). + MetricsPrefix(ENVOY_HTTPS_LISTENER). + RouteConfigName(path.Join("https", "www.example.com")). + AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)). + Get()), + }}, + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(), + ), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }), + }, + "httpproxy with HTTPBufferMaxRequestBytes equals 0 in listener config": { + ListenerConfig: ListenerConfig{ + HTTPBufferMaxRequestBytes: 0, + }, + objs: []any{ + &contour_v1.HTTPProxy{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "simple", + Namespace: "default", + }, + Spec: contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "www.example.com", + }, + Routes: []contour_v1.Route{{ + Conditions: []contour_v1.MatchCondition{{ + Prefix: "/", + }}, + Services: []contour_v1.Service{{ + Name: "backend", + Port: 80, + }}, + }}, + }, + }, + service, + }, + want: listenermap(&envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTP_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8080), + FilterChains: envoy_v3.FilterChains( + envoy_v3.HTTPConnectionManagerBuilder(). + RouteConfigName(ENVOY_HTTP_LISTENER). + MetricsPrefix(ENVOY_HTTP_LISTENER). + AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)). + DefaultFilters(). + Get(), + ), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }), + }, + + "httpsproxy with HTTPSBufferMaxRequestBytes equals 0 in listener config": { + ListenerConfig: ListenerConfig{ + HTTPSBufferMaxRequestBytes: 0, + }, + objs: []any{ + &contour_v1.HTTPProxy{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "simple", + Namespace: "default", + }, + Spec: contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "www.example.com", + TLS: &contour_v1.TLS{ + SecretName: "secret", + }, + }, + Routes: []contour_v1.Route{{ + Conditions: []contour_v1.MatchCondition{{ + Prefix: "/", + }}, + Services: []contour_v1.Service{{ + Name: "backend", + Port: 80, + }}, + }}, + }, + }, + secret, + service, + }, + want: listenermap(&envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTP_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8080), + FilterChains: envoy_v3.FilterChains(envoy_v3.HTTPConnectionManagerBuilder(). + RouteConfigName(ENVOY_HTTP_LISTENER). + MetricsPrefix(ENVOY_HTTP_LISTENER). + AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)). + DefaultFilters(). + Get(), + ), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, &envoy_config_listener_v3.Listener{ + Name: ENVOY_HTTPS_LISTENER, + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + FilterChains: []*envoy_config_listener_v3.FilterChain{{ + FilterChainMatch: &envoy_config_listener_v3.FilterChainMatch{ + ServerNames: []string{"www.example.com"}, + }, + TransportSocket: transportSocket("secret", envoy_transport_socket_tls_v3.TlsParameters_TLSv1_2, envoy_transport_socket_tls_v3.TlsParameters_TLSv1_3, nil, "h2", "http/1.1"), + Filters: envoy_v3.Filters(envoy_v3.HTTPConnectionManagerBuilder(). + AddFilter(envoy_v3.FilterMisdirectedRequests("www.example.com")). + DefaultFilters(). + MetricsPrefix(ENVOY_HTTPS_LISTENER). + RouteConfigName(path.Join("https", "www.example.com")). + AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)). + Get()), + }}, + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(), + ), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }), + }, } for name, tc := range tests { diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index e2f957d50b4..b5cdeaeaf25 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -6765,6 +6765,20 @@

EnvoyListener

AccessLog defines where Envoy logs are outputted for this listener.

+ + +bufferMaxRequestBytes +
+ +uint32 + + + +(Optional) +

Sets the max_request_bytes value for the http.buffer filter. +If the value is 0, it skips adding a Buffer filter into the filter chain.

+ +

EnvoyListenerConfig From 69803efcadd7da48d8c9d0a44e8fdef9d3dcd969 Mon Sep 17 00:00:00 2001 From: Ruzil Mukhametov Date: Mon, 8 Apr 2024 08:29:52 +0300 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Lubron Signed-off-by: Ruzil Mukhametov --- cmd/contour/serve.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go index 978507e58a6..a9301627c3b 100644 --- a/cmd/contour/serve.go +++ b/cmd/contour/serve.go @@ -139,7 +139,7 @@ func registerServe(app *kingpin.Application) (*kingpin.CmdClause, *serveContext) serve.Flag("envoy-http-access-log", "Envoy HTTP access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpAccessLog) serve.Flag("envoy-http-buffer-max-request-bytes", "Envoy HTTP buffer max request bytes.").PlaceHolder("10485760").Uint32Var(&ctx.httpBufferMaxRequestBytes) serve.Flag("envoy-https-access-log", "Envoy HTTPS access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpsAccessLog) - serve.Flag("envoy-https-buffer-max-request-bytesg", "Envoy HTTPS buffer max request bytes.").PlaceHolder("10485760").Uint32Var(&ctx.httpsBufferMaxRequestBytes) + serve.Flag("envoy-https-buffer-max-request-bytes", "Envoy HTTPS buffer max request bytes.").PlaceHolder("10485760").Uint32Var(&ctx.httpsBufferMaxRequestBytes) serve.Flag("envoy-service-http-address", "Kubernetes Service address for HTTP requests.").PlaceHolder("").StringVar(&ctx.httpAddr) serve.Flag("envoy-service-http-port", "Kubernetes Service port for HTTP requests.").PlaceHolder("").IntVar(&ctx.httpPort) serve.Flag("envoy-service-https-address", "Kubernetes Service address for HTTPS requests.").PlaceHolder("").StringVar(&ctx.httpsAddr) From f9528952fdafee21c38e3249304b7454af86882c Mon Sep 17 00:00:00 2001 From: Ruzil Mukhametov Date: Tue, 9 Apr 2024 18:41:50 +0300 Subject: [PATCH 3/4] add release-note for the http buffer filter params Signed-off-by: Ruzil Mukhametov --- changelogs/unreleased/6315-ruzmuh-minor.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelogs/unreleased/6315-ruzmuh-minor.md diff --git a/changelogs/unreleased/6315-ruzmuh-minor.md b/changelogs/unreleased/6315-ruzmuh-minor.md new file mode 100644 index 00000000000..181d22d44f4 --- /dev/null +++ b/changelogs/unreleased/6315-ruzmuh-minor.md @@ -0,0 +1,3 @@ +## http buffer filter configuration + +Introduce two optional command line flags, `envoy-http-buffer-max-request-bytes` and `envoy-https-buffer-max-request-bytes`, with default values set to `0`. If the value is non-zero, an HTTP buffer filter will be added to the HTTP filter chain immediately after the `DefaultFilters()` with the `max_request_bytes` parameter. This configuration allows setting the buffer filter for the entire HTTP listener only. From fba7631901d8f3717c0c1aaa7107678fec907e9d Mon Sep 17 00:00:00 2001 From: Ruzil Mukhametov Date: Fri, 12 Apr 2024 17:07:48 +0300 Subject: [PATCH 4/4] use config param instead of cli args Signed-off-by: Ruzil Mukhametov --- changelogs/unreleased/6315-ruzmuh-minor.md | 2 +- cmd/contour/serve.go | 2 - cmd/contour/servecontext.go | 52 ++++++++++------------ cmd/contour/servecontext_test.go | 3 +- pkg/config/parameters.go | 11 +++-- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/changelogs/unreleased/6315-ruzmuh-minor.md b/changelogs/unreleased/6315-ruzmuh-minor.md index 181d22d44f4..ee60c747625 100644 --- a/changelogs/unreleased/6315-ruzmuh-minor.md +++ b/changelogs/unreleased/6315-ruzmuh-minor.md @@ -1,3 +1,3 @@ ## http buffer filter configuration -Introduce two optional command line flags, `envoy-http-buffer-max-request-bytes` and `envoy-https-buffer-max-request-bytes`, with default values set to `0`. If the value is non-zero, an HTTP buffer filter will be added to the HTTP filter chain immediately after the `DefaultFilters()` with the `max_request_bytes` parameter. This configuration allows setting the buffer filter for the entire HTTP listener only. +Introduce two optional config parameter `http-buffer-max-request-bytes`, with default values set to `0`. If the value is non-zero, an HTTP buffer filter will be added to the HTTP filter chain immediately after the `DefaultFilters()` with the `max_request_bytes` parameter. This configuration allows setting the buffer filter for the entire HTTP listeners only (both http and https). diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go index a9301627c3b..13d71c9835b 100644 --- a/cmd/contour/serve.go +++ b/cmd/contour/serve.go @@ -137,9 +137,7 @@ func registerServe(app *kingpin.Application) (*kingpin.CmdClause, *serveContext) serve.Flag("disable-leader-election", "Disable leader election mechanism.").BoolVar(&ctx.LeaderElection.Disable) serve.Flag("envoy-http-access-log", "Envoy HTTP access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpAccessLog) - serve.Flag("envoy-http-buffer-max-request-bytes", "Envoy HTTP buffer max request bytes.").PlaceHolder("10485760").Uint32Var(&ctx.httpBufferMaxRequestBytes) serve.Flag("envoy-https-access-log", "Envoy HTTPS access log.").PlaceHolder("/path/to/file").StringVar(&ctx.httpsAccessLog) - serve.Flag("envoy-https-buffer-max-request-bytes", "Envoy HTTPS buffer max request bytes.").PlaceHolder("10485760").Uint32Var(&ctx.httpsBufferMaxRequestBytes) serve.Flag("envoy-service-http-address", "Kubernetes Service address for HTTP requests.").PlaceHolder("").StringVar(&ctx.httpAddr) serve.Flag("envoy-service-http-port", "Kubernetes Service port for HTTP requests.").PlaceHolder("").IntVar(&ctx.httpPort) serve.Flag("envoy-service-https-address", "Kubernetes Service address for HTTPS requests.").PlaceHolder("").StringVar(&ctx.httpsAddr) diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go index c2cca08aaea..a0e5aa06b72 100644 --- a/cmd/contour/servecontext.go +++ b/cmd/contour/servecontext.go @@ -77,16 +77,14 @@ type serveContext struct { useProxyProto bool // envoy's http listener parameters - httpAddr string - httpPort int - httpAccessLog string - httpBufferMaxRequestBytes uint32 + httpAddr string + httpPort int + httpAccessLog string // envoy's https listener parameters - httpsAddr string - httpsPort int - httpsAccessLog string - httpsBufferMaxRequestBytes uint32 + httpsAddr string + httpsPort int + httpsAccessLog string // PermitInsecureGRPC disables TLS on Contour's gRPC listener. PermitInsecureGRPC bool @@ -118,24 +116,22 @@ type LeaderElection struct { func newServeContext() *serveContext { // Set defaults for parameters which are then overridden via flags, ENV, or ConfigFile return &serveContext{ - Config: config.Defaults(), - statsAddr: "0.0.0.0", - statsPort: 8002, - debugAddr: "127.0.0.1", - debugPort: 6060, - healthAddr: "0.0.0.0", - healthPort: 8000, - metricsAddr: "0.0.0.0", - metricsPort: 8000, - httpAccessLog: xdscache_v3.DEFAULT_HTTP_ACCESS_LOG, - httpsAccessLog: xdscache_v3.DEFAULT_HTTPS_ACCESS_LOG, - httpAddr: "0.0.0.0", - httpsAddr: "0.0.0.0", - httpBufferMaxRequestBytes: 0, - httpsBufferMaxRequestBytes: 0, - httpPort: 8080, - httpsPort: 8443, - PermitInsecureGRPC: false, + Config: config.Defaults(), + statsAddr: "0.0.0.0", + statsPort: 8002, + debugAddr: "127.0.0.1", + debugPort: 6060, + healthAddr: "0.0.0.0", + healthPort: 8000, + metricsAddr: "0.0.0.0", + metricsPort: 8000, + httpAccessLog: xdscache_v3.DEFAULT_HTTP_ACCESS_LOG, + httpsAccessLog: xdscache_v3.DEFAULT_HTTPS_ACCESS_LOG, + httpAddr: "0.0.0.0", + httpsAddr: "0.0.0.0", + httpPort: 8080, + httpsPort: 8443, + PermitInsecureGRPC: false, ServerConfig: ServerConfig{ xdsAddr: "127.0.0.1", xdsPort: 8001, @@ -550,13 +546,13 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_v1alpha1.Co Address: ctx.httpAddr, Port: ctx.httpPort, AccessLog: ctx.httpAccessLog, - BufferMaxRequestBytes: ctx.httpBufferMaxRequestBytes, + BufferMaxRequestBytes: ctx.Config.HTTPBufferMaxRequestBytes, }, HTTPSListener: &contour_v1alpha1.EnvoyListener{ Address: ctx.httpsAddr, Port: ctx.httpsPort, AccessLog: ctx.httpsAccessLog, - BufferMaxRequestBytes: ctx.httpsBufferMaxRequestBytes, + BufferMaxRequestBytes: ctx.Config.HTTPBufferMaxRequestBytes, }, Metrics: &envoyMetrics, Health: &contour_v1alpha1.HealthConfig{ diff --git a/cmd/contour/servecontext_test.go b/cmd/contour/servecontext_test.go index ed3a3a94fcd..f24ad1764b6 100644 --- a/cmd/contour/servecontext_test.go +++ b/cmd/contour/servecontext_test.go @@ -903,8 +903,7 @@ func TestConvertServeContext(t *testing.T) { }, "envoy buffer filter": { getServeContext: func(ctx *serveContext) *serveContext { - ctx.httpBufferMaxRequestBytes = 10 - ctx.httpsBufferMaxRequestBytes = 10 + ctx.Config.HTTPBufferMaxRequestBytes = 10 return ctx }, getContourConfiguration: func(cfg contour_v1alpha1.ContourConfigurationSpec) contour_v1alpha1.ContourConfigurationSpec { diff --git a/pkg/config/parameters.go b/pkg/config/parameters.go index e1bafe249f9..394d4b8bc63 100644 --- a/pkg/config/parameters.go +++ b/pkg/config/parameters.go @@ -700,6 +700,10 @@ type Parameters struct { // GlobalExternalAuthorization optionally holds properties of the global external authorization configuration. GlobalExternalAuthorization GlobalExternalAuthorization `yaml:"globalExtAuth,omitempty"` + // HTTPBufferMaxRequestBytes defines value for max_request_bytes parameter of http buffer filter. + // If the value is non-zero, an HTTP buffer filter will be added to the HTTP filter chain. + HTTPBufferMaxRequestBytes uint32 `yaml:"http-buffer-max-request-bytes,omitempty"` + // MetricsParameters holds configurable parameters for Contour and Envoy metrics. Metrics MetricsParameters `yaml:"metrics,omitempty"` @@ -1058,9 +1062,10 @@ func Defaults() Parameters { ResponseHeadersPolicy: HeadersPolicy{}, ApplyToIngress: false, }, - EnvoyServiceName: "envoy", - EnvoyServiceNamespace: contourNamespace, - DefaultHTTPVersions: []HTTPVersionType{}, + EnvoyServiceName: "envoy", + EnvoyServiceNamespace: contourNamespace, + DefaultHTTPVersions: []HTTPVersionType{}, + HTTPBufferMaxRequestBytes: 0, Cluster: ClusterParameters{ DNSLookupFamily: AutoClusterDNSFamily, },