diff --git a/cmd/kploader.go b/cmd/kploader.go index 97b006b..b71b3d0 100644 --- a/cmd/kploader.go +++ b/cmd/kploader.go @@ -2,13 +2,9 @@ package main import ( "crypto/tls" + "github.com/fsnotify/fsnotify" "github.com/sirupsen/logrus" "sync" - "time" -) - -const ( - keypairReloadInterval = 10 * time.Minute ) type keypairLoader struct { @@ -19,7 +15,7 @@ type keypairLoader struct { } func newKeypairLoader(certPath, keyPath string) (*keypairLoader, error) { - result := &keypairLoader{ + loader := &keypairLoader{ certPath: certPath, keyPath: keyPath, } @@ -27,25 +23,66 @@ func newKeypairLoader(certPath, keyPath string) (*keypairLoader, error) { if err != nil { return nil, err } - result.cert = &cert + loader.cert = &cert + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, err + } go func() { for { - time.Sleep(keypairReloadInterval) - cert, err := tls.LoadX509KeyPair(certPath, keyPath) - if err != nil { + 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, - "cert": certPath, - "key": keyPath, - }).Warning("Failed to reload keypair") - continue + }).Error("Failed to watch keypair files for changes") } - result.certMu.Lock() - result.cert = &cert - result.certMu.Unlock() } }() - return result, nil + 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) { diff --git a/go.mod b/go.mod index bafc83a..0008a8e 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e github.com/eycorsican/go-tun2socks v1.16.11 + github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/google/gopacket v1.1.19 github.com/hashicorp/golang-lru v0.5.4 github.com/lucas-clemente/quic-go v0.24.0 diff --git a/go.sum b/go.sum index 968a151..56f7ee4 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,8 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -366,6 +368,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=