diff --git a/socketserver/certreloader/reloader.go b/socketserver/certreloader/reloader.go new file mode 100644 index 00000000..4847ee57 --- /dev/null +++ b/socketserver/certreloader/reloader.go @@ -0,0 +1,72 @@ +// Copyright 2016 Michael Stapelberg, BSD-3 +// +// https://stackoverflow.com/a/40883377/1210278 +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