fix: drop host port to compare with SNI.

This commit is contained in:
Ludovic Fernandez 2020-07-20 18:32:03 +02:00 committed by GitHub
parent ff16925f63
commit 2c7f6e4def
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 8 deletions

View file

@ -1150,6 +1150,34 @@ func (s *HTTPSSuite) TestWithDomainFronting(c *check.C) {
expectedContent: "server1", expectedContent: "server1",
expectedStatusCode: http.StatusOK, 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", desc: "Domain Fronting with same tlsOptions should follow header",
hostHeader: "site1.www.snitest.com", hostHeader: "site1.www.snitest.com",
@ -1189,9 +1217,11 @@ func (s *HTTPSSuite) TestWithDomainFronting(c *check.C) {
for _, test := range testCases { for _, test := range testCases {
test := test test := test
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443", nil) req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443", nil)
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
req.Host = test.hostHeader 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)) 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) c.Assert(err, checker.IsNil)
} }

View file

@ -5,6 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"net"
"net/http" "net/http"
"strings" "strings"
@ -141,6 +142,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
continue continue
} }
// domain is already in lower case thanks to the domain parsing
if tlsOptionsForHostSNI[domain] == nil { if tlsOptionsForHostSNI[domain] == nil {
tlsOptionsForHostSNI[domain] = make(map[string]nameAndConfig) tlsOptionsForHostSNI[domain] = make(map[string]nameAndConfig)
} }
@ -149,27 +151,46 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
TLSConfig: tlsConf, TLSConfig: tlsConf,
} }
lowerDomain := strings.ToLower(domain) if _, ok := tlsOptionsForHost[domain]; ok {
if _, ok := tlsOptionsForHost[lowerDomain]; ok {
// Multiple tlsOptions fallback to default // Multiple tlsOptions fallback to default
tlsOptionsForHost[lowerDomain] = "default" tlsOptionsForHost[domain] = "default"
} else { } else {
tlsOptionsForHost[lowerDomain] = routerHTTPConfig.TLS.Options tlsOptionsForHost[domain] = routerHTTPConfig.TLS.Options
} }
} }
} }
} }
sniCheck := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { sniCheck := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if req.TLS != nil && !strings.EqualFold(req.Host, req.TLS.ServerName) { if req.TLS == nil {
tlsOptionSNI := findTLSOptionName(tlsOptionsForHost, req.TLS.ServerName) handlerHTTPS.ServeHTTP(rw, req)
tlsOptionHeader := findTLSOptionName(tlsOptionsForHost, req.Host) 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 { 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) http.Error(rw, http.StatusText(http.StatusMisdirectedRequest), http.StatusMisdirectedRequest)
return return
} }
} }
handlerHTTPS.ServeHTTP(rw, req) handlerHTTPS.ServeHTTP(rw, req)
}) })

View file

@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/types"
) )
// Router is a TCP router. // Router is a TCP router.
@ -65,7 +66,7 @@ func (r *Router) ServeTCP(conn WriteCloser) {
} }
// FIXME Optimize and test the routing table before helloServerName // FIXME Optimize and test the routing table before helloServerName
serverName = strings.ToLower(serverName) serverName = types.CanonicalDomain(serverName)
if r.routingTable != nil && serverName != "" { if r.routingTable != nil && serverName != "" {
if target, ok := r.routingTable[serverName]; ok { if target, ok := r.routingTable[serverName]; ok {
target.ServeTCP(r.GetConn(conn, peeked)) target.ServeTCP(r.GetConn(conn, peeked))