Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions conf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type Configuration struct {
METADATA_DB_TLS_CA string
USER_PASS_BASED_AUTH bool
CONNECTION_TOKEN string
ENCRYPTION_SECRET_KEY string
// SANDBOX_SLACK_BOT_TOKEN string
// SANDBOX_SLACK_CHANNEL_ID string
// SANDBOX_UI_URL string
Expand Down
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ func runMemphis(s *server.Server) db.MetadataStorage {

s.InitializeMemphisHandlers()

err = server.EncryptOldUnencryptedValues()
if err != nil {
s.Errorf("Failed encrypt old unencrypted values: " + err.Error())
}

err = server.InitializeIntegrations()
if err != nil {
s.Errorf("Failed initializing integrations: " + err.Error())
Expand Down
63 changes: 63 additions & 0 deletions server/ciphersuites.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
package server

import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
)

// Where we maintain all of the available ciphers
Expand Down Expand Up @@ -101,3 +107,60 @@ func defaultCurvePreferences() []tls.CurveID {
tls.CurveP521,
}
}

func EncryptAES(plaintext []byte) (string, error) {
key := getAESKey()
c, err := aes.NewCipher(key)
if err != nil {
return "", err
}

blockSize := c.BlockSize()
padding := blockSize - len(plaintext)%blockSize
paddedPlaintext := append(plaintext, bytes.Repeat([]byte{byte(padding)}, padding)...)

ciphertext := make([]byte, len(paddedPlaintext))
mode := cipher.NewCBCEncrypter(c, key[:blockSize])
mode.CryptBlocks(ciphertext, paddedPlaintext)
return base64.URLEncoding.EncodeToString(ciphertext), nil
}

func DecryptAES(encryptedValue string) (string, error) {
key := getAESKey()
ciphertextBytes, err := base64.URLEncoding.DecodeString(encryptedValue)
if err != nil {
return "", err
}

c, err := aes.NewCipher(key)
if err != nil {
return "", err
}

blockSize := c.BlockSize()
if len(ciphertextBytes) < blockSize {
return "", errors.New("ciphertext too short")
}

plaintext := make([]byte, len(ciphertextBytes))
mode := cipher.NewCBCDecrypter(c, key[:blockSize])
mode.CryptBlocks(plaintext, ciphertextBytes)

padSize := int(plaintext[len(plaintext)-1])
if padSize < 1 || padSize > aes.BlockSize {
return "", fmt.Errorf("invalid padding size")
}

unpadded := plaintext[:len(plaintext)-padSize]
return string(unpadded), nil
}

func getAESKey() []byte {
var key []byte
if configuration.DOCKER_ENV == "true" || configuration.LOCAL_CLUSTER_ENV {
key = []byte(DEFAULT_ENCRYPTION_SECRET_KEY)
} else {
key = []byte(configuration.ENCRYPTION_SECRET_KEY)
}
return key
}
2 changes: 2 additions & 0 deletions server/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,6 @@ const (

// DEFAULT_FETCH_TIMEOUT is the default time that the system will wait for an account fetch to return.
DEFAULT_ACCOUNT_FETCH_TIMEOUT = 1900 * time.Millisecond

DEFAULT_ENCRYPTION_SECRET_KEY = "thisis32bitlongpassphraseimusing"
)
86 changes: 86 additions & 0 deletions server/integrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ func InitializeConnection(integrationsType string) error {
} else if !exist {
return nil
}
if value, ok := integration.Keys["secret_key"]; ok {
decryptedValue, err := DecryptAES(value)
if err != nil {
return err
}
integration.Keys["secret_key"] = decryptedValue
} else if value, ok := integration.Keys["auth_token"]; ok {
decryptedValue, err := DecryptAES(value)
if err != nil {
return err
}
integration.Keys["auth_token"] = decryptedValue
}
CacheDetails(integrationsType, integration.Keys, integration.Properties)
return nil
}
Expand All @@ -71,3 +84,76 @@ func CacheDetails(integrationType string, keys map[string]string, properties map
}

}

func EncryptOldUnencryptedValues() error {
err := encryptUnencryptedKeysByIntegrationType("s3", "secret_key")
if err != nil {
return err
}

err = encryptUnencryptedKeysByIntegrationType("slack", "auth_token")
if err != nil {
return err
}

err = encryptUnencryptedAppUsersPasswords()
if err != nil {
return err
}
return nil
}

func encryptUnencryptedKeysByIntegrationType(integrationType, keyTitle string) error {
exist, integration, err := db.GetIntegration(integrationType)
needToEncrypt := false
if err != nil {
return err
} else if !exist {
return nil
}
if value, ok := integration.Keys["secret_key"]; ok {
_, err := DecryptAES(value)
if err != nil {
needToEncrypt = true
}
} else if value, ok := integration.Keys["auth_token"]; ok {
_, err := DecryptAES(value)
if err != nil {
needToEncrypt = true
}
}
if needToEncrypt {
encryptedValue, err := EncryptAES([]byte(integration.Keys[keyTitle]))
if err != nil {
return err
}
integration.Keys[keyTitle] = encryptedValue
_, err = db.UpdateIntegration(integrationType, integration.Keys, integration.Properties)
if err != nil {
return err
}
}
return nil
}

