From 7b0cdc4baafd3b960583225baa7c9528ba7bd079 Mon Sep 17 00:00:00 2001 From: Kane York Date: Tue, 26 Sep 2017 13:04:39 -0700 Subject: [PATCH 1/3] Add certificate reloader on SIGHUP --- socketserver/certreloader/reloader.go | 69 +++++++++++++++++++ .../cmd/ffzsocketserver/socketserver.go | 17 ++++- socketserver/server/types.go | 5 -- 3 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 socketserver/certreloader/reloader.go diff --git a/socketserver/certreloader/reloader.go b/socketserver/certreloader/reloader.go new file mode 100644 index 00000000..456929f6 --- /dev/null +++ b/socketserver/certreloader/reloader.go @@ -0,0 +1,69 @@ +package certreloader + +import ( + "crypto/tls" + "log" + "os" + "os/signal" + "sync" +) + +type CertSource struct { + certMu sync.RWMutex + cert *tls.Certificate + certPath string + keyPath string +} + +// Create a CertSource +func New(certPath, keyPath string) (*CertSource, error) { + result := &CertSource{ + certPath: certPath, + keyPath: keyPath, + } + cert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return nil, err + } + result.cert = &cert + return result, nil +} + +// Automatically reload certificate on the provided signal +func (kpr *CertSource) AutoCheck(sig os.Signal) { + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, sig) + for range c { + log.Printf("Received %v, reloading TLS certificate and key from %q and %q", sig, kpr.certPath, kpr.keyPath) + if err := kpr.maybeReload(); err != nil { + log.Printf("Keeping old TLS certificate because the new one could not be loaded: %v", err) + } + } + }() +} + +// Check() can be called manually to reload the certificate +func (kpr *CertSource) Check() error { + return kpr.maybeReload() +} + +func (kpr *CertSource) maybeReload() error { + newCert, err := tls.LoadX509KeyPair(kpr.certPath, kpr.keyPath) + if err != nil { + return err + } + kpr.certMu.Lock() + defer kpr.certMu.Unlock() + kpr.cert = &newCert + return nil +} + +// Returns a tls.Config.GetCertificate function. +func (kpr *CertSource) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) { + return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { + kpr.certMu.RLock() + defer kpr.certMu.RUnlock() + return kpr.cert, nil + } +} diff --git a/socketserver/cmd/ffzsocketserver/socketserver.go b/socketserver/cmd/ffzsocketserver/socketserver.go index cc28b327..1f7f2db0 100644 --- a/socketserver/cmd/ffzsocketserver/socketserver.go +++ b/socketserver/cmd/ffzsocketserver/socketserver.go @@ -1,7 +1,10 @@ package main // import "github.com/FrankerFaceZ/FrankerFaceZ/socketserver/cmd/ffzsocketserver" +import _ "net/http/pprof" + import ( "context" + "crypto/tls" "encoding/json" "flag" "fmt" @@ -14,11 +17,10 @@ import ( "syscall" "time" + "github.com/FrankerFaceZ/FrankerFaceZ/socketserver/certreloader" "github.com/FrankerFaceZ/FrankerFaceZ/socketserver/server" ) -import _ "net/http/pprof" - var configFilename = flag.String("config", "config.json", "Configuration file, including the keypairs for the NaCl crypto library, for communicating with the backend.") var flagGenerateKeys = flag.Bool("genkeys", false, "Generate NaCl keys instead of serving requests.\nArguments: [int serverId] [base64 backendPublic]\nThe backend public key can either be specified in base64 on the command line, or put in the json file later.") @@ -69,12 +71,21 @@ func main() { signal.Notify(stopSig, syscall.SIGTERM) if conf.UseSSL { + reloader, err := certreloader.New(conf.SSLCertificateFile, conf.SSLKeyFile) + if err != nil { + log.Fatalln("Could not load TLS certificate:", err) + } + reloader.AutoCheck(syscall.SIGHUP) + server1 = &http.Server{ Addr: conf.SSLListenAddr, Handler: http.DefaultServeMux, + TLSConfig: &tls.Config{ + GetCertificate: reloader.GetCertificateFunc(), + }, } go func() { - if err := server1.ListenAndServeTLS(conf.SSLCertificateFile, conf.SSLKeyFile); err != nil { + if err := server1.ListenAndServeTLS("", ""); err != nil { log.Println("ListenAndServeTLS:", err) stopSig <- os.Interrupt } diff --git a/socketserver/server/types.go b/socketserver/server/types.go index 3de13127..1097a159 100644 --- a/socketserver/server/types.go +++ b/socketserver/server/types.go @@ -35,11 +35,6 @@ type ConfigFile struct { // Path to key file. SSLKeyFile string - UseESLogStashing bool - ESServer string - ESIndexPrefix string - ESHostName string - // Nacl keys OurPrivateKey []byte OurPublicKey []byte From 206e36a5216ae5bb33385d9a24884dde825b4eb8 Mon Sep 17 00:00:00 2001 From: Kane York Date: Tue, 26 Sep 2017 13:04:54 -0700 Subject: [PATCH 2/3] Fix compile --- socketserver/server/subscriptions.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/socketserver/server/subscriptions.go b/socketserver/server/subscriptions.go index 007aea03..136fa603 100644 --- a/socketserver/server/subscriptions.go +++ b/socketserver/server/subscriptions.go @@ -156,8 +156,11 @@ func UnsubscribeSingleChat(client *ClientInfo, channelName string) { // - write lock to SubscriptionInfos // - write lock to ClientInfo func UnsubscribeAll(client *ClientInfo) { - if StopAcceptingConnections { - return // no need to remove from a high-contention list when the server is closing + select { + case <-StopAcceptingConnectionsCh: + // Skip high-contention client removal operations while server shutting down + return + default: } GlobalSubscriptionLock.Lock() From d135d0cde947645f58c4575efacec494f9329eac Mon Sep 17 00:00:00 2001 From: Kane York Date: Tue, 26 Sep 2017 13:06:20 -0700 Subject: [PATCH 3/3] add copyright notice --- socketserver/certreloader/reloader.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/socketserver/certreloader/reloader.go b/socketserver/certreloader/reloader.go index 456929f6..4847ee57 100644 --- a/socketserver/certreloader/reloader.go +++ b/socketserver/certreloader/reloader.go @@ -1,3 +1,6 @@ +// Copyright 2016 Michael Stapelberg, BSD-3 +// +// https://stackoverflow.com/a/40883377/1210278 package certreloader import (