From 2c7f6e4def0436a42a06118a455fcdf7b2e2fcb3 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 20 Jul 2020 18:32:03 +0200 Subject: [PATCH] fix: drop host port to compare with SNI. --- integration/https_test.go | 30 ++++++++++++++++++++++++++++ pkg/server/router/tcp/router.go | 35 ++++++++++++++++++++++++++------- pkg/tcp/router.go | 3 ++- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/integration/https_test.go b/integration/https_test.go index 6b39e343f..e6c6c0553 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -1150,6 +1150,34 @@ func (s *HTTPSSuite) TestWithDomainFronting(c *check.C) { expectedContent: "server1", expectedStatusCode: http.StatusOK, }, + { + desc: "Simple case with port in the Host Header", + hostHeader: "site3.www.snitest.com:4443", + serverName: "site3.www.snitest.com", + expectedContent: "server3", + expectedStatusCode: http.StatusOK, + }, + { + desc: "Spaces after the host header", + hostHeader: "site3.www.snitest.com ", + serverName: "site3.www.snitest.com", + expectedContent: "server3", + expectedStatusCode: http.StatusOK, + }, + { + desc: "Spaces after the servername", + hostHeader: "site3.www.snitest.com", + serverName: "site3.www.snitest.com ", + expectedContent: "server3", + expectedStatusCode: http.StatusOK, + }, + { + desc: "Spaces after the servername and host header", + hostHeader: "site3.www.snitest.com ", + serverName: "site3.www.snitest.com ", + expectedContent: "server3", + expectedStatusCode: http.StatusOK, + }, { desc: "Domain Fronting with same tlsOptions should follow header", hostHeader: "site1.www.snitest.com", @@ -1189,9 +1217,11 @@ func (s *HTTPSSuite) TestWithDomainFronting(c *check.C) { for _, test := range testCases { test := test + req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443", nil) c.Assert(err, checker.IsNil) req.Host = test.hostHeader + err = try.RequestWithTransport(req, 500*time.Millisecond, &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true, ServerName: test.serverName}}, try.StatusCodeIs(test.expectedStatusCode), try.BodyContains(test.expectedContent)) c.Assert(err, checker.IsNil) } diff --git a/pkg/server/router/tcp/router.go b/pkg/server/router/tcp/router.go index 8d8dbc7b8..f164039c7 100644 --- a/pkg/server/router/tcp/router.go +++ b/pkg/server/router/tcp/router.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "errors" "fmt" + "net" "net/http" "strings" @@ -141,6 +142,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string continue } + // domain is already in lower case thanks to the domain parsing if tlsOptionsForHostSNI[domain] == nil { tlsOptionsForHostSNI[domain] = make(map[string]nameAndConfig) } @@ -149,27 +151,46 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string TLSConfig: tlsConf, } - lowerDomain := strings.ToLower(domain) - if _, ok := tlsOptionsForHost[lowerDomain]; ok { + if _, ok := tlsOptionsForHost[domain]; ok { // Multiple tlsOptions fallback to default - tlsOptionsForHost[lowerDomain] = "default" + tlsOptionsForHost[domain] = "default" } else { - tlsOptionsForHost[lowerDomain] = routerHTTPConfig.TLS.Options + tlsOptionsForHost[domain] = routerHTTPConfig.TLS.Options } } } } sniCheck := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - if req.TLS != nil && !strings.EqualFold(req.Host, req.TLS.ServerName) { - tlsOptionSNI := findTLSOptionName(tlsOptionsForHost, req.TLS.ServerName) - tlsOptionHeader := findTLSOptionName(tlsOptionsForHost, req.Host) + if req.TLS == nil { + handlerHTTPS.ServeHTTP(rw, req) + return + } + + host, _, err := net.SplitHostPort(req.Host) + if err != nil { + host = req.Host + } + + host = strings.TrimSpace(host) + serverName := strings.TrimSpace(req.TLS.ServerName) + + // Domain Fronting + if !strings.EqualFold(host, serverName) { + tlsOptionSNI := findTLSOptionName(tlsOptionsForHost, serverName) + tlsOptionHeader := findTLSOptionName(tlsOptionsForHost, host) if tlsOptionHeader != tlsOptionSNI { + log.WithoutContext(). + WithField("host", host). + WithField("req.Host", req.Host). + WithField("req.TLS.ServerName", req.TLS.ServerName). + Debugf("TLS options difference: SNI=%s, Header:%s", tlsOptionSNI, tlsOptionHeader) http.Error(rw, http.StatusText(http.StatusMisdirectedRequest), http.StatusMisdirectedRequest) return } } + handlerHTTPS.ServeHTTP(rw, req) }) diff --git a/pkg/tcp/router.go b/pkg/tcp/router.go index c66e987ce..601f18190 100644 --- a/pkg/tcp/router.go +++ b/pkg/tcp/router.go @@ -11,6 +11,7 @@ import ( "time" "github.com/containous/traefik/v2/pkg/log" + "github.com/containous/traefik/v2/pkg/types" ) // Router is a TCP router. @@ -65,7 +66,7 @@ func (r *Router) ServeTCP(conn WriteCloser) { } // FIXME Optimize and test the routing table before helloServerName - serverName = strings.ToLower(serverName) + serverName = types.CanonicalDomain(serverName) if r.routingTable != nil && serverName != "" { if target, ok := r.routingTable[serverName]; ok { target.ServeTCP(r.GetConn(conn, peeked))