func encryptUnencryptedAppUsersPasswords() error {
users, err := db.GetAllUsersByType("application")
if err != nil {
return err
}
for _, user := range users {
_, err := DecryptAES(user.Password)
if err != nil {
password, err := EncryptAES([]byte(user.Password))
if err != nil {
return err
}

err = db.ChangeUserPassword(user.Username, password)
if err != nil {
return err
}
}
}
return nil
}
7 changes: 6 additions & 1 deletion server/memphis_handlers_user_mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,12 @@ func (umh UserMgmtHandler) AddUser(c *gin.Context) {
c.AbortWithStatusJSON(SHOWABLE_ERROR_STATUS_CODE, gin.H{"message": "Password was not provided"})
return
}
password = body.Password
password, err = EncryptAES([]byte(body.Password))
if err != nil {
serv.Errorf("AddUser: User " + body.Username + ": " + err.Error())
c.AbortWithStatusJSON(500, gin.H{"message": "Server error"})
return
}
avatarId = 1
if body.AvatarId > 0 {
avatarId = body.AvatarId
Expand Down
6 changes: 5 additions & 1 deletion server/memphis_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,11 @@ func (s *Server) GetMemphisOpts(opts Options) (Options, error) {
appUsers := []*User{{Username: "root", Password: configuration.ROOT_PASSWORD}}
appUsers = append(appUsers, &User{Username: MEMPHIS_USERNAME, Password: configuration.CONNECTION_TOKEN})
for _, user := range users {
appUsers = append(appUsers, &User{Username: user.Username, Password: user.Password})
decryptedUserPassword, err := DecryptAES(user.Password)
if err != nil {
return Options{}, err
}
appUsers = append(appUsers, &User{Username: user.Username, Password: decryptedUserPassword})
}
opts.Users = appUsers
}
Expand Down
17 changes: 15 additions & 2 deletions server/notifications_slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,13 @@ func createSlackIntegration(keys map[string]string, properties map[string]bool,
if err != nil {
return slackIntegration, err
}
integrationRes, insertErr := db.InsertNewIntegration("slack", keys, properties)
cloneKeys := copyMaps(keys)
encryptedValue, err := EncryptAES([]byte(keys["auth_token"]))
if err != nil {
return models.Integration{}, err
}
cloneKeys["auth_token"] = encryptedValue
integrationRes, insertErr := db.InsertNewIntegration("slack", cloneKeys, properties)
if insertErr != nil {
return slackIntegration, insertErr
}
Expand Down Expand Up @@ -236,10 +242,17 @@ func updateSlackIntegration(authToken string, channelID string, pmAlert bool, sv
return slackIntegration, err
}
keys, properties := createIntegrationsKeysAndProperties("slack", authToken, channelID, pmAlert, svfAlert, disconnectAlert, "", "", "", "")
slackIntegration, err = db.UpdateIntegration("slack", keys, properties)
cloneKeys := copyMaps(keys)
encryptedValue, err := EncryptAES([]byte(authToken))
if err != nil {
return models.Integration{}, err
}
cloneKeys["auth_token"] = encryptedValue
slackIntegration, err = db.UpdateIntegration("slack", cloneKeys, properties)
if err != nil {
return models.Integration{}, err
}

integrationToUpdate := models.CreateIntegrationSchema{
Name: "slack",
Keys: keys,
Expand Down
23 changes: 21 additions & 2 deletions server/storage_s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ func (it IntegrationsHandler) handleS3Integrtation(keys map[string]string) (int,
if !exist {
return SHOWABLE_ERROR_STATUS_CODE, map[string]string{}, errors.New("secret key is invalid")
}
if value, ok := integrationFromDb.Keys["secret_key"]; ok {
decryptedValue, err := DecryptAES(value)
if err != nil {
return 500, map[string]string{}, err
}
integrationFromDb.Keys["secret_key"] = decryptedValue
}
secretKey = integrationFromDb.Keys["secret_key"]
keys["secret_key"] = secretKey
}
Expand Down Expand Up @@ -141,7 +148,13 @@ func createS3Integration(keys map[string]string, properties map[string]bool) (mo
if err != nil {
return models.Integration{}, err
} else if !exist {
integrationRes, insertErr := db.InsertNewIntegration("s3", keys, properties)
cloneKeys := copyMaps(keys)
encryptedValue, err := EncryptAES([]byte(keys["secret_key"]))
if err != nil {
return models.Integration{}, err
}
cloneKeys["secret_key"] = encryptedValue
integrationRes, insertErr := db.InsertNewIntegration("s3", cloneKeys, properties)
if insertErr != nil {
return models.Integration{}, insertErr
}
Expand All @@ -167,7 +180,13 @@ func createS3Integration(keys map[string]string, properties map[string]bool) (mo
}

func updateS3Integration(keys map[string]string, properties map[string]bool) (models.Integration, error) {
s3Integration, err := db.UpdateIntegration("s3", keys, properties)
cloneKeys := copyMaps(keys)
encryptedValue, err := EncryptAES([]byte(keys["secret_key"]))
if err != nil {
return models.Integration{}, err
}
cloneKeys["secret_key"] = encryptedValue
s3Integration, err := db.UpdateIntegration("s3", cloneKeys, properties)
if err != nil {
return models.Integration{}, err
}
Expand Down
13 changes: 13 additions & 0 deletions server/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,16 @@ func copyStrings(src []string) []string {
copy(dst, src)
return dst
}

// copyMaps make a new map of the same size than `src` and copy its content.
// If `src` is nil, then this returns `nil`
func copyMaps(src map[string]string) map[string]string {
if src == nil {
return nil
}
dst := make(map[string]string, len(src))
for k, v := range src {
dst[k] = v
}
return dst
}