feat(be): add access key encryption

This commit is contained in:
Denis Gukov 2021-08-31 04:02:41 +05:00
parent ab098b8b82
commit 7443671059
5 changed files with 112 additions and 6 deletions

View File

@ -152,8 +152,14 @@ func UpdateKey(w http.ResponseWriter, r *http.Request) {
// override secret
key.Secret = oldKey.Secret
} else {
secret := *key.Secret + "\n"
key.Secret = &secret
*key.Secret += "\n"
err := key.EncryptSecret()
if err != nil {
helpers.WriteError(w, err)
return
}
}
if err := helpers.Store(r).UpdateAccessKey(key); err != nil {

View File

@ -345,7 +345,13 @@ func (t *task) installKey(key db.AccessKey) error {
}
}
return ioutil.WriteFile(path, []byte(*key.Secret), 0600)
secret, err := key.DecryptSecret()
if err != nil {
return err
}
return ioutil.WriteFile(path, []byte(secret), 0600)
}
func (t *task) updateRepository() error {

View File

@ -29,7 +29,7 @@ func doSetup() int {
var config *util.ConfigType
for {
config = &util.ConfigType{}
config.GenerateCookieSecrets()
config.GenerateSecrets()
setup.InteractiveSetup(config)
if setup.AskConfigConfirmation(config) {

View File

@ -1,6 +1,12 @@
package db
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"strconv"
"github.com/ansible-semaphore/semaphore/util"
@ -30,3 +36,88 @@ type AccessKey struct {
func (key AccessKey) GetPath() string {
return util.Config.TmpPath + "/access_key_" + strconv.Itoa(key.ID)
}
func (key *AccessKey) EncryptSecret() error {
if key.Secret == nil || *key.Secret == "" {
return nil
}
if util.Config.AccessKeyEncryption == "" { // do not encrypt if secret key not specified
return nil
}
plaintext := []byte(*key.Secret)
encryption, err := base64.StdEncoding.DecodeString(util.Config.CookieEncryption)
if err != nil {
return err
}
c, err := aes.NewCipher(encryption)
if err != nil {
return err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return err
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return err
}
secret := string(gcm.Seal(nonce, nonce, plaintext, nil))
key.Secret = &secret
return nil
}
func (key AccessKey) DecryptSecret() (string, error) {
if key.Secret == nil || *key.Secret == "" {
return "", nil
}
ciphertext := []byte(*key.Secret)
if ciphertext[len(ciphertext) - 1] == '\n' { // not encrypted string
return *key.Secret, nil
}
if util.Config.AccessKeyEncryption == "" { // do not decrypt if secret key not specified
return *key.Secret, nil
}
encryption, err := base64.StdEncoding.DecodeString(util.Config.CookieEncryption)
if err != nil {
return "", err
}
c, err := aes.NewCipher(encryption)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return "", err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return "", fmt.Errorf("ciphertext too short")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
encrypted, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(encrypted), nil
}

View File

@ -66,6 +66,7 @@ type ConfigType struct {
// cookie hashing & encryption
CookieHash string `json:"cookie_hash"`
CookieEncryption string `json:"cookie_encryption"`
AccessKeyEncryption string `json:"access_key_encryption"`
// email alerting
EmailSender string `json:"email_sender"`
@ -292,11 +293,13 @@ func (conf *ConfigType) GetDBConfig() (dbConfig DbConfig, err error) {
return
}
//GenerateCookieSecrets generates cookie secret during setup
func (conf *ConfigType) GenerateCookieSecrets() {
//GenerateSecrets generates cookie secret during setup
func (conf *ConfigType) GenerateSecrets() {
hash := securecookie.GenerateRandomKey(32)
encryption := securecookie.GenerateRandomKey(32)
accessKeyEncryption := securecookie.GenerateRandomKey(32)
conf.CookieHash = base64.StdEncoding.EncodeToString(hash)
conf.CookieEncryption = base64.StdEncoding.EncodeToString(encryption)
conf.AccessKeyEncryption = base64.StdEncoding.EncodeToString(accessKeyEncryption)
}