Skip to content

Commit

Permalink
internal: filter misdirected TLS requests
Browse files Browse the repository at this point in the history
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.

Signed-off-by: James Peach <jpeach@vmware.com>
  • Loading branch information
jpeach committed Apr 29, 2020
1 parent f7bce9a commit db6bea0
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 7 deletions.
3 changes: 3 additions & 0 deletions internal/contour/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ func visitListeners(root dag.Vertex, lvc *ListenerVisitorConfig) map[string]*v2.
// Add a listener if there are vhosts bound to http.
if lv.http {
cm := envoy.HTTPConnectionManagerBuilder().
DefaultFilters().
RouteConfigName(ENVOY_HTTP_LISTENER).
MetricsPrefix(ENVOY_HTTP_LISTENER).
AccessLoggers(lvc.newInsecureAccessLog()).
Expand Down Expand Up @@ -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()).
Expand Down
59 changes: 52 additions & 7 deletions internal/envoy/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package envoy

import (
"fmt"
"sort"
"time"

Expand All @@ -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"
Expand Down Expand Up @@ -70,6 +72,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
Expand Down Expand Up @@ -102,6 +105,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.
//
Expand All @@ -114,13 +138,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,
Expand Down Expand Up @@ -169,6 +187,7 @@ func HTTPConnectionManager(routename string, accesslogger []*accesslog.AccessLog
MetricsPrefix(routename).
AccessLoggers(accesslogger).
RequestTimeout(requestTimeout).
DefaultFilters().
Get()
}

Expand Down Expand Up @@ -284,6 +303,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: toAny(&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{
Expand Down

0 comments on commit db6bea0

Please sign in to comment.