diff --git a/apis/projectcontour/v1alpha1/contourconfig.go b/apis/projectcontour/v1alpha1/contourconfig.go index 0c193b0641f..738b11da75f 100644 --- a/apis/projectcontour/v1alpha1/contourconfig.go +++ b/apis/projectcontour/v1alpha1/contourconfig.go @@ -736,6 +736,19 @@ type NetworkParameters struct { // Contour's default is 9001. // +optional EnvoyAdminPort *int `json:"adminPort,omitempty"` + + // EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + // before any processing of request by HTTP filters or routing. This + // affects the upstream host header. Without setting this option to true, incoming + // requests with host example.com. will not match against route with domains + // match set to example.com. + // + // See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + // for more information. + // + // Contour's default is false. + // +optional + EnvoyStripTrailingHostDot *bool `json:"stripTrailingHostDot,omitempty"` } // RateLimitServiceConfig defines properties of a global Rate Limit Service. diff --git a/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go b/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go index ba974b98aa0..d1e3d42945d 100644 --- a/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go +++ b/apis/projectcontour/v1alpha1/zz_generated.deepcopy.go @@ -1077,6 +1077,11 @@ func (in *NetworkParameters) DeepCopyInto(out *NetworkParameters) { *out = new(int) **out = **in } + if in.EnvoyStripTrailingHostDot != nil { + in, out := &in.EnvoyStripTrailingHostDot, &out.EnvoyStripTrailingHostDot + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkParameters. diff --git a/changelogs/unreleased/6792-saley89-small.md b/changelogs/unreleased/6792-saley89-small.md new file mode 100644 index 00000000000..675660ca494 --- /dev/null +++ b/changelogs/unreleased/6792-saley89-small.md @@ -0,0 +1 @@ +Adds a new configuration option `strip-trailing-host-dot` which defines if trailing dot of the host should be removed from host/authority header before any processing of request by HTTP filters or routing. \ No newline at end of file diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go index c4a459f1045..634a03db97e 100644 --- a/cmd/contour/serve.go +++ b/cmd/contour/serve.go @@ -464,6 +464,7 @@ func (s *Server) doServe() error { MergeSlashes: !*contourConfiguration.Envoy.Listener.DisableMergeSlashes, ServerHeaderTransformation: contourConfiguration.Envoy.Listener.ServerHeaderTransformation, XffNumTrustedHops: *contourConfiguration.Envoy.Network.XffNumTrustedHops, + StripTrailingHostDot: *contourConfiguration.Envoy.Network.EnvoyStripTrailingHostDot, ConnectionBalancer: contourConfiguration.Envoy.Listener.ConnectionBalancer, MaxRequestsPerConnection: contourConfiguration.Envoy.Listener.MaxRequestsPerConnection, HTTP2MaxConcurrentStreams: contourConfiguration.Envoy.Listener.HTTP2MaxConcurrentStreams, diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go index 64bae0a281a..9ecaa3b4272 100644 --- a/cmd/contour/servecontext.go +++ b/cmd/contour/servecontext.go @@ -580,8 +580,9 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_v1alpha1.Co }, }, Network: &contour_v1alpha1.NetworkParameters{ - XffNumTrustedHops: &ctx.Config.Network.XffNumTrustedHops, - EnvoyAdminPort: &ctx.Config.Network.EnvoyAdminPort, + XffNumTrustedHops: &ctx.Config.Network.XffNumTrustedHops, + EnvoyAdminPort: &ctx.Config.Network.EnvoyAdminPort, + EnvoyStripTrailingHostDot: &ctx.Config.Network.EnvoyStripTrailingHostDot, }, }, Gateway: gatewayConfig, diff --git a/cmd/contour/servecontext_test.go b/cmd/contour/servecontext_test.go index 852cc3f6cdd..4ee43b23b48 100644 --- a/cmd/contour/servecontext_test.go +++ b/cmd/contour/servecontext_test.go @@ -483,8 +483,9 @@ func TestConvertServeContext(t *testing.T) { }, }, Network: &contour_v1alpha1.NetworkParameters{ - EnvoyAdminPort: ptr.To(9001), - XffNumTrustedHops: ptr.To(uint32(0)), + EnvoyAdminPort: ptr.To(9001), + XffNumTrustedHops: ptr.To(uint32(0)), + EnvoyStripTrailingHostDot: ptr.To(false), }, }, Gateway: nil, diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index 2bd91065cd6..eecffc63f10 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -521,6 +521,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- @@ -4337,6 +4348,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index 3a979fa3881..708140edfc1 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -736,6 +736,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- @@ -4552,6 +4563,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index 579495e6aa9..74f849055b5 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -532,6 +532,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- @@ -4348,6 +4359,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index b270884aaa0..3feb4d24b12 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -557,6 +557,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- @@ -4373,6 +4384,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index 86a2caf02e8..eae4fb6191b 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -736,6 +736,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- @@ -4552,6 +4563,17 @@ spec: Contour's default is 0. format: int32 type: integer + stripTrailingHostDot: + description: |- + EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + before any processing of request by HTTP filters or routing. This + affects the upstream host header. Without setting this option to true, incoming + requests with host example.com. will not match against route with domains + match set to example.com. + See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + for more information. + Contour's default is false. + type: boolean type: object service: description: |- diff --git a/internal/contourconfig/contourconfiguration.go b/internal/contourconfig/contourconfiguration.go index 44539247e0f..256808d3e24 100644 --- a/internal/contourconfig/contourconfiguration.go +++ b/internal/contourconfig/contourconfiguration.go @@ -127,8 +127,9 @@ func Defaults() contour_v1alpha1.ContourConfigurationSpec { }, }, Network: &contour_v1alpha1.NetworkParameters{ - XffNumTrustedHops: ptr.To(uint32(0)), - EnvoyAdminPort: ptr.To(9001), + XffNumTrustedHops: ptr.To(uint32(0)), + EnvoyAdminPort: ptr.To(9001), + EnvoyStripTrailingHostDot: ptr.To(false), }, }, Gateway: nil, diff --git a/internal/contourconfig/contourconfiguration_test.go b/internal/contourconfig/contourconfiguration_test.go index 8b7c6fb6495..b68b30a07e8 100644 --- a/internal/contourconfig/contourconfiguration_test.go +++ b/internal/contourconfig/contourconfiguration_test.go @@ -130,8 +130,9 @@ func TestOverlayOnDefaults(t *testing.T) { }, }, Network: &contour_v1alpha1.NetworkParameters{ - XffNumTrustedHops: ptr.To(uint32(77)), - EnvoyAdminPort: ptr.To(9997), + XffNumTrustedHops: ptr.To(uint32(77)), + EnvoyAdminPort: ptr.To(9997), + EnvoyStripTrailingHostDot: ptr.To(true), }, }, Gateway: &contour_v1alpha1.GatewayConfig{ diff --git a/internal/envoy/v3/listener.go b/internal/envoy/v3/listener.go index d458e65bfef..fc859158dcf 100644 --- a/internal/envoy/v3/listener.go +++ b/internal/envoy/v3/listener.go @@ -185,6 +185,7 @@ type httpConnectionManagerBuilder struct { serverHeaderTransformation envoy_filter_network_http_connection_manager_v3.HttpConnectionManager_ServerHeaderTransformation forwardClientCertificate *dag.ClientCertificateDetails numTrustedHops uint32 + stripTrailingHostDot bool tracingConfig *envoy_filter_network_http_connection_manager_v3.HttpConnectionManager_Tracing maxRequestsPerConnection *uint32 http2MaxConcurrentStreams *uint32 @@ -294,6 +295,11 @@ func (b *httpConnectionManagerBuilder) NumTrustedHops(num uint32) *httpConnectio return b } +func (b *httpConnectionManagerBuilder) StripTrailingHostDot(strip bool) *httpConnectionManagerBuilder { + b.stripTrailingHostDot = strip + return b +} + // MaxRequestsPerConnection sets max requests per connection for the downstream. func (b *httpConnectionManagerBuilder) MaxRequestsPerConnection(maxRequestsPerConnection *uint32) *httpConnectionManagerBuilder { b.maxRequestsPerConnection = maxRequestsPerConnection @@ -506,8 +512,9 @@ func (b *httpConnectionManagerBuilder) Get() *envoy_config_listener_v3.Filter { AllowChunkedLength: b.allowChunkedLength, }, - UseRemoteAddress: wrapperspb.Bool(true), - XffNumTrustedHops: b.numTrustedHops, + UseRemoteAddress: wrapperspb.Bool(true), + XffNumTrustedHops: b.numTrustedHops, + StripTrailingHostDot: b.stripTrailingHostDot, NormalizePath: wrapperspb.Bool(true), diff --git a/internal/envoy/v3/listener_test.go b/internal/envoy/v3/listener_test.go index 7f91b3d6626..2cc564d924d 100644 --- a/internal/envoy/v3/listener_test.go +++ b/internal/envoy/v3/listener_test.go @@ -673,6 +673,7 @@ func TestHTTPConnectionManager(t *testing.T) { serverHeaderTranformation contour_v1alpha1.ServerHeaderTransformationType forwardClientCertificate *dag.ClientCertificateDetails xffNumTrustedHops uint32 + stripTrailingHostDot bool maxRequestsPerConnection *uint32 http2MaxConcurrentStreams *uint32 want *envoy_config_listener_v3.Filter @@ -1370,6 +1371,54 @@ func TestHTTPConnectionManager(t *testing.T) { }, }, }, + "enable StripTrailingHostDot": { + routename: "default/kuard", + accesslogger: FileAccessLogEnvoy("/dev/stdout", "", nil, contour_v1alpha1.LogLevelInfo), + stripTrailingHostDot: true, + want: &envoy_config_listener_v3.Filter{ + Name: wellknown.HTTPConnectionManager, + ConfigType: &envoy_config_listener_v3.Filter_TypedConfig{ + TypedConfig: protobuf.MustMarshalAny(&envoy_filter_network_http_connection_manager_v3.HttpConnectionManager{ + StatPrefix: "default/kuard", + RouteSpecifier: &envoy_filter_network_http_connection_manager_v3.HttpConnectionManager_Rds{ + Rds: &envoy_filter_network_http_connection_manager_v3.Rds{ + RouteConfigName: "default/kuard", + ConfigSource: &envoy_config_core_v3.ConfigSource{ + ResourceApiVersion: envoy_config_core_v3.ApiVersion_V3, + ConfigSourceSpecifier: &envoy_config_core_v3.ConfigSource_ApiConfigSource{ + ApiConfigSource: &envoy_config_core_v3.ApiConfigSource{ + ApiType: envoy_config_core_v3.ApiConfigSource_GRPC, + TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + GrpcServices: []*envoy_config_core_v3.GrpcService{{ + TargetSpecifier: &envoy_config_core_v3.GrpcService_EnvoyGrpc_{ + EnvoyGrpc: &envoy_config_core_v3.GrpcService_EnvoyGrpc{ + ClusterName: "contour", + Authority: "contour", + }, + }, + }}, + }, + }, + }, + }, + }, + HttpFilters: defaultHTTPFilters, + HttpProtocolOptions: &envoy_config_core_v3.Http1ProtocolOptions{ + // Enable support for HTTP/1.0 requests that carry + // a Host: header. See #537. + AcceptHttp_10: true, + }, + CommonHttpProtocolOptions: &envoy_config_core_v3.HttpProtocolOptions{}, + AccessLog: FileAccessLogEnvoy("/dev/stdout", "", nil, contour_v1alpha1.LogLevelInfo), + UseRemoteAddress: wrapperspb.Bool(true), + NormalizePath: wrapperspb.Bool(true), + PreserveExternalRequestId: true, + MergeSlashes: false, + StripTrailingHostDot: true, + }), + }, + }, + }, "maxRequestsPerConnection set to 1": { routename: "default/kuard", accesslogger: FileAccessLogEnvoy("/dev/stdout", "", nil, contour_v1alpha1.LogLevelInfo), @@ -1486,6 +1535,7 @@ func TestHTTPConnectionManager(t *testing.T) { MergeSlashes(tc.mergeSlashes). ServerHeaderTransformation(tc.serverHeaderTranformation). NumTrustedHops(tc.xffNumTrustedHops). + StripTrailingHostDot(tc.stripTrailingHostDot). ForwardClientCertificate(tc.forwardClientCertificate). MaxRequestsPerConnection(tc.maxRequestsPerConnection). HTTP2MaxConcurrentStreams(tc.http2MaxConcurrentStreams). diff --git a/internal/featuretests/v3/listeners_test.go b/internal/featuretests/v3/listeners_test.go index b7b4270e62c..525c7601140 100644 --- a/internal/featuretests/v3/listeners_test.go +++ b/internal/featuretests/v3/listeners_test.go @@ -1228,6 +1228,63 @@ func TestHTTPProxyXffNumTrustedHops(t *testing.T) { }) } +func TestHTTPProxyStripTrailingHostDot(t *testing.T) { + rh, c, done := setup(t, func(conf *xdscache_v3.ListenerConfig) { + conf.StripTrailingHostDot = true + }) + + defer done() + envoyGen := envoy_v3.NewEnvoyGen(envoy_v3.EnvoyGenOpt{ + XDSClusterName: envoy_v3.DefaultXDSClusterName, + }) + + rh.OnAdd(fixture.NewService("backend"). + WithPorts(core_v1.ServicePort{Name: "http", Port: 80})) + + // p1 is a httpproxy + p1 := &contour_v1.HTTPProxy{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "simple", + Namespace: "default", + }, + Spec: contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "kuard.example.com", + }, + Routes: []contour_v1.Route{{ + Conditions: []contour_v1.MatchCondition{{ + Prefix: "/", + }}, + Services: []contour_v1.Service{{ + Name: "backend", + Port: 80, + }}, + }}, + }, + } + rh.OnAdd(p1) + + // verify that the xff-num-trusted-hops have been set to 1. + httpListener := defaultHTTPListener() + + httpListener.FilterChains = envoy_v3.FilterChains(envoyGen.HTTPConnectionManagerBuilder(). + RouteConfigName("ingress_http"). + MetricsPrefix("ingress_http"). + AccessLoggers(envoy_v3.FileAccessLogEnvoy("/dev/stdout", "", nil, contour_v1alpha1.LogLevelInfo)). + RequestTimeout(timeout.DurationSetting(0)). + StripTrailingHostDot(true). + DefaultFilters(). + Get()) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + Resources: resources(t, + httpListener, + statsListener(), + ), + TypeUrl: listenerType, + }) +} + func TestHTTPProxyServerHeaderTransformation(t *testing.T) { rh, c, done := setup(t, func(conf *xdscache_v3.ListenerConfig) { conf.ServerHeaderTransformation = contour_v1alpha1.AppendIfAbsentServerHeader diff --git a/internal/xdscache/v3/listener.go b/internal/xdscache/v3/listener.go index 23a95719cad..16619593c85 100644 --- a/internal/xdscache/v3/listener.go +++ b/internal/xdscache/v3/listener.go @@ -119,6 +119,10 @@ type ListenerConfig struct { // right side of the x-forwarded-for HTTP header to trust. XffNumTrustedHops uint32 + // StripTrailingHostDot sets if trailing dot of the host should be removed from host/authority header before any + // processing of request by HTTP filters or routing. + StripTrailingHostDot bool + // ConnectionBalancer // The validated value is 'exact'. // If no configuration is specified, Envoy will not attempt to balance active connections between worker threads @@ -383,6 +387,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) { MergeSlashes(cfg.MergeSlashes). ServerHeaderTransformation(cfg.ServerHeaderTransformation). NumTrustedHops(cfg.XffNumTrustedHops). + StripTrailingHostDot(cfg.StripTrailingHostDot). MaxRequestsPerConnection(cfg.MaxRequestsPerConnection). HTTP2MaxConcurrentStreams(cfg.HTTP2MaxConcurrentStreams). AddFilter(httpGlobalExternalAuthConfig(cfg.GlobalExternalAuthConfig)). @@ -458,6 +463,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) { MergeSlashes(cfg.MergeSlashes). ServerHeaderTransformation(cfg.ServerHeaderTransformation). NumTrustedHops(cfg.XffNumTrustedHops). + StripTrailingHostDot(cfg.StripTrailingHostDot). Tracing(envoy_v3.TracingConfig(envoyTracingConfig(cfg.TracingConfig))). AddFilter(envoy_v3.GlobalRateLimitFilter(envoyGlobalRateLimitConfig(cfg.RateLimitConfig))). ForwardClientCertificate(forwardClientCertificate). @@ -539,6 +545,7 @@ func (c *ListenerCache) OnChange(root *dag.DAG) { MergeSlashes(cfg.MergeSlashes). ServerHeaderTransformation(cfg.ServerHeaderTransformation). NumTrustedHops(cfg.XffNumTrustedHops). + StripTrailingHostDot(cfg.StripTrailingHostDot). Tracing(envoy_v3.TracingConfig(envoyTracingConfig(cfg.TracingConfig))). AddFilter(envoy_v3.GlobalRateLimitFilter(envoyGlobalRateLimitConfig(cfg.RateLimitConfig))). ForwardClientCertificate(forwardClientCertificate). diff --git a/internal/xdscache/v3/listener_test.go b/internal/xdscache/v3/listener_test.go index 6b4b20ce5dc..18f9d32d33c 100644 --- a/internal/xdscache/v3/listener_test.go +++ b/internal/xdscache/v3/listener_test.go @@ -2117,6 +2117,48 @@ func TestListenerVisit(t *testing.T) { SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), }), }, + "httpproxy with StripTrailingHostDot set in listener config": { + ListenerConfig: ListenerConfig{ + StripTrailingHostDot: true, + }, + 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( + envoyGen.HTTPConnectionManagerBuilder(). + RouteConfigName(ENVOY_HTTP_LISTENER). + MetricsPrefix(ENVOY_HTTP_LISTENER). + AccessLoggers(envoy_v3.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG, "", nil, contour_v1alpha1.LogLevelInfo)). + DefaultFilters(). + StripTrailingHostDot(true). + Get(), + ), + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }), + }, "httpsproxy with secret with stream idle timeout set in listener config": { ListenerConfig: ListenerConfig{ Timeouts: contourconfig.Timeouts{ diff --git a/pkg/config/parameters.go b/pkg/config/parameters.go index e6fa67302d1..2890fe9e343 100644 --- a/pkg/config/parameters.go +++ b/pkg/config/parameters.go @@ -458,6 +458,16 @@ type NetworkParameters struct { // Configure the port used to access the Envoy Admin interface. // If configured to port "0" then the admin interface is disabled. EnvoyAdminPort int `yaml:"admin-port,omitempty"` + + // EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header + // before any processing of request by HTTP filters or routing. This + // affects the upstream host header. Without setting this option to true, incoming + // requests with host example.com. will not match against route with domains + // match set to example.com. + // + // See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot + // for more information. + EnvoyStripTrailingHostDot bool `yaml:"strip-trailing-host-dot,omitempty"` } // ListenerParameters hold various configurable listener values. @@ -1037,8 +1047,9 @@ func Defaults() Parameters { DNSLookupFamily: AutoClusterDNSFamily, }, Network: NetworkParameters{ - XffNumTrustedHops: 0, - EnvoyAdminPort: 9001, + XffNumTrustedHops: 0, + EnvoyStripTrailingHostDot: false, + EnvoyAdminPort: 9001, }, Listener: ListenerParameters{ ConnectionBalancer: "", diff --git a/pkg/config/parameters_test.go b/pkg/config/parameters_test.go index 3580f0a36d4..efa44d2d13f 100644 --- a/pkg/config/parameters_test.go +++ b/pkg/config/parameters_test.go @@ -445,6 +445,15 @@ network: admin-port: 9001 `) + check(func(t *testing.T, conf *Parameters) { + assert.True(t, conf.Network.EnvoyStripTrailingHostDot) + }, ` +network: + strip-trailing-host-dot: true + num-trusted-hops: 0 + admin-port: 9001 +`) + check(func(t *testing.T, conf *Parameters) { assert.Equal(t, ptr.To(uint32(1)), conf.Listener.MaxRequestsPerConnection) }, ` diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index 4e858b99569..925bb4a751a 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -8273,6 +8273,26 @@

NetworkParameters

Contour’s default is 9001.

+ + +stripTrailingHostDot +
+ +bool + + + +(Optional) +

EnvoyStripTrailingHostDot defines if trailing dot of the host should be removed from host/authority header +before any processing of request by HTTP filters or routing. This +affects the upstream host header. Without setting this option to true, incoming +requests with host example.com. will not match against route with domains +match set to example.com.

+

See https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot +for more information.

+

Contour’s default is false.

+ +

NetworkPublishing diff --git a/site/content/docs/main/configuration.md b/site/content/docs/main/configuration.md index d96ba39314f..1a82048fcf3 100644 --- a/site/content/docs/main/configuration.md +++ b/site/content/docs/main/configuration.md @@ -179,10 +179,11 @@ _This is Envoy's default setting value and is not explicitly configured by Conto The network configuration block can be used to configure various parameters network connections. -| Field Name | Type | Default | Description | -| ---------------- | ---- | ------- | ----------------------------------------------------------------------------------------------------------------------- | -| num-trusted-hops | int | 0 | Configures the number of additional ingress proxy hops from the right side of the x-forwarded-for HTTP header to trust. | -| admin-port | int | 9001 | Configures the Envoy Admin read-only listener on Envoy. Set to `0` to disable. | +| Field Name | Type | Default | Description | +|-------------------------|------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| num-trusted-hops | int | 0 | Configures the number of additional ingress proxy hops from the right side of the x-forwarded-for HTTP header to trust. | +| admin-port | int | 9001 | Configures the Envoy Admin read-only listener on Envoy. Set to `0` to disable. | +| strip-trailing-host-dot | bool | false | Defines if trailing dot of the host should be removed from host/authority header before any processing of request by HTTP filters or routing. This affects the upstream host header. Without setting this option to true, incoming requests with host example.com. will not match against route with domains match set to example.com. See [the Envoy documentation][15] for more information. | ### Listener Configuration @@ -525,3 +526,4 @@ connects to Contour: [12]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto#envoy-v3-api-field-extensions-filters-network-http-connection-manager-v3-httpconnectionmanager-request-timeout [13]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto#envoy-v3-api-field-extensions-filters-network-http-connection-manager-v3-httpconnectionmanager-delayed-close-timeout [14]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener.proto#config-listener-v3-listener-connectionbalanceconfig +[15]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto?highlight=strip_trailing_host_dot \ No newline at end of file