From 2fca419c7f2356026c1a9d8bd2ac2be9e9f2f240 Mon Sep 17 00:00:00 2001
From: Andrey Meshkov <am@adguard.com>
Date: Sat, 16 May 2020 02:02:50 +0300
Subject: [PATCH] *(home): fix TLS module initialization

Continue to work even when TLS cert-key pair is invalid

Closes: https://github.com/AdguardTeam/AdGuardHome/issues/1677
---
 dnsforward/config.go |  9 +++++----
 home/home.go         | 14 ++++++++++----
 home/tls.go          | 11 +++++++++--
 3 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/dnsforward/config.go b/dnsforward/config.go
index ed5ec3e9..1fe9dfb2 100644
--- a/dnsforward/config.go
+++ b/dnsforward/config.go
@@ -85,10 +85,11 @@ type FilteringConfig struct {
 
 // TLSConfig is the TLS configuration for HTTPS, DNS-over-HTTPS, and DNS-over-TLS
 type TLSConfig struct {
-	TLSListenAddr    *net.TCPAddr `yaml:"-" json:"-"`
-	StrictSNICheck   bool         `yaml:"strict_sni_check" json:"-"`                  // Reject connection if the client uses server name (in SNI) that doesn't match the certificate
-	CertificateChain string       `yaml:"certificate_chain" json:"certificate_chain"` // PEM-encoded certificates chain
-	PrivateKey       string       `yaml:"private_key" json:"private_key"`             // PEM-encoded private key
+	TLSListenAddr  *net.TCPAddr `yaml:"-" json:"-"`
+	StrictSNICheck bool         `yaml:"strict_sni_check" json:"-"` // Reject connection if the client uses server name (in SNI) that doesn't match the certificate
+
+	CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` // PEM-encoded certificates chain
+	PrivateKey       string `yaml:"private_key" json:"private_key"`             // PEM-encoded private key
 
 	CertificatePath string `yaml:"certificate_path" json:"certificate_path"` // certificate file name
 	PrivateKeyPath  string `yaml:"private_key_path" json:"private_key_path"` // private key file name
diff --git a/home/home.go b/home/home.go
index 76052e20..579101cf 100644
--- a/home/home.go
+++ b/home/home.go
@@ -589,28 +589,34 @@ func printHTTPAddresses(proto string) {
 	if Context.tls != nil {
 		Context.tls.WriteDiskConfig(&tlsConf)
 	}
+
+	port := strconv.Itoa(config.BindPort)
+	if proto == "https" {
+		port = strconv.Itoa(tlsConf.PortHTTPS)
+	}
+
 	if proto == "https" && tlsConf.ServerName != "" {
 		if tlsConf.PortHTTPS == 443 {
 			log.Printf("Go to https://%s", tlsConf.ServerName)
 		} else {
-			log.Printf("Go to https://%s:%d", tlsConf.ServerName, tlsConf.PortHTTPS)
+			log.Printf("Go to https://%s:%s", tlsConf.ServerName, port)
 		}
 	} else if config.BindHost == "0.0.0.0" {
 		log.Println("AdGuard Home is available on the following addresses:")
 		ifaces, err := util.GetValidNetInterfacesForWeb()
 		if err != nil {
 			// That's weird, but we'll ignore it
-			address = net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
+			address = net.JoinHostPort(config.BindHost, port)
 			log.Printf("Go to %s://%s", proto, address)
 			return
 		}
 
 		for _, iface := range ifaces {
-			address = net.JoinHostPort(iface.Addresses[0], strconv.Itoa(config.BindPort))
+			address = net.JoinHostPort(iface.Addresses[0], port)
 			log.Printf("Go to %s://%s", proto, address)
 		}
 	} else {
-		address = net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
+		address = net.JoinHostPort(config.BindHost, port)
 		log.Printf("Go to %s://%s", proto, address)
 	}
 }
diff --git a/home/tls.go b/home/tls.go
index 4bd3aea8..157cda2a 100644
--- a/home/tls.go
+++ b/home/tls.go
@@ -39,7 +39,14 @@ func tlsCreate(conf tlsConfigSettings) *TLSMod {
 	t.conf = conf
 	if t.conf.Enabled {
 		if !t.load() {
-			return nil
+			// Something is not valid - return an empty TLS config
+			return &TLSMod{conf: tlsConfigSettings{
+				Enabled:             conf.Enabled,
+				ServerName:          conf.ServerName,
+				PortHTTPS:           conf.PortHTTPS,
+				PortDNSOverTLS:      conf.PortDNSOverTLS,
+				AllowUnencryptedDOH: conf.AllowUnencryptedDOH,
+			}}
 		}
 		t.setCertFileTime()
 	}
@@ -55,7 +62,7 @@ func (t *TLSMod) load() bool {
 	// validate current TLS config and update warnings (it could have been loaded from file)
 	data := validateCertificates(string(t.conf.CertificateChainData), string(t.conf.PrivateKeyData), t.conf.ServerName)
 	if !data.ValidPair {
-		log.Error(data.WarningValidation)
+		log.Error("failed to validate certificate: %s", data.WarningValidation)
 		return false
 	}
 	t.status = data