Pull request: home: fix tls config comparison

Closes #3411.

Squashed commit of the following:

commit 42deaa1ca804429a5c523641997b4e8d453952e6
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Aug 3 19:47:32 2021 +0300

    home: imp code

commit a69f6469e5e76e17c83537f7a763f047201fd15a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Aug 3 18:54:59 2021 +0300

    home: fix tls config comparison
This commit is contained in:
Ainar Garipov 2021-08-03 19:55:04 +03:00
parent c14bde280e
commit 5c2ead5f60

View file

@ -19,6 +19,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
@ -30,9 +31,9 @@ var tlsWebHandlersRegistered = false
// TLSMod - TLS module object // TLSMod - TLS module object
type TLSMod struct { type TLSMod struct {
certLastMod time.Time // last modification time of the certificate file certLastMod time.Time // last modification time of the certificate file
conf tlsConfigSettings
confLock sync.Mutex
status tlsConfigStatus status tlsConfigStatus
confLock sync.Mutex
conf tlsConfigSettings
} }
// Create TLS module // Create TLS module
@ -209,8 +210,8 @@ type tlsConfigStatus struct {
// field ordering is important -- yaml fields will mirror ordering from here // field ordering is important -- yaml fields will mirror ordering from here
type tlsConfig struct { type tlsConfig struct {
tlsConfigSettings `json:",inline"`
tlsConfigStatus `json:",inline"` tlsConfigStatus `json:",inline"`
tlsConfigSettings `json:",inline"`
} }
func (t *TLSMod) handleTLSStatus(w http.ResponseWriter, _ *http.Request) { func (t *TLSMod) handleTLSStatus(w http.ResponseWriter, _ *http.Request) {
@ -247,6 +248,41 @@ func (t *TLSMod) handleTLSValidate(w http.ResponseWriter, r *http.Request) {
marshalTLS(w, data) marshalTLS(w, data)
} }
func (t *TLSMod) setConfig(newConf tlsConfigSettings, status tlsConfigStatus) (restartHTTPS bool) {
t.confLock.Lock()
defer t.confLock.Unlock()
// Reset the DNSCrypt data before comparing, since we currently do not
// accept these from the frontend.
//
// TODO(a.garipov): Define a custom comparer for dnsforward.TLSConfig.
newConf.DNSCryptConfigFile = t.conf.DNSCryptConfigFile
newConf.PortDNSCrypt = t.conf.PortDNSCrypt
if !cmp.Equal(t.conf, newConf, cmp.AllowUnexported(dnsforward.TLSConfig{})) {
log.Info("tls config has changed, restarting https server")
restartHTTPS = true
} else {
log.Info("tls config has not changed")
}
// Note: don't do just `t.conf = data` because we must preserve all other members of t.conf
t.conf.Enabled = newConf.Enabled
t.conf.ServerName = newConf.ServerName
t.conf.ForceHTTPS = newConf.ForceHTTPS
t.conf.PortHTTPS = newConf.PortHTTPS
t.conf.PortDNSOverTLS = newConf.PortDNSOverTLS
t.conf.PortDNSOverQUIC = newConf.PortDNSOverQUIC
t.conf.CertificateChain = newConf.CertificateChain
t.conf.CertificatePath = newConf.CertificatePath
t.conf.CertificateChainData = newConf.CertificateChainData
t.conf.PrivateKey = newConf.PrivateKey
t.conf.PrivateKeyPath = newConf.PrivateKeyPath
t.conf.PrivateKeyData = newConf.PrivateKeyData
t.status = status
return restartHTTPS
}
func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) { func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
data, err := unmarshalTLS(r) data, err := unmarshalTLS(r)
if err != nil { if err != nil {
@ -266,42 +302,28 @@ func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
tlsConfigStatus: t.status, tlsConfigStatus: t.status,
} }
marshalTLS(w, data2) marshalTLS(w, data2)
return return
} }
status = validateCertificates(string(data.CertificateChainData), string(data.PrivateKeyData), data.ServerName)
restartHTTPS := false
t.confLock.Lock() status = validateCertificates(string(data.CertificateChainData), string(data.PrivateKeyData), data.ServerName)
if !cmp.Equal(t.conf, data) {
log.Printf("tls config settings have changed, will restart HTTPS server") restartHTTPS := t.setConfig(data, status)
restartHTTPS = true
}
// Note: don't do just `t.conf = data` because we must preserve all other members of t.conf
t.conf.Enabled = data.Enabled
t.conf.ServerName = data.ServerName
t.conf.ForceHTTPS = data.ForceHTTPS
t.conf.PortHTTPS = data.PortHTTPS
t.conf.PortDNSOverTLS = data.PortDNSOverTLS
t.conf.PortDNSOverQUIC = data.PortDNSOverQUIC
t.conf.CertificateChain = data.CertificateChain
t.conf.CertificatePath = data.CertificatePath
t.conf.CertificateChainData = data.CertificateChainData
t.conf.PrivateKey = data.PrivateKey
t.conf.PrivateKeyPath = data.PrivateKeyPath
t.conf.PrivateKeyData = data.PrivateKeyData
t.status = status
t.confLock.Unlock()
t.setCertFileTime() t.setCertFileTime()
onConfigModified() onConfigModified()
err = reconfigureDNSServer() err = reconfigureDNSServer()
if err != nil { if err != nil {
httpError(w, http.StatusInternalServerError, "%s", err) httpError(w, http.StatusInternalServerError, "%s", err)
return return
} }
data2 := tlsConfig{ data2 := tlsConfig{
tlsConfigSettings: data, tlsConfigSettings: data,
tlsConfigStatus: t.status, tlsConfigStatus: t.status,
} }
marshalTLS(w, data2) marshalTLS(w, data2)
if f, ok := w.(http.Flusher); ok { if f, ok := w.(http.Flusher); ok {
f.Flush() f.Flush()