mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
kms: remove support for compat secrets
also document how to activate the deprecated builtin provider
This commit is contained in:
parent
4df0ae82ac
commit
6ee51c5cc1
6 changed files with 11 additions and 89 deletions
|
@ -6,7 +6,7 @@
|
|||
[![Docker Pulls](https://img.shields.io/docker/pulls/drakkan/sftpgo)](https://hub.docker.com/r/drakkan/sftpgo)
|
||||
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
|
||||
|
||||
Fully featured and highly configurable SFTP server with optional HTTP, FTP/S and WebDAV support, written in Go.
|
||||
Fully featured and highly configurable SFTP server with optional HTTP, FTP/S and WebDAV support.
|
||||
Several storage backends are supported: local filesystem, encrypted local filesystem, S3 (compatible) Object Storage, Google Cloud Storage, Azure Blob Storage, SFTP.
|
||||
|
||||
## Features
|
||||
|
|
|
@ -19,7 +19,7 @@ We first generate a random key, then the per-object encryption key is derived fr
|
|||
1. a master key is provided: the encryption key is derived using the HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as defined in [RFC 5869](http://tools.ietf.org/html/rfc5869)
|
||||
2. no master key is provided: the encryption key is derived as simple hash of the random key. This is the default configuration.
|
||||
|
||||
For compatibility with SFTPGo versions 1.2.x and before we also support encryption based on `AES-256-GCM`. The data encrypted with this algorithm will never use the master key to keep backward compatibility.
|
||||
For compatibility with SFTPGo versions 1.2.x and before we also support encryption based on `AES-256-GCM`. The data encrypted with this algorithm will never use the master key to keep backward compatibility. You can activate it using `builtin://` as `url` but this is not recommended.
|
||||
|
||||
### Cloud providers
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ const (
|
|||
maxRequestSize = 1048576 // 1MB
|
||||
maxLoginBodySize = 262144 // 256 KB
|
||||
httpdMaxEditFileSize = 1048576 // 1 MB
|
||||
maxMultipartMem = 8388608 // 8MB
|
||||
maxMultipartMem = 10485760 // 10 MB
|
||||
osWindows = "windows"
|
||||
otpHeaderCode = "X-SFTPGO-OTP"
|
||||
)
|
||||
|
|
|
@ -2857,14 +2857,6 @@ func TestSecretObject(t *testing.T) {
|
|||
require.Equal(t, kms.SecretStatusPlain, s.GetStatus())
|
||||
require.Equal(t, "test data", s.GetPayload())
|
||||
require.Empty(t, s.GetKey())
|
||||
|
||||
oldFormat := "$aes$5b97e3a3324a2f53e2357483383367c0$0ed3132b584742ab217866219da633266782b69b13e50ebc6ddfb7c4fbf2f2a414c6d5f813"
|
||||
s, err = kms.GetSecretFromCompatString(oldFormat)
|
||||
require.NoError(t, err)
|
||||
require.True(t, s.IsValid())
|
||||
require.Equal(t, kms.SecretStatusPlain, s.GetStatus())
|
||||
require.Equal(t, "test data", s.GetPayload())
|
||||
require.Empty(t, s.GetKey())
|
||||
}
|
||||
|
||||
func TestSecretObjectCompatibility(t *testing.T) {
|
||||
|
@ -8988,6 +8980,10 @@ func TestShareUncompressed(t *testing.T) {
|
|||
checkResponseCode(t, http.StatusOK, rr)
|
||||
assert.Equal(t, "application/octet-stream", rr.Header().Get("Content-Type"))
|
||||
|
||||
share, err = dataprovider.ShareExists(objectID, user.Username)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, share.UsedTokens)
|
||||
|
||||
user.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
|
@ -8998,6 +8994,10 @@ func TestShareUncompressed(t *testing.T) {
|
|||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
|
||||
share, err = dataprovider.ShareExists(objectID, user.Username)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, share.UsedTokens)
|
||||
|
||||
user.Permissions["/"] = []string{dataprovider.PermAny}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -125,15 +125,6 @@ func NewPlainSecret(payload string) *Secret {
|
|||
return NewSecret(SecretStatusPlain, payload, "", "")
|
||||
}
|
||||
|
||||
// GetSecretFromCompatString returns a secret from the previous format
|
||||
func GetSecretFromCompatString(secret string) (*Secret, error) {
|
||||
plain, err := util.DecryptData(secret)
|
||||
if err != nil {
|
||||
return &Secret{}, errMalformedCiphertext
|
||||
}
|
||||
return NewSecret(SecretStatusPlain, plain, "", ""), nil
|
||||
}
|
||||
|
||||
// Initialize configures the KMS support
|
||||
func (c *Configuration) Initialize() error {
|
||||
if c.Secrets.MasterKeyString != "" {
|
||||
|
|
69
util/util.go
69
util/util.go
|
@ -3,8 +3,6 @@ package util
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
|
@ -12,7 +10,6 @@ import (
|
|||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -154,72 +151,6 @@ func NilIfEmpty(s string) *string {
|
|||
return &s
|
||||
}
|
||||
|
||||
// EncryptData encrypts data using the given key
|
||||
func EncryptData(data string) (string, error) {
|
||||
var result string
|
||||
key := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||
return result, err
|
||||
}
|
||||
keyHex := hex.EncodeToString(key)
|
||||
block, err := aes.NewCipher([]byte(keyHex))
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return result, err
|
||||
}
|
||||
ciphertext := gcm.Seal(nonce, nonce, []byte(data), nil)
|
||||
result = fmt.Sprintf("$aes$%s$%x", keyHex, ciphertext)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// RemoveDecryptionKey returns encrypted data without the decryption key
|
||||
func RemoveDecryptionKey(encryptData string) string {
|
||||
vals := strings.Split(encryptData, "$")
|
||||
if len(vals) == 4 {
|
||||
return fmt.Sprintf("$%v$%v", vals[1], vals[3])
|
||||
}
|
||||
return encryptData
|
||||
}
|
||||
|
||||
// DecryptData decrypts data encrypted using EncryptData
|
||||
func DecryptData(data string) (string, error) {
|
||||
var result string
|
||||
vals := strings.Split(data, "$")
|
||||
if len(vals) != 4 {
|
||||
return "", errors.New("data to decrypt is not in the correct format")
|
||||
}
|
||||
key := vals[2]
|
||||
encrypted, err := hex.DecodeString(vals[3])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
block, err := aes.NewCipher([]byte(key))
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(encrypted) < nonceSize {
|
||||
return result, errors.New("malformed ciphertext")
|
||||
}
|
||||
nonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:]
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
// GenerateRSAKeys generate rsa private and public keys and write the
|
||||
// private key to specified file and the public key to the specified
|
||||
// file adding the .pub suffix
|
||||
|
|
Loading…
Reference in a new issue