CVE-2022-46153: Improper Certificate Validation (CWE-295)
value: Traefik is an open source HTTP reverse proxy and load balancer. In affected versions there is a potential vulnerability in Traefik managing TLS connections. A router configured with a not well-formatted TLSOption is exposed with an empty TLSOption. For instance, a route secured using an mTLS connection set with a wrong CA file is exposed without verifying the client certificates. Users are advised to upgrade to version 2.9.6. Users unable to upgrade should check their logs to detect the error messages and fix your TLS options.
CWE description: The product does not validate, or incorrectly validates, a certificate.
CVSS v3 base score: 6.5
Published: 2022-12-08T22:15Z
Repository: https://github.com/traefik/traefik
Language: Go
Files changed: integration/fixtures/https/https_invalid_tls_options.toml, integration/fixtures/tcp/multi-tls-options.toml, integration/https_test.go, integration/tcp_test.go, pkg/server/router/router.go, pkg/server/router/router_test.go, pkg/server/router/tcp/manager.go, pkg/server/router/tcp/router.go, pkg/server/routerfactory.go, pkg/tls/tlsmanager.go, pkg/tls/tlsmanager_test.go
Commit date: 2022-12-06 18:28:05 +0100
Commit message: Handle broken TLS conf better

Co-authored-by: Jean-Baptiste Doumenjou <925513+jbdoumenjou@users.noreply.github.com>
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
Diff: diff --git a/integration/fixtures/https/https_invalid_tls_options.toml b/integration/fixtures/https/https_invalid_tls_options.toml
new file mode 100644
index 000000000..cc915c4ca
--- /dev/null
+++ b/integration/fixtures/https/https_invalid_tls_options.toml
@@ -0,0 +1,60 @@
+[global]
+  checkNewVersion = false
+  sendAnonymousUsage = false
+
+[log]
+  level = "DEBUG"
+
+[entryPoints.websecure]
+  address = ":4443"
+
+[api]
+  insecure = true
+
+[providers.file]
+  filename = "{{ .SelfFilename }}"
+
+## dynamic configuration ##
+
+[http.routers]
+
+  [http.routers.router1]
+    entryPoints = ["websecure"]
+    service = "service1"
+    rule = "Host(`snitest.com`)"
+    [http.routers.router1.tls]
+      options = "invalidTLSOptions"
+
+  [http.routers.router2]
+    entryPoints = ["websecure"]
+    service = "service1"
+    rule = "Host(`snitest.org`)"
+    [http.routers.router2.tls]
+
+  # fallback router
+  [http.routers.router3]
+    entryPoints = ["websecure"]
+    service = "service1"
+    rule = "Path(`/`)"
+    [http.routers.router3.tls]
+
+[[http.services.service1.loadBalancer.servers]]
+  url = "http://127.0.0.1:9010"
+
+[[tls.certificates]]
+  certFile = "fixtures/https/snitest.com.cert"
+  keyFile = "fixtures/https/snitest.com.key"
+
+[[tls.certificates]]
+  certFile = "fixtures/https/snitest.org.cert"
+  keyFile = "fixtures/https/snitest.org.key"
+
+[tls.options]
+
+  [tls.options.default.clientAuth]
+    # Missing caFile to have an invalid mTLS configuration.
+    clientAuthType = "RequireAndVerifyClientCert"
+
+  [tls.options.invalidTLSOptions.clientAuth]
+    # Missing caFile to have an invalid mTLS configuration.
+    clientAuthType = "RequireAndVerifyClientCert"
diff --git a/integration/fixtures/tcp/multi-tls-options.toml b/integration/fixtures/tcp/multi-tls-options.toml
index f0b4a931c..480df2d60 100644
--- a/integration/fixtures/tcp/multi-tls-options.toml
+++ b/integration/fixtures/tcp/multi-tls-options.toml
@@ -31,10 +31,17 @@
       service = "whoami-no-cert"
       entryPoints = [ "tcp" ]
       [tcp.routers.to-whoami-sni-strict.tls]
         options = "bar"
 
+    [tcp.routers.to-whoami-invalid-tls]
+      rule = "HostSNI(`whoami-i.test`)"
+      service = "whoami-no-cert"
+      entryPoints = [ "tcp" ]
+      [tcp.routers.to-whoami-invalid-tls.tls]
+        options = "invalid"
+
     [tcp.services.whoami-no-cert]
       [tcp.services.whoami-no-cert.loadBalancer]
         [[tcp.services.whoami-no-cert.loadBalancer.servers]]
         address = "{{ .WhoamiNoCert }}"
 
@@ -43,5 +50,9 @@
   [tls.options.foo]
     minVersion = "VersionTLS12"
 
   [tls.options.bar]
     minVersion = "VersionTLS13"
+
+  [tls.options.invalid.clientAuth]
+    # Missing CA files to have an invalid mTLS configuration.
+    clientAuthType = "RequireAndVerifyClientCert"
diff --git a/integration/https_test.go b/integration/https_test.go
index 868d31aff..19983faeb 100644
--- a/integration/https_test.go
+++ b/integration/https_test.go
@@ -1224,5 +1224,55 @@ func (s *HTTPSSuite) TestWithDomainFronting(c *check.C) {
 
 		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)
 	}
 }
+
+// TestWithInvalidTLSOption verifies the behavior when using an invalid tlsOption configuration.
+func (s *HTTPSSuite) TestWithInvalidTLSOption(c *check.C) {
+	backend := startTestServer("9010", http.StatusOK, "server1")
+	defer backend.Close()
+
+	file := s.adaptFile(c, "fixtures/https/https_invalid_tls_options.toml", struct{}{})
+	defer os.Remove(file)
+	cmd, display := s.traefikCmd(withConfigFile(file))
+	defer display(c)
+	err := cmd.Start()
+	c.Assert(err, checker.IsNil)
+	defer s.killCmd(cmd)
+
+	// wait for Traefik
+	err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
+	c.Assert(err, checker.IsNil)
+
+	testCases := []struct {
+		desc       string
+		serverName string
+	}{
+		{
+			desc:       "With invalid TLS Options specified",
+			serverName: "snitest.com",
+		},
+		{
+			desc:       "With invalid Default TLS Options",
+			serverName: "snitest.org",
+		},
+		{
+			desc: "With TLS Options without servername (fallback to default)",
+		},
+	}
+
+	for _, test := range testCases {
+		test := test
+
+		tlsConfig := &tls.Config{
+			InsecureSkipVerify: true,
+		}
+		if test.serverName != "" {
+			tlsConfig.ServerName = test.serverName
+		}
+
+		conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
+		c.Assert(err, checker.NotNil, check.Commentf("connected to server successfully"))
+		c.Assert(conn, checker.IsNil)
+	}
+}
diff --git a/integration/tcp_test.go b/integration/tcp_test.go
index 52a3813c9..0a60ae5b4 100644
--- a/integration/tcp_test.go
+++ b/integration/tcp_test.go
@@ -114,10 +114,18 @@ func (s *TCPSuite) TestTLSOptions(c *check.C) {
 
 	// Check that we cannot use a client tls version <= 1.2 with hostSNI 'whoami-d.test'
 	_, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS12)
 	c.Assert(err, checker.NotNil)
 	c.Assert(err.Error(), checker.Contains, "protocol version not supported")
