From 6a20e7411b1904b3ba8d8db0a802016e0e7fe7c6 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Tue, 4 Jan 2022 16:07:41 +0100 Subject: [PATCH] sdk: add a logger interface we are now ready to make the sdk a separate module Signed-off-by: Nicola Murino --- common/protocol_test.go | 1 - config/config_test.go | 1 - dataprovider/dataprovider.go | 3 +- ftpd/ftpd_test.go | 1 - go.sum | 4 -- httpd/httpd_test.go | 1 - httpd/server.go | 3 +- kms/kms.go | 2 - logger/logger.go | 61 ++++++++++++++++++------- main.go | 1 - {kms => sdk/kms}/builtin.go | 28 ++++++------ sdk/kms/kms.go | 12 ++--- {kms => sdk/kms}/local.go | 26 +++++------ {logger => sdk/logger}/hclog_adapter.go | 34 ++++++++------ sdk/logger/logger.go | 56 +++++++++++++++++++++++ sdk/plugin/auth.go | 2 +- sdk/plugin/kms.go | 4 +- sdk/plugin/metadata.go | 2 +- sdk/plugin/notifier.go | 4 +- sdk/plugin/plugin.go | 46 +++++++++---------- sdk/plugin/searcher.go | 2 +- sdk/plugin/util.go | 6 +-- sdk/user.go | 2 +- sdk/util/util.go | 31 +++++++++++++ sftpd/sftpd_test.go | 1 - telemetry/telemetry.go | 3 +- tests/eventsearcher/go.sum | 4 +- util/util.go | 14 ------ webdavd/server.go | 3 +- webdavd/webdavd_test.go | 1 - 30 files changed, 225 insertions(+), 134 deletions(-) delete mode 100644 kms/kms.go rename {kms => sdk/kms}/builtin.go (78%) rename {kms => sdk/kms}/local.go (80%) rename {logger => sdk/logger}/hclog_adapter.go (79%) create mode 100644 sdk/logger/logger.go create mode 100644 sdk/util/util.go diff --git a/common/protocol_test.go b/common/protocol_test.go index 4c6ea296..1775aff1 100644 --- a/common/protocol_test.go +++ b/common/protocol_test.go @@ -34,7 +34,6 @@ import ( "github.com/drakkan/sftpgo/v2/dataprovider" "github.com/drakkan/sftpgo/v2/httpclient" "github.com/drakkan/sftpgo/v2/httpdtest" - _ "github.com/drakkan/sftpgo/v2/kms" "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/mfa" "github.com/drakkan/sftpgo/v2/sdk" diff --git a/config/config_test.go b/config/config_test.go index 44fca8da..a6313c2c 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -17,7 +17,6 @@ import ( "github.com/drakkan/sftpgo/v2/ftpd" "github.com/drakkan/sftpgo/v2/httpclient" "github.com/drakkan/sftpgo/v2/httpd" - _ "github.com/drakkan/sftpgo/v2/kms" "github.com/drakkan/sftpgo/v2/mfa" "github.com/drakkan/sftpgo/v2/sdk/kms" "github.com/drakkan/sftpgo/v2/sdk/plugin" diff --git a/dataprovider/dataprovider.go b/dataprovider/dataprovider.go index e8e003c0..5febc61a 100644 --- a/dataprovider/dataprovider.go +++ b/dataprovider/dataprovider.go @@ -52,6 +52,7 @@ import ( "github.com/drakkan/sftpgo/v2/sdk" "github.com/drakkan/sftpgo/v2/sdk/kms" "github.com/drakkan/sftpgo/v2/sdk/plugin" + sdkutil "github.com/drakkan/sftpgo/v2/sdk/util" "github.com/drakkan/sftpgo/v2/util" "github.com/drakkan/sftpgo/v2/vfs" ) @@ -2889,7 +2890,7 @@ func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, var tlsCert string if cert != nil { var err error - tlsCert, err = util.EncodeTLSCertToPem(cert) + tlsCert, err = sdkutil.EncodeTLSCertToPem(cert) if err != nil { return nil, err } diff --git a/ftpd/ftpd_test.go b/ftpd/ftpd_test.go index a98e5f0e..cd841803 100644 --- a/ftpd/ftpd_test.go +++ b/ftpd/ftpd_test.go @@ -32,7 +32,6 @@ import ( "github.com/drakkan/sftpgo/v2/dataprovider" "github.com/drakkan/sftpgo/v2/ftpd" "github.com/drakkan/sftpgo/v2/httpdtest" - _ "github.com/drakkan/sftpgo/v2/kms" "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/mfa" "github.com/drakkan/sftpgo/v2/sdk" diff --git a/go.sum b/go.sum index 9c247f5b..d0ae806b 100644 --- a/go.sum +++ b/go.sum @@ -140,8 +140,6 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.25 h1:BbdvHAi+t9LRiaYUyd53noq9jcaAcfzOhSVbKfr6Avs= -github.com/aws/aws-sdk-go v1.42.25/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= github.com/aws/aws-sdk-go v1.42.26 h1:3+GcxzyI+kvqoASDNeeLZfqGkvyNMrE9IDuErxPRtCA= github.com/aws/aws-sdk-go v1.42.26/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4= @@ -271,8 +269,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.8-0.20220103230436-7dbe9a0bd10f h1:6kLofhLkWj7lgCc+mvcVLnwhTzQYgL/yW/Y0e/JYwjg= github.com/go-chi/chi/v5 v5.0.8-0.20220103230436-7dbe9a0bd10f/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/jwtauth/v5 v5.0.2 h1:CSKtr+b6Jnfy5T27sMaiBPxaVE/bjnjS3ramFQ0526w= diff --git a/httpd/httpd_test.go b/httpd/httpd_test.go index d03328fd..8c5edbf3 100644 --- a/httpd/httpd_test.go +++ b/httpd/httpd_test.go @@ -45,7 +45,6 @@ import ( "github.com/drakkan/sftpgo/v2/httpclient" "github.com/drakkan/sftpgo/v2/httpd" "github.com/drakkan/sftpgo/v2/httpdtest" - _ "github.com/drakkan/sftpgo/v2/kms" "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/mfa" "github.com/drakkan/sftpgo/v2/sdk" diff --git a/httpd/server.go b/httpd/server.go index 337d1f08..0d8dca45 100644 --- a/httpd/server.go +++ b/httpd/server.go @@ -25,6 +25,7 @@ import ( "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/mfa" "github.com/drakkan/sftpgo/v2/sdk" + sdklogger "github.com/drakkan/sftpgo/v2/sdk/logger" "github.com/drakkan/sftpgo/v2/smtp" "github.com/drakkan/sftpgo/v2/util" "github.com/drakkan/sftpgo/v2/version" @@ -75,7 +76,7 @@ func (s *httpdServer) listenAndServe() error { WriteTimeout: 60 * time.Second, IdleTimeout: 60 * time.Second, MaxHeaderBytes: 1 << 16, // 64KB - ErrorLog: log.New(&logger.StdLoggerWrapper{Sender: logSender}, "", 0), + ErrorLog: log.New(&sdklogger.StdLoggerWrapper{Sender: logSender}, "", 0), } if certMgr != nil && s.binding.EnableHTTPS { config := &tls.Config{ diff --git a/kms/kms.go b/kms/kms.go deleted file mode 100644 index 39f18b05..00000000 --- a/kms/kms.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package kms provides built-in Key Management Services support -package kms diff --git a/logger/logger.go b/logger/logger.go index f0d92461..316d902e 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -16,8 +16,11 @@ import ( "time" ftpserverlog "github.com/fclairamb/go-log" + "github.com/hashicorp/go-hclog" "github.com/rs/zerolog" lumberjack "gopkg.in/natefinch/lumberjack.v2" + + sdklogger "github.com/drakkan/sftpgo/v2/sdk/logger" ) const ( @@ -41,22 +44,25 @@ var ( rollingLogger *lumberjack.Logger ) -// StdLoggerWrapper is a wrapper for standard logger compatibility -type StdLoggerWrapper struct { - Sender string +type logWrapper struct{} + +// LogWithKeyVals logs at the specified level for the specified sender adding the specified key vals +func (l *logWrapper) LogWithKeyVals(level hclog.Level, sender, msg string, args ...interface{}) { + LogWithKeyVals(level, sender, msg, args...) } -// Write implements the io.Writer interface. This is useful to set as a writer -// for the standard library log. -func (l *StdLoggerWrapper) Write(p []byte) (n int, err error) { - n = len(p) - if n > 0 && p[n-1] == '\n' { - // Trim CR added by stdlog. - p = p[0 : n-1] +// Log logs at the specified level for the specified sender +func (l *logWrapper) Log(level hclog.Level, sender, format string, v ...interface{}) { + switch level { + case hclog.Info: + Log(LevelInfo, sender, "", format, v...) + case hclog.Warn: + Log(LevelWarn, sender, "", format, v...) + case hclog.Error: + Log(LevelError, sender, "", format, v...) + default: + Log(LevelDebug, sender, "", format, v...) } - - Log(LevelError, l.Sender, "", string(p)) - return } // LeveledLogger is a logger that accepts a message string and a variadic number of key-value pairs @@ -176,6 +182,7 @@ func InitLogger(logFilePath string, logMaxSize int, logMaxBackups int, logMaxAge consoleLogger = zerolog.Nop() } logger = logger.Level(level) + sdklogger.SetLogger(&logWrapper{}) } // InitStdErrLogger configures the logger to write to stderr @@ -184,6 +191,7 @@ func InitStdErrLogger(level zerolog.Level) { output: os.Stderr, }).Level(level) consoleLogger = zerolog.Nop() + sdklogger.SetLogger(&logWrapper{}) } // DisableLogger disable the main logger. @@ -191,6 +199,7 @@ func InitStdErrLogger(level zerolog.Level) { func DisableLogger() { logger = zerolog.Nop() rollingLogger = nil + sdklogger.DisableLogger() } // EnableConsoleLogger enables the console logger @@ -210,6 +219,24 @@ func RotateLogFile() error { return errors.New("logging to file is disabled") } +// LogWithKeyVals logs at the specified level for the specified sender adding the specified key vals +func LogWithKeyVals(level hclog.Level, sender, msg string, args ...interface{}) { + var ev *zerolog.Event + switch level { + case hclog.Info: + ev = logger.Info() + case hclog.Warn: + ev = logger.Warn() + case hclog.Error: + ev = logger.Error() + default: + ev = logger.Debug() + } + ev.Timestamp().Str("sender", sender) + addKeysAndValues(ev, args...) + ev.Msg(msg) +} + // Log logs at the specified level for the specified sender func Log(level LogLevel, sender string, connectionID string, format string, v ...interface{}) { var ev *zerolog.Event @@ -231,22 +258,22 @@ func Log(level LogLevel, sender string, connectionID string, format string, v .. } // Debug logs at debug level for the specified sender -func Debug(sender string, connectionID string, format string, v ...interface{}) { +func Debug(sender, connectionID, format string, v ...interface{}) { Log(LevelDebug, sender, connectionID, format, v...) } // Info logs at info level for the specified sender -func Info(sender string, connectionID string, format string, v ...interface{}) { +func Info(sender, connectionID, format string, v ...interface{}) { Log(LevelInfo, sender, connectionID, format, v...) } // Warn logs at warn level for the specified sender -func Warn(sender string, connectionID string, format string, v ...interface{}) { +func Warn(sender, connectionID, format string, v ...interface{}) { Log(LevelWarn, sender, connectionID, format, v...) } // Error logs at error level for the specified sender -func Error(sender string, connectionID string, format string, v ...interface{}) { +func Error(sender, connectionID, format string, v ...interface{}) { Log(LevelError, sender, connectionID, format, v...) } diff --git a/main.go b/main.go index c7b7659a..4bdac6e5 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,6 @@ import ( "go.uber.org/automaxprocs/maxprocs" "github.com/drakkan/sftpgo/v2/cmd" - _ "github.com/drakkan/sftpgo/v2/kms" ) func main() { diff --git a/kms/builtin.go b/sdk/kms/builtin.go similarity index 78% rename from kms/builtin.go rename to sdk/kms/builtin.go index 4d900d64..bfb001e9 100644 --- a/kms/builtin.go +++ b/sdk/kms/builtin.go @@ -8,8 +8,6 @@ import ( "encoding/hex" "errors" "io" - - sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms" ) var ( @@ -17,14 +15,14 @@ var ( ) type builtinSecret struct { - sdkkms.BaseSecret + BaseSecret } func init() { - sdkkms.RegisterSecretProvider(sdkkms.SchemeBuiltin, sdkkms.SecretStatusAES256GCM, newBuiltinSecret) + RegisterSecretProvider(SchemeBuiltin, SecretStatusAES256GCM, newBuiltinSecret) } -func newBuiltinSecret(base sdkkms.BaseSecret, url, masterKey string) sdkkms.SecretProvider { +func newBuiltinSecret(base BaseSecret, url, masterKey string) SecretProvider { return &builtinSecret{ BaseSecret: base, } @@ -35,7 +33,7 @@ func (s *builtinSecret) Name() string { } func (s *builtinSecret) IsEncrypted() bool { - return s.Status == sdkkms.SecretStatusAES256GCM + return s.Status == SecretStatusAES256GCM } func (s *builtinSecret) deriveKey(key []byte) []byte { @@ -51,10 +49,10 @@ func (s *builtinSecret) deriveKey(key []byte) []byte { func (s *builtinSecret) Encrypt() error { if s.Payload == "" { - return sdkkms.ErrInvalidSecret + return ErrInvalidSecret } switch s.Status { - case sdkkms.SecretStatusPlain: + case SecretStatusPlain: key := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, key); err != nil { return err @@ -78,16 +76,16 @@ func (s *builtinSecret) Encrypt() error { ciphertext := gcm.Seal(nonce, nonce, []byte(s.Payload), aad) s.Key = hex.EncodeToString(key) s.Payload = hex.EncodeToString(ciphertext) - s.Status = sdkkms.SecretStatusAES256GCM + s.Status = SecretStatusAES256GCM return nil default: - return sdkkms.ErrWrongSecretStatus + return ErrWrongSecretStatus } } func (s *builtinSecret) Decrypt() error { switch s.Status { - case sdkkms.SecretStatusAES256GCM: + case SecretStatusAES256GCM: encrypted, err := hex.DecodeString(s.Payload) if err != nil { return err @@ -117,18 +115,18 @@ func (s *builtinSecret) Decrypt() error { if err != nil { return err } - s.Status = sdkkms.SecretStatusPlain + s.Status = SecretStatusPlain s.Payload = string(plaintext) s.Key = "" s.AdditionalData = "" return nil default: - return sdkkms.ErrWrongSecretStatus + return ErrWrongSecretStatus } } -func (s *builtinSecret) Clone() sdkkms.SecretProvider { - baseSecret := sdkkms.BaseSecret{ +func (s *builtinSecret) Clone() SecretProvider { + baseSecret := BaseSecret{ Status: s.Status, Payload: s.Payload, Key: s.Key, diff --git a/sdk/kms/kms.go b/sdk/kms/kms.go index a186f93e..4f66ad5d 100644 --- a/sdk/kms/kms.go +++ b/sdk/kms/kms.go @@ -8,8 +8,8 @@ import ( "strings" "sync" - "github.com/drakkan/sftpgo/v2/logger" - "github.com/drakkan/sftpgo/v2/util" + "github.com/drakkan/sftpgo/v2/sdk/logger" + "github.com/drakkan/sftpgo/v2/sdk/util" ) // SecretProvider defines the interface for a KMS secrets provider @@ -141,7 +141,7 @@ func (c *Configuration) Initialize() error { config.Secrets.URL = SchemeLocal + "://" } for k, v := range secretProviders { - logger.Debug(logSender, "", "secret provider registered for scheme: %#v, encrypted status: %#v", + logger.Debug(logSender, "secret provider registered for scheme: %#v, encrypted status: %#v", k, v.encryptedStatus) } return nil @@ -166,8 +166,8 @@ func (c *Configuration) getSecretProvider(base BaseSecret) SecretProvider { } } // we assume that SchemeLocal is always registered - logger.Warn(logSender, "", "no secret provider registered for URL %v, fallback to local provider", c.Secrets.URL) - return secretProviders[SchemeLocal].newFn(base, c.Secrets.URL, c.Secrets.masterKey) + logger.Warn(logSender, "no secret provider registered for URL %v, fallback to local provider", c.Secrets.URL) + return NewLocalSecret(base, c.Secrets.URL, c.Secrets.masterKey) } // Secret defines the struct used to store confidential data @@ -217,7 +217,7 @@ func (s *Secret) UnmarshalJSON(data []byte) error { return nil } } - logger.Debug(logSender, "", "no provider registered for status %#v", baseSecret.Status) + logger.Debug(logSender, "no provider registered for status %#v", baseSecret.Status) return ErrInvalidSecret } diff --git a/kms/local.go b/sdk/kms/local.go similarity index 80% rename from kms/local.go rename to sdk/kms/local.go index f710718f..a360b163 100644 --- a/kms/local.go +++ b/sdk/kms/local.go @@ -9,21 +9,19 @@ import ( "gocloud.dev/secrets/localsecrets" "golang.org/x/crypto/hkdf" - - sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms" ) func init() { - sdkkms.RegisterSecretProvider(sdkkms.SchemeLocal, sdkkms.SecretStatusSecretBox, NewLocalSecret) + RegisterSecretProvider(SchemeLocal, SecretStatusSecretBox, NewLocalSecret) } type localSecret struct { - sdkkms.BaseSecret + BaseSecret masterKey string } // NewLocalSecret returns a SecretProvider that use a locally provided symmetric key -func NewLocalSecret(base sdkkms.BaseSecret, url, masterKey string) sdkkms.SecretProvider { +func NewLocalSecret(base BaseSecret, url, masterKey string) SecretProvider { return &localSecret{ BaseSecret: base, masterKey: masterKey, @@ -35,15 +33,15 @@ func (s *localSecret) Name() string { } func (s *localSecret) IsEncrypted() bool { - return s.Status == sdkkms.SecretStatusSecretBox + return s.Status == SecretStatusSecretBox } func (s *localSecret) Encrypt() error { - if s.Status != sdkkms.SecretStatusPlain { - return sdkkms.ErrWrongSecretStatus + if s.Status != SecretStatusPlain { + return ErrWrongSecretStatus } if s.Payload == "" { - return sdkkms.ErrInvalidSecret + return ErrInvalidSecret } secretKey, err := localsecrets.NewRandomKey() if err != nil { @@ -62,14 +60,14 @@ func (s *localSecret) Encrypt() error { } s.Key = hex.EncodeToString(secretKey[:]) s.Payload = base64.StdEncoding.EncodeToString(ciphertext) - s.Status = sdkkms.SecretStatusSecretBox + s.Status = SecretStatusSecretBox s.Mode = s.getEncryptionMode() return nil } func (s *localSecret) Decrypt() error { if !s.IsEncrypted() { - return sdkkms.ErrWrongSecretStatus + return ErrWrongSecretStatus } encrypted, err := base64.StdEncoding.DecodeString(s.Payload) if err != nil { @@ -90,7 +88,7 @@ func (s *localSecret) Decrypt() error { if err != nil { return err } - s.Status = sdkkms.SecretStatusPlain + s.Status = SecretStatusPlain s.Payload = string(plaintext) s.Key = "" s.AdditionalData = "" @@ -131,8 +129,8 @@ func (s *localSecret) getEncryptionMode() int { return 1 } -func (s *localSecret) Clone() sdkkms.SecretProvider { - baseSecret := sdkkms.BaseSecret{ +func (s *localSecret) Clone() SecretProvider { + baseSecret := BaseSecret{ Status: s.Status, Payload: s.Payload, Key: s.Key, diff --git a/logger/hclog_adapter.go b/sdk/logger/hclog_adapter.go similarity index 79% rename from logger/hclog_adapter.go rename to sdk/logger/hclog_adapter.go index 4dc15208..9805dfb1 100644 --- a/logger/hclog_adapter.go +++ b/sdk/logger/hclog_adapter.go @@ -5,7 +5,6 @@ import ( "log" "github.com/hashicorp/go-hclog" - "github.com/rs/zerolog" ) // HCLogAdapter is an adapter for hclog.Logger @@ -15,20 +14,7 @@ type HCLogAdapter struct { // Log emits a message and key/value pairs at a provided log level func (l *HCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) { - var ev *zerolog.Event - switch level { - case hclog.Info: - ev = logger.Info() - case hclog.Warn: - ev = logger.Warn() - case hclog.Error: - ev = logger.Error() - default: - ev = logger.Debug() - } - ev.Timestamp().Str("sender", l.Name()) - addKeysAndValues(ev, args...) - ev.Msg(msg) + logger.LogWithKeyVals(level, l.Name(), msg, args...) } // Trace emits a message and key/value pairs at the TRACE level @@ -75,3 +61,21 @@ func (l *HCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Lo func (l *HCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { return &StdLoggerWrapper{Sender: l.Name()} } + +// StdLoggerWrapper is a wrapper for standard logger compatibility +type StdLoggerWrapper struct { + Sender string +} + +// Write implements the io.Writer interface. This is useful to set as a writer +// for the standard library log. +func (l *StdLoggerWrapper) Write(p []byte) (n int, err error) { + n = len(p) + if n > 0 && p[n-1] == '\n' { + // Trim CR added by stdlog. + p = p[0 : n-1] + } + + logger.Log(hclog.Error, l.Sender, "", string(p)) + return +} diff --git a/sdk/logger/logger.go b/sdk/logger/logger.go new file mode 100644 index 00000000..a41dc946 --- /dev/null +++ b/sdk/logger/logger.go @@ -0,0 +1,56 @@ +// Package logger provides logging capabilities. +package logger + +import "github.com/hashicorp/go-hclog" + +var ( + logger Logger +) + +func init() { + DisableLogger() +} + +// Logger interface +type Logger interface { + // LogWithKeyVals logs at the specified level for the specified sender adding the specified key vals + LogWithKeyVals(level hclog.Level, sender, msg string, args ...interface{}) + // Log logs at the specified level for the specified sender + Log(level hclog.Level, sender, format string, v ...interface{}) +} + +// SetLogger sets the specified logger +func SetLogger(l Logger) { + logger = l +} + +// DisableLogger disables logging +func DisableLogger() { + logger = &noLogger{} +} + +type noLogger struct{} + +func (*noLogger) LogWithKeyVals(level hclog.Level, sender, msg string, args ...interface{}) {} + +func (*noLogger) Log(level hclog.Level, sender, format string, v ...interface{}) {} + +// Debug logs at debug level for the specified sender +func Debug(sender, format string, v ...interface{}) { + logger.Log(hclog.Debug, sender, format, v...) +} + +// Info logs at info level for the specified sender +func Info(sender, format string, v ...interface{}) { + logger.Log(hclog.Info, sender, format, v...) +} + +// Warn logs at warn level for the specified sender +func Warn(sender, format string, v ...interface{}) { + logger.Log(hclog.Warn, sender, format, v...) +} + +// Error logs at error level for the specified sender +func Error(sender, format string, v ...interface{}) { + logger.Log(hclog.Error, sender, format, v...) +} diff --git a/sdk/plugin/auth.go b/sdk/plugin/auth.go index 4cf408ff..4e05bbac 100644 --- a/sdk/plugin/auth.go +++ b/sdk/plugin/auth.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" - "github.com/drakkan/sftpgo/v2/logger" + "github.com/drakkan/sftpgo/v2/sdk/logger" "github.com/drakkan/sftpgo/v2/sdk/plugin/auth" ) diff --git a/sdk/plugin/kms.go b/sdk/plugin/kms.go index 6e80a51b..d74c734c 100644 --- a/sdk/plugin/kms.go +++ b/sdk/plugin/kms.go @@ -9,10 +9,10 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" - "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/sdk/kms" + "github.com/drakkan/sftpgo/v2/sdk/logger" kmsplugin "github.com/drakkan/sftpgo/v2/sdk/plugin/kms" - "github.com/drakkan/sftpgo/v2/util" + "github.com/drakkan/sftpgo/v2/sdk/util" ) var ( diff --git a/sdk/plugin/metadata.go b/sdk/plugin/metadata.go index dae88724..cecebaeb 100644 --- a/sdk/plugin/metadata.go +++ b/sdk/plugin/metadata.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" - "github.com/drakkan/sftpgo/v2/logger" + "github.com/drakkan/sftpgo/v2/sdk/logger" "github.com/drakkan/sftpgo/v2/sdk/plugin/metadata" ) diff --git a/sdk/plugin/notifier.go b/sdk/plugin/notifier.go index c770c463..ffb1a727 100644 --- a/sdk/plugin/notifier.go +++ b/sdk/plugin/notifier.go @@ -10,9 +10,9 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" - "github.com/drakkan/sftpgo/v2/logger" + "github.com/drakkan/sftpgo/v2/sdk/logger" "github.com/drakkan/sftpgo/v2/sdk/plugin/notifier" - "github.com/drakkan/sftpgo/v2/util" + "github.com/drakkan/sftpgo/v2/sdk/util" ) // NotifierConfig defines configuration parameters for notifiers plugins diff --git a/sdk/plugin/plugin.go b/sdk/plugin/plugin.go index b1fe6569..abef2c8c 100644 --- a/sdk/plugin/plugin.go +++ b/sdk/plugin/plugin.go @@ -11,14 +11,14 @@ import ( "github.com/hashicorp/go-hclog" - "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/sdk/kms" + "github.com/drakkan/sftpgo/v2/sdk/logger" "github.com/drakkan/sftpgo/v2/sdk/plugin/auth" "github.com/drakkan/sftpgo/v2/sdk/plugin/eventsearcher" kmsplugin "github.com/drakkan/sftpgo/v2/sdk/plugin/kms" "github.com/drakkan/sftpgo/v2/sdk/plugin/metadata" "github.com/drakkan/sftpgo/v2/sdk/plugin/notifier" - "github.com/drakkan/sftpgo/v2/util" + "github.com/drakkan/sftpgo/v2/sdk/util" ) const ( @@ -100,7 +100,7 @@ type Manager struct { // Initialize initializes the configured plugins func Initialize(configs []Config, logVerbose bool) error { - logger.Debug(logSender, "", "initialize") + logger.Debug(logSender, "initialize") Handler = Manager{ Configs: configs, done: make(chan bool), @@ -135,7 +135,7 @@ func Initialize(configs []Config, logVerbose bool) error { kmsID++ kms.RegisterSecretProvider(config.KMSOptions.Scheme, config.KMSOptions.EncryptedStatus, Handler.Configs[idx].newKMSPluginSecretProvider) - logger.Debug(logSender, "", "registered secret provider for scheme: %v, encrypted status: %v", + logger.Debug(logSender, "registered secret provider for scheme: %v, encrypted status: %v", config.KMSOptions.Scheme, config.KMSOptions.EncryptedStatus) case auth.PluginName: plugin, err := newAuthPlugin(config) @@ -357,7 +357,7 @@ func (m *Manager) Authenticate(username, password, ip, protocol string, pkey str case AuthScopeTLSCertificate: cert, err := util.EncodeTLSCertToPem(tlsCert) if err != nil { - logger.Warn(logSender, "", "unable to encode tls certificate to pem: %v", err) + logger.Error(logSender, "unable to encode tls certificate to pem: %v", err) return nil, fmt.Errorf("unable to encode tls cert to pem: %w", err) } return m.checkUserAndTLSCert(username, cert, ip, protocol, userAsJSON) @@ -520,10 +520,10 @@ func (m *Manager) restartNotifierPlugin(config Config, idx int) { if atomic.LoadInt32(&m.closed) == 1 { return } - logger.Info(logSender, "", "try to restart crashed notifier plugin %#v, idx: %v", config.Cmd, idx) + logger.Info(logSender, "try to restart crashed notifier plugin %#v, idx: %v", config.Cmd, idx) plugin, err := newNotifierPlugin(config) if err != nil { - logger.Warn(logSender, "", "unable to restart notifier plugin %#v, err: %v", config.Cmd, err) + logger.Error(logSender, "unable to restart notifier plugin %#v, err: %v", config.Cmd, err) return } @@ -538,10 +538,10 @@ func (m *Manager) restartKMSPlugin(config Config, idx int) { if atomic.LoadInt32(&m.closed) == 1 { return } - logger.Info(logSender, "", "try to restart crashed kms plugin %#v, idx: %v", config.Cmd, idx) + logger.Info(logSender, "try to restart crashed kms plugin %#v, idx: %v", config.Cmd, idx) plugin, err := newKMSPlugin(config) if err != nil { - logger.Warn(logSender, "", "unable to restart kms plugin %#v, err: %v", config.Cmd, err) + logger.Error(logSender, "unable to restart kms plugin %#v, err: %v", config.Cmd, err) return } @@ -554,10 +554,10 @@ func (m *Manager) restartAuthPlugin(config Config, idx int) { if atomic.LoadInt32(&m.closed) == 1 { return } - logger.Info(logSender, "", "try to restart crashed auth plugin %#v, idx: %v", config.Cmd, idx) + logger.Info(logSender, "try to restart crashed auth plugin %#v, idx: %v", config.Cmd, idx) plugin, err := newAuthPlugin(config) if err != nil { - logger.Warn(logSender, "", "unable to restart auth plugin %#v, err: %v", config.Cmd, err) + logger.Error(logSender, "unable to restart auth plugin %#v, err: %v", config.Cmd, err) return } @@ -570,10 +570,10 @@ func (m *Manager) restartSearcherPlugin(config Config) { if atomic.LoadInt32(&m.closed) == 1 { return } - logger.Info(logSender, "", "try to restart crashed searcher plugin %#v", config.Cmd) + logger.Info(logSender, "try to restart crashed searcher plugin %#v", config.Cmd) plugin, err := newSearcherPlugin(config) if err != nil { - logger.Warn(logSender, "", "unable to restart searcher plugin %#v, err: %v", config.Cmd, err) + logger.Error(logSender, "unable to restart searcher plugin %#v, err: %v", config.Cmd, err) return } @@ -586,10 +586,10 @@ func (m *Manager) restartMetadaterPlugin(config Config) { if atomic.LoadInt32(&m.closed) == 1 { return } - logger.Info(logSender, "", "try to restart crashed metadater plugin %#v", config.Cmd) + logger.Info(logSender, "try to restart crashed metadater plugin %#v", config.Cmd) plugin, err := newMetadaterPlugin(config) if err != nil { - logger.Warn(logSender, "", "unable to restart metadater plugin %#v, err: %v", config.Cmd, err) + logger.Error(logSender, "unable to restart metadater plugin %#v, err: %v", config.Cmd, err) return } @@ -600,40 +600,40 @@ func (m *Manager) restartMetadaterPlugin(config Config) { // Cleanup releases all the active plugins func (m *Manager) Cleanup() { - logger.Debug(logSender, "", "cleanup") + logger.Debug(logSender, "cleanup") atomic.StoreInt32(&m.closed, 1) close(m.done) m.notifLock.Lock() for _, n := range m.notifiers { - logger.Debug(logSender, "", "cleanup notifier plugin %v", n.config.Cmd) + logger.Debug(logSender, "cleanup notifier plugin %v", n.config.Cmd) n.cleanup() } m.notifLock.Unlock() m.kmsLock.Lock() for _, k := range m.kms { - logger.Debug(logSender, "", "cleanup kms plugin %v", k.config.Cmd) + logger.Debug(logSender, "cleanup kms plugin %v", k.config.Cmd) k.cleanup() } m.kmsLock.Unlock() m.authLock.Lock() for _, a := range m.auths { - logger.Debug(logSender, "", "cleanup auth plugin %v", a.config.Cmd) + logger.Debug(logSender, "cleanup auth plugin %v", a.config.Cmd) a.cleanup() } m.authLock.Unlock() if m.hasSearcher { m.searcherLock.Lock() - logger.Debug(logSender, "", "cleanup searcher plugin %v", m.searcher.config.Cmd) + logger.Debug(logSender, "cleanup searcher plugin %v", m.searcher.config.Cmd) m.searcher.cleanup() m.searcherLock.Unlock() } if m.hasMetadater { m.metadaterLock.Lock() - logger.Debug(logSender, "", "cleanup metadater plugin %v", m.metadater.config.Cmd) + logger.Debug(logSender, "cleanup metadater plugin %v", m.metadater.config.Cmd) m.metadater.cleanup() m.metadaterLock.Unlock() } @@ -648,14 +648,14 @@ func setLogLevel(logVerbose bool) { } func startCheckTicker() { - logger.Debug(logSender, "", "start plugins checker") + logger.Debug(logSender, "start plugins checker") checker := time.NewTicker(30 * time.Second) go func() { for { select { case <-Handler.done: - logger.Debug(logSender, "", "handler done, stop plugins checker") + logger.Debug(logSender, "handler done, stop plugins checker") checker.Stop() return case <-checker.C: diff --git a/sdk/plugin/searcher.go b/sdk/plugin/searcher.go index c515193b..574bb006 100644 --- a/sdk/plugin/searcher.go +++ b/sdk/plugin/searcher.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" - "github.com/drakkan/sftpgo/v2/logger" + "github.com/drakkan/sftpgo/v2/sdk/logger" "github.com/drakkan/sftpgo/v2/sdk/plugin/eventsearcher" ) diff --git a/sdk/plugin/util.go b/sdk/plugin/util.go index ba96270a..09e7ee1f 100644 --- a/sdk/plugin/util.go +++ b/sdk/plugin/util.go @@ -3,7 +3,7 @@ package plugin import ( "github.com/shirou/gopsutil/v3/process" - "github.com/drakkan/sftpgo/v2/logger" + "github.com/drakkan/sftpgo/v2/sdk/logger" ) func killProcess(processPath string) { @@ -16,10 +16,10 @@ func killProcess(processPath string) { if err == nil { if cmdLine == processPath { err = p.Kill() - logger.Debug(logSender, "", "killed process %v, pid %v, err %v", cmdLine, p.Pid, err) + logger.Debug(logSender, "killed process %v, pid %v, err %v", cmdLine, p.Pid, err) return } } } - logger.Debug(logSender, "", "no match for plugin process %v", processPath) + logger.Debug(logSender, "no match for plugin process %v", processPath) } diff --git a/sdk/user.go b/sdk/user.go index 99c97e23..0ea600d1 100644 --- a/sdk/user.go +++ b/sdk/user.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/drakkan/sftpgo/v2/sdk/kms" - "github.com/drakkan/sftpgo/v2/util" + "github.com/drakkan/sftpgo/v2/sdk/util" ) // Web Client/user REST API restrictions diff --git a/sdk/util/util.go b/sdk/util/util.go new file mode 100644 index 00000000..51186389 --- /dev/null +++ b/sdk/util/util.go @@ -0,0 +1,31 @@ +// Package util provides some common utility methods +package util + +import ( + "crypto/x509" + "encoding/pem" + "errors" +) + +// IsStringInSlice searches a string in a slice and returns true if the string is found +func IsStringInSlice(obj string, list []string) bool { + for i := 0; i < len(list); i++ { + if list[i] == obj { + return true + } + } + return false +} + +// EncodeTLSCertToPem returns the specified certificate PEM encoded. +// This can be verified using openssl x509 -in cert.crt -text -noout +func EncodeTLSCertToPem(tlsCert *x509.Certificate) (string, error) { + if len(tlsCert.Raw) == 0 { + return "", errors.New("invalid x509 certificate, no der contents") + } + publicKeyBlock := pem.Block{ + Type: "CERTIFICATE", + Bytes: tlsCert.Raw, + } + return string(pem.EncodeToMemory(&publicKeyBlock)), nil +} diff --git a/sftpd/sftpd_test.go b/sftpd/sftpd_test.go index 7a84a984..959fa822 100644 --- a/sftpd/sftpd_test.go +++ b/sftpd/sftpd_test.go @@ -42,7 +42,6 @@ import ( "github.com/drakkan/sftpgo/v2/config" "github.com/drakkan/sftpgo/v2/dataprovider" "github.com/drakkan/sftpgo/v2/httpdtest" - _ "github.com/drakkan/sftpgo/v2/kms" "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/mfa" "github.com/drakkan/sftpgo/v2/sdk" diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go index 9fc45e7a..048b4b31 100644 --- a/telemetry/telemetry.go +++ b/telemetry/telemetry.go @@ -16,6 +16,7 @@ import ( "github.com/drakkan/sftpgo/v2/common" "github.com/drakkan/sftpgo/v2/logger" + sdklogger "github.com/drakkan/sftpgo/v2/sdk/logger" "github.com/drakkan/sftpgo/v2/util" ) @@ -95,7 +96,7 @@ func (c Conf) Initialize(configDir string) error { WriteTimeout: 60 * time.Second, IdleTimeout: 60 * time.Second, MaxHeaderBytes: 1 << 14, // 16KB - ErrorLog: log.New(&logger.StdLoggerWrapper{Sender: logSender}, "", 0), + ErrorLog: log.New(&sdklogger.StdLoggerWrapper{Sender: logSender}, "", 0), } if certificateFile != "" && certificateKeyFile != "" { certMgr, err = common.NewCertManager(certificateFile, certificateKeyFile, configDir, logSender) diff --git a/tests/eventsearcher/go.sum b/tests/eventsearcher/go.sum index 3ef9182c..3c3f0f02 100644 --- a/tests/eventsearcher/go.sum +++ b/tests/eventsearcher/go.sum @@ -126,7 +126,7 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.25/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= +github.com/aws/aws-sdk-go v1.42.26/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= @@ -229,7 +229,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.8-0.20220103230436-7dbe9a0bd10f/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/jwtauth/v5 v5.0.2/go.mod h1:TeA7vmPe3uYThvHw8O8W13HOOpOd4MTgToxL41gZyjs= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= diff --git a/util/util.go b/util/util.go index dfaa9f21..935aaa79 100644 --- a/util/util.go +++ b/util/util.go @@ -11,7 +11,6 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" - "errors" "fmt" "html/template" "io" @@ -423,19 +422,6 @@ func GetTLSCiphersFromNames(cipherNames []string) []uint16 { return ciphers } -// EncodeTLSCertToPem returns the specified certificate PEM encoded. -// This can be verified using openssl x509 -in cert.crt -text -noout -func EncodeTLSCertToPem(tlsCert *x509.Certificate) (string, error) { - if len(tlsCert.Raw) == 0 { - return "", errors.New("invalid x509 certificate, no der contents") - } - publicKeyBlock := pem.Block{ - Type: "CERTIFICATE", - Bytes: tlsCert.Raw, - } - return string(pem.EncodeToMemory(&publicKeyBlock)), nil -} - // CheckTCP4Port quits the app if bind on the given IPv4 port fails. // This is a ugly hack to avoid to bind on an already used port. // It is required on Windows only. Upstream does not consider this diff --git a/webdavd/server.go b/webdavd/server.go index 1b333aee..841bc5a5 100644 --- a/webdavd/server.go +++ b/webdavd/server.go @@ -23,6 +23,7 @@ import ( "github.com/drakkan/sftpgo/v2/dataprovider" "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/metric" + sdklogger "github.com/drakkan/sftpgo/v2/sdk/logger" "github.com/drakkan/sftpgo/v2/util" ) @@ -39,7 +40,7 @@ func (s *webDavServer) listenAndServe(compressor *middleware.Compressor) error { WriteTimeout: 60 * time.Second, IdleTimeout: 60 * time.Second, MaxHeaderBytes: 1 << 16, // 64KB - ErrorLog: log.New(&logger.StdLoggerWrapper{Sender: logSender}, "", 0), + ErrorLog: log.New(&sdklogger.StdLoggerWrapper{Sender: logSender}, "", 0), } if s.config.Cors.Enabled { c := cors.New(cors.Options{ diff --git a/webdavd/webdavd_test.go b/webdavd/webdavd_test.go index 4e37e50d..e0ed2771 100644 --- a/webdavd/webdavd_test.go +++ b/webdavd/webdavd_test.go @@ -31,7 +31,6 @@ import ( "github.com/drakkan/sftpgo/v2/dataprovider" "github.com/drakkan/sftpgo/v2/httpclient" "github.com/drakkan/sftpgo/v2/httpdtest" - _ "github.com/drakkan/sftpgo/v2/kms" "github.com/drakkan/sftpgo/v2/logger" "github.com/drakkan/sftpgo/v2/sdk" "github.com/drakkan/sftpgo/v2/sdk/kms"