Apply the same approach as the rules system on the TLS configuration choice
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
This commit is contained in:
parent
4da33c2bc2
commit
0c83ee736c
2 changed files with 132 additions and 38 deletions
|
@ -177,8 +177,8 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||||
|
|
||||||
// Domain Fronting
|
// Domain Fronting
|
||||||
if !strings.EqualFold(host, serverName) {
|
if !strings.EqualFold(host, serverName) {
|
||||||
tlsOptionSNI := findTLSOptionName(tlsOptionsForHost, serverName)
|
tlsOptionHeader := findTLSOptionName(tlsOptionsForHost, host, true)
|
||||||
tlsOptionHeader := findTLSOptionName(tlsOptionsForHost, host)
|
tlsOptionSNI := findTLSOptionName(tlsOptionsForHost, serverName, false)
|
||||||
|
|
||||||
if tlsOptionHeader != tlsOptionSNI {
|
if tlsOptionHeader != tlsOptionSNI {
|
||||||
log.WithoutContext().
|
log.WithoutContext().
|
||||||
|
@ -322,16 +322,43 @@ func (m *Manager) buildTCPHandler(ctx context.Context, router *runtime.TCPRouter
|
||||||
return tcp.NewChain().Extend(*mHandler).Then(sHandler)
|
return tcp.NewChain().Extend(*mHandler).Then(sHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findTLSOptionName(tlsOptionsForHost map[string]string, host string) string {
|
func findTLSOptionName(tlsOptionsForHost map[string]string, host string, fqdn bool) string {
|
||||||
|
name := findTLSOptName(tlsOptionsForHost, host, fqdn)
|
||||||
|
if name != "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
name = findTLSOptName(tlsOptionsForHost, strings.ToLower(host), fqdn)
|
||||||
|
if name != "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
return traefiktls.DefaultTLSConfigName
|
||||||
|
}
|
||||||
|
|
||||||
|
func findTLSOptName(tlsOptionsForHost map[string]string, host string, fqdn bool) string {
|
||||||
tlsOptions, ok := tlsOptionsForHost[host]
|
tlsOptions, ok := tlsOptionsForHost[host]
|
||||||
if ok {
|
if ok {
|
||||||
return tlsOptions
|
return tlsOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsOptions, ok = tlsOptionsForHost[strings.ToLower(host)]
|
if !fqdn {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if last := len(host) - 1; last >= 0 && host[last] == '.' {
|
||||||
|
tlsOptions, ok = tlsOptionsForHost[host[:last]]
|
||||||
|
if ok {
|
||||||
|
return tlsOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsOptions, ok = tlsOptionsForHost[host+"."]
|
||||||
if ok {
|
if ok {
|
||||||
return tlsOptions
|
return tlsOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
return traefiktls.DefaultTLSConfigName
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
TCPRouter: &dynamic.TCPRouter{
|
TCPRouter: &dynamic.TCPRouter{
|
||||||
|
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "foo-service",
|
Service: "foo-service",
|
||||||
Rule: "HostSNI(`foo.bar`)",
|
Rule: "HostSNI(`foo.bar`)",
|
||||||
|
@ -136,7 +135,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
|
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "foo-service",
|
Service: "foo-service",
|
||||||
Rule: "Host(`bar.foo`) && PathPrefix(`/path`)",
|
Rule: "Host(`bar.foo`) && PathPrefix(`/path`)",
|
||||||
|
@ -240,7 +238,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
TCPRouter: &dynamic.TCPRouter{
|
TCPRouter: &dynamic.TCPRouter{
|
||||||
|
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "foo-service",
|
Service: "foo-service",
|
||||||
Rule: "HostSNI(`foo.bar`)",
|
Rule: "HostSNI(`foo.bar`)",
|
||||||
|
@ -340,9 +337,26 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDomainFronting(t *testing.T) {
|
func TestDomainFronting(t *testing.T) {
|
||||||
|
tlsOptionsBase := map[string]traefiktls.Options{
|
||||||
|
"default": {
|
||||||
|
MinVersion: "VersionTLS10",
|
||||||
|
},
|
||||||
|
"host1@file": {
|
||||||
|
MinVersion: "VersionTLS12",
|
||||||
|
},
|
||||||
|
"host1@crd": {
|
||||||
|
MinVersion: "VersionTLS12",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPoints := []string{"web"}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
routers map[string]*runtime.RouterInfo
|
routers map[string]*runtime.RouterInfo
|
||||||
|
tlsOptions map[string]traefiktls.Options
|
||||||
|
host string
|
||||||
|
ServerName string
|
||||||
expectedStatus int
|
expectedStatus int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -350,7 +364,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -359,12 +373,15 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@file": {
|
"router-2@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusMisdirectedRequest,
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -372,7 +389,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -381,7 +398,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@file": {
|
"router-2@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -389,6 +406,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -396,7 +416,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -405,7 +425,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@file": {
|
"router-2@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`) && PathPrefix(`/foo`)",
|
Rule: "Host(`host1.local`) && PathPrefix(`/foo`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "default",
|
Options: "default",
|
||||||
|
@ -414,7 +434,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-3@file": {
|
"router-3@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -422,6 +442,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusMisdirectedRequest,
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -429,7 +452,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -438,7 +461,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@file": {
|
"router-2@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`) && PathPrefix(`/bar`)",
|
Rule: "Host(`host1.local`) && PathPrefix(`/bar`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -447,7 +470,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-3@file": {
|
"router-3@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -455,6 +478,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -462,7 +488,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -471,7 +497,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@crd": {
|
"router-2@crd": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -479,6 +505,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusMisdirectedRequest,
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -486,7 +515,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1@crd",
|
Options: "host1@crd",
|
||||||
|
@ -495,7 +524,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@crd": {
|
"router-2@crd": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1@crd",
|
Options: "host1@crd",
|
||||||
|
@ -503,25 +532,63 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "Request is misdirected when server name is empty and the host name is an FQDN, but router's rule is not",
|
||||||
|
routers: map[string]*runtime.RouterInfo{
|
||||||
|
"router-1@file": {
|
||||||
|
Router: &dynamic.Router{
|
||||||
|
EntryPoints: entryPoints,
|
||||||
|
Rule: "Host(`host1.local`)",
|
||||||
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
|
Options: "host1@file",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tlsOptions: map[string]traefiktls.Options{
|
||||||
|
"default": {
|
||||||
|
MinVersion: "VersionTLS13",
|
||||||
|
},
|
||||||
|
"host1@file": {
|
||||||
|
MinVersion: "VersionTLS12",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
host: "host1.local.",
|
||||||
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Request is misdirected when server name is empty and the host name is not FQDN, but router's rule is",
|
||||||
|
routers: map[string]*runtime.RouterInfo{
|
||||||
|
"router-1@file": {
|
||||||
|
Router: &dynamic.Router{
|
||||||
|
EntryPoints: entryPoints,
|
||||||
|
Rule: "Host(`host1.local.`)",
|
||||||
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
|
Options: "host1@file",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tlsOptions: map[string]traefiktls.Options{
|
||||||
|
"default": {
|
||||||
|
MinVersion: "VersionTLS13",
|
||||||
|
},
|
||||||
|
"host1@file": {
|
||||||
|
MinVersion: "VersionTLS12",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
host: "host1.local",
|
||||||
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
entryPoints := []string{"web"}
|
|
||||||
tlsOptions := map[string]traefiktls.Options{
|
|
||||||
"default": {
|
|
||||||
MinVersion: "VersionTLS10",
|
|
||||||
},
|
|
||||||
"host1@file": {
|
|
||||||
MinVersion: "VersionTLS12",
|
|
||||||
},
|
|
||||||
"host1@crd": {
|
|
||||||
MinVersion: "VersionTLS12",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := &runtime.Configuration{
|
conf := &runtime.Configuration{
|
||||||
Routers: test.routers,
|
Routers: test.routers,
|
||||||
}
|
}
|
||||||
|
@ -529,7 +596,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
serviceManager := tcp.NewManager(conf)
|
serviceManager := tcp.NewManager(conf)
|
||||||
|
|
||||||
tlsManager := traefiktls.NewManager()
|
tlsManager := traefiktls.NewManager()
|
||||||
tlsManager.UpdateConfigs(context.Background(), map[string]traefiktls.Store{}, tlsOptions, []*traefiktls.CertAndStores{})
|
tlsManager.UpdateConfigs(context.Background(), map[string]traefiktls.Store{}, test.tlsOptions, []*traefiktls.CertAndStores{})
|
||||||
|
|
||||||
httpsHandler := map[string]http.Handler{
|
httpsHandler := map[string]http.Handler{
|
||||||
"web": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}),
|
"web": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}),
|
||||||
|
@ -545,9 +612,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
req.Host = "host1.local"
|
req.Host = test.host
|
||||||
req.TLS = &tls.ConnectionState{
|
req.TLS = &tls.ConnectionState{
|
||||||
ServerName: "host2.local",
|
ServerName: test.ServerName,
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
|
Loading…
Reference in a new issue