+
+	// Check that we can't reach a route with an invalid mTLS configuration.
+	conn, err := tls.Dial("tcp", "127.0.0.1:8093", &tls.Config{
+		ServerName:         "whoami-i.test",
+		InsecureSkipVerify: true,
+	})
+	c.Assert(conn, checker.IsNil)
+	c.Assert(err, checker.NotNil)
 }
 
 func (s *TCPSuite) TestNonTLSFallback(c *check.C) {
 	file := s.adaptFile(c, "fixtures/tcp/non-tls-fallback.toml", struct {
 		WhoamiA      string
diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go
index 6f1819dbe..6359102de 100644
--- a/pkg/server/router/router.go
+++ b/pkg/server/router/router.go
@@ -1,10 +1,11 @@
 package router
 
 import (
 	"context"
 	"errors"
+	"fmt"
 	"net/http"
 
 	"github.com/containous/alice"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
 	"github.com/traefik/traefik/v2/pkg/log"
@@ -14,10 +15,11 @@ import (
 	"github.com/traefik/traefik/v2/pkg/middlewares/recovery"
 	"github.com/traefik/traefik/v2/pkg/middlewares/tracing"
 	httpmuxer "github.com/traefik/traefik/v2/pkg/muxer/http"
 	"github.com/traefik/traefik/v2/pkg/server/middleware"
 	"github.com/traefik/traefik/v2/pkg/server/provider"
+	"github.com/traefik/traefik/v2/pkg/tls"
 )
 
 type middlewareBuilder interface {
 	BuildChain(ctx context.Context, names []string) *alice.Chain
 }
@@ -33,21 +35,23 @@ type Manager struct {
 	serviceManager     serviceManager
 	metricsRegistry    metrics.Registry
 	middlewaresBuilder middlewareBuilder
 	chainBuilder       *middleware.ChainBuilder
 	conf               *runtime.Configuration
+	tlsManager         *tls.Manager
 }
 
-// NewManager Creates a new Manager.
-func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, chainBuilder *middleware.ChainBuilder, metricsRegistry metrics.Registry) *Manager {
+// NewManager creates a new Manager.
+func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, chainBuilder *middleware.ChainBuilder, metricsRegistry metrics.Registry, tlsManager *tls.Manager) *Manager {
 	return &Manager{
 		routerHandlers:     make(map[string]http.Handler),
 		serviceManager:     serviceManager,
 		metricsRegistry:    metricsRegistry,
 		middlewaresBuilder: middlewaresBuilder,
 		chainBuilder:       chainBuilder,
 		conf:               conf,
+		tlsManager:         tlsManager,
 	}
 }
 
 func (m *Manager) getHTTPRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*runtime.RouterInfo {
 	if m.conf != nil {
@@ -139,10 +143,21 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, routerConfig *runtime.RouterInfo) (http.Handler, error) {
 	if handler, ok := m.routerHandlers[routerName]; ok {
 		return handler, nil
 	}
 
+	if routerConfig.TLS != nil {
+		// Don't build the router if the TLSOptions configuration is invalid.
+		tlsOptionsName := tls.DefaultTLSConfigName
+		if len(routerConfig.TLS.Options) > 0 && routerConfig.TLS.Options != tls.DefaultTLSConfigName {
+			tlsOptionsName = provider.GetQualifiedName(ctx, routerConfig.TLS.Options)
+		}
+		if _, err := m.tlsManager.Get(tls.DefaultTLSStoreName, tlsOptionsName); err != nil {
+			return nil, fmt.Errorf("building router handler: %w", err)
+		}
+	}
+
 	handler, err := m.buildHTTPHandler(ctx, routerConfig, routerName)
 	if err != nil {
 		return nil, err
 	}
 
diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go
index 26e8a68a3..0ba85b527 100644
--- a/pkg/server/router/router_test.go
+++ b/pkg/server/router/router_test.go
@@ -18,10 +18,11 @@ import (
 	"github.com/traefik/traefik/v2/pkg/middlewares/capture"
 	"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
 	"github.com/traefik/traefik/v2/pkg/server/middleware"
 	"github.com/traefik/traefik/v2/pkg/server/service"
 	"github.com/traefik/traefik/v2/pkg/testhelpers"
+	"github.com/traefik/traefik/v2/pkg/tls"
 	"github.com/traefik/traefik/v2/pkg/types"
 )
 
 func TestRouterManager_Get(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
@@ -315,12 +316,13 @@ func TestRouterManager_Get(t *testing.T) {
 			roundTripperManager := service.NewRoundTripperManager()
 			roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
 			serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
 			middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
 			chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
+			tlsManager := tls.NewManager()
 
-			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
+			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
 
 			handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
 
 			w := httptest.NewRecorder()
 			req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
@@ -421,12 +423,13 @@ func TestAccessLog(t *testing.T) {
 			roundTripperManager := service.NewRoundTripperManager()
 			roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
 			serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
 			middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
 			chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
+			tlsManager := tls.NewManager()
 
-			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
+			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
 
 			handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
 
 			w := httptest.NewRecorder()
 			req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
@@ -460,10 +463,11 @@ func TestRuntimeConfiguration(t *testing.T) {
 	testCases := []struct {
 		desc             string
 		serviceConfig    map[string]*dynamic.Service
 		routerConfig     map[string]*dynamic.Router
 		middlewareConfig map[string]*dynamic.Middleware
+		tlsOptions       map[string]tls.Options
 		expectedError    int
 	}{
 		{
 			desc: "No error",
 			serviceConfig: map[string]*dynamic.Service{
@@ -663,11 +667,10 @@ func TestRuntimeConfiguration(t *testing.T) {
 					Middlewares: []string{"unknown"},
 				},
 			},
 			expectedError: 1,
 		},
-
 		{
 			desc: "Router with broken middleware",
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
 					LoadBalancer: &dynamic.ServersLoadBalancer{
@@ -694,12 +697,75 @@ func TestRuntimeConfiguration(t *testing.T) {
 					Middlewares: []string{"auth"},
 				},
 			},
 			expectedError: 2,
 		},
+		{
+			desc: "Router with broken tlsOption",
+			serviceConfig: map[string]*dynamic.Service{
+				"foo-service": {
+					LoadBalancer: &dynamic.ServersLoadBalancer{
+						Servers: []dynamic.Server{
+							{
+								URL: "http://127.0.0.1",
+							},
+						},
+					},
+				},
+			},
+			middlewareConfig: map[string]*dynamic.Middleware{},
+			routerConfig: map[string]*dynamic.Router{
+				"bar": {
+					EntryPoints: []string{"web"},
+					Service:     "foo-service",
+					Rule:        "Host(`foo.bar`)",
+					TLS: &dynamic.RouterTLSConfig{
+						Options: "broken-tlsOption",
+					},
+				},
+			},
+			tlsOptions: map[string]tls.Options{
+				"broken-tlsOption": {
+					ClientAuth: tls.ClientAuth{
+						ClientAuthType: "foobar",
+					},
+				},
+			},
+			expectedError: 1,
+		},
+		{
+			desc: "Router with broken default tlsOption",
+			serviceConfig: map[string]*dynamic.Service{
+				"foo-service": {
+					LoadBalancer: &dynamic.ServersLoadBalancer{
+						Servers: []dynamic.Server{
+							{
+								URL: "http://127.0.0.1",
+							},
+						},
+					},
+				},
+			},
+			middlewareConfig: map[string]*dynamic.Middleware{},
+			routerConfig: map[string]*dynamic.Router{
+				"bar": {
+					EntryPoints: []string{"web"},
+					Service:     "foo-service",
+					Rule:        "Host(`foo.bar`)",
+					TLS:         &dynamic.RouterTLSConfig{},
+				},
+			},
+			tlsOptions: map[string]tls.Options{
+				"default": {
+					ClientAuth: tls.ClientAuth{
+						ClientAuthType: "foobar",
+					},
+				},
+			},
+			expectedError: 1,
+		},
 	}
-
 	for _, test := range testCases {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
 			t.Parallel()
 
@@ -709,21 +775,27 @@ func TestRuntimeConfiguration(t *testing.T) {
 				HTTP: &dynamic.HTTPConfiguration{
 					Services:    test.serviceConfig,
 					Routers:     test.routerConfig,
 					Middlewares: test.middlewareConfig,
 				},
+				TLS: &dynamic.TLSConfiguration{
+					Options: test.tlsOptions,
+				},
 			})
 
 			roundTripperManager := service.NewRoundTripperManager()
 			roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
 			serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
 			middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
 			chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
+			tlsManager := tls.NewManager()
+			tlsManager.UpdateConfigs(context.Background(), nil, test.tlsOptions, nil)
 
-			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
+			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
 
 			_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
+			_ = routerManager.BuildHandlers(context.Background(), entryPoints, true)
 
 			// even though rtConf was passed by argument to the manager builders above,
 			// it's ok to use it as the result we check, because everything worth checking
 			// can be accessed by pointers in it.
 			var allErrors int
@@ -791,12 +863,13 @@ func TestProviderOnMiddlewares(t *testing.T) {
 	roundTripperManager := service.NewRoundTripperManager()
 	roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
 	serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
 	middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
 	chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
+	tlsManager := tls.NewManager()
 
-	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
+	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
 
 	_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
 
 	assert.Equal(t, []string{"chain@file", "m1@file"}, rtConf.Routers["router@file"].Middlewares)
 	assert.Equal(t, []string{"m1@file", "m2@file", "m1@file"}, rtConf.Middlewares["chain@file"].Chain.Middlewares)
@@ -859,12 +932,13 @@ func BenchmarkRouterServe(b *testing.B) {
 	})
 
 	serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
 	middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
 	chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
+	tlsManager := tls.NewManager()
 
-	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
+	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
 
 	handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
 
 	w := httptest.NewRecorder()
 	req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
diff --git a/pkg/server/router/tcp/manager.go b/pkg/server/router/tcp/manager.go
index ff92f3112..b149f5f0b 100644
--- a/pkg/server/router/tcp/manager.go
+++ b/pkg/server/router/tcp/manager.go
@@ -101,22 +101,25 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		return nil, err
 	}
 
 	router.SetHTTPHandler(handlerHTTP)
 
+	// Even though the error is seemingly ignored (aside from logging it),
+	// we actually rely later on the fact that a tls config is nil (which happens when an error is returned) to take special steps
+	// when assigning a handler to a route.
 	defaultTLSConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, traefiktls.DefaultTLSConfigName)
 	if err != nil {
 		log.FromContext(ctx).Errorf("Error during the build of the default TLS configuration: %v", err)
 	}
 
-	// Keyed by domain. The source of truth for doing SNI checking, and for what TLS
-	// options will actually be used for the connection.
+	// Keyed by domain. The source of truth for doing SNI checking (domain fronting).
 	// As soon as there's (at least) two different tlsOptions found for the same domain,
 	// we set the value to the default TLS conf.
 	tlsOptionsForHost := map[string]string{}
 
 	// Keyed by domain, then by options reference.
+	// The actual source of truth for what TLS options will actually be used for the connection.
 	// As opposed to tlsOptionsForHost, it keeps track of all the (different) TLS
 	// options that occur for a given host name, so that later on we can set relevant
 	// errors and logging for all the routers concerned (i.e. wrongly configured).
 	tlsOptionsForHostSNI := map[string]map[string]nameAndConfig{}
 
@@ -140,25 +143,24 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 			logger.Error(routerErr)
 			continue
 		}
 
 		if len(domains) == 0 {
-			// Extra Host(*) rule, for HTTPS routers with no Host rule, and for requests for
-			// which the SNI does not match _any_ of the other existing routers Host. This is
-			// only about choosing the TLS configuration. The actual routing will be done
-			// further on by the HTTPS handler. See examples below.
+			// Extra Host(*) rule, for HTTPS routers with no Host rule,
+			// and for requests for which the SNI does not match _any_ of the other existing routers Host.
+			// This is only about choosing the TLS configuration.
+			// The actual routing will be done further on by the HTTPS handler.
+			// See examples below.
 			router.AddHTTPTLSConfig("*", defaultTLSConf)
 
-			// The server name (from a Host(SNI) rule) is the only parameter (available in
-			// HTTP routing rules) on which we can map a TLS config, because it is the only one
-			// accessible before decryption (we obtain it during the ClientHello). Therefore,
-			// when a router has no Host rule, it does not make any sense to specify some TLS
-			// options. Consequently, when it comes to deciding what TLS config will be used,
-			// for a request that will match an HTTPS router with no Host rule, the result will
-			// depend on the _others_ existing routers (their Host rule, to be precise), and
-			// the TLS options associated with them, even though they don't match the incoming
-			// request. Consider the following examples:
+			// The server name (from a Host(SNI) rule) is the only parameter (available in HTTP routing rules) on which we can map a TLS config,
+			// because it is the only one accessible before decryption (we obtain it during the ClientHello).
+			// Therefore, when a router has no Host rule, it does not make any sense to specify some TLS options.
+			// Consequently, when it comes to deciding what TLS config will be used,
+			// for a request that will match an HTTPS router with no Host rule,
+			// the result will depend on the _others_ existing routers (their Host rule, to be precise), and the TLS options associated with them,
+			// even though they don't match the incoming request. Consider the following examples:
 
 			//	# conf1
 			//	httpRouter1:
 			//		rule: PathPrefix("/foo")
 			//	# Wherever the request comes from, the TLS config used will be the default one, because of the Host(*) fallback.
@@ -168,21 +170,23 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 			//		rule: PathPrefix("/foo")
 			//
 			//	httpRouter2:
 			//		rule: Host("foo.com") && PathPrefix("/bar")
 			//		tlsoptions: myTLSOptions
-			//	# When a request for "/foo" comes, even though it won't be routed by
-			//	httpRouter2, if its SNI is set to foo.com, myTLSOptions will be used for the TLS
-			//	connection. Otherwise, it will fallback to the default TLS config.
+			//	# When a request for "/foo" comes, even though it won't be routed by httpRouter2,
+			//	# if its SNI is set to foo.com, myTLSOptions will be used for the TLS connection.
+			//	# Otherwise, it will fallback to the default TLS config.
 			logger.Warnf("No domain found in rule %v, the TLS options applied for this router will depend on the SNI of each request", routerHTTPConfig.Rule)
 		}
 
-		tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
-		if err != nil {
-			routerHTTPConfig.AddError(err, true)
-			logger.Error(err)
-			continue
+		// Even though the error is seemingly ignored (aside from logging it),
+		// we actually rely later on the fact that a tls config is nil (which happens when an error is returned) to take special steps
+		// when assigning a handler to a route.
+		tlsConf, tlsConfErr := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
+		if tlsConfErr != nil {
+			// Note: we do not call AddError here because we already did so when buildRouterHandler errored for the same reason.
+			logger.Error(tlsConfErr)
 		}
 
 		for _, domain := range domains {
 			// domain is already in lower case thanks to the domain parsing
 			if tlsOptionsForHostSNI[domain] == nil {
@@ -202,10 +206,11 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		}
 	}
 
 	sniCheck := snicheck.New(tlsOptionsForHost, handlerHTTPS)
 
+	// Keep in mind that defaultTLSConf might be nil here.
 	router.SetHTTPSHandler(sniCheck, defaultTLSConf)
 
 	logger := log.FromContext(ctx)
 	for hostSNI, tlsConfigs := range tlsOptionsForHostSNI {
 		if len(tlsConfigs) == 1 {
@@ -215,26 +220,46 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 				optionsName = k
 				config = v.TLSConfig
 				break
 			}
 
-			logger.Debugf("Adding route for %s with TLS options %s", hostSNI, optionsName)
+			if config == nil {
+				// we use nil config as a signal to insert a handler
+				// that enforces that TLS connection attempts to the corresponding (broken) router should fail.
+				logger.Debugf("Adding special closing route for %s because broken TLS options %s", hostSNI, optionsName)
+				router.AddHTTPTLSConfig(hostSNI, nil)
+				continue
+			}
 
+			logger.Debugf("Adding route for %s with TLS options %s", hostSNI, optionsName)
 			router.AddHTTPTLSConfig(hostSNI, config)
-		} else {
-			routers := make([]string, 0, len(tlsConfigs))
-			for _, v := range tlsConfigs {
-				configsHTTP[v.routerName].AddError(fmt.Errorf("found different TLS options for routers on the same host %v, so using the default TLS options instead", hostSNI), false)
-				routers = append(routers, v.routerName)
-			}
+			continue
+		}
+
+		// multiple tlsConfigs
 
-			logger.Warnf("Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v", hostSNI, routers)
+		routers := make([]string, 0, len(tlsConfigs))
+		for _, v := range tlsConfigs {
+			configsHTTP[v.routerName].AddError(fmt.Errorf("found different TLS options for routers on the same host %v, so using the default TLS options instead", hostSNI), false)
+			routers = append(routers, v.routerName)
+		}
 
-			router.AddHTTPTLSConfig(hostSNI, defaultTLSConf)
+		logger.Warnf("Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v", hostSNI, routers)
+		if defaultTLSConf == nil {
+			logger.Debugf("Adding special closing route for %s because broken default TLS options", hostSNI)
 		}
+
+		router.AddHTTPTLSConfig(hostSNI, defaultTLSConf)
 	}
 
+	m.addTCPHandlers(ctx, configs, router)
+
+	return router, nil
+}
+
+// addTCPHandlers creates the TCP handlers defined in configs, and adds them to router.
+func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtime.TCPRouterInfo, router *Router) {
 	for routerName, routerConfig := range configs {
 		ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName))
 		logger := log.FromContext(ctxRouter)
 
 		if routerConfig.Service == "" {
@@ -249,17 +274,10 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 			routerConfig.AddError(err, true)
 			logger.Error(err)
 			continue
 		}
 
-		handler, err := m.buildTCPHandler(ctxRouter, routerConfig)
-		if err != nil {
-			routerConfig.AddError(err, true)
-			logger.Error(err)
-			continue
-		}
-
 		domains, err := tcpmuxer.ParseHostSNI(routerConfig.Rule)
 		if err != nil {
 			routerErr := fmt.Errorf("invalid rule: %q , %w", routerConfig.Rule, err)
 			routerConfig.AddError(routerErr, true)
 			logger.Error(routerErr)
@@ -272,10 +290,20 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 			routerErr := fmt.Errorf("invalid rule: %q , has HostSNI matcher, but no TLS on router", routerConfig.Rule)
 			routerConfig.AddError(routerErr, true)
 			logger.Error(routerErr)
 		}
 
+		var handler tcp.Handler
+		if routerConfig.TLS == nil || routerConfig.TLS.Passthrough {
+			handler, err = m.buildTCPHandler(ctxRouter, routerConfig)
+			if err != nil {
+				routerConfig.AddError(err, true)
+				logger.Error(err)
+				continue
+			}
+		}
+
 		if routerConfig.TLS == nil {
 			logger.Debugf("Adding route for %q", routerConfig.Rule)
 			if err := router.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
 				routerConfig.AddError(err, true)
 				logger.Error(err)
@@ -283,11 +311,11 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 			continue
 		}
 
 		if routerConfig.TLS.Passthrough {
 			logger.Debugf("Adding Passthrough route for %q", routerConfig.Rule)
-			if err := router.AddRouteTLS(routerConfig.Rule, routerConfig.Priority, handler, nil); err != nil {
+			if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
 				routerConfig.AddError(err, true)
 				logger.Error(err)
 			}
 			continue
 		}
@@ -313,11 +341,19 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		}
 
 		tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
 		if err != nil {
 			routerConfig.AddError(err, true)
+
 			logger.Error(err)
+			logger.Debugf("Adding special TLS closing route for %q because broken TLS options %s", routerConfig.Rule, tlsOptionsName)
+
+			err = router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, &brokenTLSRouter{})
+			if err != nil {
+				routerConfig.AddError(err, true)
+				logger.Error(err)
+			}
 			continue
 		}
 
 		// Now that the Rule is not just about the Host, we could theoretically have a config like:
 		//	router1:
@@ -325,24 +361,34 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		//		tlsOption: tlsOne
 		//	router2:
 		//		rule: HostSNI(foo.com) && ClientIP(IP2)
 		//		tlsOption: tlsTwo
 		// i.e. same HostSNI but different tlsOptions
-		// This is only applicable if the muxer can decide about the routing _before_
-		// telling the client about the tlsConf (i.e. before the TLS HandShake). This seems
-		// to be the case so far with the existing matchers (HostSNI, and ClientIP), so
-		// it's all good. Otherwise, we would have to do as for HTTPS, i.e. disallow
-		// different TLS configs for the same HostSNIs.
+		// This is only applicable if the muxer can decide about the routing _before_ telling the client about the tlsConf (i.e. before the TLS HandShake).
+		// This seems to be the case so far with the existing matchers (HostSNI, and ClientIP), so it's all good.
+		// Otherwise, we would have to do as for HTTPS, i.e. disallow different TLS configs for the same HostSNIs.
+
+		handler, err = m.buildTCPHandler(ctxRouter, routerConfig)
+		if err != nil {
+			routerConfig.AddError(err, true)
+			logger.Error(err)
+			continue
+		}
+
+		handler = &tcp.TLSHandler{
+			Next:   handler,
+			Config: tlsConf,
+		}
 
 		logger.Debugf("Adding TLS route for %q", routerConfig.Rule)
-		if err := router.AddRouteTLS(routerConfig.Rule, routerConfig.Priority, handler, tlsConf); err != nil {
+
+		err = router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, handler)
+		if err != nil {
 			routerConfig.AddError(err, true)
 			logger.Error(err)
 		}
 	}
-
-	return router, nil
 }
 
 func (m *Manager) buildTCPHandler(ctx context.Context, router *runtime.TCPRouterInfo) (tcp.Handler, error) {
 	var qualifiedNames []string
 	for _, name := range router.Middlewares {
diff --git a/pkg/server/router/tcp/router.go b/pkg/server/router/tcp/router.go
index 0a41e80e2..dca4e6fae 100644
--- a/pkg/server/router/tcp/router.go
+++ b/pkg/server/router/tcp/router.go
@@ -25,23 +25,24 @@ type Router struct {
 	muxerTCPTLS tcpmuxer.Muxer
 	// Contains HTTPS routes.
 	muxerHTTPS tcpmuxer.Muxer
 
 	// Forwarder handlers.
-	// Handles all HTTP requests.
+	// httpForwarder handles all HTTP requests.
 	httpForwarder tcp.Handler
-	// Handles (indirectly through muxerHTTPS, or directly) all HTTPS requests.
+	// httpsForwarder handles (indirectly through muxerHTTPS, or directly) all HTTPS requests.
 	httpsForwarder tcp.Handler
 
-	// Neither is used directly, but they are held here, and recreated on config
-	// reload, so that they can be passed to the Switcher at the end of the config
-	// reload phase.
+	// Neither is used directly, but they are held here, and recreated on config reload,
+	// so that they can be passed to the Switcher at the end of the config reload phase.
 	httpHandler  http.Handler
 	httpsHandler http.Handler
 
 	// TLS configs.
-	httpsTLSConfig    *tls.Config            // default TLS config
+	httpsTLSConfig *tls.Config // default TLS config
+	// hostHTTPTLSConfig contains TLS configs keyed by SNI.
+	// A nil config is the hint to set up a brokenTLSRouter.
 	hostHTTPTLSConfig map[string]*tls.Config // TLS configs keyed by SNI
 }
 
 // NewRouter returns a new TCP router.
 func NewRouter() (*Router, error) {
@@ -78,15 +79,15 @@ func (r *Router) GetTLSGetClientInfo() func(info *tls.ClientHelloInfo) (*tls.Con
 	}
 }
 
 // ServeTCP forwards the connection to the right TCP/HTTP handler.
 func (r *Router) ServeTCP(conn tcp.WriteCloser) {
-	// Handling Non-TLS TCP connection early if there is neither HTTP(S) nor TLS
-	// routers on the entryPoint, and if there is at least one non-TLS TCP router.
-	// In the case of a non-TLS TCP client (that does not "send" first), we would
-	// block forever on clientHelloInfo, which is why we want to detect and
-	// handle that case first and foremost.
+	// Handling Non-TLS TCP connection early if there is neither HTTP(S) nor TLS routers on the entryPoint,
+	// and if there is at least one non-TLS TCP router.
+	// In the case of a non-TLS TCP client (that does not "send" first),
+	// we would block forever on clientHelloInfo,
+	// which is why we want to detect and handle that case first and foremost.
 	if r.muxerTCP.HasRoutes() && !r.muxerTCPTLS.HasRoutes() && !r.muxerHTTPS.HasRoutes() {
 		connData, err := tcpmuxer.NewConnData("", conn, nil)
 		if err != nil {
 			log.WithoutContext().Errorf("Error while reading TCP connection data: %v", err)
 			conn.Close()
@@ -150,13 +151,13 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
 	// (which is also incidentally the same used in the last block below for 404s).
 	// The added value from doing Match is to find and use the specific TLS config
 	// (wrapped inside the returned handler) requested for the given HostSNI.
 	handlerHTTPS, catchAllHTTPS := r.muxerHTTPS.Match(connData)
 	if handlerHTTPS != nil && !catchAllHTTPS {
-		// In order not to depart from the behavior in 2.6, we only allow an HTTPS router
-		// to take precedence over a TCP-TLS router if it is _not_ an HostSNI(*) router (so
-		// basically any router that has a specific HostSNI based rule).
+		// In order not to depart from the behavior in 2.6,
+		// we only allow an HTTPS router to take precedence over a TCP-TLS router if it is _not_ an HostSNI(*) router
+		// (so basically any router that has a specific HostSNI based rule).
 		handlerHTTPS.ServeTCP(r.GetConn(conn, hello.peeked))
 		return
 	}
 
 	// Contains also TCP TLS passthrough routes.
@@ -178,11 +179,11 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
 	if handlerTCPTLS != nil {
 		handlerTCPTLS.ServeTCP(r.GetConn(conn, hello.peeked))
 		return
 	}
 
-	// needed to handle 404s for HTTPS, as well as all non-Host (e.g. PathPrefix) matches.
+	// To handle 404s for HTTPS.
 	if r.httpsForwarder != nil {
 		r.httpsForwarder.ServeTCP(r.GetConn(conn, hello.peeked))
 		return
 	}
 
@@ -192,23 +193,10 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
 // AddRoute defines a handler for the given rule.
 func (r *Router) AddRoute(rule string, priority int, target tcp.Handler) error {
 	return r.muxerTCP.AddRoute(rule, priority, target)
 }
 
-// AddRouteTLS defines a handler for a given rule and sets the matching tlsConfig.
-func (r *Router) AddRouteTLS(rule string, priority int, target tcp.Handler, config *tls.Config) error {
-	// TLS PassThrough
-	if config == nil {
-		return r.muxerTCPTLS.AddRoute(rule, priority, target)
-	}
-
-	return r.muxerTCPTLS.AddRoute(rule, priority, &tcp.TLSHandler{
-		Next:   target,
-		Config: config,
-	})
-}
-
 // AddHTTPTLSConfig defines a handler for a given sniHost and sets the matching tlsConfig.
 func (r *Router) AddHTTPTLSConfig(sniHost string, config *tls.Config) {
 	if r.hostHTTPTLSConfig == nil {
 		r.hostHTTPTLSConfig = map[string]*tls.Config{}
 	}
@@ -240,24 +228,48 @@ func (r *Router) GetHTTPSHandler() http.Handler {
 // SetHTTPForwarder sets the tcp handler that will forward the connections to an http handler.
 func (r *Router) SetHTTPForwarder(handler tcp.Handler) {
 	r.httpForwarder = handler
 }
 
-// SetHTTPSForwarder sets the tcp handler that will forward the TLS connections to an http handler.
+// brokenTLSRouter is associated to a Host(SNI) rule for which we know the TLS conf is broken.
+// It is used to make sure any attempt to connect to that hostname is closed,
+// since we cannot proceed with the intended TLS conf.
+type brokenTLSRouter struct{}
+
+// ServeTCP instantly closes the connection.
+func (t *brokenTLSRouter) ServeTCP(conn tcp.WriteCloser) {
+	_ = conn.Close()
+}
+
+// SetHTTPSForwarder sets the tcp handler that will forward the TLS connections to an HTTP handler.
+// It also sets up each TLS handler (with its TLS config) for each Host(SNI) rule we previously kept track of.
+// It sets up a special handler that closes the connection if a TLS config is nil.
 func (r *Router) SetHTTPSForwarder(handler tcp.Handler) {
 	for sniHost, tlsConf := range r.hostHTTPTLSConfig {
+		var tcpHandler tcp.Handler
+		if tlsConf == nil {
+			tcpHandler = &brokenTLSRouter{}
+		} else {
+			tcpHandler = &tcp.TLSHandler{
+				Next:   handler,
+				Config: tlsConf,
+			}
+		}
+
 		// muxerHTTPS only contains single HostSNI rules (and no other kind of rules),
 		// so there's no need for specifying a priority for them.
-		err := r.muxerHTTPS.AddRoute("HostSNI(`"+sniHost+"`)", 0, &tcp.TLSHandler{
-			Next:   handler,
-			Config: tlsConf,
-		})
+		err := r.muxerHTTPS.AddRoute("HostSNI(`"+sniHost+"`)", 0, tcpHandler)
 		if err != nil {
 			log.WithoutContext().Errorf("Error while adding route for host: %v", err)
 		}
 	}
 
+	if r.httpsTLSConfig == nil {
+		r.httpsForwarder = &brokenTLSRouter{}
+		return
+	}
+
 	r.httpsForwarder = &tcp.TLSHandler{
 		Next:   handler,
 		Config: r.httpsTLSConfig,
 	}
 }
@@ -273,19 +285,18 @@ func (r *Router) SetHTTPSHandler(handler http.Handler, config *tls.Config) {
 	r.httpsTLSConfig = config
 }
 
 // Conn is a connection proxy that handles Peeked bytes.
 type Conn struct {
-	// Peeked are the bytes that have been read from Conn for the
-	// purposes of route matching, but have not yet been consumed
-	// by Read calls. It set to nil by Read when fully consumed.
+	// Peeked are the bytes that have been read from Conn for the purposes of route matching,
+	// but have not yet been consumed by Read calls.
+	// It set to nil by Read when fully consumed.
 	Peeked []byte
 
 	// Conn is the underlying connection.
-	// It can be type asserted against *net.TCPConn or other types
-	// as needed. It should not be read from directly unless
-	// Peeked is nil.
+	// It can be type asserted against *net.TCPConn or other types as needed.
+	// It should not be read from directly unless Peeked is nil.
 	tcp.WriteCloser
 }
 
 // Read reads bytes from the connection (using the buffer prior to actually reading).
 func (c *Conn) Read(p []byte) (n int, err error) {
@@ -318,19 +329,18 @@ func clientHelloInfo(br *bufio.Reader) (*clientHello, error) {
 			log.WithoutContext().Errorf("Error while Peeking first byte: %s", err)
 		}
 		return nil, err
 	}
 
-	// No valid TLS record has a type of 0x80, however SSLv2 handshakes
-	// start with a uint16 length where the MSB is set and the first record
-	// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
-	// an SSLv2 client.
+	// No valid TLS record has a type of 0x80, however SSLv2 handshakes start with an uint16 length
+	// where the MSB is set and the first record is always < 256 bytes long.
+	// Therefore, typ == 0x80 strongly suggests an SSLv2 client.
 	const recordTypeSSLv2 = 0x80
 	const recordTypeHandshake = 0x16
 	if hdr[0] != recordTypeHandshake {
 		if hdr[0] == recordTypeSSLv2 {
-			// we consider SSLv2 as TLS and it will be refused by real TLS handshake.
+			// we consider SSLv2 as TLS, and it will be refused by real TLS handshake.
 			return &clientHello{
 				isTLS:  true,
 				peeked: getPeeked(br),
 			}, nil
 		}
diff --git a/pkg/server/routerfactory.go b/pkg/server/routerfactory.go
index fff7e3eea..6b7b80ff4 100644
--- a/pkg/server/routerfactory.go
+++ b/pkg/server/routerfactory.go
@@ -70,11 +70,11 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string
 	// HTTP
 	serviceManager := f.managerFactory.Build(rtConf)
 
 	middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, f.pluginBuilder)
 
-	routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.chainBuilder, f.metricsRegistry)
+	routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.chainBuilder, f.metricsRegistry, f.tlsManager)
 
 	handlersNonTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, false)
 	handlersTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, true)
 
 	serviceManager.LaunchHealthCheck()
diff --git a/pkg/tls/tlsmanager.go b/pkg/tls/tlsmanager.go
index 0c7daa992..91fd6402f 100644
--- a/pkg/tls/tlsmanager.go
+++ b/pkg/tls/tlsmanager.go
@@ -155,50 +155,44 @@ func sanitizeDomains(domain types.Domain) ([]string, error) {
 // Get gets the TLS configuration to use for a given store / configuration.
 func (m *Manager) Get(storeName, configName string) (*tls.Config, error) {
 	m.lock.RLock()
 	defer m.lock.RUnlock()
 
-	var tlsConfig *tls.Config
-	var err error
-
 	sniStrict := false
 	config, ok := m.configs[configName]
-	if ok {
-		sniStrict = config.SniStrict
-		tlsConfig, err = buildTLSConfig(config)
-	} else {
-		err = fmt.Errorf("unknown TLS options: %s", configName)
+	if !ok {
+		return nil, fmt.Errorf("unknown TLS options: %s", configName)
 	}
+
+	sniStrict = config.SniStrict
+	tlsConfig, err := buildTLSConfig(config)
 	if err != nil {
-		tlsConfig = &tls.Config{}
+		return nil, fmt.Errorf("building TLS config: %w", err)
 	}
 
 	store := m.getStore(storeName)
 	if store == nil {
 		err = fmt.Errorf("TLS store %s not found", storeName)
 	}
 	acmeTLSStore := m.getStore(tlsalpn01.ACMETLS1Protocol)
-	if acmeTLSStore == nil {
+	if acmeTLSStore == nil && err == nil {
 		err = fmt.Errorf("ACME TLS store %s not found", tlsalpn01.ACMETLS1Protocol)
 	}
 
 	tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
 		domainToCheck := types.CanonicalDomain(clientHello.ServerName)
 
 		if isACMETLS(clientHello) {
 			certificate := acmeTLSStore.GetBestCertificate(clientHello)
 			if certificate == nil {
 				log.WithoutContext().Debugf("TLS: no certificate for TLSALPN challenge: %s", domainToCheck)
-				// We want the user to eventually get the (alertUnrecognizedName) "unrecognized
-				// name" error.
-				// Unfortunately, if we returned an error here, since we can't use
-				// the unexported error (errNoCertificates) that our caller (config.getCertificate
-				// in crypto/tls) uses as a sentinel, it would report an (alertInternalError)
-				// "internal error" instead of an alertUnrecognizedName.
-				// Which is why we return no error, and we let the caller detect that there's
-				// actually no certificate, and fall back into the flow that will report
-				// the desired error.
+				// We want the user to eventually get the (alertUnrecognizedName) "unrecognized name" error.
+				// Unfortunately, if we returned an error here,
+				// since we can't use the unexported error (errNoCertificates) that our caller (config.getCertificate in crypto/tls) uses as a sentinel,
+				// it would report an (alertInternalError) "internal error" instead of an alertUnrecognizedName.
+				// Which is why we return no error, and we let the caller detect that there's actually no certificate,
+				// and fall back into the flow that will report the desired error.
 				// https://cs.opensource.google/go/go/+/dev.boringcrypto.go1.17:src/crypto/tls/common.go;l=1058
 				return nil, nil
 			}
 
 			return certificate, nil
diff --git a/pkg/tls/tlsmanager_test.go b/pkg/tls/tlsmanager_test.go
index 7f3853299..08acf04a2 100644
--- a/pkg/tls/tlsmanager_test.go
+++ b/pkg/tls/tlsmanager_test.go
@@ -117,12 +117,13 @@ func TestManager_Get(t *testing.T) {
 			KeyFile:  localhostKey,
 		},
 	}}
 
 	tlsConfigs := map[string]Options{
-		"foo": {MinVersion: "VersionTLS12"},
-		"bar": {MinVersion: "VersionTLS11"},
+		"foo":     {MinVersion: "VersionTLS12"},
+		"bar":     {MinVersion: "VersionTLS11"},
+		"invalid": {CurvePreferences: []string{"42"}},
 	}
 
 	testCases := []struct {
 		desc               string
 		tlsOptionsName     string
@@ -138,19 +139,24 @@ func TestManager_Get(t *testing.T) {
 			desc:               "Get another tls config from a valid name",
 			tlsOptionsName:     "bar",
 			expectedMinVersion: uint16(tls.VersionTLS11),
 		},
 		{
-			desc:           "Get an tls config from an invalid name",
+			desc:           "Get a tls config from an invalid name",
 			tlsOptionsName: "unknown",
 			expectedError:  true,
 		},
 		{
-			desc:           "Get an tls config from unexisting 'default' name",
+			desc:           "Get a tls config from unexisting 'default' name",
 			tlsOptionsName: "default",
 			expectedError:  true,
 		},
+		{
+			desc:           "Get an invalid tls config",
+			tlsOptionsName: "invalid",
+			expectedError:  true,
+		},
 	}
 
 	tlsManager := NewManager()
 	tlsManager.UpdateConfigs(context.Background(), nil, tlsConfigs, dynamicConfigs)
 
@@ -159,46 +165,17 @@ func TestManager_Get(t *testing.T) {
 		t.Run(test.desc, func(t *testing.T) {
 			t.Parallel()
 
 			config, err := tlsManager.Get("default", test.tlsOptionsName)
 			if test.expectedError {
-				assert.Error(t, err)
+				require.Nil(t, config)
+				require.Error(t, err)
 				return
 			}
 
-			assert.NoError(t, err)
-			assert.Equal(t, config.MinVersion, test.expectedMinVersion)
-		})
-	}
-}
-
-func TestManager_Get_GetCertificate(t *testing.T) {
-	testCases := []struct {
-		desc                 string
-		expectedGetConfigErr require.ErrorAssertionFunc
-		expectedCertificate  assert.ValueAssertionFunc
-	}{
-		{
-			desc:                 "Get a default certificate from non-existing store",
-			expectedGetConfigErr: require.Error,
-			expectedCertificate:  assert.Nil,
-		},
-	}
-
-	tlsManager := NewManager()
-
-	for _, test := range testCases {
-		test := test
-		t.Run(test.desc, func(t *testing.T) {
-			t.Parallel()
-
-			config, err := tlsManager.Get("default", "foo")
-			test.expectedGetConfigErr(t, err)
-
-			certificate, err := config.GetCertificate(&tls.ClientHelloInfo{})
 			require.NoError(t, err)
-			test.expectedCertificate(t, certificate)
+			assert.Equal(t, config.MinVersion, test.expectedMinVersion)
 		})
 	}
 }
 
 func TestClientAuth(t *testing.T) {

Vulnerable code: // NewManager Creates a new Manager.
func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, chainBuilder *middleware.ChainBuilder, metricsRegistry metrics.Registry) *Manager {
			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())


			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
	// Keyed by domain. The source of truth for doing SNI checking, and for what TLS
	// options will actually be used for the connection.
			// Extra Host(*) rule, for HTTPS routers with no Host rule, and for requests for
			// which the SNI does not match _any_ of the other existing routers Host. This is
			// only about choosing the TLS configuration. The actual routing will be done
			// further on by the HTTPS handler. See examples below.
			// The server name (from a Host(SNI) rule) is the only parameter (available in
			// HTTP routing rules) on which we can map a TLS config, because it is the only one
			// accessible before decryption (we obtain it during the ClientHello). Therefore,
			// when a router has no Host rule, it does not make any sense to specify some TLS
			// options. Consequently, when it comes to deciding what TLS config will be used,
			// for a request that will match an HTTPS router with no Host rule, the result will
			// depend on the _others_ existing routers (their Host rule, to be precise), and
			// the TLS options associated with them, even though they don't match the incoming
			// request. Consider the following examples:
			//	# When a request for "/foo" comes, even though it won't be routed by
			//	httpRouter2, if its SNI is set to foo.com, myTLSOptions will be used for the TLS
			//	connection. Otherwise, it will fallback to the default TLS config.
		tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
		if err != nil {
			routerHTTPConfig.AddError(err, true)
			logger.Error(err)
			continue
			logger.Debugf("Adding route for %s with TLS options %s", hostSNI, optionsName)
		} else {
			routers := make([]string, 0, len(tlsConfigs))
			for _, v := range tlsConfigs {
				configsHTTP[v.routerName].AddError(fmt.Errorf("found different TLS options for routers on the same host %v, so using the default TLS options instead", hostSNI), false)
				routers = append(routers, v.routerName)
			}
			logger.Warnf("Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v", hostSNI, routers)
			router.AddHTTPTLSConfig(hostSNI, defaultTLSConf)
		handler, err := m.buildTCPHandler(ctxRouter, routerConfig)
		if err != nil {
			routerConfig.AddError(err, true)
			logger.Error(err)
			continue
		}

			if err := router.AddRouteTLS(routerConfig.Rule, routerConfig.Priority, handler, nil); err != nil {
		// This is only applicable if the muxer can decide about the routing _before_
		// telling the client about the tlsConf (i.e. before the TLS HandShake). This seems
		// to be the case so far with the existing matchers (HostSNI, and ClientIP), so
		// it's all good. Otherwise, we would have to do as for HTTPS, i.e. disallow
		// different TLS configs for the same HostSNIs.
		if err := router.AddRouteTLS(routerConfig.Rule, routerConfig.Priority, handler, tlsConf); err != nil {

	return router, nil
	// Handles all HTTP requests.
	// Handles (indirectly through muxerHTTPS, or directly) all HTTPS requests.
	// Neither is used directly, but they are held here, and recreated on config
	// reload, so that they can be passed to the Switcher at the end of the config
	// reload phase.
	httpsTLSConfig    *tls.Config            // default TLS config
	// Handling Non-TLS TCP connection early if there is neither HTTP(S) nor TLS
	// routers on the entryPoint, and if there is at least one non-TLS TCP router.
	// In the case of a non-TLS TCP client (that does not "send" first), we would
	// block forever on clientHelloInfo, which is why we want to detect and
	// handle that case first and foremost.
		// In order not to depart from the behavior in 2.6, we only allow an HTTPS router
		// to take precedence over a TCP-TLS router if it is _not_ an HostSNI(*) router (so
		// basically any router that has a specific HostSNI based rule).
	// needed to handle 404s for HTTPS, as well as all non-Host (e.g. PathPrefix) matches.
// AddRouteTLS defines a handler for a given rule and sets the matching tlsConfig.
func (r *Router) AddRouteTLS(rule string, priority int, target tcp.Handler, config *tls.Config) error {
	// TLS PassThrough
	if config == nil {
		return r.muxerTCPTLS.AddRoute(rule, priority, target)
	}

	return r.muxerTCPTLS.AddRoute(rule, priority, &tcp.TLSHandler{
		Next:   target,
		Config: config,
	})
}

// SetHTTPSForwarder sets the tcp handler that will forward the TLS connections to an http handler.
		err := r.muxerHTTPS.AddRoute("HostSNI(`"+sniHost+"`)", 0, &tcp.TLSHandler{
			Next:   handler,
			Config: tlsConf,
		})
	// Peeked are the bytes that have been read from Conn for the
	// purposes of route matching, but have not yet been consumed
	// by Read calls. It set to nil by Read when fully consumed.
	// It can be type asserted against *net.TCPConn or other types
	// as needed. It should not be read from directly unless
	// Peeked is nil.
	// No valid TLS record has a type of 0x80, however SSLv2 handshakes
	// start with a uint16 length where the MSB is set and the first record
	// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
	// an SSLv2 client.
			// we consider SSLv2 as TLS and it will be refused by real TLS handshake.
	routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.chainBuilder, f.metricsRegistry)
	var tlsConfig *tls.Config
	var err error

	if ok {
		sniStrict = config.SniStrict
		tlsConfig, err = buildTLSConfig(config)
	} else {
		err = fmt.Errorf("unknown TLS options: %s", configName)
		tlsConfig = &tls.Config{}
	if acmeTLSStore == nil {
				// We want the user to eventually get the (alertUnrecognizedName) "unrecognized
				// name" error.
				// Unfortunately, if we returned an error here, since we can't use
				// the unexported error (errNoCertificates) that our caller (config.getCertificate
				// in crypto/tls) uses as a sentinel, it would report an (alertInternalError)
				// "internal error" instead of an alertUnrecognizedName.
				// Which is why we return no error, and we let the caller detect that there's
				// actually no certificate, and fall back into the flow that will report
				// the desired error.
		"foo": {MinVersion: "VersionTLS12"},
		"bar": {MinVersion: "VersionTLS11"},
			desc:           "Get an tls config from an invalid name",
			desc:           "Get an tls config from unexisting 'default' name",
				assert.Error(t, err)
			assert.NoError(t, err)
			assert.Equal(t, config.MinVersion, test.expectedMinVersion)
		})
	}
}

func TestManager_Get_GetCertificate(t *testing.T) {
	testCases := []struct {
		desc                 string
		expectedGetConfigErr require.ErrorAssertionFunc
		expectedCertificate  assert.ValueAssertionFunc
	}{
		{
			desc:                 "Get a default certificate from non-existing store",
			expectedGetConfigErr: require.Error,
			expectedCertificate:  assert.Nil,
		},
	}

	tlsManager := NewManager()

	for _, test := range testCases {
		test := test
		t.Run(test.desc, func(t *testing.T) {
			t.Parallel()

			config, err := tlsManager.Get("default", "foo")
			test.expectedGetConfigErr(t, err)

			certificate, err := config.GetCertificate(&tls.ClientHelloInfo{})
			test.expectedCertificate(t, certificate)
Fixed code: [global]
  checkNewVersion = false
  sendAnonymousUsage = false

[log]
  level = "DEBUG"

[entryPoints.websecure]
  address = ":4443"

[api]
  insecure = true

[providers.file]
  filename = "{{ .SelfFilename }}"

## dynamic configuration ##

[http.routers]

  [http.routers.router1]
    entryPoints = ["websecure"]
    service = "service1"
    rule = "Host(`snitest.com`)"
    [http.routers.router1.tls]
      options = "invalidTLSOptions"

  [http.routers.router2]
    entryPoints = ["websecure"]
    service = "service1"
    rule = "Host(`snitest.org`)"
    [http.routers.router2.tls]

  # fallback router
  [http.routers.router3]
    entryPoints = ["websecure"]
    service = "service1"
    rule = "Path(`/`)"
    [http.routers.router3.tls]

[[http.services.service1.loadBalancer.servers]]
  url = "http://127.0.0.1:9010"

[[tls.certificates]]
  certFile = "fixtures/https/snitest.com.cert"
  keyFile = "fixtures/https/snitest.com.key"

[[tls.certificates]]
  certFile = "fixtures/https/snitest.org.cert"
  keyFile = "fixtures/https/snitest.org.key"

[tls.options]

  [tls.options.default.clientAuth]
    # Missing caFile to have an invalid mTLS configuration.
    clientAuthType = "RequireAndVerifyClientCert"

  [tls.options.invalidTLSOptions.clientAuth]
    # Missing caFile to have an invalid mTLS configuration.
    clientAuthType = "RequireAndVerifyClientCert"
    [tcp.routers.to-whoami-invalid-tls]
      rule = "HostSNI(`whoami-i.test`)"
      service = "whoami-no-cert"
      entryPoints = [ "tcp" ]
      [tcp.routers.to-whoami-invalid-tls.tls]
        options = "invalid"


  [tls.options.invalid.clientAuth]
    # Missing CA files to have an invalid mTLS configuration.
    clientAuthType = "RequireAndVerifyClientCert"

// TestWithInvalidTLSOption verifies the behavior when using an invalid tlsOption configuration.
func (s *HTTPSSuite) TestWithInvalidTLSOption(c *check.C) {
	backend := startTestServer("9010", http.StatusOK, "server1")
	defer backend.Close()

	file := s.adaptFile(c, "fixtures/https/https_invalid_tls_options.toml", struct{}{})
	defer os.Remove(file)
	cmd, display := s.traefikCmd(withConfigFile(file))
	defer display(c)
	err := cmd.Start()
	c.Assert(err, checker.IsNil)
	defer s.killCmd(cmd)

	// wait for Traefik
	err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
	c.Assert(err, checker.IsNil)

	testCases := []struct {
		desc       string
		serverName string
	}{
		{
			desc:       "With invalid TLS Options specified",
			serverName: "snitest.com",
		},
		{
			desc:       "With invalid Default TLS Options",
			serverName: "snitest.org",
		},
		{
			desc: "With TLS Options without servername (fallback to default)",
		},
	}

	for _, test := range testCases {
		test := test

		tlsConfig := &tls.Config{
			InsecureSkipVerify: true,
		}
		if test.serverName != "" {
			tlsConfig.ServerName = test.serverName
		}

		conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
		c.Assert(err, checker.NotNil, check.Commentf("connected to server successfully"))
		c.Assert(conn, checker.IsNil)
	}
}

	// Check that we can't reach a route with an invalid mTLS configuration.
	conn, err := tls.Dial("tcp", "127.0.0.1:8093", &tls.Config{
		ServerName:         "whoami-i.test",
		InsecureSkipVerify: true,
	})
	c.Assert(conn, checker.IsNil)
	c.Assert(err, checker.NotNil)
	"fmt"
	"github.com/traefik/traefik/v2/pkg/tls"
	tlsManager         *tls.Manager
// NewManager creates a new Manager.
func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, chainBuilder *middleware.ChainBuilder, metricsRegistry metrics.Registry, tlsManager *tls.Manager) *Manager {
		tlsManager:         tlsManager,
	if routerConfig.TLS != nil {
		// Don't build the router if the TLSOptions configuration is invalid.
		tlsOptionsName := tls.DefaultTLSConfigName
		if len(routerConfig.TLS.Options) > 0 && routerConfig.TLS.Options != tls.DefaultTLSConfigName {
			tlsOptionsName = provider.GetQualifiedName(ctx, routerConfig.TLS.Options)
		}
		if _, err := m.tlsManager.Get(tls.DefaultTLSStoreName, tlsOptionsName); err != nil {
			return nil, fmt.Errorf("building router handler: %w", err)
		}
	}

	"github.com/traefik/traefik/v2/pkg/tls"
			tlsManager := tls.NewManager()
			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
			tlsManager := tls.NewManager()
			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
		tlsOptions       map[string]tls.Options
		{
			desc: "Router with broken tlsOption",
			serviceConfig: map[string]*dynamic.Service{
				"foo-service": {
					LoadBalancer: &dynamic.ServersLoadBalancer{
						Servers: []dynamic.Server{
							{
								URL: "http://127.0.0.1",
							},
						},
					},
				},
			},
			middlewareConfig: map[string]*dynamic.Middleware{},
			routerConfig: map[string]*dynamic.Router{
				"bar": {
					EntryPoints: []string{"web"},
					Service:     "foo-service",
					Rule:        "Host(`foo.bar`)",
					TLS: &dynamic.RouterTLSConfig{
						Options: "broken-tlsOption",
					},
				},
			},
			tlsOptions: map[string]tls.Options{
				"broken-tlsOption": {
					ClientAuth: tls.ClientAuth{
						ClientAuthType: "foobar",
					},
				},
			},
			expectedError: 1,
		},
		{
			desc: "Router with broken default tlsOption",
			serviceConfig: map[string]*dynamic.Service{
				"foo-service": {
					LoadBalancer: &dynamic.ServersLoadBalancer{
						Servers: []dynamic.Server{
							{
								URL: "http://127.0.0.1",
							},
						},
					},
				},
			},
			middlewareConfig: map[string]*dynamic.Middleware{},
			routerConfig: map[string]*dynamic.Router{
				"bar": {
					EntryPoints: []string{"web"},
					Service:     "foo-service",
					Rule:        "Host(`foo.bar`)",
					TLS:         &dynamic.RouterTLSConfig{},
				},
			},
			tlsOptions: map[string]tls.Options{
				"default": {
					ClientAuth: tls.ClientAuth{
						ClientAuthType: "foobar",
					},
				},
			},
			expectedError: 1,
		},
				TLS: &dynamic.TLSConfiguration{
					Options: test.tlsOptions,
				},
			tlsManager := tls.NewManager()
			tlsManager.UpdateConfigs(context.Background(), nil, test.tlsOptions, nil)
			routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
			_ = routerManager.BuildHandlers(context.Background(), entryPoints, true)
	tlsManager := tls.NewManager()
	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
	tlsManager := tls.NewManager()
	routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
	// Even though the error is seemingly ignored (aside from logging it),
	// we actually rely later on the fact that a tls config is nil (which happens when an error is returned) to take special steps
	// when assigning a handler to a route.
	// Keyed by domain. The source of truth for doing SNI checking (domain fronting).
	// The actual source of truth for what TLS options will actually be used for the connection.
			// Extra Host(*) rule, for HTTPS routers with no Host rule,
			// and for requests for which the SNI does not match _any_ of the other existing routers Host.
			// This is only about choosing the TLS configuration.
			// The actual routing will be done further on by the HTTPS handler.
			// See examples below.
			// The server name (from a Host(SNI) rule) is the only parameter (available in HTTP routing rules) on which we can map a TLS config,
			// because it is the only one accessible before decryption (we obtain it during the ClientHello).
			// Therefore, when a router has no Host rule, it does not make any sense to specify some TLS options.
			// Consequently, when it comes to deciding what TLS config will be used,
			// for a request that will match an HTTPS router with no Host rule,
			// the result will depend on the _others_ existing routers (their Host rule, to be precise), and the TLS options associated with them,
			// even though they don't match the incoming request. Consider the following examples:
			//	# When a request for "/foo" comes, even though it won't be routed by httpRouter2,
			//	# if its SNI is set to foo.com, myTLSOptions will be used for the TLS connection.
			//	# Otherwise, it will fallback to the default TLS config.
		// Even though the error is seemingly ignored (aside from logging it),
		// we actually rely later on the fact that a tls config is nil (which happens when an error is returned) to take special steps
		// when assigning a handler to a route.
		tlsConf, tlsConfErr := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
		if tlsConfErr != nil {
			// Note: we do not call AddError here because we already did so when buildRouterHandler errored for the same reason.
			logger.Error(tlsConfErr)
	// Keep in mind that defaultTLSConf might be nil here.
			if config == nil {
				// we use nil config as a signal to insert a handler
				// that enforces that TLS connection attempts to the corresponding (broken) router should fail.
				logger.Debugf("Adding special closing route for %s because broken TLS options %s", hostSNI, optionsName)
				router.AddHTTPTLSConfig(hostSNI, nil)
				continue
			}
			logger.Debugf("Adding route for %s with TLS options %s", hostSNI, optionsName)
			continue
		}

		// multiple tlsConfigs
		routers := make([]string, 0, len(tlsConfigs))
		for _, v := range tlsConfigs {
			configsHTTP[v.routerName].AddError(fmt.Errorf("found different TLS options for routers on the same host %v, so using the default TLS options instead", hostSNI), false)
			routers = append(routers, v.routerName)
		}
		logger.Warnf("Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v", hostSNI, routers)
		if defaultTLSConf == nil {
			logger.Debugf("Adding special closing route for %s because broken default TLS options", hostSNI)

		router.AddHTTPTLSConfig(hostSNI, defaultTLSConf)
	m.addTCPHandlers(ctx, configs, router)

	return router, nil
}

// addTCPHandlers creates the TCP handlers defined in configs, and adds them to router.
func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtime.TCPRouterInfo, router *Router) {
		var handler tcp.Handler
		if routerConfig.TLS == nil || routerConfig.TLS.Passthrough {
			handler, err = m.buildTCPHandler(ctxRouter, routerConfig)
			if err != nil {
				routerConfig.AddError(err, true)
				logger.Error(err)
				continue
			}
		}

			if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {

			logger.Debugf("Adding special TLS closing route for %q because broken TLS options %s", routerConfig.Rule, tlsOptionsName)

			err = router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, &brokenTLSRouter{})
			if err != nil {
				routerConfig.AddError(err, true)
				logger.Error(err)
			}
		// This is only applicable if the muxer can decide about the routing _before_ telling the client about the tlsConf (i.e. before the TLS HandShake).
		// This seems to be the case so far with the existing matchers (HostSNI, and ClientIP), so it's all good.
		// Otherwise, we would have to do as for HTTPS, i.e. disallow different TLS configs for the same HostSNIs.

		handler, err = m.buildTCPHandler(ctxRouter, routerConfig)
		if err != nil {
			routerConfig.AddError(err, true)
			logger.Error(err)
			continue
		}

		handler = &tcp.TLSHandler{
			Next:   handler,
			Config: tlsConf,
		}

		err = router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, handler)
		if err != nil {
	// httpForwarder handles all HTTP requests.
	// httpsForwarder handles (indirectly through muxerHTTPS, or directly) all HTTPS requests.
	// Neither is used directly, but they are held here, and recreated on config reload,
	// so that they can be passed to the Switcher at the end of the config reload phase.
	httpsTLSConfig *tls.Config // default TLS config
	// hostHTTPTLSConfig contains TLS configs keyed by SNI.
	// A nil config is the hint to set up a brokenTLSRouter.
	// Handling Non-TLS TCP connection early if there is neither HTTP(S) nor TLS routers on the entryPoint,
	// and if there is at least one non-TLS TCP router.
	// In the case of a non-TLS TCP client (that does not "send" first),
	// we would block forever on clientHelloInfo,
	// which is why we want to detect and handle that case first and foremost.
		// In order not to depart from the behavior in 2.6,
		// we only allow an HTTPS router to take precedence over a TCP-TLS router if it is _not_ an HostSNI(*) router
		// (so basically any router that has a specific HostSNI based rule).
	// To handle 404s for HTTPS.
// brokenTLSRouter is associated to a Host(SNI) rule for which we know the TLS conf is broken.
// It is used to make sure any attempt to connect to that hostname is closed,
// since we cannot proceed with the intended TLS conf.
type brokenTLSRouter struct{}

// ServeTCP instantly closes the connection.
func (t *brokenTLSRouter) ServeTCP(conn tcp.WriteCloser) {
	_ = conn.Close()
}

// SetHTTPSForwarder sets the tcp handler that will forward the TLS connections to an HTTP handler.
// It also sets up each TLS handler (with its TLS config) for each Host(SNI) rule we previously kept track of.
// It sets up a special handler that closes the connection if a TLS config is nil.
		var tcpHandler tcp.Handler
		if tlsConf == nil {
			tcpHandler = &brokenTLSRouter{}
		} else {
			tcpHandler = &tcp.TLSHandler{
				Next:   handler,
				Config: tlsConf,
			}
		}

		err := r.muxerHTTPS.AddRoute("HostSNI(`"+sniHost+"`)", 0, tcpHandler)
	if r.httpsTLSConfig == nil {
		r.httpsForwarder = &brokenTLSRouter{}
		return
	}

	// Peeked are the bytes that have been read from Conn for the purposes of route matching,
	// but have not yet been consumed by Read calls.
	// It set to nil by Read when fully consumed.
	// It can be type asserted against *net.TCPConn or other types as needed.
	// It should not be read from directly unless Peeked is nil.
	// No valid TLS record has a type of 0x80, however SSLv2 handshakes start with an uint16 length
	// where the MSB is set and the first record is always < 256 bytes long.
	// Therefore, typ == 0x80 strongly suggests an SSLv2 client.
			// we consider SSLv2 as TLS, and it will be refused by real TLS handshake.
	routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.chainBuilder, f.metricsRegistry, f.tlsManager)
	if !ok {
		return nil, fmt.Errorf("unknown TLS options: %s", configName)

	sniStrict = config.SniStrict
	tlsConfig, err := buildTLSConfig(config)
		return nil, fmt.Errorf("building TLS config: %w", err)
	if acmeTLSStore == nil && err == nil {
				// We want the user to eventually get the (alertUnrecognizedName) "unrecognized name" error.
				// Unfortunately, if we returned an error here,
				// since we can't use the unexported error (errNoCertificates) that our caller (config.getCertificate in crypto/tls) uses as a sentinel,
				// it would report an (alertInternalError) "internal error" instead of an alertUnrecognizedName.
				// Which is why we return no error, and we let the caller detect that there's actually no certificate,
				// and fall back into the flow that will report the desired error.
		"foo":     {MinVersion: "VersionTLS12"},
		"bar":     {MinVersion: "VersionTLS11"},
		"invalid": {CurvePreferences: []string{"42"}},
			desc:           "Get a tls config from an invalid name",
			desc:           "Get a tls config from unexisting 'default' name",
		{
			desc:           "Get an invalid tls config",
			tlsOptionsName: "invalid",
			expectedError:  true,
		},
				require.Nil(t, config)
				require.Error(t, err)
			assert.Equal(t, config.MinVersion, test.expectedMinVersion)