Repository: traefik/traefik (language: go)

Issue:
Handle TLS for unknown names correctly
### Welcome!

- [X] Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
- [X] Yes, I've searched similar issues on the [Traefik community forum](https://community.containo.us) and didn't find any.

### What did you do?

Set up traefik on a host with multiple DNS names. One of them is available for traefik in a cert and has routers, the other one is not supposed to be available on https. When connecting with that name that's not supposed to be available via https to port 443, traefik should give back a TLS error alert 112 (unrecognized_name).

### What did you see instead?

When connecting with that name that's not supposed to be available via https to port 443, traefik gives back a TLS error alert 80 (internal_error), but should give back a TLS error alert 112 (unrecognized_name).

### What version of Traefik are you using?

Version:      2.5.2
Codename:     livarot
Go version:   go1.17
Built:        2021-09-02T15:07:43Z
OS/Arch:      linux/amd64

### What is your environment & configuration?

Not applicable, as this is not config dependent.

### If applicable, please paste the log output in DEBUG level

_No response_


Discussion / hints:

Fix patch:
diff --git a/pkg/tls/tlsmanager.go b/pkg/tls/tlsmanager.go
index 35ba50faca..50e809e8aa 100644
--- a/pkg/tls/tlsmanager.go
+++ b/pkg/tls/tlsmanager.go
@@ -143,7 +143,18 @@ func (m *Manager) Get(storeName, configName string) (*tls.Config, error) {
 		if isACMETLS(clientHello) {
 			certificate := acmeTLSStore.GetBestCertificate(clientHello)
 			if certificate == nil {
-				return nil, fmt.Errorf("no certificate for TLSALPN challenge: %s", domainToCheck)
+				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.
+				// https://cs.opensource.google/go/go/+/dev.boringcrypto.go1.17:src/crypto/tls/common.go;l=1058
+				return nil, nil
 			}
 
 			return certificate, nil
@@ -155,7 +166,9 @@ func (m *Manager) Get(storeName, configName string) (*tls.Config, error) {
 		}
 
 		if sniStrict {
-			return nil, fmt.Errorf("strict SNI enabled - No certificate found for domain: %q, closing connection", domainToCheck)
+			log.WithoutContext().Debugf("TLS: strict SNI enabled - No certificate found for domain: %q, closing connection", domainToCheck)
+			// Same comment as above, as in the isACMETLS case.
+			return nil, nil
 		}
 
 		log.WithoutContext().Debugf("Serving default certificate for request: %q", domainToCheck)


Test patch: