Add service token v2 api into cli

This commit is contained in:
Maidul Islam
2023-01-05 16:58:10 -05:00
parent c921eb8781
commit 764636cd47
12 changed files with 349 additions and 144 deletions
-2
View File
@@ -14,8 +14,6 @@ const (
SECRET_TYPE_SHARED = "shared"
)
var INFISICAL_URL = "https://app.infisical.com/api"
func GetHomeDir() (string, error) {
directory, err := os.UserHomeDir()
return directory, err
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/99designs/keyring"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/models"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
@@ -92,7 +93,7 @@ func IsUserLoggedIn() (hasUserLoggedIn bool, theUsersEmail string, err error) {
response, err := httpClient.
R().
Post(fmt.Sprintf("%v/v1/auth/checkAuth", INFISICAL_URL))
Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL))
if err != nil {
return false, "", err
@@ -132,7 +133,7 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
response, err := httpClient.
R().
Post(fmt.Sprintf("%v/v1/auth/checkAuth", INFISICAL_URL))
Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL))
if err != nil {
return LoggedInUserDetails{}, err
-81
View File
@@ -1,81 +0,0 @@
package util
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
"github.com/Infisical/infisical-merge/packages/models"
"golang.org/x/crypto/nacl/box"
)
// will decrypt cipher text to plain text using iv and tag
func DecryptSymmetric(key []byte, cipherText []byte, tag []byte, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCMWithNonceSize(block, len(iv))
if err != nil {
return nil, err
}
var nonce = iv
var ciphertext = append(cipherText, tag...) // the aesgcm open method expects auth tag at the end of the cipher text
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
func GenerateNewKey() (newKey []byte, keyErr error) {
key := make([]byte, 16) // block size defaults to 16 so this is fine
_, err := rand.Read(key)
return key, err
}
// Will encrypt a plain text with the provided key
func EncryptSymmetric(plaintext []byte, key []byte) (result models.SymmetricEncryptionResult, err error) {
block, err := aes.NewCipher(key)
if err != nil {
return models.SymmetricEncryptionResult{}, err
}
aesgcm, err := cipher.NewGCMWithNonceSize(block, 16) // default is 12, 16 because https://github.com/Infisical/infisical/blob/bea0ff6e05a4de73a5db625d4ae181a015b50855/backend/src/utils/aes-gcm.ts#L4
if err != nil {
return models.SymmetricEncryptionResult{}, err
}
// create a nonce
nonce := make([]byte, aesgcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err)
}
ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
ciphertextOnly := ciphertext[:len(ciphertext)-16] // combines the auth tag with the cipher text so we need to extract it
authTag := ciphertext[len(ciphertext)-16:]
return models.SymmetricEncryptionResult{
CipherText: ciphertextOnly,
AuthTag: authTag,
Nonce: nonce,
}, nil
}
func DecryptAsymmetric(ciphertext []byte, nonce []byte, publicKey []byte, privateKey []byte) (plainText []byte) {
plainTextToReturn, _ := box.Open(nil, ciphertext, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey))
return plainTextToReturn
}
func EncryptAssymmetric(message []byte, nonce []byte, publicKey []byte, privateKey []byte) (encryptedMessage []byte) {
encryptedPlainText := box.Seal(nil, message, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey))
return encryptedPlainText
}
+56
View File
@@ -0,0 +1,56 @@
package util
import (
"encoding/base64"
"fmt"
)
type DecodedSymmetricEncryptionDetails = struct {
Cipher []byte
IV []byte
Tag []byte
Key []byte
}
func GetBase64DecodedSymmetricEncryptionDetails(key string, cipher string, IV string, tag string) (DecodedSymmetricEncryptionDetails, error) {
cipherx, err := base64.StdEncoding.DecodeString(cipher)
if err != nil {
return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode cipher text [err=%v]", err)
}
keyx, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode key [err=%v]", err)
}
IVx, err := base64.StdEncoding.DecodeString(IV)
if err != nil {
return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode IV [err=%v]", err)
}
tagx, err := base64.StdEncoding.DecodeString(tag)
if err != nil {
return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode tag [err=%v]", err)
}
return DecodedSymmetricEncryptionDetails{
Key: keyx,
Cipher: cipherx,
IV: IVx,
Tag: tagx,
}, nil
}
func IsSecretEnvironmentValid(env string) bool {
if env == "prod" || env == "dev" || env == "test" || env == "staging" {
return true
}
return false
}
func IsSecretTypeValid(s string) bool {
if s == "personal" || s == "shared" {
return true
}
return false
}
+182 -91
View File
@@ -2,13 +2,16 @@ package util
import (
"encoding/base64"
"errors"
"fmt"
"os"
"regexp"
"strings"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/crypto"
"github.com/Infisical/infisical-merge/packages/http"
"github.com/Infisical/infisical-merge/packages/models"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
)
@@ -23,7 +26,7 @@ func getSecretsByWorkspaceIdAndEnvName(httpClient resty.Client, envName string,
SetQueryParam("environment", envName).
SetQueryParam("channel", "cli").
SetResult(&pullSecretsRequestResponse).
Get(fmt.Sprintf("%v/v1/secret/%v", INFISICAL_URL, workspace.WorkspaceId)) // need to change workspace id
Get(fmt.Sprintf("%v/v1/secret/%v", config.INFISICAL_URL, workspace.WorkspaceId)) // need to change workspace id
if err != nil {
return nil, err
@@ -55,7 +58,7 @@ func getSecretsByWorkspaceIdAndEnvName(httpClient resty.Client, envName string,
}
// log.Debugln("workspaceKey", workspaceKey, "nonce", nonce, "senderPublicKey", senderPublicKey, "currentUsersPrivateKey", currentUsersPrivateKey)
workspaceKeyInBytes := DecryptAsymmetric(workspaceKey, nonce, senderPublicKey, currentUsersPrivateKey)
workspaceKeyInBytes := crypto.DecryptAsymmetric(workspaceKey, nonce, senderPublicKey, currentUsersPrivateKey)
var listOfEnv []models.SingleEnvironmentVariable
for _, secret := range pullSecretsRequestResponse.Secrets {
@@ -63,7 +66,7 @@ func getSecretsByWorkspaceIdAndEnvName(httpClient resty.Client, envName string,
key_tag, _ := base64.StdEncoding.DecodeString(secret.SecretKeyTag)
key_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretKeyCiphertext)
plainTextKey, err := DecryptSymmetric(workspaceKeyInBytes, key_ciphertext, key_tag, key_iv)
plainTextKey, err := crypto.DecryptSymmetric(workspaceKeyInBytes, key_ciphertext, key_tag, key_iv)
if err != nil {
return nil, err
}
@@ -72,7 +75,7 @@ func getSecretsByWorkspaceIdAndEnvName(httpClient resty.Client, envName string,
value_tag, _ := base64.StdEncoding.DecodeString(secret.SecretValueTag)
value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValueCiphertext)
plainTextValue, err := DecryptSymmetric(workspaceKeyInBytes, value_ciphertext, value_tag, value_iv)
plainTextValue, err := crypto.DecryptSymmetric(workspaceKeyInBytes, value_ciphertext, value_tag, value_iv)
if err != nil {
return nil, err
}
@@ -116,90 +119,116 @@ func GetSecretsFromAPIUsingCurrentLoggedInUser(envName string, userCreds models.
return secrets, nil
}
func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, projectId string) ([]models.SingleEnvironmentVariable, error) {
if infisicalToken == "" || projectId == "" || envName == "" {
return nil, errors.New("infisical token, project id and or environment name cannot be empty")
}
splitToken := strings.Split(infisicalToken, ",")
JTWToken := splitToken[0]
temPrivateKey := splitToken[1]
// func GetPlainTextSecretsViaJWT(JTWToken string, privateKey string, workspaceId string, environment string) ([]models.SingleEnvironmentVariable, error) {
// create http client
httpClient := resty.New().
SetAuthToken(JTWToken).
// // get key first
// // Get workspace key
// workspaceKey, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.EncryptedKey)
// if err != nil {
// return nil, err
// }
// nonce, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.Nonce)
// if err != nil {
// return nil, err
// }
// senderPublicKey, err := base64.StdEncoding.DecodeString(pullSecretsRequestResponse.Key.Sender.PublicKey)
// if err != nil {
// return nil, err
// }
// currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(userCreds.PrivateKey)
// if err != nil {
// return nil, err
// }
// // log.Debugln("workspaceKey", workspaceKey, "nonce", nonce, "senderPublicKey", senderPublicKey, "currentUsersPrivateKey", currentUsersPrivateKey)
// workspaceKeyInBytes := crypto.DecryptAsymmetric(workspaceKey, nonce, senderPublicKey, currentUsersPrivateKey)
// httpClient := resty.New()
// httpClient.SetAuthToken(JTWToken).
// SetHeader("Accept", "application/json")
// serviceTokenDetails, err := http.CallGetServiceTokenDetailsV2(httpClient)
// if err != nil {
// return nil, fmt.Errorf("unable to get service token details. [err=%v]", err)
// }
// encryptedSecrets, err := http.CallGetSecretsV2(httpClient, models.GetEncryptedSecretsV2Request{
// WorkspaceId: workspaceId,
// EnvironmentName: environment,
// })
// if err != nil {
// return nil, err
// }
// // workspace key
// decodedSymmetricEncryptionDetails, err := GetBase64DecodedSymmetricEncryptionDetails(serviceTokenParts[3], serviceTokenDetails.EncryptedKey, serviceTokenDetails.Iv, serviceTokenDetails.Tag)
// if err != nil {
// return nil, fmt.Errorf("unable to decode symmetric encryption details [err=%v]", err)
// }
// plainTextWorkspaceKey, err := crypto.DecryptSymmetric(decodedSymmetricEncryptionDetails.Key, decodedSymmetricEncryptionDetails.Cipher, decodedSymmetricEncryptionDetails.Tag, decodedSymmetricEncryptionDetails.IV)
// if err != nil {
// return nil, fmt.Errorf("unable to decrypt the required workspace key")
// }
// plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets)
// if err != nil {
// return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
// }
// return plainTextSecrets, nil
// }
func GetPlainTextSecretsViaServiceToken(fullServiceToken string) ([]models.SingleEnvironmentVariable, error) {
// encryotion key, http,
serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4)
if len(serviceTokenParts) < 4 {
return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
}
serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2])
httpClient := resty.New()
httpClient.SetAuthToken(serviceToken).
SetHeader("Accept", "application/json")
var pullSecretsByInfisicalTokenResponse models.PullSecretsByInfisicalTokenResponse
response, err := httpClient.
R().
SetQueryParam("environment", envName).
SetQueryParam("channel", "cli").
SetResult(&pullSecretsByInfisicalTokenResponse).
Get(fmt.Sprintf("%v/v1/secret/%v/service-token", INFISICAL_URL, projectId))
serviceTokenDetails, err := http.CallGetServiceTokenDetailsV2(httpClient)
if err != nil {
return nil, fmt.Errorf("unable to get service token details. [err=%v]", err)
}
encryptedSecrets, err := http.CallGetSecretsV2(httpClient, models.GetEncryptedSecretsV2Request{
WorkspaceId: serviceTokenDetails.Workspace,
EnvironmentName: serviceTokenDetails.Environment,
})
if err != nil {
return nil, err
}
if response.StatusCode() > 299 {
return nil, fmt.Errorf(response.Status())
}
// Get workspace key
workspaceKey, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.EncryptedKey)
decodedSymmetricEncryptionDetails, err := GetBase64DecodedSymmetricEncryptionDetails(serviceTokenParts[3], serviceTokenDetails.EncryptedKey, serviceTokenDetails.Iv, serviceTokenDetails.Tag)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to decode symmetric encryption details [err=%v]", err)
}
nonce, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.Nonce)
plainTextWorkspaceKey, err := crypto.DecryptSymmetric(decodedSymmetricEncryptionDetails.Key, decodedSymmetricEncryptionDetails.Cipher, decodedSymmetricEncryptionDetails.Tag, decodedSymmetricEncryptionDetails.IV)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to decrypt the required workspace key")
}
senderPublicKey, err := base64.StdEncoding.DecodeString(pullSecretsByInfisicalTokenResponse.Key.Sender.PublicKey)
plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
}
currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(temPrivateKey)
if err != nil {
return nil, err
}
// workspaceKeyInBytes, _ := box.Open(nil, workspaceKey, (*[24]byte)(nonce), (*[32]byte)(senderPublicKey), (*[32]byte)(currentUsersPrivateKey))
workspaceKeyInBytes := DecryptAsymmetric(workspaceKey, nonce, senderPublicKey, currentUsersPrivateKey)
var listOfEnv []models.SingleEnvironmentVariable
for _, secret := range pullSecretsByInfisicalTokenResponse.Secrets {
key_iv, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Iv)
key_tag, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Tag)
key_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretKey.Ciphertext)
plainTextKey, err := DecryptSymmetric(workspaceKeyInBytes, key_ciphertext, key_tag, key_iv)
if err != nil {
return nil, err
}
value_iv, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Iv)
value_tag, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Tag)
value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValue.Ciphertext)
plainTextValue, err := DecryptSymmetric(workspaceKeyInBytes, value_ciphertext, value_tag, value_iv)
if err != nil {
return nil, err
}
env := models.SingleEnvironmentVariable{
Key: string(plainTextKey),
Value: string(plainTextValue),
Type: string(secret.Type),
ID: secret.ID,
}
listOfEnv = append(listOfEnv, env)
}
return listOfEnv, nil
return plainTextSecrets, nil
}
func GetAllEnvironmentVariables(projectId string, envName string) ([]models.SingleEnvironmentVariable, error) {
@@ -246,14 +275,7 @@ func GetAllEnvironmentVariables(projectId string, envName string) ([]models.Sing
return envsFromApi, nil
} else {
envsFromApi, err := GetSecretsFromAPIUsingInfisicalToken(infisicalToken, envName, projectId)
if err != nil {
log.Errorln("Something went wrong when pulling secrets using your Infisical token. Double check the token, project id or environment name (dev, prod, ect.)")
log.Debugln(err)
return nil, err
}
return envsFromApi, nil
return GetPlainTextSecretsViaServiceToken(infisicalToken)
}
}
@@ -267,7 +289,7 @@ func GetWorkSpacesFromAPI(userCreds models.UserCredentials) (workspaces []models
response, err := httpClient.
R().
SetResult(&getWorkSpacesResponse).
Get(fmt.Sprintf("%v/v1/workspace", INFISICAL_URL))
Get(fmt.Sprintf("%v/v1/workspace", config.INFISICAL_URL))
if err != nil {
return nil, err
@@ -389,16 +411,85 @@ func OverrideWithPersonalSecrets(secrets []models.SingleEnvironmentVariable) []m
return secretsToReturn
}
func IsSecretEnvironmentValid(env string) bool {
if env == "prod" || env == "dev" || env == "test" || env == "staging" {
return true
func GetPlainTextSecrets(key []byte, encryptedSecrets models.GetEncryptedSecretsV2Response) ([]models.SingleEnvironmentVariable, error) {
plainTextSecrets := []models.SingleEnvironmentVariable{}
for _, secret := range encryptedSecrets {
// Decrypt key
key_iv, err := base64.StdEncoding.DecodeString(secret.SecretKeyIV)
if err != nil {
return nil, fmt.Errorf("unable to decode secret IV for secret key")
}
key_tag, err := base64.StdEncoding.DecodeString(secret.SecretKeyTag)
if err != nil {
return nil, fmt.Errorf("unable to decode secret authentication tag for secret key")
}
key_ciphertext, err := base64.StdEncoding.DecodeString(secret.SecretKeyCiphertext)
if err != nil {
return nil, fmt.Errorf("unable to decode secret cipher text for secret key")
}
plainTextKey, err := crypto.DecryptSymmetric(key, key_ciphertext, key_tag, key_iv)
if err != nil {
return nil, fmt.Errorf("unable to symmetrically decrypt secret key")
}
// Decrypt value
value_iv, err := base64.StdEncoding.DecodeString(secret.SecretValueIV)
if err != nil {
return nil, fmt.Errorf("unable to decode secret IV for secret value")
}
value_tag, err := base64.StdEncoding.DecodeString(secret.SecretValueTag)
if err != nil {
return nil, fmt.Errorf("unable to decode secret authentication tag for secret value")
}
value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValueCiphertext)
if err != nil {
return nil, fmt.Errorf("unable to decode secret cipher text for secret key")
}
plainTextValue, err := crypto.DecryptSymmetric(key, value_ciphertext, value_tag, value_iv)
if err != nil {
return nil, fmt.Errorf("unable to symmetrically decrypt secret value")
}
plainTextSecret := models.SingleEnvironmentVariable{
Key: string(plainTextKey),
Value: string(plainTextValue),
Type: string(secret.Type),
ID: secret.ID,
}
plainTextSecrets = append(plainTextSecrets, plainTextSecret)
}
return false
return plainTextSecrets, nil
}
func IsSecretTypeValid(s string) bool {
if s == "personal" || s == "shared" {
return true
}
return false
}
// func GetWorkspaceKeyViaEnvelopeEncryption(JTWToken string, workspaceId string, privateKey string) {
// httpClient := resty.New().
// SetAuthToken(JTWToken).
// SetHeader("Accept", "application/json")
// request := models.GetEncryptedWorkspaceKeyRequest{
// WorkspaceId: workspaceId,
// }
// workspaceKeyResponse, err := http.CallGetEncryptedWorkspaceKey(httpClient, request)
// if err != nil {
// log.Errorf("unable to get your encrypted workspace key. [err=%v]", err)
// return
// }
// 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)
// // decrypt workspace key
// plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
// }