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 @@
Contour’s default is 9001.
+stripTrailingHostDot
+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.
+