mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-22 07:30:25 +00:00
move kms definitions to the sdk package
This is the first step to make the sdk a separate module Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
08a7f08d6e
commit
0e1d8fc4d9
38 changed files with 520 additions and 502 deletions
|
@ -14,8 +14,8 @@ import (
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/common"
|
"github.com/drakkan/sftpgo/v2/common"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/service"
|
"github.com/drakkan/sftpgo/v2/service"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/version"
|
"github.com/drakkan/sftpgo/v2/version"
|
||||||
|
|
|
@ -19,8 +19,8 @@ import (
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,8 +13,8 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,11 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/httpclient"
|
"github.com/drakkan/sftpgo/v2/httpclient"
|
||||||
"github.com/drakkan/sftpgo/v2/httpdtest"
|
"github.com/drakkan/sftpgo/v2/httpdtest"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
_ "github.com/drakkan/sftpgo/v2/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,9 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/ftpd"
|
"github.com/drakkan/sftpgo/v2/ftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/httpclient"
|
"github.com/drakkan/sftpgo/v2/httpclient"
|
||||||
"github.com/drakkan/sftpgo/v2/httpd"
|
"github.com/drakkan/sftpgo/v2/httpd"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/smtp"
|
"github.com/drakkan/sftpgo/v2/smtp"
|
||||||
|
|
|
@ -17,8 +17,9 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/ftpd"
|
"github.com/drakkan/sftpgo/v2/ftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/httpclient"
|
"github.com/drakkan/sftpgo/v2/httpclient"
|
||||||
"github.com/drakkan/sftpgo/v2/httpd"
|
"github.com/drakkan/sftpgo/v2/httpd"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
_ "github.com/drakkan/sftpgo/v2/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/smtp"
|
"github.com/drakkan/sftpgo/v2/smtp"
|
||||||
|
|
|
@ -15,10 +15,10 @@ import (
|
||||||
passwordvalidator "github.com/wagslane/go-password-validator"
|
passwordvalidator "github.com/wagslane/go-password-validator"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -46,11 +46,11 @@ import (
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/httpclient"
|
"github.com/drakkan/sftpgo/v2/httpclient"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/metric"
|
"github.com/drakkan/sftpgo/v2/metric"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
|
|
|
@ -15,10 +15,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,8 +18,8 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/common"
|
"github.com/drakkan/sftpgo/v2/common"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/httpdtest"
|
"github.com/drakkan/sftpgo/v2/httpdtest"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBasicFTPHandlingCryptFs(t *testing.T) {
|
func TestBasicFTPHandlingCryptFs(t *testing.T) {
|
||||||
|
|
|
@ -32,10 +32,11 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/ftpd"
|
"github.com/drakkan/sftpgo/v2/ftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/httpdtest"
|
"github.com/drakkan/sftpgo/v2/httpdtest"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
_ "github.com/drakkan/sftpgo/v2/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,9 +8,9 @@ import (
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/common"
|
"github.com/drakkan/sftpgo/v2/common"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/smtp"
|
"github.com/drakkan/sftpgo/v2/smtp"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
|
|
|
@ -45,10 +45,11 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/httpclient"
|
"github.com/drakkan/sftpgo/v2/httpclient"
|
||||||
"github.com/drakkan/sftpgo/v2/httpd"
|
"github.com/drakkan/sftpgo/v2/httpd"
|
||||||
"github.com/drakkan/sftpgo/v2/httpdtest"
|
"github.com/drakkan/sftpgo/v2/httpdtest"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
_ "github.com/drakkan/sftpgo/v2/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/smtp"
|
"github.com/drakkan/sftpgo/v2/smtp"
|
||||||
|
|
|
@ -33,8 +33,8 @@ import (
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/common"
|
"github.com/drakkan/sftpgo/v2/common"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
|
|
|
@ -16,9 +16,9 @@ import (
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/common"
|
"github.com/drakkan/sftpgo/v2/common"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/smtp"
|
"github.com/drakkan/sftpgo/v2/smtp"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/version"
|
"github.com/drakkan/sftpgo/v2/version"
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/httpclient"
|
"github.com/drakkan/sftpgo/v2/httpclient"
|
||||||
"github.com/drakkan/sftpgo/v2/httpd"
|
"github.com/drakkan/sftpgo/v2/httpd"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/version"
|
"github.com/drakkan/sftpgo/v2/version"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
|
|
|
@ -6,18 +6,25 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMalformedCiphertext = errors.New("malformed ciphertext")
|
||||||
)
|
)
|
||||||
|
|
||||||
type builtinSecret struct {
|
type builtinSecret struct {
|
||||||
BaseSecret
|
sdkkms.BaseSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterSecretProvider(SchemeBuiltin, SecretStatusAES256GCM, newBuiltinSecret)
|
sdkkms.RegisterSecretProvider(sdkkms.SchemeBuiltin, sdkkms.SecretStatusAES256GCM, newBuiltinSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBuiltinSecret(base BaseSecret, url, masterKey string) SecretProvider {
|
func newBuiltinSecret(base sdkkms.BaseSecret, url, masterKey string) sdkkms.SecretProvider {
|
||||||
return &builtinSecret{
|
return &builtinSecret{
|
||||||
BaseSecret: base,
|
BaseSecret: base,
|
||||||
}
|
}
|
||||||
|
@ -28,7 +35,7 @@ func (s *builtinSecret) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *builtinSecret) IsEncrypted() bool {
|
func (s *builtinSecret) IsEncrypted() bool {
|
||||||
return s.Status == SecretStatusAES256GCM
|
return s.Status == sdkkms.SecretStatusAES256GCM
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *builtinSecret) deriveKey(key []byte) []byte {
|
func (s *builtinSecret) deriveKey(key []byte) []byte {
|
||||||
|
@ -44,10 +51,10 @@ func (s *builtinSecret) deriveKey(key []byte) []byte {
|
||||||
|
|
||||||
func (s *builtinSecret) Encrypt() error {
|
func (s *builtinSecret) Encrypt() error {
|
||||||
if s.Payload == "" {
|
if s.Payload == "" {
|
||||||
return ErrInvalidSecret
|
return sdkkms.ErrInvalidSecret
|
||||||
}
|
}
|
||||||
switch s.Status {
|
switch s.Status {
|
||||||
case SecretStatusPlain:
|
case sdkkms.SecretStatusPlain:
|
||||||
key := make([]byte, 32)
|
key := make([]byte, 32)
|
||||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -71,16 +78,16 @@ func (s *builtinSecret) Encrypt() error {
|
||||||
ciphertext := gcm.Seal(nonce, nonce, []byte(s.Payload), aad)
|
ciphertext := gcm.Seal(nonce, nonce, []byte(s.Payload), aad)
|
||||||
s.Key = hex.EncodeToString(key)
|
s.Key = hex.EncodeToString(key)
|
||||||
s.Payload = hex.EncodeToString(ciphertext)
|
s.Payload = hex.EncodeToString(ciphertext)
|
||||||
s.Status = SecretStatusAES256GCM
|
s.Status = sdkkms.SecretStatusAES256GCM
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return ErrWrongSecretStatus
|
return sdkkms.ErrWrongSecretStatus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *builtinSecret) Decrypt() error {
|
func (s *builtinSecret) Decrypt() error {
|
||||||
switch s.Status {
|
switch s.Status {
|
||||||
case SecretStatusAES256GCM:
|
case sdkkms.SecretStatusAES256GCM:
|
||||||
encrypted, err := hex.DecodeString(s.Payload)
|
encrypted, err := hex.DecodeString(s.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -110,18 +117,18 @@ func (s *builtinSecret) Decrypt() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.Status = SecretStatusPlain
|
s.Status = sdkkms.SecretStatusPlain
|
||||||
s.Payload = string(plaintext)
|
s.Payload = string(plaintext)
|
||||||
s.Key = ""
|
s.Key = ""
|
||||||
s.AdditionalData = ""
|
s.AdditionalData = ""
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return ErrWrongSecretStatus
|
return sdkkms.ErrWrongSecretStatus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *builtinSecret) Clone() SecretProvider {
|
func (s *builtinSecret) Clone() sdkkms.SecretProvider {
|
||||||
baseSecret := BaseSecret{
|
baseSecret := sdkkms.BaseSecret{
|
||||||
Status: s.Status,
|
Status: s.Status,
|
||||||
Payload: s.Payload,
|
Payload: s.Payload,
|
||||||
Key: s.Key,
|
Key: s.Key,
|
||||||
|
|
446
kms/kms.go
446
kms/kms.go
|
@ -1,446 +1,2 @@
|
||||||
// Package kms provides Key Management Services support
|
// Package kms provides built-in Key Management Services support
|
||||||
package kms
|
package kms
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SecretProvider defines the interface for a KMS secrets provider
|
|
||||||
type SecretProvider interface {
|
|
||||||
Name() string
|
|
||||||
Encrypt() error
|
|
||||||
Decrypt() error
|
|
||||||
IsEncrypted() bool
|
|
||||||
GetStatus() SecretStatus
|
|
||||||
GetPayload() string
|
|
||||||
GetKey() string
|
|
||||||
GetAdditionalData() string
|
|
||||||
GetMode() int
|
|
||||||
SetKey(string)
|
|
||||||
SetAdditionalData(string)
|
|
||||||
SetStatus(SecretStatus)
|
|
||||||
Clone() SecretProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
logSender = "kms"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SecretStatus defines the statuses of a Secret object
|
|
||||||
type SecretStatus = string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SecretStatusPlain means the secret is in plain text and must be encrypted
|
|
||||||
SecretStatusPlain SecretStatus = "Plain"
|
|
||||||
// SecretStatusAES256GCM means the secret is encrypted using AES-256-GCM
|
|
||||||
SecretStatusAES256GCM SecretStatus = "AES-256-GCM"
|
|
||||||
// SecretStatusSecretBox means the secret is encrypted using a locally provided symmetric key
|
|
||||||
SecretStatusSecretBox SecretStatus = "Secretbox"
|
|
||||||
// SecretStatusGCP means we use keys from Google Cloud Platform’s Key Management Service
|
|
||||||
// (GCP KMS) to keep information secret
|
|
||||||
SecretStatusGCP SecretStatus = "GCP"
|
|
||||||
// SecretStatusAWS means we use customer master keys from Amazon Web Service’s
|
|
||||||
// Key Management Service (AWS KMS) to keep information secret
|
|
||||||
SecretStatusAWS SecretStatus = "AWS"
|
|
||||||
// SecretStatusVaultTransit means we use the transit secrets engine in Vault
|
|
||||||
// to keep information secret
|
|
||||||
SecretStatusVaultTransit SecretStatus = "VaultTransit"
|
|
||||||
// SecretStatusAzureKeyVault means we use Azure KeyVault to keep information secret
|
|
||||||
SecretStatusAzureKeyVault SecretStatus = "AzureKeyVault"
|
|
||||||
// SecretStatusRedacted means the secret is redacted
|
|
||||||
SecretStatusRedacted SecretStatus = "Redacted"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scheme defines the supported URL scheme
|
|
||||||
type Scheme = string
|
|
||||||
|
|
||||||
// supported URL schemes
|
|
||||||
const (
|
|
||||||
SchemeLocal Scheme = "local"
|
|
||||||
SchemeBuiltin Scheme = "builtin"
|
|
||||||
SchemeAWS Scheme = "awskms"
|
|
||||||
SchemeGCP Scheme = "gcpkms"
|
|
||||||
SchemeVaultTransit Scheme = "hashivault"
|
|
||||||
SchemeAzureKeyVault Scheme = "azurekeyvault"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Configuration defines the KMS configuration
|
|
||||||
type Configuration struct {
|
|
||||||
Secrets Secrets `json:"secrets" mapstructure:"secrets"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secrets define the KMS configuration for encryption/decryption
|
|
||||||
type Secrets struct {
|
|
||||||
URL string `json:"url" mapstructure:"url"`
|
|
||||||
MasterKeyPath string `json:"master_key_path" mapstructure:"master_key_path"`
|
|
||||||
MasterKeyString string `json:"master_key" mapstructure:"master_key"`
|
|
||||||
masterKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
type registeredSecretProvider struct {
|
|
||||||
encryptedStatus SecretStatus
|
|
||||||
newFn func(base BaseSecret, url, masterKey string) SecretProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrWrongSecretStatus defines the error to return if the secret status is not appropriate
|
|
||||||
// for the request operation
|
|
||||||
ErrWrongSecretStatus = errors.New("wrong secret status")
|
|
||||||
// ErrInvalidSecret defines the error to return if a secret is not valid
|
|
||||||
ErrInvalidSecret = errors.New("invalid secret")
|
|
||||||
errMalformedCiphertext = errors.New("malformed ciphertext")
|
|
||||||
validSecretStatuses = []string{SecretStatusPlain, SecretStatusAES256GCM, SecretStatusSecretBox,
|
|
||||||
SecretStatusVaultTransit, SecretStatusAWS, SecretStatusGCP, SecretStatusRedacted}
|
|
||||||
config Configuration
|
|
||||||
secretProviders = make(map[string]registeredSecretProvider)
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterSecretProvider register a new secret provider
|
|
||||||
func RegisterSecretProvider(scheme string, encryptedStatus SecretStatus, fn func(base BaseSecret, url, masterKey string) SecretProvider) {
|
|
||||||
secretProviders[scheme] = registeredSecretProvider{
|
|
||||||
encryptedStatus: encryptedStatus,
|
|
||||||
newFn: fn,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSecret builds a new Secret using the provided arguments
|
|
||||||
func NewSecret(status SecretStatus, payload, key, data string) *Secret {
|
|
||||||
return config.newSecret(status, payload, key, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEmptySecret returns an empty secret
|
|
||||||
func NewEmptySecret() *Secret {
|
|
||||||
return NewSecret("", "", "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPlainSecret stores the give payload in a plain text secret
|
|
||||||
func NewPlainSecret(payload string) *Secret {
|
|
||||||
return NewSecret(SecretStatusPlain, payload, "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize configures the KMS support
|
|
||||||
func (c *Configuration) Initialize() error {
|
|
||||||
if c.Secrets.MasterKeyString != "" {
|
|
||||||
c.Secrets.masterKey = c.Secrets.MasterKeyString
|
|
||||||
}
|
|
||||||
if c.Secrets.masterKey == "" && c.Secrets.MasterKeyPath != "" {
|
|
||||||
mKey, err := os.ReadFile(c.Secrets.MasterKeyPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Secrets.masterKey = strings.TrimSpace(string(mKey))
|
|
||||||
}
|
|
||||||
config = *c
|
|
||||||
if config.Secrets.URL == "" {
|
|
||||||
config.Secrets.URL = SchemeLocal + "://"
|
|
||||||
}
|
|
||||||
for k, v := range secretProviders {
|
|
||||||
logger.Debug(logSender, "", "secret provider registered for scheme: %#v, encrypted status: %#v",
|
|
||||||
k, v.encryptedStatus)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Configuration) newSecret(status SecretStatus, payload, key, data string) *Secret {
|
|
||||||
base := BaseSecret{
|
|
||||||
Status: status,
|
|
||||||
Key: key,
|
|
||||||
Payload: payload,
|
|
||||||
AdditionalData: data,
|
|
||||||
}
|
|
||||||
return &Secret{
|
|
||||||
provider: c.getSecretProvider(base),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Configuration) getSecretProvider(base BaseSecret) SecretProvider {
|
|
||||||
for k, v := range secretProviders {
|
|
||||||
if strings.HasPrefix(c.Secrets.URL, k) {
|
|
||||||
return v.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
|
|
||||||
type Secret struct {
|
|
||||||
sync.RWMutex
|
|
||||||
provider SecretProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON return the JSON encoding of the Secret object
|
|
||||||
func (s *Secret) MarshalJSON() ([]byte, error) {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return json.Marshal(&BaseSecret{
|
|
||||||
Status: s.provider.GetStatus(),
|
|
||||||
Payload: s.provider.GetPayload(),
|
|
||||||
Key: s.provider.GetKey(),
|
|
||||||
AdditionalData: s.provider.GetAdditionalData(),
|
|
||||||
Mode: s.provider.GetMode(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON parses the JSON-encoded data and stores the result
|
|
||||||
// in the Secret object
|
|
||||||
func (s *Secret) UnmarshalJSON(data []byte) error {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
baseSecret := BaseSecret{}
|
|
||||||
err := json.Unmarshal(data, &baseSecret)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if baseSecret.isEmpty() {
|
|
||||||
s.provider = config.getSecretProvider(baseSecret)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if baseSecret.Status == SecretStatusPlain || baseSecret.Status == SecretStatusRedacted {
|
|
||||||
s.provider = config.getSecretProvider(baseSecret)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range secretProviders {
|
|
||||||
if v.encryptedStatus == baseSecret.Status {
|
|
||||||
s.provider = v.newFn(baseSecret, config.Secrets.URL, config.Secrets.masterKey)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Debug(logSender, "", "no provider registered for status %#v", baseSecret.Status)
|
|
||||||
|
|
||||||
return ErrInvalidSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEqual returns true if all the secrets fields are equal
|
|
||||||
func (s *Secret) IsEqual(other *Secret) bool {
|
|
||||||
if s.GetStatus() != other.GetStatus() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.GetPayload() != other.GetPayload() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.GetKey() != other.GetKey() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.GetAdditionalData() != other.GetAdditionalData() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.GetMode() != other.GetMode() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone returns a copy of the secret object
|
|
||||||
func (s *Secret) Clone() *Secret {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return &Secret{
|
|
||||||
provider: s.provider.Clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEncrypted returns true if the secret is encrypted
|
|
||||||
// This isn't a pointer receiver because we don't want to pass
|
|
||||||
// a pointer to html template
|
|
||||||
func (s *Secret) IsEncrypted() bool {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return s.provider.IsEncrypted()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPlain returns true if the secret is in plain text
|
|
||||||
func (s *Secret) IsPlain() bool {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return s.provider.GetStatus() == SecretStatusPlain
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotPlainAndNotEmpty returns true if the secret is not plain and not empty.
|
|
||||||
// This is an utility method, we update the secret for an existing user
|
|
||||||
// if it is empty or plain
|
|
||||||
func (s *Secret) IsNotPlainAndNotEmpty() bool {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return !s.IsPlain() && !s.IsEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRedacted returns true if the secret is redacted
|
|
||||||
func (s *Secret) IsRedacted() bool {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return s.provider.GetStatus() == SecretStatusRedacted
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPayload returns the secret payload
|
|
||||||
func (s *Secret) GetPayload() string {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return s.provider.GetPayload()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAdditionalData returns the secret additional data
|
|
||||||
func (s *Secret) GetAdditionalData() string {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return s.provider.GetAdditionalData()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStatus returns the secret status
|
|
||||||
func (s *Secret) GetStatus() SecretStatus {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return s.provider.GetStatus()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetKey returns the secret key
|
|
||||||
func (s *Secret) GetKey() string {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return s.provider.GetKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMode returns the secret mode
|
|
||||||
func (s *Secret) GetMode() int {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
return s.provider.GetMode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAdditionalData sets the given additional data
|
|
||||||
func (s *Secret) SetAdditionalData(value string) {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
s.provider.SetAdditionalData(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetStatus sets the status for this secret
|
|
||||||
func (s *Secret) SetStatus(value SecretStatus) {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
s.provider.SetStatus(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetKey sets the key for this secret
|
|
||||||
func (s *Secret) SetKey(value string) {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
s.provider.SetKey(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if all fields are empty
|
|
||||||
func (s *Secret) IsEmpty() bool {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
if s.provider.GetStatus() != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.provider.GetPayload() != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.provider.GetKey() != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.provider.GetAdditionalData() != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid returns true if the secret is not empty and valid
|
|
||||||
func (s *Secret) IsValid() bool {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
if !s.IsValidInput() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch s.provider.GetStatus() {
|
|
||||||
case SecretStatusAES256GCM, SecretStatusSecretBox:
|
|
||||||
if len(s.provider.GetKey()) != 64 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case SecretStatusAWS, SecretStatusGCP, SecretStatusVaultTransit:
|
|
||||||
key := s.provider.GetKey()
|
|
||||||
if key != "" && len(key) != 64 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValidInput returns true if the secret is a valid user input
|
|
||||||
func (s *Secret) IsValidInput() bool {
|
|
||||||
s.RLock()
|
|
||||||
defer s.RUnlock()
|
|
||||||
|
|
||||||
if !util.IsStringInSlice(s.provider.GetStatus(), validSecretStatuses) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if s.provider.GetPayload() == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide hides info to decrypt data
|
|
||||||
func (s *Secret) Hide() {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
s.provider.SetKey("")
|
|
||||||
s.provider.SetAdditionalData("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt encrypts a plain text Secret object
|
|
||||||
func (s *Secret) Encrypt() error {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
return s.provider.Encrypt()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt decrypts a Secret object
|
|
||||||
func (s *Secret) Decrypt() error {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
return s.provider.Decrypt()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryDecrypt decrypts a Secret object if encrypted.
|
|
||||||
// It returns a nil error if the object is not encrypted
|
|
||||||
func (s *Secret) TryDecrypt() error {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
if s.provider.IsEncrypted() {
|
|
||||||
return s.provider.Decrypt()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
26
kms/local.go
26
kms/local.go
|
@ -9,19 +9,21 @@ import (
|
||||||
|
|
||||||
"gocloud.dev/secrets/localsecrets"
|
"gocloud.dev/secrets/localsecrets"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
|
sdkkms "github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterSecretProvider(SchemeLocal, SecretStatusSecretBox, NewLocalSecret)
|
sdkkms.RegisterSecretProvider(sdkkms.SchemeLocal, sdkkms.SecretStatusSecretBox, NewLocalSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
type localSecret struct {
|
type localSecret struct {
|
||||||
BaseSecret
|
sdkkms.BaseSecret
|
||||||
masterKey string
|
masterKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalSecret returns a SecretProvider that use a locally provided symmetric key
|
// NewLocalSecret returns a SecretProvider that use a locally provided symmetric key
|
||||||
func NewLocalSecret(base BaseSecret, url, masterKey string) SecretProvider {
|
func NewLocalSecret(base sdkkms.BaseSecret, url, masterKey string) sdkkms.SecretProvider {
|
||||||
return &localSecret{
|
return &localSecret{
|
||||||
BaseSecret: base,
|
BaseSecret: base,
|
||||||
masterKey: masterKey,
|
masterKey: masterKey,
|
||||||
|
@ -33,15 +35,15 @@ func (s *localSecret) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *localSecret) IsEncrypted() bool {
|
func (s *localSecret) IsEncrypted() bool {
|
||||||
return s.Status == SecretStatusSecretBox
|
return s.Status == sdkkms.SecretStatusSecretBox
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *localSecret) Encrypt() error {
|
func (s *localSecret) Encrypt() error {
|
||||||
if s.Status != SecretStatusPlain {
|
if s.Status != sdkkms.SecretStatusPlain {
|
||||||
return ErrWrongSecretStatus
|
return sdkkms.ErrWrongSecretStatus
|
||||||
}
|
}
|
||||||
if s.Payload == "" {
|
if s.Payload == "" {
|
||||||
return ErrInvalidSecret
|
return sdkkms.ErrInvalidSecret
|
||||||
}
|
}
|
||||||
secretKey, err := localsecrets.NewRandomKey()
|
secretKey, err := localsecrets.NewRandomKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -60,14 +62,14 @@ func (s *localSecret) Encrypt() error {
|
||||||
}
|
}
|
||||||
s.Key = hex.EncodeToString(secretKey[:])
|
s.Key = hex.EncodeToString(secretKey[:])
|
||||||
s.Payload = base64.StdEncoding.EncodeToString(ciphertext)
|
s.Payload = base64.StdEncoding.EncodeToString(ciphertext)
|
||||||
s.Status = SecretStatusSecretBox
|
s.Status = sdkkms.SecretStatusSecretBox
|
||||||
s.Mode = s.getEncryptionMode()
|
s.Mode = s.getEncryptionMode()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *localSecret) Decrypt() error {
|
func (s *localSecret) Decrypt() error {
|
||||||
if !s.IsEncrypted() {
|
if !s.IsEncrypted() {
|
||||||
return ErrWrongSecretStatus
|
return sdkkms.ErrWrongSecretStatus
|
||||||
}
|
}
|
||||||
encrypted, err := base64.StdEncoding.DecodeString(s.Payload)
|
encrypted, err := base64.StdEncoding.DecodeString(s.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -88,7 +90,7 @@ func (s *localSecret) Decrypt() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.Status = SecretStatusPlain
|
s.Status = sdkkms.SecretStatusPlain
|
||||||
s.Payload = string(plaintext)
|
s.Payload = string(plaintext)
|
||||||
s.Key = ""
|
s.Key = ""
|
||||||
s.AdditionalData = ""
|
s.AdditionalData = ""
|
||||||
|
@ -129,8 +131,8 @@ func (s *localSecret) getEncryptionMode() int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *localSecret) Clone() SecretProvider {
|
func (s *localSecret) Clone() sdkkms.SecretProvider {
|
||||||
baseSecret := BaseSecret{
|
baseSecret := sdkkms.BaseSecret{
|
||||||
Status: s.Status,
|
Status: s.Status,
|
||||||
Payload: s.Payload,
|
Payload: s.Payload,
|
||||||
Key: s.Key,
|
Key: s.Key,
|
||||||
|
|
1
main.go
1
main.go
|
@ -11,6 +11,7 @@ import (
|
||||||
"go.uber.org/automaxprocs/maxprocs"
|
"go.uber.org/automaxprocs/maxprocs"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/cmd"
|
"github.com/drakkan/sftpgo/v2/cmd"
|
||||||
|
_ "github.com/drakkan/sftpgo/v2/kms"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package sdk
|
package sdk
|
||||||
|
|
||||||
import "github.com/drakkan/sftpgo/v2/kms"
|
import "github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
|
|
||||||
// FilesystemProvider defines the supported storage filesystems
|
// FilesystemProvider defines the supported storage filesystems
|
||||||
type FilesystemProvider int
|
type FilesystemProvider int
|
||||||
|
|
446
sdk/kms/kms.go
Normal file
446
sdk/kms/kms.go
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
// Package kms provides Key Management Services support
|
||||||
|
package kms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecretProvider defines the interface for a KMS secrets provider
|
||||||
|
type SecretProvider interface {
|
||||||
|
Name() string
|
||||||
|
Encrypt() error
|
||||||
|
Decrypt() error
|
||||||
|
IsEncrypted() bool
|
||||||
|
GetStatus() SecretStatus
|
||||||
|
GetPayload() string
|
||||||
|
GetKey() string
|
||||||
|
GetAdditionalData() string
|
||||||
|
GetMode() int
|
||||||
|
SetKey(string)
|
||||||
|
SetAdditionalData(string)
|
||||||
|
SetStatus(SecretStatus)
|
||||||
|
Clone() SecretProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
logSender = "kms"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecretStatus defines the statuses of a Secret object
|
||||||
|
type SecretStatus = string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SecretStatusPlain means the secret is in plain text and must be encrypted
|
||||||
|
SecretStatusPlain SecretStatus = "Plain"
|
||||||
|
// SecretStatusAES256GCM means the secret is encrypted using AES-256-GCM
|
||||||
|
SecretStatusAES256GCM SecretStatus = "AES-256-GCM"
|
||||||
|
// SecretStatusSecretBox means the secret is encrypted using a locally provided symmetric key
|
||||||
|
SecretStatusSecretBox SecretStatus = "Secretbox"
|
||||||
|
// SecretStatusGCP means we use keys from Google Cloud Platform’s Key Management Service
|
||||||
|
// (GCP KMS) to keep information secret
|
||||||
|
SecretStatusGCP SecretStatus = "GCP"
|
||||||
|
// SecretStatusAWS means we use customer master keys from Amazon Web Service’s
|
||||||
|
// Key Management Service (AWS KMS) to keep information secret
|
||||||
|
SecretStatusAWS SecretStatus = "AWS"
|
||||||
|
// SecretStatusVaultTransit means we use the transit secrets engine in Vault
|
||||||
|
// to keep information secret
|
||||||
|
SecretStatusVaultTransit SecretStatus = "VaultTransit"
|
||||||
|
// SecretStatusAzureKeyVault means we use Azure KeyVault to keep information secret
|
||||||
|
SecretStatusAzureKeyVault SecretStatus = "AzureKeyVault"
|
||||||
|
// SecretStatusRedacted means the secret is redacted
|
||||||
|
SecretStatusRedacted SecretStatus = "Redacted"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scheme defines the supported URL scheme
|
||||||
|
type Scheme = string
|
||||||
|
|
||||||
|
// supported URL schemes
|
||||||
|
const (
|
||||||
|
SchemeLocal Scheme = "local"
|
||||||
|
SchemeBuiltin Scheme = "builtin"
|
||||||
|
SchemeAWS Scheme = "awskms"
|
||||||
|
SchemeGCP Scheme = "gcpkms"
|
||||||
|
SchemeVaultTransit Scheme = "hashivault"
|
||||||
|
SchemeAzureKeyVault Scheme = "azurekeyvault"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configuration defines the KMS configuration
|
||||||
|
type Configuration struct {
|
||||||
|
Secrets Secrets `json:"secrets" mapstructure:"secrets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secrets define the KMS configuration for encryption/decryption
|
||||||
|
type Secrets struct {
|
||||||
|
URL string `json:"url" mapstructure:"url"`
|
||||||
|
MasterKeyPath string `json:"master_key_path" mapstructure:"master_key_path"`
|
||||||
|
MasterKeyString string `json:"master_key" mapstructure:"master_key"`
|
||||||
|
masterKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
type registeredSecretProvider struct {
|
||||||
|
encryptedStatus SecretStatus
|
||||||
|
newFn func(base BaseSecret, url, masterKey string) SecretProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrWrongSecretStatus defines the error to return if the secret status is not appropriate
|
||||||
|
// for the request operation
|
||||||
|
ErrWrongSecretStatus = errors.New("wrong secret status")
|
||||||
|
// ErrInvalidSecret defines the error to return if a secret is not valid
|
||||||
|
ErrInvalidSecret = errors.New("invalid secret")
|
||||||
|
validSecretStatuses = []string{SecretStatusPlain, SecretStatusAES256GCM, SecretStatusSecretBox,
|
||||||
|
SecretStatusVaultTransit, SecretStatusAWS, SecretStatusGCP, SecretStatusRedacted}
|
||||||
|
config Configuration
|
||||||
|
secretProviders = make(map[string]registeredSecretProvider)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterSecretProvider register a new secret provider
|
||||||
|
func RegisterSecretProvider(scheme string, encryptedStatus SecretStatus, fn func(base BaseSecret, url, masterKey string) SecretProvider) {
|
||||||
|
secretProviders[scheme] = registeredSecretProvider{
|
||||||
|
encryptedStatus: encryptedStatus,
|
||||||
|
newFn: fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSecret builds a new Secret using the provided arguments
|
||||||
|
func NewSecret(status SecretStatus, payload, key, data string) *Secret {
|
||||||
|
return config.newSecret(status, payload, key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEmptySecret returns an empty secret
|
||||||
|
func NewEmptySecret() *Secret {
|
||||||
|
return NewSecret("", "", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPlainSecret stores the give payload in a plain text secret
|
||||||
|
func NewPlainSecret(payload string) *Secret {
|
||||||
|
return NewSecret(SecretStatusPlain, payload, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize configures the KMS support
|
||||||
|
func (c *Configuration) Initialize() error {
|
||||||
|
if c.Secrets.MasterKeyString != "" {
|
||||||
|
c.Secrets.masterKey = c.Secrets.MasterKeyString
|
||||||
|
}
|
||||||
|
if c.Secrets.masterKey == "" && c.Secrets.MasterKeyPath != "" {
|
||||||
|
mKey, err := os.ReadFile(c.Secrets.MasterKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Secrets.masterKey = strings.TrimSpace(string(mKey))
|
||||||
|
}
|
||||||
|
config = *c
|
||||||
|
if config.Secrets.URL == "" {
|
||||||
|
config.Secrets.URL = SchemeLocal + "://"
|
||||||
|
}
|
||||||
|
for k, v := range secretProviders {
|
||||||
|
logger.Debug(logSender, "", "secret provider registered for scheme: %#v, encrypted status: %#v",
|
||||||
|
k, v.encryptedStatus)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Configuration) newSecret(status SecretStatus, payload, key, data string) *Secret {
|
||||||
|
base := BaseSecret{
|
||||||
|
Status: status,
|
||||||
|
Key: key,
|
||||||
|
Payload: payload,
|
||||||
|
AdditionalData: data,
|
||||||
|
}
|
||||||
|
return &Secret{
|
||||||
|
provider: c.getSecretProvider(base),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Configuration) getSecretProvider(base BaseSecret) SecretProvider {
|
||||||
|
for k, v := range secretProviders {
|
||||||
|
if strings.HasPrefix(c.Secrets.URL, k) {
|
||||||
|
return v.newFn(base, c.Secrets.URL, c.Secrets.masterKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secret defines the struct used to store confidential data
|
||||||
|
type Secret struct {
|
||||||
|
sync.RWMutex
|
||||||
|
provider SecretProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON return the JSON encoding of the Secret object
|
||||||
|
func (s *Secret) MarshalJSON() ([]byte, error) {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return json.Marshal(&BaseSecret{
|
||||||
|
Status: s.provider.GetStatus(),
|
||||||
|
Payload: s.provider.GetPayload(),
|
||||||
|
Key: s.provider.GetKey(),
|
||||||
|
AdditionalData: s.provider.GetAdditionalData(),
|
||||||
|
Mode: s.provider.GetMode(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON parses the JSON-encoded data and stores the result
|
||||||
|
// in the Secret object
|
||||||
|
func (s *Secret) UnmarshalJSON(data []byte) error {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
baseSecret := BaseSecret{}
|
||||||
|
err := json.Unmarshal(data, &baseSecret)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if baseSecret.isEmpty() {
|
||||||
|
s.provider = config.getSecretProvider(baseSecret)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if baseSecret.Status == SecretStatusPlain || baseSecret.Status == SecretStatusRedacted {
|
||||||
|
s.provider = config.getSecretProvider(baseSecret)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range secretProviders {
|
||||||
|
if v.encryptedStatus == baseSecret.Status {
|
||||||
|
s.provider = v.newFn(baseSecret, config.Secrets.URL, config.Secrets.masterKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.Debug(logSender, "", "no provider registered for status %#v", baseSecret.Status)
|
||||||
|
|
||||||
|
return ErrInvalidSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEqual returns true if all the secrets fields are equal
|
||||||
|
func (s *Secret) IsEqual(other *Secret) bool {
|
||||||
|
if s.GetStatus() != other.GetStatus() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.GetPayload() != other.GetPayload() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.GetKey() != other.GetKey() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.GetAdditionalData() != other.GetAdditionalData() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.GetMode() != other.GetMode() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a copy of the secret object
|
||||||
|
func (s *Secret) Clone() *Secret {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return &Secret{
|
||||||
|
provider: s.provider.Clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEncrypted returns true if the secret is encrypted
|
||||||
|
// This isn't a pointer receiver because we don't want to pass
|
||||||
|
// a pointer to html template
|
||||||
|
func (s *Secret) IsEncrypted() bool {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.provider.IsEncrypted()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPlain returns true if the secret is in plain text
|
||||||
|
func (s *Secret) IsPlain() bool {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.provider.GetStatus() == SecretStatusPlain
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotPlainAndNotEmpty returns true if the secret is not plain and not empty.
|
||||||
|
// This is an utility method, we update the secret for an existing user
|
||||||
|
// if it is empty or plain
|
||||||
|
func (s *Secret) IsNotPlainAndNotEmpty() bool {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return !s.IsPlain() && !s.IsEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRedacted returns true if the secret is redacted
|
||||||
|
func (s *Secret) IsRedacted() bool {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.provider.GetStatus() == SecretStatusRedacted
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayload returns the secret payload
|
||||||
|
func (s *Secret) GetPayload() string {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.provider.GetPayload()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAdditionalData returns the secret additional data
|
||||||
|
func (s *Secret) GetAdditionalData() string {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.provider.GetAdditionalData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatus returns the secret status
|
||||||
|
func (s *Secret) GetStatus() SecretStatus {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.provider.GetStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKey returns the secret key
|
||||||
|
func (s *Secret) GetKey() string {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.provider.GetKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMode returns the secret mode
|
||||||
|
func (s *Secret) GetMode() int {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.provider.GetMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAdditionalData sets the given additional data
|
||||||
|
func (s *Secret) SetAdditionalData(value string) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.provider.SetAdditionalData(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStatus sets the status for this secret
|
||||||
|
func (s *Secret) SetStatus(value SecretStatus) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.provider.SetStatus(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKey sets the key for this secret
|
||||||
|
func (s *Secret) SetKey(value string) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.provider.SetKey(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if all fields are empty
|
||||||
|
func (s *Secret) IsEmpty() bool {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
if s.provider.GetStatus() != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.provider.GetPayload() != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.provider.GetKey() != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.provider.GetAdditionalData() != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns true if the secret is not empty and valid
|
||||||
|
func (s *Secret) IsValid() bool {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
if !s.IsValidInput() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch s.provider.GetStatus() {
|
||||||
|
case SecretStatusAES256GCM, SecretStatusSecretBox:
|
||||||
|
if len(s.provider.GetKey()) != 64 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case SecretStatusAWS, SecretStatusGCP, SecretStatusVaultTransit:
|
||||||
|
key := s.provider.GetKey()
|
||||||
|
if key != "" && len(key) != 64 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidInput returns true if the secret is a valid user input
|
||||||
|
func (s *Secret) IsValidInput() bool {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
if !util.IsStringInSlice(s.provider.GetStatus(), validSecretStatuses) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.provider.GetPayload() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide hides info to decrypt data
|
||||||
|
func (s *Secret) Hide() {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.provider.SetKey("")
|
||||||
|
s.provider.SetAdditionalData("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts a plain text Secret object
|
||||||
|
func (s *Secret) Encrypt() error {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
return s.provider.Encrypt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a Secret object
|
||||||
|
func (s *Secret) Decrypt() error {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
return s.provider.Decrypt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryDecrypt decrypts a Secret object if encrypted.
|
||||||
|
// It returns a nil error if the object is not encrypted
|
||||||
|
func (s *Secret) TryDecrypt() error {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
if s.provider.IsEncrypted() {
|
||||||
|
return s.provider.Decrypt()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/go-plugin"
|
"github.com/hashicorp/go-plugin"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
kmsplugin "github.com/drakkan/sftpgo/v2/sdk/plugin/kms"
|
kmsplugin "github.com/drakkan/sftpgo/v2/sdk/plugin/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin/auth"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin/auth"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin/eventsearcher"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin/eventsearcher"
|
||||||
kmsplugin "github.com/drakkan/sftpgo/v2/sdk/plugin/kms"
|
kmsplugin "github.com/drakkan/sftpgo/v2/sdk/plugin/kms"
|
||||||
|
|
|
@ -3,7 +3,7 @@ package sdk
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/config"
|
"github.com/drakkan/sftpgo/v2/config"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/ftpd"
|
"github.com/drakkan/sftpgo/v2/ftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/version"
|
"github.com/drakkan/sftpgo/v2/version"
|
||||||
|
|
|
@ -15,8 +15,8 @@ import (
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/httpdtest"
|
"github.com/drakkan/sftpgo/v2/httpdtest"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ import (
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/common"
|
"github.com/drakkan/sftpgo/v2/common"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
|
@ -42,10 +42,11 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/config"
|
"github.com/drakkan/sftpgo/v2/config"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/httpdtest"
|
"github.com/drakkan/sftpgo/v2/httpdtest"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
_ "github.com/drakkan/sftpgo/v2/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
|
|
|
@ -3,8 +3,8 @@ package vfs
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,9 @@ import (
|
||||||
"google.golang.org/api/iterator"
|
"google.golang.org/api/iterator"
|
||||||
"google.golang.org/api/option"
|
"google.golang.org/api/option"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/metric"
|
"github.com/drakkan/sftpgo/v2/metric"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/version"
|
"github.com/drakkan/sftpgo/v2/version"
|
||||||
|
|
|
@ -19,9 +19,9 @@ import (
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/version"
|
"github.com/drakkan/sftpgo/v2/version"
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,9 +16,9 @@ import (
|
||||||
"github.com/eikenb/pipeat"
|
"github.com/eikenb/pipeat"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk/plugin/metadata"
|
"github.com/drakkan/sftpgo/v2/sdk/plugin/metadata"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
|
|
|
@ -22,8 +22,8 @@ import (
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/common"
|
"github.com/drakkan/sftpgo/v2/common"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,9 +31,10 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/httpclient"
|
"github.com/drakkan/sftpgo/v2/httpclient"
|
||||||
"github.com/drakkan/sftpgo/v2/httpdtest"
|
"github.com/drakkan/sftpgo/v2/httpdtest"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
_ "github.com/drakkan/sftpgo/v2/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
"github.com/drakkan/sftpgo/v2/webdavd"
|
"github.com/drakkan/sftpgo/v2/webdavd"
|
||||||
|
|
Loading…
Reference in a new issue