mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-08 23:28:39 +03:00
sdk: add a logger interface
we are now ready to make the sdk a separate module Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
137
sdk/kms/builtin.go
Normal file
137
sdk/kms/builtin.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
errMalformedCiphertext = errors.New("malformed ciphertext")
|
||||
)
|
||||
|
||||
type builtinSecret struct {
|
||||
BaseSecret
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterSecretProvider(SchemeBuiltin, SecretStatusAES256GCM, newBuiltinSecret)
|
||||
}
|
||||
|
||||
func newBuiltinSecret(base BaseSecret, url, masterKey string) SecretProvider {
|
||||
return &builtinSecret{
|
||||
BaseSecret: base,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *builtinSecret) Name() string {
|
||||
return "Builtin"
|
||||
}
|
||||
|
||||
func (s *builtinSecret) IsEncrypted() bool {
|
||||
return s.Status == SecretStatusAES256GCM
|
||||
}
|
||||
|
||||
func (s *builtinSecret) deriveKey(key []byte) []byte {
|
||||
var combined []byte
|
||||
combined = append(combined, key...)
|
||||
if s.AdditionalData != "" {
|
||||
combined = append(combined, []byte(s.AdditionalData)...)
|
||||
}
|
||||
combined = append(combined, key...)
|
||||
hash := sha256.Sum256(combined)
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
func (s *builtinSecret) Encrypt() error {
|
||||
if s.Payload == "" {
|
||||
return ErrInvalidSecret
|
||||
}
|
||||
switch s.Status {
|
||||
case SecretStatusPlain:
|
||||
key := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||
return err
|
||||
}
|
||||
block, err := aes.NewCipher(s.deriveKey(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return err
|
||||
}
|
||||
var aad []byte
|
||||
if s.AdditionalData != "" {
|
||||
aad = []byte(s.AdditionalData)
|
||||
}
|
||||
ciphertext := gcm.Seal(nonce, nonce, []byte(s.Payload), aad)
|
||||
s.Key = hex.EncodeToString(key)
|
||||
s.Payload = hex.EncodeToString(ciphertext)
|
||||
s.Status = SecretStatusAES256GCM
|
||||
return nil
|
||||
default:
|
||||
return ErrWrongSecretStatus
|
||||
}
|
||||
}
|
||||
|
||||
func (s *builtinSecret) Decrypt() error {
|
||||
switch s.Status {
|
||||
case SecretStatusAES256GCM:
|
||||
encrypted, err := hex.DecodeString(s.Payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := hex.DecodeString(s.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
block, err := aes.NewCipher(s.deriveKey(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(encrypted) < nonceSize {
|
||||
return errMalformedCiphertext
|
||||
}
|
||||
nonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:]
|
||||
var aad []byte
|
||||
if s.AdditionalData != "" {
|
||||
aad = []byte(s.AdditionalData)
|
||||
}
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, aad)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Status = SecretStatusPlain
|
||||
s.Payload = string(plaintext)
|
||||
s.Key = ""
|
||||
s.AdditionalData = ""
|
||||
return nil
|
||||
default:
|
||||
return ErrWrongSecretStatus
|
||||
}
|
||||
}
|
||||
|
||||
func (s *builtinSecret) Clone() SecretProvider {
|
||||
baseSecret := BaseSecret{
|
||||
Status: s.Status,
|
||||
Payload: s.Payload,
|
||||
Key: s.Key,
|
||||
AdditionalData: s.AdditionalData,
|
||||
Mode: s.Mode,
|
||||
}
|
||||
return newBuiltinSecret(baseSecret, "", "")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
141
sdk/kms/local.go
Normal file
141
sdk/kms/local.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
|
||||
"gocloud.dev/secrets/localsecrets"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterSecretProvider(SchemeLocal, SecretStatusSecretBox, NewLocalSecret)
|
||||
}
|
||||
|
||||
type localSecret struct {
|
||||
BaseSecret
|
||||
masterKey string
|
||||
}
|
||||
|
||||
// NewLocalSecret returns a SecretProvider that use a locally provided symmetric key
|
||||
func NewLocalSecret(base BaseSecret, url, masterKey string) SecretProvider {
|
||||
return &localSecret{
|
||||
BaseSecret: base,
|
||||
masterKey: masterKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *localSecret) Name() string {
|
||||
return "Local"
|
||||
}
|
||||
|
||||
func (s *localSecret) IsEncrypted() bool {
|
||||
return s.Status == SecretStatusSecretBox
|
||||
}
|
||||
|
||||
func (s *localSecret) Encrypt() error {
|
||||
if s.Status != SecretStatusPlain {
|
||||
return ErrWrongSecretStatus
|
||||
}
|
||||
if s.Payload == "" {
|
||||
return ErrInvalidSecret
|
||||
}
|
||||
secretKey, err := localsecrets.NewRandomKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := s.deriveKey(secretKey[:], false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keeper := localsecrets.NewKeeper(key)
|
||||
defer keeper.Close()
|
||||
|
||||
ciphertext, err := keeper.Encrypt(context.Background(), []byte(s.Payload))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Key = hex.EncodeToString(secretKey[:])
|
||||
s.Payload = base64.StdEncoding.EncodeToString(ciphertext)
|
||||
s.Status = SecretStatusSecretBox
|
||||
s.Mode = s.getEncryptionMode()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *localSecret) Decrypt() error {
|
||||
if !s.IsEncrypted() {
|
||||
return ErrWrongSecretStatus
|
||||
}
|
||||
encrypted, err := base64.StdEncoding.DecodeString(s.Payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secretKey, err := hex.DecodeString(s.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := s.deriveKey(secretKey[:], true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keeper := localsecrets.NewKeeper(key)
|
||||
defer keeper.Close()
|
||||
|
||||
plaintext, err := keeper.Decrypt(context.Background(), encrypted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Status = SecretStatusPlain
|
||||
s.Payload = string(plaintext)
|
||||
s.Key = ""
|
||||
s.AdditionalData = ""
|
||||
s.Mode = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *localSecret) deriveKey(key []byte, isForDecryption bool) ([32]byte, error) {
|
||||
var masterKey []byte
|
||||
if s.masterKey == "" || (isForDecryption && s.Mode == 0) {
|
||||
var combined []byte
|
||||
combined = append(combined, key...)
|
||||
if s.AdditionalData != "" {
|
||||
combined = append(combined, []byte(s.AdditionalData)...)
|
||||
}
|
||||
combined = append(combined, key...)
|
||||
hash := sha256.Sum256(combined)
|
||||
masterKey = hash[:]
|
||||
} else {
|
||||
masterKey = []byte(s.masterKey)
|
||||
}
|
||||
var derivedKey [32]byte
|
||||
var info []byte
|
||||
if s.AdditionalData != "" {
|
||||
info = []byte(s.AdditionalData)
|
||||
}
|
||||
kdf := hkdf.New(sha256.New, masterKey, key, info)
|
||||
if _, err := io.ReadFull(kdf, derivedKey[:]); err != nil {
|
||||
return derivedKey, err
|
||||
}
|
||||
return derivedKey, nil
|
||||
}
|
||||
|
||||
func (s *localSecret) getEncryptionMode() int {
|
||||
if s.masterKey == "" {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (s *localSecret) Clone() SecretProvider {
|
||||
baseSecret := BaseSecret{
|
||||
Status: s.Status,
|
||||
Payload: s.Payload,
|
||||
Key: s.Key,
|
||||
AdditionalData: s.AdditionalData,
|
||||
Mode: s.Mode,
|
||||
}
|
||||
return NewLocalSecret(baseSecret, "", s.masterKey)
|
||||
}
|
||||
81
sdk/logger/hclog_adapter.go
Normal file
81
sdk/logger/hclog_adapter.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
)
|
||||
|
||||
// HCLogAdapter is an adapter for hclog.Logger
|
||||
type HCLogAdapter struct {
|
||||
hclog.Logger
|
||||
}
|
||||
|
||||
// Log emits a message and key/value pairs at a provided log level
|
||||
func (l *HCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
|
||||
logger.LogWithKeyVals(level, l.Name(), msg, args...)
|
||||
}
|
||||
|
||||
// Trace emits a message and key/value pairs at the TRACE level
|
||||
func (l *HCLogAdapter) Trace(msg string, args ...interface{}) {
|
||||
l.Log(hclog.Debug, msg, args...)
|
||||
}
|
||||
|
||||
// Debug emits a message and key/value pairs at the DEBUG level
|
||||
func (l *HCLogAdapter) Debug(msg string, args ...interface{}) {
|
||||
l.Log(hclog.Debug, msg, args...)
|
||||
}
|
||||
|
||||
// Info emits a message and key/value pairs at the INFO level
|
||||
func (l *HCLogAdapter) Info(msg string, args ...interface{}) {
|
||||
l.Log(hclog.Info, msg, args...)
|
||||
}
|
||||
|
||||
// Warn emits a message and key/value pairs at the WARN level
|
||||
func (l *HCLogAdapter) Warn(msg string, args ...interface{}) {
|
||||
l.Log(hclog.Warn, msg, args...)
|
||||
}
|
||||
|
||||
// Error emits a message and key/value pairs at the ERROR level
|
||||
func (l *HCLogAdapter) Error(msg string, args ...interface{}) {
|
||||
l.Log(hclog.Error, msg, args...)
|
||||
}
|
||||
|
||||
// With creates a sub-logger
|
||||
func (l *HCLogAdapter) With(args ...interface{}) hclog.Logger {
|
||||
return &HCLogAdapter{Logger: l.Logger.With(args...)}
|
||||
}
|
||||
|
||||
// Named creates a logger that will prepend the name string on the front of all messages
|
||||
func (l *HCLogAdapter) Named(name string) hclog.Logger {
|
||||
return &HCLogAdapter{Logger: l.Logger.Named(name)}
|
||||
}
|
||||
|
||||
// StandardLogger returns a value that conforms to the stdlib log.Logger interface
|
||||
func (l *HCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
|
||||
return log.New(&StdLoggerWrapper{Sender: l.Name()}, "", 0)
|
||||
}
|
||||
|
||||
// StandardWriter returns a value that conforms to io.Writer, which can be passed into log.SetOutput()
|
||||
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
|
||||
}
|
||||
56
sdk/logger/logger.go
Normal file
56
sdk/logger/logger.go
Normal file
@@ -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...)
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
31
sdk/util/util.go
Normal file
31
sdk/util/util.go
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user