package hy import ( "crypto/tls" "sync" "github.com/fsnotify/fsnotify" "github.com/sirupsen/logrus" ) type keypairLoader struct { certMu sync.RWMutex cert *tls.Certificate certPath string keyPath string } func newKeypairLoader(certPath, keyPath string) (*keypairLoader, error) { loader := &keypairLoader{ certPath: certPath, keyPath: keyPath, } cert, err := tls.LoadX509KeyPair(certPath, keyPath) if err != nil { return nil, err } loader.cert = &cert watcher, err := fsnotify.NewWatcher() if err != nil { return nil, err } go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } switch event.Op { case fsnotify.Create, fsnotify.Write, fsnotify.Rename, fsnotify.Chmod: logrus.WithFields(logrus.Fields{ "file": event.Name, }).Info("Keypair change detected, reloading...") if err := loader.load(); err != nil { logrus.WithFields(logrus.Fields{ "error": err, }).Error("Failed to reload keypair") } else { logrus.Info("Keypair successfully reloaded") } case fsnotify.Remove: _ = watcher.Add(event.Name) // Workaround for vim // https://github.com/fsnotify/fsnotify/issues/92 } case err, ok := <-watcher.Errors: if !ok { return } logrus.WithFields(logrus.Fields{ "error": err, }).Error("Failed to watch keypair files for changes") } } }() err = watcher.Add(certPath) if err != nil { _ = watcher.Close() return nil, err } err = watcher.Add(keyPath) if err != nil { _ = watcher.Close() return nil, err } return loader, nil } func (kpr *keypairLoader) load() error { cert, err := tls.LoadX509KeyPair(kpr.certPath, kpr.keyPath) if err != nil { return err } kpr.certMu.Lock() kpr.cert = &cert kpr.certMu.Unlock() return nil } func (kpr *keypairLoader) 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 } }