From a78acf1482421769ecc8589c773db2c3e3fc5241 Mon Sep 17 00:00:00 2001 From: James Peach Date: Wed, 29 Apr 2020 16:56:36 +1000 Subject: [PATCH] internal: filter misdirected TLS requests TLS routes are specialized to a unique virtual hostname. However, if wildcard certificates are being used, browsers will aggressively coalesce and reuse server connections even when the full origin hostname doesn't match. This results on 404 responses because each TLS virtual host only has routes for one host. We can avoid this behaviour bleeding out to users by generating a 421 Misdirected Request response if the request authority doesn't match the FQDN of the virtual host. In this case, the browser is supposed to understand that the request wasn't processed and re-send it on a new connection. This fixes #1493. Signed-off-by: James Peach --- .../httpproxy/004-https-sni-enforcement.yaml | 37 +--- .../009-https-misdirected-request.yaml | 192 ++++++++++++++++++ .../testsuite/policies/contour-client.rego | 8 + .../testsuite/policies/contour-resources.rego | 11 + .../testsuite/policies/contour-response.rego | 17 ++ internal/contour/listener.go | 13 +- internal/contour/listener_test.go | 6 +- internal/e2e/lds_test.go | 84 +++----- internal/envoy/listener.go | 61 +++++- .../featuretests/downstreamvalidation_test.go | 21 +- internal/featuretests/envoy.go | 87 ++++---- internal/featuretests/fallbackcert_test.go | 112 ++++------ internal/featuretests/tcpproxy_test.go | 59 +++--- .../tlscertificatedelegation_test.go | 16 +- .../featuretests/tlsprotocolversion_test.go | 24 +-- site/_resources/envoy.md | 2 +- 16 files changed, 476 insertions(+), 274 deletions(-) create mode 100644 _integration/testsuite/httpproxy/009-https-misdirected-request.yaml diff --git a/_integration/testsuite/httpproxy/004-https-sni-enforcement.yaml b/_integration/testsuite/httpproxy/004-https-sni-enforcement.yaml index 3b1c3fee297..bdef79e0299 100644 --- a/_integration/testsuite/httpproxy/004-https-sni-enforcement.yaml +++ b/_integration/testsuite/httpproxy/004-https-sni-enforcement.yaml @@ -117,7 +117,7 @@ fatal_proxy_is_not_present[msg] { msg := sprintf("HTTPProxy for %q is not present", [ fqdn ]) } ---- +--- Name := "echo-one" @@ -243,7 +243,7 @@ fatal_proxy_is_not_present[msg] { msg := sprintf("HTTPProxy for %q is not present", [ fqdn ]) } ---- +--- Name := "echo-two" @@ -309,9 +309,10 @@ error_path_routing_mismatch[msg] { --- import data.contour.http.client +import data.contour.http.response # Ensure that sending a request to "echo-one" with the SNI from "echo-two" -# generates a 404. +# generates a 4xx response status. Response := client.Get({ "url": sprintf("https://%s/https-sni-enforcement/%d", [ @@ -324,23 +325,18 @@ Response := client.Get({ "tls_server_name": "echo-two.projectcontour.io", }) -Wanted := 404 - -error_non_404_response [msg] { - Response.status_code != Wanted - - msg := sprintf("got status %d, wanted %d", [ - Response.status_code, Wanted - ]) +error_non_400_response [msg] { + not response.is_4xx(Response) + msg := sprintf("got status %d, wanted 4xx", [ Response.status_code ]) } - --- import data.contour.http.client +import data.contour.http.response # Ensure that sending a request to "echo-two" with the SNI from "echo-one" -# generates a 404. +# generates a 4xx response status. Response := client.Get({ "url": sprintf("https://%s/https-sni-enforcement/%d", [ @@ -353,18 +349,7 @@ Response := client.Get({ "tls_server_name": "echo-one.projectcontour.io", }) -Wanted := 404 - -error_no_response { - not Response -} - error_non_404_response [msg] { - Response.status_code != Wanted - - msg := sprintf("got status %d, wanted %d", [ - Response.status_code, Wanted - ]) + not response.is_4xx(Response) + msg := sprintf("got status %d, wanted 4xx", [ Response.status_code ]) } - - diff --git a/_integration/testsuite/httpproxy/009-https-misdirected-request.yaml b/_integration/testsuite/httpproxy/009-https-misdirected-request.yaml new file mode 100644 index 00000000000..71b27fb432b --- /dev/null +++ b/_integration/testsuite/httpproxy/009-https-misdirected-request.yaml @@ -0,0 +1,192 @@ +# Copyright 2020 VMware, Inc. +# +# 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. + +import data.contour.resources + +# Ensure that cert-manager is installed. +# Version check the certificates resource. + +Group := "cert-manager.io" +Version := "v1alpha2" + +have_certmanager_version { + v := resources.versions["certificates"] + v[_].Group == Group + v[_].Version == Version +} + +skip[msg] { + not resources.is_supported("certificates") + msg := "cert-manager is not installed" +} + +skip[msg] { + not have_certmanager_version + + avail := resources.versions["certificates"] + + msg := concat("\n", [ + sprintf("cert-manager version %s/%s is not installed", [Group, Version]), + "available versions:", + yaml.marshal(avail) + ]) +} + +--- + +# Create a self-signed issuer to give us secrets. + +apiVersion: cert-manager.io/v1alpha2 +kind: Issuer +metadata: + name: selfsigned +spec: + selfSigned: {} + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ingress-conformance-echo +$apply: + fixture: + as: echo + +--- + +apiVersion: v1 +kind: Service +metadata: + name: ingress-conformance-echo +$apply: + fixture: + as: echo + +--- + +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: echo-cert +spec: + dnsNames: + - echo.projectcontour.io + secretName: echo + issuerRef: + name: selfsigned + +--- + +apiVersion: projectcontour.io/v1 +kind: HTTPProxy +metadata: + name: echo +spec: + virtualhost: + fqdn: echo.projectcontour.io + tls: + secretName: echo + routes: + - services: + - name: echo + port: 80 + +--- + +import data.contour.resources + +Name := "echo" + +fatal_proxy_is_not_present[msg] { + not resources.is_present("httpproxies", Name) + msg := sprintf("HTTPProxy for %q is not present", [ Name ]) +} + +--- + +import data.contour.resources + +Name := "echo" + +fatal_proxy_is_not_valid[msg] { + status := resources.status("httpproxies", Name) + + object.get(status, "currentStatus", "") != "valid" + + msg := sprintf("HTTPProxy %q is not valid\n%s", [ + Name, yaml.marshal(status) + ]) +} + +--- + +import data.contour.http.client +import data.contour.http.request +import data.contour.http.response + +Response := client.Get({ + "url": sprintf("https://%s/misdirected/%d", [ + client.target_addr, time.now_ns() + ]), + "headers": { + "Host": "echo.projectcontour.io", + "User-Agent": client.ua("misdirected-request"), + }, + "tls_insecure_skip_verify": true, +}) + +error_non_200_response [msg] { + not response.status_is(Response, 200) + msg := sprintf("got status %d, wanted %d", [Response.status_code, 200]) +} + +error_wrong_routing [msg] { + not response.has_testid(Response) + msg := "response has missing body or test ID" +} + +error_wrong_routing[msg] { + wanted := "echo" + testid := response.testid(Response) + testid != wanted + msg := sprintf("got test ID %q, wanted %q", [testid, wanted]) +} + +--- + +import data.contour.http.client +import data.contour.http.request +import data.contour.http.response + +# Send a request with a Host header that doesn't match the SNI name that +# we have for the proxy document. We expect the mismatch will generate a +# 421 response, not 404. + +Response := client.Get({ + "url": sprintf("https://%s/misdirected/%d", [ + client.target_addr, time.now_ns() + ]), + "headers": { + "Host": "echo-two.projectcontour.io", + "User-Agent": client.ua("misdirected-request"), + }, + "tls_server_name": "echo.projectcontour.io", + "tls_insecure_skip_verify": true, +}) + +error_non_421_response [msg] { + not response.status_is(Response, 421) + msg := sprintf("got status %d, wanted %d", [Response.status_code, 421]) +} diff --git a/_integration/testsuite/policies/contour-client.rego b/_integration/testsuite/policies/contour-client.rego index 97b741a6b82..3197d528216 100644 --- a/_integration/testsuite/policies/contour-client.rego +++ b/_integration/testsuite/policies/contour-client.rego @@ -38,4 +38,12 @@ Get(params) = response { } response := http.send(object.union(to_send, params)) +} else = response { + # If the Get wasn't evaluated for any reason, return a dummy object to ensure + # subsequent field references are valid. + response := { + "status_code": 0, + "body": {}, + "headers": {}, + } } diff --git a/_integration/testsuite/policies/contour-resources.rego b/_integration/testsuite/policies/contour-resources.rego index dab9af2cb53..ade42935b55 100644 --- a/_integration/testsuite/policies/contour-resources.rego +++ b/_integration/testsuite/policies/contour-resources.rego @@ -81,3 +81,14 @@ get(resource, name) = obj { } else = obj { obj := {} } + +# status returns the status field of the named resource. If the resource +# is not present, and empty object is returned. Implemented in terms of +# 'get', so namespace syntax works for the object name. +# +# Examples: +# resources.status("httpproxies", "foo") +status(resource, name) = s { + r := get(resource, name) + s := object.get(r, "status", {}) +} diff --git a/_integration/testsuite/policies/contour-response.rego b/_integration/testsuite/policies/contour-response.rego index eef84d5fd22..a24d588ca7a 100644 --- a/_integration/testsuite/policies/contour-response.rego +++ b/_integration/testsuite/policies/contour-response.rego @@ -44,3 +44,20 @@ testid(resp) = value { b := body(resp) value := object.get(b, "TestId", "") } + +# Return true if the response status matches. +status_is(resp, expected_code) = true { + status_code := object.get(resp, "status_code", 0) + status_code == expected_code +} else = false { + true +} + +# Return true if the response status is in the 4xx range. +is_4xx(resp) = true { + status_code := object.get(resp, "status_code", 0) + status_code >= 400 + status_code < 500 +} else = false { + true +} diff --git a/internal/contour/listener.go b/internal/contour/listener.go index c1331aba4cc..fa732c4ad89 100644 --- a/internal/contour/listener.go +++ b/internal/contour/listener.go @@ -1,4 +1,4 @@ -// Copyright © 2019 VMware +// Copyright © 2020 VMware // 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 @@ -298,6 +298,7 @@ func visitListeners(root dag.Vertex, lvc *ListenerVisitorConfig) map[string]*v2. if lv.http { // Add a listener if there are vhosts bound to http. cm := envoy.HTTPConnectionManagerBuilder(). + DefaultFilters(). RouteConfigName(ENVOY_HTTP_LISTENER). MetricsPrefix(ENVOY_HTTP_LISTENER). AccessLoggers(lvc.newInsecureAccessLog()). @@ -366,6 +367,8 @@ func (v *listenerVisitor) visit(vertex dag.Vertex) { // coded into monitoring dashboards. filters = envoy.Filters( envoy.HTTPConnectionManagerBuilder(). + AddFilter(envoy.FilterMisdirectedRequests(vh.VirtualHost.Name)). + DefaultFilters(). RouteConfigName(path.Join("https", vh.VirtualHost.Name)). MetricsPrefix(ENVOY_HTTPS_LISTENER). AccessLoggers(v.ListenerVisitorConfig.newSecureAccessLog()). @@ -403,10 +406,12 @@ func (v *listenerVisitor) visit(vertex dag.Vertex) { v.listeners[ENVOY_HTTPS_LISTENER].FilterChains = append(v.listeners[ENVOY_HTTPS_LISTENER].FilterChains, envoy.FilterChainTLS(vh.VirtualHost.Name, downstreamTLS, filters)) - // If this VirtualHost has enabled the fallback certificate then set a default FilterChain which will allow - // routes with this vhost to accept non SNI TLS requests + // If this VirtualHost has enabled the fallback certificate then set a default + // FilterChain which will allow routes with this vhost to accept non-SNI TLS requests. + // Note that we don't add the misdirected requests filter on this chain because at this + // point we don't actually know the full set of server names that will be bound to the + // filter chain through the ENVOY_FALLBACK_ROUTECONFIG route configuration. if vh.FallbackCertificate != nil && !envoy.ContainsFallbackFilterChain(v.listeners[ENVOY_HTTPS_LISTENER].FilterChains) { - // Construct the downstreamTLSContext passing the configured fallbackCertificate. The TLS minProtocolVersion will use // the value defined in the Contour Configuration file if defined. downstreamTLS = envoy.DownstreamTLSContext( diff --git a/internal/contour/listener_test.go b/internal/contour/listener_test.go index 2d970392224..0a8497463b1 100644 --- a/internal/contour/listener_test.go +++ b/internal/contour/listener_test.go @@ -1,4 +1,4 @@ -// Copyright © 2019 VMware +// Copyright © 2020 VMware // 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 @@ -129,6 +129,8 @@ func TestListenerCacheQuery(t *testing.T) { func TestListenerVisit(t *testing.T) { httpsFilterFor := func(vhost string) *envoy_api_v2_listener.Filter { return envoy.HTTPConnectionManagerBuilder(). + AddFilter(envoy.FilterMisdirectedRequests(vhost)). + DefaultFilters(). MetricsPrefix(ENVOY_HTTPS_LISTENER). RouteConfigName(path.Join("https", vhost)). AccessLoggers(envoy.FileAccessLogEnvoy(DEFAULT_HTTP_ACCESS_LOG)). @@ -790,6 +792,8 @@ func TestListenerVisit(t *testing.T) { }, TransportSocket: transportSocket("secret", envoy_api_v2_auth.TlsParameters_TLSv1_1, "h2", "http/1.1"), Filters: envoy.Filters(envoy.HTTPConnectionManagerBuilder(). + AddFilter(envoy.FilterMisdirectedRequests("whatever.example.com")). + DefaultFilters(). MetricsPrefix(ENVOY_HTTPS_LISTENER). RouteConfigName(path.Join("https", "whatever.example.com")). AccessLoggers(envoy.FileAccessLogEnvoy("/tmp/https_access.log")). diff --git a/internal/e2e/lds_test.go b/internal/e2e/lds_test.go index eee60075bc5..7f6e138ae52 100644 --- a/internal/e2e/lds_test.go +++ b/internal/e2e/lds_test.go @@ -1,4 +1,4 @@ -// Copyright © 2019 VMware +// Copyright © 2020 VMware // 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 @@ -15,6 +15,7 @@ package e2e import ( "context" + "path" "testing" v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2" @@ -32,6 +33,16 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) +func httpsFilterFor(vhost string) *envoy_api_v2_listener.Filter { + return envoy.HTTPConnectionManagerBuilder(). + AddFilter(envoy.FilterMisdirectedRequests(vhost)). + DefaultFilters(). + RouteConfigName(path.Join("https", vhost)). + MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). + AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). + Get() +} + func TestNonTLSListener(t *testing.T) { rh, cc, done := setup(t) defer done() @@ -227,11 +238,7 @@ func TestTLSListener(t *testing.T) { envoy.TLSInspector(), ), FilterChains: filterchaintls("kuard.example.com", s1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), + httpsFilterFor("kuard.example.com"), "h2", "http/1.1"), }, staticListener(), @@ -279,11 +286,7 @@ func TestTLSListener(t *testing.T) { envoy.TLSInspector(), ), FilterChains: filterchaintls("kuard.example.com", s1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), + httpsFilterFor("kuard.example.com"), "h2", "http/1.1"), }, staticListener(), @@ -400,11 +403,7 @@ func TestIngressRouteTLSListener(t *testing.T) { envoy.TLSInspector(), ), FilterChains: filterchaintls("kuard.example.com", secret1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), + httpsFilterFor("kuard.example.com"), "h2", "http/1.1"), } @@ -460,13 +459,7 @@ func TestIngressRouteTLSListener(t *testing.T) { envoy_api_v2_auth.TlsParameters_TLSv1_3, nil, "h2", "http/1.1"), - envoy.Filters( - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - ), + envoy.Filters(httpsFilterFor("kuard.example.com")), ), }, } @@ -558,11 +551,7 @@ func TestLDSFilter(t *testing.T) { envoy.TLSInspector(), ), FilterChains: filterchaintls("kuard.example.com", s1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), + httpsFilterFor("kuard.example.com"), "h2", "http/1.1"), }, ), @@ -747,11 +736,7 @@ func TestLDSIngressHTTPSUseProxyProtocol(t *testing.T) { envoy.TLSInspector(), ), FilterChains: filterchaintls("kuard.example.com", s1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), + httpsFilterFor("kuard.example.com"), "h2", "http/1.1"), } assert.Equal(t, &v2.DiscoveryResponse{ @@ -861,13 +846,7 @@ func TestLDSCustomAddressAndPort(t *testing.T) { envoy.TLSInspector(), ), FilterChains: filterchaintls("kuard.example.com", s1, - - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - + httpsFilterFor("kuard.example.com"), "h2", "http/1.1"), } assert.Equal(t, &v2.DiscoveryResponse{ @@ -967,11 +946,12 @@ func TestLDSCustomAccessLogPaths(t *testing.T) { ), FilterChains: filterchaintls("kuard.example.com", s1, envoy.HTTPConnectionManagerBuilder(). + AddFilter(envoy.FilterMisdirectedRequests("kuard.example.com")). + DefaultFilters(). RouteConfigName("https/kuard.example.com"). MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). AccessLoggers(envoy.FileAccessLogEnvoy("/tmp/https_access.log")). Get(), - "h2", "http/1.1"), } assert.Equal(t, &v2.DiscoveryResponse{ @@ -1071,11 +1051,7 @@ func TestIngressRouteHTTPS(t *testing.T) { envoy.TLSInspector(), ), FilterChains: filterchaintls("example.com", s1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), + httpsFilterFor("example.com"), "h2", "http/1.1"), } assert.Equal(t, &v2.DiscoveryResponse{ @@ -1162,13 +1138,7 @@ func TestIngressRouteMinimumTLSVersion(t *testing.T) { envoy_api_v2_auth.TlsParameters_TLSv1_2, nil, "h2", "http/1.1"), - envoy.Filters( - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - ), + envoy.Filters(httpsFilterFor("kuard.example.com")), ), }, } @@ -1230,13 +1200,7 @@ func TestIngressRouteMinimumTLSVersion(t *testing.T) { envoy_api_v2_auth.TlsParameters_TLSv1_3, nil, "h2", "http/1.1"), - envoy.Filters( - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - ), + envoy.Filters(httpsFilterFor("kuard.example.com")), ), }, } diff --git a/internal/envoy/listener.go b/internal/envoy/listener.go index ab14490200a..dc30c6a9a94 100644 --- a/internal/envoy/listener.go +++ b/internal/envoy/listener.go @@ -1,4 +1,4 @@ -// Copyright © 2019 VMware +// Copyright © 2020 VMware // 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 @@ -14,6 +14,7 @@ package envoy import ( + "fmt" "sort" "time" @@ -22,6 +23,7 @@ import ( envoy_api_v2_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" envoy_api_v2_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener" accesslog "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2" + lua "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/lua/v2" http "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2" tcp "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/tcp_proxy/v2" "github.com/envoyproxy/go-control-plane/pkg/wellknown" @@ -67,6 +69,7 @@ type httpConnectionManagerBuilder struct { metricsPrefix string accessLoggers []*accesslog.AccessLog requestTimeout time.Duration + filters []*http.HttpFilter } // RouteConfigName sets the name of the RDS element that contains @@ -99,6 +102,27 @@ func (b *httpConnectionManagerBuilder) RequestTimeout(timeout time.Duration) *ht return b } +func (b *httpConnectionManagerBuilder) DefaultFilters() *httpConnectionManagerBuilder { + b.filters = append(b.filters, + &http.HttpFilter{ + Name: wellknown.Gzip, + }, + &http.HttpFilter{ + Name: wellknown.GRPCWeb, + }, + &http.HttpFilter{ + Name: wellknown.Router, + }, + ) + + return b +} + +func (b *httpConnectionManagerBuilder) AddFilter(f *http.HttpFilter) *httpConnectionManagerBuilder { + b.filters = append(b.filters, f) + return b +} + // Get returns a new http.HttpConnectionManager filter, constructed // from the builder settings. // @@ -111,13 +135,7 @@ func (b *httpConnectionManagerBuilder) Get() *envoy_api_v2_listener.Filter { ConfigSource: ConfigSource("contour"), }, }, - HttpFilters: []*http.HttpFilter{{ - Name: wellknown.Gzip, - }, { - Name: wellknown.GRPCWeb, - }, { - Name: wellknown.Router, - }}, + HttpFilters: b.filters, CommonHttpProtocolOptions: &envoy_api_v2_core.HttpProtocolOptions{ // Sets the idle timeout for HTTP connections to 60 seconds. // This is chosen as a rough default to stop idle connections wasting resources, @@ -166,6 +184,7 @@ func HTTPConnectionManager(routename string, accesslogger []*accesslog.AccessLog MetricsPrefix(routename). AccessLoggers(accesslogger). RequestTimeout(requestTimeout). + DefaultFilters(). Get() } @@ -281,6 +300,32 @@ func FilterChains(filters ...*envoy_api_v2_listener.Filter) []*envoy_api_v2_list } } +func FilterMisdirectedRequests(fqdn string) *http.HttpFilter { + code := ` +function envoy_on_request(request_handle) + local headers = request_handle:headers() + local host = headers:get(":authority") + + if host ~= "%s" then + request_handle:respond({ + [":status"] = "421", + }, + "" + ) + end +end +` + + return &http.HttpFilter{ + Name: "envoy.filters.http.lua", + ConfigType: &http.HttpFilter_TypedConfig{ + TypedConfig: protobuf.MustMarshalAny(&lua.Lua{ + InlineCode: fmt.Sprintf(code, fqdn), + }), + }, + } +} + // FilterChainTLS returns a TLS enabled envoy_api_v2_listener.FilterChain. func FilterChainTLS(domain string, downstream *envoy_api_v2_auth.DownstreamTlsContext, filters []*envoy_api_v2_listener.Filter) *envoy_api_v2_listener.FilterChain { fc := &envoy_api_v2_listener.FilterChain{ diff --git a/internal/featuretests/downstreamvalidation_test.go b/internal/featuretests/downstreamvalidation_test.go index cdf601092cd..18cb8fabbf7 100644 --- a/internal/featuretests/downstreamvalidation_test.go +++ b/internal/featuretests/downstreamvalidation_test.go @@ -18,7 +18,6 @@ import ( v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2" projcontour "github.com/projectcontour/contour/apis/projectcontour/v1" - "github.com/projectcontour/contour/internal/contour" "github.com/projectcontour/contour/internal/dag" "github.com/projectcontour/contour/internal/envoy" "github.com/projectcontour/contour/internal/fixture" @@ -104,18 +103,16 @@ func TestDownstreamTLSCertificateValidation(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("example.com", serverTLSSecret, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - &dag.PeerValidationContext{ - CACertificate: &dag.Secret{ - Object: clientCASecret, + FilterChains: appendFilterChains( + filterchaintls("example.com", serverTLSSecret, + httpsFilterFor("example.com"), + &dag.PeerValidationContext{ + CACertificate: &dag.Secret{ + Object: clientCASecret, + }, }, - }, - "h2", "http/1.1", + "h2", "http/1.1", + ), ), } diff --git a/internal/featuretests/envoy.go b/internal/featuretests/envoy.go index 77d6312d155..0df907dcac1 100644 --- a/internal/featuretests/envoy.go +++ b/internal/featuretests/envoy.go @@ -1,4 +1,4 @@ -// Copyright © 2019 VMware +// Copyright © 2020 VMware // 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 @@ -16,6 +16,7 @@ package featuretests // envoy helpers import ( + "path" "time" v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2" @@ -219,18 +220,51 @@ func weightedClusters(clusters []weightedCluster) *envoy_api_v2_route.WeightedCl return &wc } -func filterchaintls(domain string, secret *v1.Secret, filter *envoy_api_v2_listener.Filter, peerValidationContext *dag.PeerValidationContext, alpn ...string) []*envoy_api_v2_listener.FilterChain { - return []*envoy_api_v2_listener.FilterChain{ - envoy.FilterChainTLS( - domain, - envoy.DownstreamTLSContext( - &dag.Secret{Object: secret}, - envoy_api_v2_auth.TlsParameters_TLSv1_1, - peerValidationContext, - alpn...), - envoy.Filters(filter), +// appendFilterChains is a helper to turn variadic FilterChain arguments into the corresponding slice. +func appendFilterChains(chains ...*envoy_api_v2_listener.FilterChain) []*envoy_api_v2_listener.FilterChain { + return chains +} + +// filterchaintls returns a FilterChain wrapping the given virtual host. +func filterchaintls(domain string, secret *v1.Secret, filter *envoy_api_v2_listener.Filter, peerValidationContext *dag.PeerValidationContext, alpn ...string) *envoy_api_v2_listener.FilterChain { + return envoy.FilterChainTLS( + domain, + envoy.DownstreamTLSContext( + &dag.Secret{Object: secret}, + envoy_api_v2_auth.TlsParameters_TLSv1_1, + peerValidationContext, + alpn...), + envoy.Filters(filter), + ) +} + +// filterchaintlsfallback returns a FilterChain for the given TLS fallback certificate. +func filterchaintlsfallback(fallbackSecret *v1.Secret, peerValidationContext *dag.PeerValidationContext, alpn ...string) *envoy_api_v2_listener.FilterChain { + return envoy.FilterChainTLSFallback( + envoy.DownstreamTLSContext( + &dag.Secret{Object: fallbackSecret}, + envoy_api_v2_auth.TlsParameters_TLSv1_1, + peerValidationContext, + alpn...), + envoy.Filters( + envoy.HTTPConnectionManagerBuilder(). + RouteConfigName(contour.ENVOY_FALLBACK_ROUTECONFIG). + MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). + AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). + RequestTimeout(0). + Get(), ), - } + ) +} + +func httpsFilterFor(vhost string) *envoy_api_v2_listener.Filter { + return envoy.HTTPConnectionManagerBuilder(). + AddFilter(envoy.FilterMisdirectedRequests(vhost)). + DefaultFilters(). + RouteConfigName(path.Join("https", vhost)). + MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). + AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). + Get() } func tcpproxy(statPrefix, cluster string) *envoy_api_v2_listener.Filter { @@ -252,32 +286,3 @@ func tcpproxy(statPrefix, cluster string) *envoy_api_v2_listener.Filter { func staticListener() *v2.Listener { return envoy.StatsListener("0.0.0.0", 8002) } - -func filterchaintlsfallback(domain string, domainSecret, fallbackSecret *v1.Secret, filter *envoy_api_v2_listener.Filter, peerValidationContext *dag.PeerValidationContext, alpn ...string) []*envoy_api_v2_listener.FilterChain { - return []*envoy_api_v2_listener.FilterChain{ - envoy.FilterChainTLS( - domain, - envoy.DownstreamTLSContext( - &dag.Secret{Object: domainSecret}, - envoy_api_v2_auth.TlsParameters_TLSv1_1, - peerValidationContext, - alpn...), - envoy.Filters(filter), - ), - envoy.FilterChainTLSFallback( - envoy.DownstreamTLSContext( - &dag.Secret{Object: fallbackSecret}, - envoy_api_v2_auth.TlsParameters_TLSv1_1, - peerValidationContext, - alpn...), - envoy.Filters( - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName(contour.ENVOY_FALLBACK_ROUTECONFIG). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - RequestTimeout(0). - Get(), - ), - ), - } -} diff --git a/internal/featuretests/fallbackcert_test.go b/internal/featuretests/fallbackcert_test.go index c4d2e9b0925..2692c867b4b 100644 --- a/internal/featuretests/fallbackcert_test.go +++ b/internal/featuretests/fallbackcert_test.go @@ -17,9 +17,9 @@ import ( "testing" projcontour "github.com/projectcontour/contour/apis/projectcontour/v1" + "github.com/projectcontour/contour/internal/fixture" v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2" - "github.com/projectcontour/contour/internal/contour" "github.com/projectcontour/contour/internal/envoy" v1 "k8s.io/api/core/v1" @@ -67,12 +67,8 @@ func TestFallbackCertificate(t *testing.T) { rh.OnAdd(s1) // Valid HTTPProxy without FallbackCertificate enabled - proxy1 := &projcontour.HTTPProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple", - Namespace: s1.Namespace, - }, - Spec: projcontour.HTTPProxySpec{ + proxy1 := fixture.NewProxy("simple").WithSpec( + projcontour.HTTPProxySpec{ VirtualHost: &projcontour.VirtualHost{ Fqdn: "fallback.example.com", TLS: &projcontour.TLS{ @@ -86,11 +82,12 @@ func TestFallbackCertificate(t *testing.T) { Port: 80, }}, }}, - }, - } + }) rh.OnAdd(proxy1) + // We should start with a single generic HTTPS service. c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{ + TypeUrl: listenerType, Resources: resources(t, &v2.Listener{ Name: "ingress_https", @@ -98,26 +95,18 @@ func TestFallbackCertificate(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("fallback.example.com", sec1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/fallback.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - nil, - "h2", "http/1.1"), + FilterChains: appendFilterChains( + filterchaintls("fallback.example.com", sec1, + httpsFilterFor("fallback.example.com"), + nil, "h2", "http/1.1"), + ), }, ), - TypeUrl: listenerType, }) // Valid HTTPProxy with FallbackCertificate enabled - proxy2 := &projcontour.HTTPProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple", - Namespace: s1.Namespace, - }, - Spec: projcontour.HTTPProxySpec{ + proxy2 := fixture.NewProxy("simple").WithSpec( + projcontour.HTTPProxySpec{ VirtualHost: &projcontour.VirtualHost{ Fqdn: "fallback.example.com", TLS: &projcontour.TLS{ @@ -131,12 +120,14 @@ func TestFallbackCertificate(t *testing.T) { Port: 80, }}, }}, - }, - } + }) rh.OnUpdate(proxy1, proxy2) + // Now we should still have the generic HTTPS service filter, + // but also the fallback certificate filter. c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{ + TypeUrl: listenerType, Resources: resources(t, &v2.Listener{ Name: "ingress_https", @@ -144,26 +135,19 @@ func TestFallbackCertificate(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintlsfallback("fallback.example.com", sec1, fallbackSecret, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/fallback.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - nil, - "h2", "http/1.1"), + FilterChains: appendFilterChains( + filterchaintls("fallback.example.com", sec1, + httpsFilterFor("fallback.example.com"), + nil, "h2", "http/1.1"), + filterchaintlsfallback(fallbackSecret, nil, "h2", "http/1.1"), + ), }, ), - TypeUrl: listenerType, }) - // InValid HTTPProxy with FallbackCertificate enabled along with ClientValidation - proxy3 := &projcontour.HTTPProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple", - Namespace: s1.Namespace, - }, - Spec: projcontour.HTTPProxySpec{ + // Invalid HTTPProxy with FallbackCertificate enabled along with ClientValidation + proxy3 := fixture.NewProxy("simple").WithSpec( + projcontour.HTTPProxySpec{ VirtualHost: &projcontour.VirtualHost{ Fqdn: "fallback.example.com", TLS: &projcontour.TLS{ @@ -180,23 +164,18 @@ func TestFallbackCertificate(t *testing.T) { Port: 80, }}, }}, - }, - } + }) rh.OnUpdate(proxy2, proxy3) c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{ - Resources: nil, TypeUrl: listenerType, + Resources: nil, }) // Valid HTTPProxy with FallbackCertificate enabled - proxy4 := &projcontour.HTTPProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "simple-two", - Namespace: s1.Namespace, - }, - Spec: projcontour.HTTPProxySpec{ + proxy4 := fixture.NewProxy("simple-two").WithSpec( + projcontour.HTTPProxySpec{ VirtualHost: &projcontour.VirtualHost{ Fqdn: "anotherfallback.example.com", TLS: &projcontour.TLS{ @@ -210,13 +189,13 @@ func TestFallbackCertificate(t *testing.T) { Port: 80, }}, }}, - }, - } + }) rh.OnUpdate(proxy3, proxy2) // proxy3 is invalid, resolve that to test two valid proxies rh.OnAdd(proxy4) c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{ + TypeUrl: listenerType, Resources: resources(t, &v2.Listener{ Name: "ingress_https", @@ -224,30 +203,23 @@ func TestFallbackCertificate(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: append(filterchaintls("anotherfallback.example.com", sec1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/anotherfallback.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - nil, - "h2", "http/1.1"), filterchaintlsfallback("fallback.example.com", sec1, fallbackSecret, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/fallback.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - nil, - "h2", "http/1.1")...), + FilterChains: appendFilterChains( + filterchaintls("anotherfallback.example.com", sec1, + httpsFilterFor("anotherfallback.example.com"), + nil, "h2", "http/1.1"), + filterchaintls("fallback.example.com", sec1, + httpsFilterFor("fallback.example.com"), + nil, "h2", "http/1.1"), + filterchaintlsfallback(fallbackSecret, nil, "h2", "http/1.1"), + ), }, ), - TypeUrl: listenerType, }) rh.OnDelete(fallbackSecret) c.Request(listenerType, "ingress_https").Equals(&v2.DiscoveryResponse{ - Resources: nil, TypeUrl: listenerType, + Resources: nil, }) } diff --git a/internal/featuretests/tcpproxy_test.go b/internal/featuretests/tcpproxy_test.go index f6860873be1..7b06a12e258 100644 --- a/internal/featuretests/tcpproxy_test.go +++ b/internal/featuretests/tcpproxy_test.go @@ -1,4 +1,4 @@ -// Copyright © 2019 VMware +// Copyright © 2020 VMware // 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 @@ -88,9 +88,11 @@ func TestTCPProxy(t *testing.T) { c.Request(listenerType).Equals(&v2.DiscoveryResponse{ Resources: resources(t, &v2.Listener{ - Name: "ingress_https", - Address: envoy.SocketAddress("0.0.0.0", 8443), - FilterChains: filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/correct-backend/80/da39a3ee5e"), nil), + Name: "ingress_https", + Address: envoy.SocketAddress("0.0.0.0", 8443), + FilterChains: appendFilterChains( + filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/correct-backend/80/da39a3ee5e"), nil), + ), ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), @@ -142,9 +144,11 @@ func TestTCPProxy(t *testing.T) { c.Request(listenerType).Equals(&v2.DiscoveryResponse{ Resources: resources(t, &v2.Listener{ - Name: "ingress_https", - Address: envoy.SocketAddress("0.0.0.0", 8443), - FilterChains: filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/correct-backend/80/da39a3ee5e"), nil), + Name: "ingress_https", + Address: envoy.SocketAddress("0.0.0.0", 8443), + FilterChains: appendFilterChains( + filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/correct-backend/80/da39a3ee5e"), nil), + ), ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), @@ -232,9 +236,11 @@ func TestTCPProxyDelegation(t *testing.T) { c.Request(listenerType).Equals(&v2.DiscoveryResponse{ Resources: resources(t, &v2.Listener{ - Name: "ingress_https", - Address: envoy.SocketAddress("0.0.0.0", 8443), - FilterChains: filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "app/backend/80/da39a3ee5e"), nil), + Name: "ingress_https", + Address: envoy.SocketAddress("0.0.0.0", 8443), + FilterChains: appendFilterChains( + filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "app/backend/80/da39a3ee5e"), nil), + ), ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), @@ -295,9 +301,11 @@ func TestTCPProxyDelegation(t *testing.T) { c.Request(listenerType).Equals(&v2.DiscoveryResponse{ Resources: resources(t, &v2.Listener{ - Name: "ingress_https", - Address: envoy.SocketAddress("0.0.0.0", 8443), - FilterChains: filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "app/backend/80/da39a3ee5e"), nil), + Name: "ingress_https", + Address: envoy.SocketAddress("0.0.0.0", 8443), + FilterChains: appendFilterChains( + filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "app/backend/80/da39a3ee5e"), nil), + ), ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), @@ -523,11 +531,10 @@ func TestTCPProxyTLSBackend(t *testing.T) { &v2.Listener{ Name: "ingress_https", Address: envoy.SocketAddress("0.0.0.0", 8443), - FilterChains: filterchaintls( - "k8s.run.ubisoft.org", - s1, - tcpproxy("ingress_https", svc.Namespace+"/"+svc.Name+"/443/da39a3ee5e"), - nil), + FilterChains: appendFilterChains( + filterchaintls("k8s.run.ubisoft.org", s1, + tcpproxy("ingress_https", svc.Namespace+"/"+svc.Name+"/443/da39a3ee5e"), nil), + ), ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), @@ -630,9 +637,11 @@ func TestTCPProxyAndHTTPService(t *testing.T) { &v2.Listener{ // ingress_https is present for // kuard-tcp.example.com:443 terminated at envoy then forwarded to default/backend:80 - Name: "ingress_https", - Address: envoy.SocketAddress("0.0.0.0", 8443), - FilterChains: filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/backend/80/da39a3ee5e"), nil), + Name: "ingress_https", + Address: envoy.SocketAddress("0.0.0.0", 8443), + FilterChains: appendFilterChains( + filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/backend/80/da39a3ee5e"), nil), + ), ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), @@ -733,9 +742,11 @@ func TestTCPProxyAndHTTPServicePermitInsecure(t *testing.T) { &v2.Listener{ // ingress_https is present for // kuard-tcp.example.com:443 terminated at envoy then tcpproxied to default/backend:80 - Name: "ingress_https", - Address: envoy.SocketAddress("0.0.0.0", 8443), - FilterChains: filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/backend/80/da39a3ee5e"), nil), + Name: "ingress_https", + Address: envoy.SocketAddress("0.0.0.0", 8443), + FilterChains: appendFilterChains( + filterchaintls("kuard-tcp.example.com", s1, tcpproxy("ingress_https", "default/backend/80/da39a3ee5e"), nil), + ), ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), diff --git a/internal/featuretests/tlscertificatedelegation_test.go b/internal/featuretests/tlscertificatedelegation_test.go index e5376f98327..ab368f219d3 100644 --- a/internal/featuretests/tlscertificatedelegation_test.go +++ b/internal/featuretests/tlscertificatedelegation_test.go @@ -1,4 +1,4 @@ -// Copyright © 2019 VMware +// Copyright © 2020 VMware // 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 @@ -19,7 +19,6 @@ import ( v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2" ingressroutev1 "github.com/projectcontour/contour/apis/contour/v1beta1" projcontour "github.com/projectcontour/contour/apis/projectcontour/v1" - "github.com/projectcontour/contour/internal/contour" "github.com/projectcontour/contour/internal/envoy" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -127,14 +126,11 @@ func TestTLSCertificateDelegation(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("example.com", sec1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - nil, - "h2", "http/1.1"), + FilterChains: appendFilterChains( + filterchaintls("example.com", sec1, + httpsFilterFor("example.com"), + nil, "h2", "http/1.1"), + ), } c.Request(listenerType).Equals(&v2.DiscoveryResponse{ diff --git a/internal/featuretests/tlsprotocolversion_test.go b/internal/featuretests/tlsprotocolversion_test.go index 74ea408262e..d0a6b9bac64 100644 --- a/internal/featuretests/tlsprotocolversion_test.go +++ b/internal/featuretests/tlsprotocolversion_test.go @@ -1,4 +1,4 @@ -// Copyright © 2019 VMware +// Copyright © 2020 VMware // 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 @@ -21,7 +21,6 @@ import ( envoy_api_v2_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener" ingressroutev1 "github.com/projectcontour/contour/apis/contour/v1beta1" projcontour "github.com/projectcontour/contour/apis/projectcontour/v1" - "github.com/projectcontour/contour/internal/contour" "github.com/projectcontour/contour/internal/dag" "github.com/projectcontour/contour/internal/envoy" v1 "k8s.io/api/core/v1" @@ -90,14 +89,11 @@ func TestTLSMinimumProtocolVersion(t *testing.T) { ListenerFilters: envoy.ListenerFilters( envoy.TLSInspector(), ), - FilterChains: filterchaintls("kuard.example.com", sec1, - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - nil, - "h2", "http/1.1"), + FilterChains: appendFilterChains( + filterchaintls("kuard.example.com", sec1, + httpsFilterFor("kuard.example.com"), + nil, "h2", "http/1.1"), + ), }, ), TypeUrl: listenerType, @@ -144,13 +140,7 @@ func TestTLSMinimumProtocolVersion(t *testing.T) { envoy_api_v2_auth.TlsParameters_TLSv1_3, nil, "h2", "http/1.1"), - envoy.Filters( - envoy.HTTPConnectionManagerBuilder(). - RouteConfigName("https/kuard.example.com"). - MetricsPrefix(contour.ENVOY_HTTPS_LISTENER). - AccessLoggers(envoy.FileAccessLogEnvoy("/dev/stdout")). - Get(), - ), + envoy.Filters(httpsFilterFor("kuard.example.com")), ), }, } diff --git a/site/_resources/envoy.md b/site/_resources/envoy.md index 4a239c93354..8592d39340a 100644 --- a/site/_resources/envoy.md +++ b/site/_resources/envoy.md @@ -34,7 +34,7 @@ If you are using the image recommended in our [example deployment][3] no action If you are providing your own Envoy it must be compiled with the following extensions: - `access_loggers`: `envoy.access_loggers.file`,`envoy.access_loggers.http_grpc`,`envoy.access_loggers.tcp_grpc` -- `filters.http`: `envoy.buffer`,`envoy.cors`,`envoy.csrf`,`envoy.fault`,`envoy.filters.http.adaptive_concurrency`,`envoy.filters.http.dynamic_forward_proxy`,`envoy.filters.http.grpc_http1_reverse_bridge`,`envoy.filters.http.grpc_stats`,`envoy.filters.http.header_to_metadata`,`envoy.filters.http.original_src`,`envoy.grpc_http1_bridge`,`envoy.grpc_json_transcoder`,`envoy.grpc_web`,`envoy.gzip`,`envoy.health_check`,`envoy.ip_tagging`,`envoy.router` +- `filters.http`: `envoy.buffer`, `envoy.cors`, `envoy.csrf`, `envoy.fault`, `envoy.filters.http.adaptive_concurrency`, `envoy.filters.http.dynamic_forward_proxy`, `envoy.filters.http.grpc_http1_reverse_bridge`, `envoy.filters.http.grpc_stats`, `envoy.filters.http.header_to_metadata`, `envoy.filters.http.lua`, `envoy.filters.http.original_src`, `envoy.grpc_http1_bridge`, `envoy.grpc_json_transcoder`, `envoy.grpc_web`, `envoy.gzip`, `envoy.health_check`, `envoy.ip_tagging`, `envoy.router` - `filters.listener`: `envoy.listener.http_inspector`,`envoy.listener.original_dst`,`envoy.listener.original_src`,`envoy.listener.proxy_protocol`,`envoy.listener.tls_inspector` - `filters.network`: `envoy.echo`,`envoy.filters.network.sni_cluster`,`envoy.http_connection_manager`,`envoy.tcp_proxy` - `stat_sinks`: `envoy.metrics_service`