mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 02:27:37 +00:00
Set secrets raw support
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -9,6 +10,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/Infisical/infisical-merge/packages/api"
|
"github.com/Infisical/infisical-merge/packages/api"
|
||||||
"github.com/Infisical/infisical-merge/packages/crypto"
|
"github.com/Infisical/infisical-merge/packages/crypto"
|
||||||
@@ -806,3 +808,339 @@ func GetPlainTextWorkspaceKey(authenticationToken string, receiverPrivateKey str
|
|||||||
|
|
||||||
return crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey), nil
|
return crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetEncryptedSecrets(secretArgs []string, secretType string, environmentName string, secretsPath string) ([]models.SecretSetOperation, error) {
|
||||||
|
|
||||||
|
workspaceFile, err := GetWorkSpaceFromFile()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to get your local config details [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
loggedInUserDetails, err := GetCurrentLoggedInUserDetails()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to authenticate [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if loggedInUserDetails.LoginExpired {
|
||||||
|
PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := resty.New().
|
||||||
|
SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken).
|
||||||
|
SetHeader("Accept", "application/json")
|
||||||
|
|
||||||
|
request := api.GetEncryptedWorkspaceKeyRequest{
|
||||||
|
WorkspaceId: workspaceFile.WorkspaceId,
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaceKeyResponse, err := api.CallGetEncryptedWorkspaceKey(httpClient, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to get your encrypted workspace key [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedWorkspaceKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.EncryptedKey)
|
||||||
|
encryptedWorkspaceKeySenderPublicKey, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Sender.PublicKey)
|
||||||
|
encryptedWorkspaceKeyNonce, _ := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce)
|
||||||
|
currentUsersPrivateKey, _ := base64.StdEncoding.DecodeString(loggedInUserDetails.UserCredentials.PrivateKey)
|
||||||
|
|
||||||
|
if len(currentUsersPrivateKey) == 0 || len(encryptedWorkspaceKeySenderPublicKey) == 0 {
|
||||||
|
log.Debug().Msgf("Missing credentials for generating plainTextEncryptionKey: [currentUsersPrivateKey=%s] [encryptedWorkspaceKeySenderPublicKey=%s]", currentUsersPrivateKey, encryptedWorkspaceKeySenderPublicKey)
|
||||||
|
PrintErrorMessageAndExit("Some required user credentials are missing to generate your [plainTextEncryptionKey]. Please run [infisical login] then try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt workspace key
|
||||||
|
plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||||
|
|
||||||
|
infisicalTokenEnv := os.Getenv(INFISICAL_TOKEN_NAME)
|
||||||
|
|
||||||
|
// pull current secrets
|
||||||
|
secrets, err := GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, SecretsPath: secretsPath, InfisicalToken: infisicalTokenEnv}, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to retrieve secrets [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsToCreate := []api.Secret{}
|
||||||
|
secretsToModify := []api.Secret{}
|
||||||
|
secretOperations := []models.SecretSetOperation{}
|
||||||
|
|
||||||
|
sharedSecretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets))
|
||||||
|
personalSecretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets))
|
||||||
|
|
||||||
|
for _, secret := range secrets {
|
||||||
|
if secret.Type == SECRET_TYPE_PERSONAL {
|
||||||
|
personalSecretMapByName[secret.Key] = secret
|
||||||
|
} else {
|
||||||
|
sharedSecretMapByName[secret.Key] = secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range secretArgs {
|
||||||
|
splitKeyValueFromArg := strings.SplitN(arg, "=", 2)
|
||||||
|
if splitKeyValueFromArg[0] == "" || splitKeyValueFromArg[1] == "" {
|
||||||
|
PrintErrorMessageAndExit("ensure that each secret has a none empty key and value. Modify the input and try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
if unicode.IsNumber(rune(splitKeyValueFromArg[0][0])) {
|
||||||
|
PrintErrorMessageAndExit("keys of secrets cannot start with a number. Modify the key name(s) and try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key and value from argument
|
||||||
|
key := splitKeyValueFromArg[0]
|
||||||
|
value := splitKeyValueFromArg[1]
|
||||||
|
|
||||||
|
hashedKey := fmt.Sprintf("%x", sha256.Sum256([]byte(key)))
|
||||||
|
encryptedKey, err := crypto.EncryptSymmetric([]byte(key), []byte(plainTextEncryptionKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to encrypt your secrets [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashedValue := fmt.Sprintf("%x", sha256.Sum256([]byte(value)))
|
||||||
|
encryptedValue, err := crypto.EncryptSymmetric([]byte(value), []byte(plainTextEncryptionKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to encrypt your secrets [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingSecret models.SingleEnvironmentVariable
|
||||||
|
var doesSecretExist bool
|
||||||
|
|
||||||
|
if secretType == SECRET_TYPE_SHARED {
|
||||||
|
existingSecret, doesSecretExist = sharedSecretMapByName[key]
|
||||||
|
} else {
|
||||||
|
existingSecret, doesSecretExist = personalSecretMapByName[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
if doesSecretExist {
|
||||||
|
// case: secret exists in project so it needs to be modified
|
||||||
|
encryptedSecretDetails := api.Secret{
|
||||||
|
ID: existingSecret.ID,
|
||||||
|
SecretValueCiphertext: base64.StdEncoding.EncodeToString(encryptedValue.CipherText),
|
||||||
|
SecretValueIV: base64.StdEncoding.EncodeToString(encryptedValue.Nonce),
|
||||||
|
SecretValueTag: base64.StdEncoding.EncodeToString(encryptedValue.AuthTag),
|
||||||
|
SecretValueHash: hashedValue,
|
||||||
|
PlainTextKey: key,
|
||||||
|
Type: existingSecret.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add to modifications if the value is different
|
||||||
|
if existingSecret.Value != value {
|
||||||
|
secretsToModify = append(secretsToModify, encryptedSecretDetails)
|
||||||
|
secretOperations = append(secretOperations, models.SecretSetOperation{
|
||||||
|
SecretKey: key,
|
||||||
|
SecretValue: value,
|
||||||
|
SecretOperation: "SECRET VALUE MODIFIED",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Current value is same as exisitng so no change
|
||||||
|
secretOperations = append(secretOperations, models.SecretSetOperation{
|
||||||
|
SecretKey: key,
|
||||||
|
SecretValue: value,
|
||||||
|
SecretOperation: "SECRET VALUE UNCHANGED",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// case: secret doesn't exist in project so it needs to be created
|
||||||
|
encryptedSecretDetails := api.Secret{
|
||||||
|
SecretKeyCiphertext: base64.StdEncoding.EncodeToString(encryptedKey.CipherText),
|
||||||
|
SecretKeyIV: base64.StdEncoding.EncodeToString(encryptedKey.Nonce),
|
||||||
|
SecretKeyTag: base64.StdEncoding.EncodeToString(encryptedKey.AuthTag),
|
||||||
|
SecretKeyHash: hashedKey,
|
||||||
|
SecretValueCiphertext: base64.StdEncoding.EncodeToString(encryptedValue.CipherText),
|
||||||
|
SecretValueIV: base64.StdEncoding.EncodeToString(encryptedValue.Nonce),
|
||||||
|
SecretValueTag: base64.StdEncoding.EncodeToString(encryptedValue.AuthTag),
|
||||||
|
SecretValueHash: hashedValue,
|
||||||
|
Type: secretType,
|
||||||
|
PlainTextKey: key,
|
||||||
|
}
|
||||||
|
secretsToCreate = append(secretsToCreate, encryptedSecretDetails)
|
||||||
|
secretOperations = append(secretOperations, models.SecretSetOperation{
|
||||||
|
SecretKey: key,
|
||||||
|
SecretValue: value,
|
||||||
|
SecretOperation: "SECRET CREATED",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, secret := range secretsToCreate {
|
||||||
|
createSecretRequest := api.CreateSecretV3Request{
|
||||||
|
WorkspaceID: workspaceFile.WorkspaceId,
|
||||||
|
Environment: environmentName,
|
||||||
|
SecretName: secret.PlainTextKey,
|
||||||
|
SecretKeyCiphertext: secret.SecretKeyCiphertext,
|
||||||
|
SecretKeyIV: secret.SecretKeyIV,
|
||||||
|
SecretKeyTag: secret.SecretKeyTag,
|
||||||
|
SecretValueCiphertext: secret.SecretValueCiphertext,
|
||||||
|
SecretValueIV: secret.SecretValueIV,
|
||||||
|
SecretValueTag: secret.SecretValueTag,
|
||||||
|
Type: secret.Type,
|
||||||
|
SecretPath: secretsPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = api.CallCreateSecretsV3(httpClient, createSecretRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to process new secret creations [err=%v]", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, secret := range secretsToModify {
|
||||||
|
updateSecretRequest := api.UpdateSecretByNameV3Request{
|
||||||
|
WorkspaceID: workspaceFile.WorkspaceId,
|
||||||
|
Environment: environmentName,
|
||||||
|
SecretValueCiphertext: secret.SecretValueCiphertext,
|
||||||
|
SecretValueIV: secret.SecretValueIV,
|
||||||
|
SecretValueTag: secret.SecretValueTag,
|
||||||
|
Type: secret.Type,
|
||||||
|
SecretPath: secretsPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = api.CallUpdateSecretsV3(httpClient, updateSecretRequest, secret.PlainTextKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to process secret update request [err=%v]", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return secretOperations, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRawSecrets(secretArgs []string, secretType string, environmentName string, secretsPath string, projectId string, tokenDetails *models.TokenDetails) ([]models.SecretSetOperation, error) {
|
||||||
|
|
||||||
|
if tokenDetails == nil {
|
||||||
|
return nil, fmt.Errorf("unable to process set secret operations, token details are missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\nToken details %v", tokenDetails)
|
||||||
|
|
||||||
|
getAllEnvironmentVariablesRequest := models.GetAllSecretsParameters{Environment: environmentName, SecretsPath: secretsPath, WorkspaceId: projectId}
|
||||||
|
if tokenDetails.Type == UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||||
|
getAllEnvironmentVariablesRequest.UniversalAuthAccessToken = tokenDetails.Token
|
||||||
|
} else {
|
||||||
|
getAllEnvironmentVariablesRequest.InfisicalToken = tokenDetails.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := resty.New().
|
||||||
|
SetAuthScheme("Bearer").
|
||||||
|
SetAuthToken(tokenDetails.Token).
|
||||||
|
SetHeader("Accept", "application/json")
|
||||||
|
|
||||||
|
// pull current secrets
|
||||||
|
secrets, err := GetAllEnvironmentVariables(getAllEnvironmentVariablesRequest, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to retrieve secrets [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsToCreate := []api.RawSecret{}
|
||||||
|
secretsToModify := []api.RawSecret{}
|
||||||
|
secretOperations := []models.SecretSetOperation{}
|
||||||
|
|
||||||
|
sharedSecretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets))
|
||||||
|
personalSecretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets))
|
||||||
|
|
||||||
|
for _, secret := range secrets {
|
||||||
|
if secret.Type == SECRET_TYPE_PERSONAL {
|
||||||
|
personalSecretMapByName[secret.Key] = secret
|
||||||
|
} else {
|
||||||
|
sharedSecretMapByName[secret.Key] = secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range secretArgs {
|
||||||
|
splitKeyValueFromArg := strings.SplitN(arg, "=", 2)
|
||||||
|
if splitKeyValueFromArg[0] == "" || splitKeyValueFromArg[1] == "" {
|
||||||
|
PrintErrorMessageAndExit("ensure that each secret has a none empty key and value. Modify the input and try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
if unicode.IsNumber(rune(splitKeyValueFromArg[0][0])) {
|
||||||
|
PrintErrorMessageAndExit("keys of secrets cannot start with a number. Modify the key name(s) and try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key and value from argument
|
||||||
|
key := splitKeyValueFromArg[0]
|
||||||
|
value := splitKeyValueFromArg[1]
|
||||||
|
|
||||||
|
var existingSecret models.SingleEnvironmentVariable
|
||||||
|
var doesSecretExist bool
|
||||||
|
|
||||||
|
if secretType == SECRET_TYPE_SHARED {
|
||||||
|
existingSecret, doesSecretExist = sharedSecretMapByName[key]
|
||||||
|
} else {
|
||||||
|
existingSecret, doesSecretExist = personalSecretMapByName[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
if doesSecretExist {
|
||||||
|
// case: secret exists in project so it needs to be modified
|
||||||
|
encryptedSecretDetails := api.RawSecret{
|
||||||
|
ID: existingSecret.ID,
|
||||||
|
SecretValue: value,
|
||||||
|
SecretKey: key,
|
||||||
|
Type: existingSecret.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add to modifications if the value is different
|
||||||
|
if existingSecret.Value != value {
|
||||||
|
secretsToModify = append(secretsToModify, encryptedSecretDetails)
|
||||||
|
secretOperations = append(secretOperations, models.SecretSetOperation{
|
||||||
|
SecretKey: key,
|
||||||
|
SecretValue: value,
|
||||||
|
SecretOperation: "SECRET VALUE MODIFIED",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Current value is same as existing so no change
|
||||||
|
secretOperations = append(secretOperations, models.SecretSetOperation{
|
||||||
|
SecretKey: key,
|
||||||
|
SecretValue: value,
|
||||||
|
SecretOperation: "SECRET VALUE UNCHANGED",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// case: secret doesn't exist in project so it needs to be created
|
||||||
|
encryptedSecretDetails := api.RawSecret{
|
||||||
|
SecretKey: key,
|
||||||
|
SecretValue: value,
|
||||||
|
Type: secretType,
|
||||||
|
}
|
||||||
|
secretsToCreate = append(secretsToCreate, encryptedSecretDetails)
|
||||||
|
secretOperations = append(secretOperations, models.SecretSetOperation{
|
||||||
|
SecretKey: key,
|
||||||
|
SecretValue: value,
|
||||||
|
SecretOperation: "SECRET CREATED",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, secret := range secretsToCreate {
|
||||||
|
createSecretRequest := api.CreateRawSecretV3Request{
|
||||||
|
SecretName: secret.SecretKey,
|
||||||
|
SecretValue: secret.SecretValue,
|
||||||
|
Type: secret.Type,
|
||||||
|
SecretPath: secretsPath,
|
||||||
|
WorkspaceID: projectId,
|
||||||
|
Environment: environmentName,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = api.CallCreateRawSecretsV3(httpClient, createSecretRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to process new secret creations [err=%v]", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, secret := range secretsToModify {
|
||||||
|
updateSecretRequest := api.UpdateRawSecretByNameV3Request{
|
||||||
|
SecretName: secret.SecretKey,
|
||||||
|
SecretValue: secret.SecretValue,
|
||||||
|
SecretPath: secretsPath,
|
||||||
|
WorkspaceID: projectId,
|
||||||
|
Environment: environmentName,
|
||||||
|
Type: secret.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = api.CallUpdateRawSecretsV3(httpClient, updateSecretRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to process secret update request [err=%v]", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return secretOperations, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user