From db30f27c8f8110f75eae01bd54f5f6d62f523419 Mon Sep 17 00:00:00 2001 From: Simon Zolin Date: Wed, 19 Feb 2020 15:28:06 +0300 Subject: [PATCH] * TLS is now a separate module (logically) --- AGHTechDoc.md | 1 - home/config.go | 60 ++------ home/control.go | 35 +---- home/control_install.go | 31 ++-- home/dns.go | 14 +- home/home.go | 46 +++++- home/{control_tls.go => tls.go} | 264 ++++++++++++++++++++++++-------- 7 files changed, 267 insertions(+), 184 deletions(-) rename home/{control_tls.go => tls.go} (59%) diff --git a/AGHTechDoc.md b/AGHTechDoc.md index c686ecf7..5a8beee8 100644 --- a/AGHTechDoc.md +++ b/AGHTechDoc.md @@ -71,7 +71,6 @@ Contents: ![](doc/agh-arch.png) - ## First startup The first application startup is detected when there's no .yaml configuration file. diff --git a/home/config.go b/home/config.go index 1150c1f2..cb586131 100644 --- a/home/config.go +++ b/home/config.go @@ -50,8 +50,8 @@ type configuration struct { // An active session is automatically refreshed once a day. WebSessionTTLHours uint32 `yaml:"web_session_ttl"` - DNS dnsConfig `yaml:"dns"` - TLS tlsConfig `yaml:"tls"` + DNS dnsConfig `yaml:"dns"` + TLS tlsConfigSettings `yaml:"tls"` Filters []filter `yaml:"filters"` WhitelistFilters []filter `yaml:"whitelist_filters"` @@ -101,33 +101,6 @@ type tlsConfigSettings struct { dnsforward.TLSConfig `yaml:",inline" json:",inline"` } -// field ordering is not important -- these are for API and are recalculated on each run -type tlsConfigStatus struct { - ValidCert bool `yaml:"-" json:"valid_cert"` // ValidCert is true if the specified certificates chain is a valid chain of X509 certificates - ValidChain bool `yaml:"-" json:"valid_chain"` // ValidChain is true if the specified certificates chain is verified and issued by a known CA - Subject string `yaml:"-" json:"subject,omitempty"` // Subject is the subject of the first certificate in the chain - Issuer string `yaml:"-" json:"issuer,omitempty"` // Issuer is the issuer of the first certificate in the chain - NotBefore time.Time `yaml:"-" json:"not_before,omitempty"` // NotBefore is the NotBefore field of the first certificate in the chain - NotAfter time.Time `yaml:"-" json:"not_after,omitempty"` // NotAfter is the NotAfter field of the first certificate in the chain - DNSNames []string `yaml:"-" json:"dns_names"` // DNSNames is the value of SubjectAltNames field of the first certificate in the chain - - // key status - ValidKey bool `yaml:"-" json:"valid_key"` // ValidKey is true if the key is a valid private key - KeyType string `yaml:"-" json:"key_type,omitempty"` // KeyType is one of RSA or ECDSA - - // is usable? set by validator - ValidPair bool `yaml:"-" json:"valid_pair"` // ValidPair is true if both certificate and private key are correct - - // warnings - WarningValidation string `yaml:"-" json:"warning_validation,omitempty"` // WarningValidation is a validation warning message with the issue description -} - -// field ordering is important -- yaml fields will mirror ordering from here -type tlsConfig struct { - tlsConfigSettings `yaml:",inline" json:",inline"` - tlsConfigStatus `yaml:"-" json:",inline"` -} - // initialize to default values, will be changed later when reading config or parsing command line var config = configuration{ BindPort: 3000, @@ -147,11 +120,9 @@ var config = configuration{ FilteringEnabled: true, // whether or not use filter lists FiltersUpdateIntervalHours: 24, }, - TLS: tlsConfig{ - tlsConfigSettings: tlsConfigSettings{ - PortHTTPS: 443, - PortDNSOverTLS: 853, // needs to be passed through to dnsproxy - }, + TLS: tlsConfigSettings{ + PortHTTPS: 443, + PortDNSOverTLS: 853, // needs to be passed through to dnsproxy }, DHCP: dhcpd.ServerConfig{ LeaseDuration: 86400, @@ -225,12 +196,6 @@ func parseConfig() error { config.DNS.FiltersUpdateIntervalHours = 24 } - status := tlsConfigStatus{} - if !tlsLoadConfig(&config.TLS, &status) { - log.Error("%s", status.WarningValidation) - return err - } - return nil } @@ -259,6 +224,11 @@ func (c *configuration) write() error { if Context.auth != nil { config.Users = Context.auth.GetUsers() } + if Context.tls != nil { + tlsConf := tlsConfigSettings{} + Context.tls.WriteDiskConfig(&tlsConf) + config.TLS = tlsConf + } if Context.stats != nil { sdc := stats.DiskConfig{} @@ -308,13 +278,3 @@ func (c *configuration) write() error { return nil } - -func writeAllConfigs() error { - err := config.write() - if err != nil { - log.Error("Couldn't write config: %s", err) - return err - } - - return nil -} diff --git a/home/control.go b/home/control.go index 289cfd3c..4da42ca9 100644 --- a/home/control.go +++ b/home/control.go @@ -26,9 +26,6 @@ func returnOK(w http.ResponseWriter) { } } -func httpOK(r *http.Request, w http.ResponseWriter) { -} - func httpError(w http.ResponseWriter, code int, format string, args ...interface{}) { text := fmt.Sprintf(format, args...) log.Info(text) @@ -38,15 +35,6 @@ func httpError(w http.ResponseWriter, code int, format string, args ...interface // --------------- // dns run control // --------------- -func writeAllConfigsAndReloadDNS() error { - err := writeAllConfigs() - if err != nil { - log.Error("Couldn't write all configs: %s", err) - return err - } - return reconfigureDNSServer() -} - func addDNSAddress(dnsAddresses *[]string, addr string) { if config.DNS.Port != 53 { addr = fmt.Sprintf("%s:%d", addr, config.DNS.Port) @@ -143,23 +131,6 @@ func handleGetProfile(w http.ResponseWriter, r *http.Request) { _, _ = w.Write(data) } -// -------------- -// DNS-over-HTTPS -// -------------- -func handleDOH(w http.ResponseWriter, r *http.Request) { - if !config.TLS.AllowUnencryptedDOH && r.TLS == nil { - httpError(w, http.StatusNotFound, "Not Found") - return - } - - if !isRunning() { - httpError(w, http.StatusInternalServerError, "DNS server is not running") - return - } - - Context.dnsServer.ServeHTTP(w, r) -} - // ------------------------ // registration of handlers // ------------------------ @@ -171,8 +142,6 @@ func registerControlHandlers() { httpRegister(http.MethodPost, "/control/update", handleUpdate) httpRegister("GET", "/control/profile", handleGetProfile) - - RegisterTLSHandlers() RegisterAuthHandlers() http.HandleFunc("/dns-query", postInstall(handleDOH)) @@ -265,7 +234,7 @@ func postInstall(handler func(http.ResponseWriter, *http.Request)) func(http.Res } // enforce https? - if config.TLS.ForceHTTPS && r.TLS == nil && config.TLS.Enabled && config.TLS.PortHTTPS != 0 && Context.web.httpsServer.server != nil { + if r.TLS == nil && Context.web.forceHTTPS && Context.web.httpsServer.server != nil { // yes, and we want host from host:port host, _, err := net.SplitHostPort(r.Host) if err != nil { @@ -275,7 +244,7 @@ func postInstall(handler func(http.ResponseWriter, *http.Request)) func(http.Res // construct new URL to redirect to newURL := url.URL{ Scheme: "https", - Host: net.JoinHostPort(host, strconv.Itoa(config.TLS.PortHTTPS)), + Host: net.JoinHostPort(host, strconv.Itoa(Context.web.portHTTPS)), Path: r.URL.Path, RawQuery: r.URL.RawQuery, } diff --git a/home/control_install.go b/home/control_install.go index ab6f0ca5..e3ada191 100644 --- a/home/control_install.go +++ b/home/control_install.go @@ -35,7 +35,7 @@ type netInterfaceJSON struct { } // Get initial installation settings -func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { +func (web *Web) handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { data := firstRunData{} data.WebPort = 80 data.DNSPort = 53 @@ -93,7 +93,7 @@ type checkConfigResp struct { } // Check if ports are available, respond with results -func handleInstallCheckConfig(w http.ResponseWriter, r *http.Request) { +func (web *Web) handleInstallCheckConfig(w http.ResponseWriter, r *http.Request) { reqData := checkConfigReq{} respData := checkConfigResp{} err := json.NewDecoder(r.Body).Decode(&reqData) @@ -275,7 +275,7 @@ func copyInstallSettings(dst *configuration, src *configuration) { } // Apply new configuration, start DNS server, restart Web server -func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { +func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) { newSettings := applyConfigReq{} err := json.NewDecoder(r.Body).Decode(&newSettings) if err != nil { @@ -325,22 +325,11 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { config.DNS.BindHost = newSettings.DNS.IP config.DNS.Port = newSettings.DNS.Port - err = initDNSServer() - var err2 error - if err == nil { - err2 = startDNSServer() - if err2 != nil { - closeDNSServer() - } - } - if err != nil || err2 != nil { + err = StartMods() + if err != nil { Context.firstRun = true copyInstallSettings(&config, &curConfig) - if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't initialize DNS server: %s", err) - } else { - httpError(w, http.StatusInternalServerError, "Couldn't start DNS server: %s", err2) - } + httpError(w, http.StatusInternalServerError, "%s", err) return } @@ -369,8 +358,8 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { returnOK(w) } -func registerInstallHandlers() { - http.HandleFunc("/control/install/get_addresses", preInstall(ensureGET(handleInstallGetAddresses))) - http.HandleFunc("/control/install/check_config", preInstall(ensurePOST(handleInstallCheckConfig))) - http.HandleFunc("/control/install/configure", preInstall(ensurePOST(handleInstallConfigure))) +func (web *Web) registerInstallHandlers() { + http.HandleFunc("/control/install/get_addresses", preInstall(ensureGET(web.handleInstallGetAddresses))) + http.HandleFunc("/control/install/check_config", preInstall(ensurePOST(web.handleInstallCheckConfig))) + http.HandleFunc("/control/install/configure", preInstall(ensurePOST(web.handleInstallConfigure))) } diff --git a/home/dns.go b/home/dns.go index 81eb4fd1..3b415a8e 100644 --- a/home/dns.go +++ b/home/dns.go @@ -156,11 +156,17 @@ func generateServerConfig() dnsforward.ServerConfig { OnDNSRequest: onDNSRequest, } - if config.TLS.Enabled { - newconfig.TLSConfig = config.TLS.TLSConfig - if config.TLS.PortDNSOverTLS != 0 { - newconfig.TLSListenAddr = &net.TCPAddr{IP: net.ParseIP(config.DNS.BindHost), Port: config.TLS.PortDNSOverTLS} + tlsConf := tlsConfigSettings{} + Context.tls.WriteDiskConfig(&tlsConf) + if tlsConf.Enabled { + newconfig.TLSConfig = tlsConf.TLSConfig + if tlsConf.PortDNSOverTLS != 0 { + newconfig.TLSListenAddr = &net.TCPAddr{ + IP: net.ParseIP(config.DNS.BindHost), + Port: tlsConf.PortDNSOverTLS, + } } + newconfig.TLSAllowUnencryptedDOH = tlsConf.AllowUnencryptedDOH } newconfig.TLSv12Roots = Context.tlsRoots diff --git a/home/home.go b/home/home.go index dfa0118c..17d05d41 100644 --- a/home/home.go +++ b/home/home.go @@ -65,8 +65,9 @@ type homeContext struct { dnsFilter *dnsfilter.Dnsfilter // DNS filtering module dhcpServer *dhcpd.Server // DHCP module auth *Auth // HTTP authentication module - filters Filtering - web *Web + filters Filtering // DNS filtering module + web *Web // Web (HTTP, HTTPS) module + tls *TLSMod // TLS module // Runtime properties // -- @@ -119,6 +120,7 @@ func Main(version string, channel string, armVer string) { switch sig { case syscall.SIGHUP: Context.clients.Reload() + Context.tls.Reload() default: cleanup() @@ -247,11 +249,15 @@ func run(args options) { } config.Users = nil + Context.tls = tlsCreate(config.TLS) + if Context.tls == nil { + log.Fatalf("Can't initialize TLS module") + } + webConf := WebConfig{ firstRun: Context.firstRun, BindHost: config.BindHost, BindPort: config.BindPort, - TLS: config.TLS, } Context.web = CreateWeb(&webConf) if Context.web == nil { @@ -263,6 +269,8 @@ func run(args options) { if err != nil { log.Fatalf("%s", err) } + Context.tls.Start() + go func() { err := startDNSServer() if err != nil { @@ -282,6 +290,23 @@ func run(args options) { select {} } +// StartMods - initialize and start DNS after installation +func StartMods() error { + err := initDNSServer() + if err != nil { + return err + } + + Context.tls.Start() + + err = startDNSServer() + if err != nil { + closeDNSServer() + return err + } + return nil +} + // Check if the current user has root (administrator) rights // and if not, ask and try to run as root func requireAdminRights() { @@ -408,6 +433,11 @@ func cleanup() { if err != nil { log.Error("Couldn't stop DHCP server: %s", err) } + + if Context.tls != nil { + Context.tls.Close() + Context.tls = nil + } } // This function is called before application exits @@ -528,11 +558,13 @@ func loadOptions() options { func printHTTPAddresses(proto string) { var address string - if proto == "https" && config.TLS.ServerName != "" { - if config.TLS.PortHTTPS == 443 { - log.Printf("Go to https://%s", config.TLS.ServerName) + tlsConf := tlsConfigSettings{} + Context.tls.WriteDiskConfig(&tlsConf) + 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", config.TLS.ServerName, config.TLS.PortHTTPS) + log.Printf("Go to https://%s:%d", tlsConf.ServerName, tlsConf.PortHTTPS) } } else if config.BindHost == "0.0.0.0" { log.Println("AdGuard Home is available on the following addresses:") diff --git a/home/control_tls.go b/home/tls.go similarity index 59% rename from home/control_tls.go rename to home/tls.go index b048800d..317e7dd8 100644 --- a/home/control_tls.go +++ b/home/tls.go @@ -1,9 +1,6 @@ -// Control: TLS configuring handlers - package home import ( - "context" "crypto" "crypto/ecdsa" "crypto/rsa" @@ -16,18 +13,125 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "reflect" "strings" + "sync" "time" - "github.com/AdguardTeam/AdGuardHome/util" - "github.com/AdguardTeam/golibs/log" "github.com/joomcode/errorx" ) +var tlsWebHandlersRegistered = false + +// TLSMod - TLS module object +type TLSMod struct { + certLastMod time.Time // last modification time of the certificate file + conf tlsConfigSettings + confLock sync.Mutex + status tlsConfigStatus +} + +// Create TLS module +func tlsCreate(conf tlsConfigSettings) *TLSMod { + t := &TLSMod{} + t.conf = conf + if t.conf.Enabled { + if !t.load() { + return nil + } + t.setCertFileTime() + } + return t +} + +func (t *TLSMod) load() bool { + if !tlsLoadConfig(&t.conf, &t.status) { + return false + } + + // 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) + return false + } + t.status = data + return true +} + +// Close - close module +func (t *TLSMod) Close() { +} + +// WriteDiskConfig - write config +func (t *TLSMod) WriteDiskConfig(conf *tlsConfigSettings) { + t.confLock.Lock() + *conf = t.conf + t.confLock.Unlock() +} + +func (t *TLSMod) setCertFileTime() { + if len(t.conf.CertificatePath) == 0 { + return + } + fi, err := os.Stat(t.conf.CertificatePath) + if err != nil { + log.Error("TLS: %s", err) + return + } + t.certLastMod = fi.ModTime().UTC() +} + +// Start - start the module +func (t *TLSMod) Start() { + if !tlsWebHandlersRegistered { + tlsWebHandlersRegistered = true + t.registerWebHandlers() + } + + t.confLock.Lock() + tlsConf := t.conf + t.confLock.Unlock() + Context.web.TLSConfigChanged(tlsConf) +} + +// Reload - reload certificate file +func (t *TLSMod) Reload() { + t.confLock.Lock() + tlsConf := t.conf + t.confLock.Unlock() + + if !tlsConf.Enabled || len(tlsConf.CertificatePath) == 0 { + return + } + fi, err := os.Stat(tlsConf.CertificatePath) + if err != nil { + log.Error("TLS: %s", err) + return + } + if fi.ModTime().UTC().Equal(t.certLastMod) { + log.Debug("TLS: certificate file isn't modified") + return + } + log.Debug("TLS: certificate file is modified") + + t.confLock.Lock() + r := t.load() + t.confLock.Unlock() + if !r { + return + } + + t.certLastMod = fi.ModTime().UTC() + + _ = reconfigureDNSServer() + Context.web.TLSConfigChanged(tlsConf) +} + // Set certificate and private key data -func tlsLoadConfig(tls *tlsConfig, status *tlsConfigStatus) bool { +func tlsLoadConfig(tls *tlsConfigSettings, status *tlsConfigStatus) bool { tls.CertificateChainData = []byte(tls.CertificateChain) tls.PrivateKeyData = []byte(tls.PrivateKey) @@ -61,98 +165,115 @@ func tlsLoadConfig(tls *tlsConfig, status *tlsConfigStatus) bool { return true } -// RegisterTLSHandlers registers HTTP handlers for TLS configuration -func RegisterTLSHandlers() { - httpRegister(http.MethodGet, "/control/tls/status", handleTLSStatus) - httpRegister(http.MethodPost, "/control/tls/configure", handleTLSConfigure) - httpRegister(http.MethodPost, "/control/tls/validate", handleTLSValidate) +type tlsConfigStatus struct { + ValidCert bool `json:"valid_cert"` // ValidCert is true if the specified certificates chain is a valid chain of X509 certificates + ValidChain bool `json:"valid_chain"` // ValidChain is true if the specified certificates chain is verified and issued by a known CA + Subject string `json:"subject,omitempty"` // Subject is the subject of the first certificate in the chain + Issuer string `json:"issuer,omitempty"` // Issuer is the issuer of the first certificate in the chain + NotBefore time.Time `json:"not_before,omitempty"` // NotBefore is the NotBefore field of the first certificate in the chain + NotAfter time.Time `json:"not_after,omitempty"` // NotAfter is the NotAfter field of the first certificate in the chain + DNSNames []string `json:"dns_names"` // DNSNames is the value of SubjectAltNames field of the first certificate in the chain + + // key status + ValidKey bool `json:"valid_key"` // ValidKey is true if the key is a valid private key + KeyType string `json:"key_type,omitempty"` // KeyType is one of RSA or ECDSA + + // is usable? set by validator + ValidPair bool `json:"valid_pair"` // ValidPair is true if both certificate and private key are correct + + // warnings + WarningValidation string `json:"warning_validation,omitempty"` // WarningValidation is a validation warning message with the issue description } -func handleTLSStatus(w http.ResponseWriter, r *http.Request) { - marshalTLS(w, config.TLS) +// field ordering is important -- yaml fields will mirror ordering from here +type tlsConfig struct { + tlsConfigSettings `json:",inline"` + tlsConfigStatus `json:",inline"` } -func handleTLSValidate(w http.ResponseWriter, r *http.Request) { - data, err := unmarshalTLS(r) - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) - return +func (t *TLSMod) handleTLSStatus(w http.ResponseWriter, r *http.Request) { + t.confLock.Lock() + data := tlsConfig{ + tlsConfigSettings: t.conf, + tlsConfigStatus: t.status, } - - // check if port is available - // BUT: if we are already using this port, no need - alreadyRunning := false - if Context.web.httpsServer.server != nil { - alreadyRunning = true - } - if !alreadyRunning { - err = util.CheckPortAvailable(config.BindHost, data.PortHTTPS) - if err != nil { - httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) - return - } - } - - status := tlsConfigStatus{} - if tlsLoadConfig(&data, &status) { - status = validateCertificates(string(data.CertificateChainData), string(data.PrivateKeyData), data.ServerName) - } - data.tlsConfigStatus = status - + t.confLock.Unlock() marshalTLS(w, data) } -func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { +func (t *TLSMod) handleTLSValidate(w http.ResponseWriter, r *http.Request) { + setts, err := unmarshalTLS(r) + if err != nil { + httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) + return + } + + if !WebCheckPortAvailable(setts.PortHTTPS) { + httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", setts.PortHTTPS) + return + } + + status := tlsConfigStatus{} + if tlsLoadConfig(&setts, &status) { + status = validateCertificates(string(setts.CertificateChainData), string(setts.PrivateKeyData), setts.ServerName) + } + + data := tlsConfig{ + tlsConfigSettings: setts, + tlsConfigStatus: status, + } + marshalTLS(w, data) +} + +func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) { data, err := unmarshalTLS(r) if err != nil { httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) return } - // check if port is available - // BUT: if we are already using this port, no need - alreadyRunning := false - if Context.web.httpsServer.server != nil { - alreadyRunning = true - } - if !alreadyRunning { - err = util.CheckPortAvailable(config.BindHost, data.PortHTTPS) - if err != nil { - httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) - return - } + if !WebCheckPortAvailable(data.PortHTTPS) { + httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) + return } status := tlsConfigStatus{} if !tlsLoadConfig(&data, &status) { - data.tlsConfigStatus = status - marshalTLS(w, data) + data2 := tlsConfig{ + tlsConfigSettings: data, + tlsConfigStatus: t.status, + } + marshalTLS(w, data2) return } - data.tlsConfigStatus = validateCertificates(string(data.CertificateChainData), string(data.PrivateKeyData), data.ServerName) + status = validateCertificates(string(data.CertificateChainData), string(data.PrivateKeyData), data.ServerName) restartHTTPS := false - if !reflect.DeepEqual(config.TLS.tlsConfigSettings, data.tlsConfigSettings) { + t.confLock.Lock() + if !reflect.DeepEqual(t.conf, data) { log.Printf("tls config settings have changed, will restart HTTPS server") restartHTTPS = true } - config.TLS = data - err = writeAllConfigsAndReloadDNS() + t.conf = data + t.status = status + t.confLock.Unlock() + t.setCertFileTime() + onConfigModified() + err = reconfigureDNSServer() if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) + httpError(w, http.StatusInternalServerError, "%s", err) return } - marshalTLS(w, data) + data2 := tlsConfig{ + tlsConfigSettings: data, + tlsConfigStatus: t.status, + } + marshalTLS(w, data2) // this needs to be done in a goroutine because Shutdown() is a blocking call, and it will block // until all requests are finished, and _we_ are inside a request right now, so it will block indefinitely if restartHTTPS { go func() { time.Sleep(time.Second) // TODO: could not find a way to reliably know that data was fully sent to client by https server, so we wait a bit to let response through before closing the server - Context.web.httpsServer.cond.L.Lock() - Context.web.httpsServer.cond.Broadcast() - if Context.web.httpsServer.server != nil { - Context.web.httpsServer.server.Shutdown(context.TODO()) - } - Context.web.httpsServer.cond.L.Unlock() + Context.web.TLSConfigChanged(data) }() } } @@ -337,8 +458,8 @@ func parsePrivateKey(der []byte) (crypto.PrivateKey, string, error) { } // unmarshalTLS handles base64-encoded certificates transparently -func unmarshalTLS(r *http.Request) (tlsConfig, error) { - data := tlsConfig{} +func unmarshalTLS(r *http.Request) (tlsConfigSettings, error) { + data := tlsConfigSettings{} err := json.NewDecoder(r.Body).Decode(&data) if err != nil { return data, errorx.Decorate(err, "Failed to parse new TLS config json") @@ -389,3 +510,10 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { return } } + +// registerWebHandlers registers HTTP handlers for TLS configuration +func (t *TLSMod) registerWebHandlers() { + httpRegister("GET", "/control/tls/status", t.handleTLSStatus) + httpRegister("POST", "/control/tls/configure", t.handleTLSConfigure) + httpRegister("POST", "/control/tls/validate", t.handleTLSValidate) +}