feat(secret-import): updated cli to support secret import

This commit is contained in:
akhilmhdh
2023-07-17 23:10:14 +05:30
parent 45584e0c1a
commit cf45c3dc8b
6 changed files with 119 additions and 47 deletions
+4
View File
@@ -235,6 +235,10 @@ func CallGetSecretsV3(httpClient *resty.Client, request GetEncryptedSecretsV3Req
SetQueryParam("environment", request.Environment). SetQueryParam("environment", request.Environment).
SetQueryParam("workspaceId", request.WorkspaceId) SetQueryParam("workspaceId", request.WorkspaceId)
if request.IncludeImport {
httpRequest.SetQueryParam("include_imports", "true")
}
if request.SecretPath != "" { if request.SecretPath != "" {
httpRequest.SetQueryParam("secretPath", request.SecretPath) httpRequest.SetQueryParam("secretPath", request.SecretPath)
} }
+42 -31
View File
@@ -272,40 +272,51 @@ type GetNewAccessTokenWithRefreshTokenResponse struct {
} }
type GetEncryptedSecretsV3Request struct { type GetEncryptedSecretsV3Request struct {
Environment string `json:"environment"` Environment string `json:"environment"`
WorkspaceId string `json:"workspaceId"` WorkspaceId string `json:"workspaceId"`
SecretPath string `json:"secretPath"` SecretPath string `json:"secretPath"`
IncludeImport bool `json:"include_imports"`
}
type EncryptedSecretV3 struct {
ID string `json:"_id"`
Version int `json:"version"`
Workspace string `json:"workspace"`
Type string `json:"type"`
Tags []struct {
ID string `json:"_id"`
Name string `json:"name"`
Slug string `json:"slug"`
Workspace string `json:"workspace"`
} `json:"tags"`
Environment string `json:"environment"`
SecretKeyCiphertext string `json:"secretKeyCiphertext"`
SecretKeyIV string `json:"secretKeyIV"`
SecretKeyTag string `json:"secretKeyTag"`
SecretValueCiphertext string `json:"secretValueCiphertext"`
SecretValueIV string `json:"secretValueIV"`
SecretValueTag string `json:"secretValueTag"`
SecretCommentCiphertext string `json:"secretCommentCiphertext"`
SecretCommentIV string `json:"secretCommentIV"`
SecretCommentTag string `json:"secretCommentTag"`
Algorithm string `json:"algorithm"`
KeyEncoding string `json:"keyEncoding"`
Folder string `json:"folder"`
V int `json:"__v"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type ImportedSecretV3 struct {
Environment string `json:"environment"`
FolderId string `json:"folderId"`
SecretPath string `json:"secretPath"`
Secrets []EncryptedSecretV3 `json:"secrets"`
} }
type GetEncryptedSecretsV3Response struct { type GetEncryptedSecretsV3Response struct {
Secrets []struct { Secrets []EncryptedSecretV3 `json:"secrets"`
ID string `json:"_id"` ImportedSecrets []ImportedSecretV3 `json:"imports,omitempty"`
Version int `json:"version"`
Workspace string `json:"workspace"`
Type string `json:"type"`
Tags []struct {
ID string `json:"_id"`
Name string `json:"name"`
Slug string `json:"slug"`
Workspace string `json:"workspace"`
} `json:"tags"`
Environment string `json:"environment"`
SecretKeyCiphertext string `json:"secretKeyCiphertext"`
SecretKeyIV string `json:"secretKeyIV"`
SecretKeyTag string `json:"secretKeyTag"`
SecretValueCiphertext string `json:"secretValueCiphertext"`
SecretValueIV string `json:"secretValueIV"`
SecretValueTag string `json:"secretValueTag"`
SecretCommentCiphertext string `json:"secretCommentCiphertext"`
SecretCommentIV string `json:"secretCommentIV"`
SecretCommentTag string `json:"secretCommentTag"`
Algorithm string `json:"algorithm"`
KeyEncoding string `json:"keyEncoding"`
Folder string `json:"folder"`
V int `json:"__v"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
} `json:"secrets"`
} }
type CreateSecretV3Request struct { type CreateSecretV3Request struct {
+7 -1
View File
@@ -87,7 +87,12 @@ var runCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
} }
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}) includeImports, err := cmd.Flags().GetBool("include-imports")
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: includeImports})
if err != nil { if err != nil {
util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid") util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid")
@@ -186,6 +191,7 @@ func init() {
runCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token") runCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from") runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
runCmd.Flags().Bool("include-imports", true, "Import linked secrets ")
runCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets") runCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")") runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")")
runCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs ") runCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs ")
+7 -1
View File
@@ -54,12 +54,17 @@ var secretsCmd = &cobra.Command{
util.HandleError(err) util.HandleError(err)
} }
includeImports, err := cmd.Flags().GetBool("include-imports")
if err != nil {
util.HandleError(err)
}
tagSlugs, err := cmd.Flags().GetString("tags") tagSlugs, err := cmd.Flags().GetString("tags")
if err != nil { if err != nil {
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
} }
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}) secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: includeImports})
if err != nil { if err != nil {
util.HandleError(err) util.HandleError(err)
} }
@@ -647,6 +652,7 @@ func init() {
secretsCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token") secretsCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on")
secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
secretsCmd.Flags().Bool("include-imports", true, "Imported linked secrets ")
secretsCmd.PersistentFlags().StringP("tags", "t", "", "filter secrets by tag slugs") secretsCmd.PersistentFlags().StringP("tags", "t", "", "filter secrets by tag slugs")
secretsCmd.Flags().String("path", "/", "get secrets within a folder path") secretsCmd.Flags().String("path", "/", "get secrets within a folder path")
rootCmd.AddCommand(secretsCmd) rootCmd.AddCommand(secretsCmd)
+1
View File
@@ -65,4 +65,5 @@ type GetAllSecretsParameters struct {
TagSlugs string TagSlugs string
WorkspaceId string WorkspaceId string
SecretsPath string SecretsPath string
IncludeImport bool
} }
+58 -14
View File
@@ -17,7 +17,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string) ([]models.SingleEnvironmentVariable, api.GetServiceTokenDetailsResponse, error) { func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool) ([]models.SingleEnvironmentVariable, api.GetServiceTokenDetailsResponse, error) {
serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4) serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4)
if len(serviceTokenParts) < 4 { if len(serviceTokenParts) < 4 {
return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("invalid service token entered. Please double check your service token and try again") return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
@@ -45,9 +45,10 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment str
} }
encryptedSecrets, err := api.CallGetSecretsV3(httpClient, api.GetEncryptedSecretsV3Request{ encryptedSecrets, err := api.CallGetSecretsV3(httpClient, api.GetEncryptedSecretsV3Request{
WorkspaceId: serviceTokenDetails.Workspace, WorkspaceId: serviceTokenDetails.Workspace,
Environment: environment, Environment: environment,
SecretPath: secretPath, SecretPath: secretPath,
IncludeImport: includeImports,
}) })
if err != nil { if err != nil {
@@ -64,15 +65,22 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment str
return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to decrypt the required workspace key") return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to decrypt the required workspace key")
} }
plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets) plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets.Secrets)
if err != nil { if err != nil {
return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to decrypt your secrets [err=%v]", err) return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
} }
if includeImports {
plainTextSecrets, err = InjectImportedSecret(plainTextWorkspaceKey, plainTextSecrets, encryptedSecrets.ImportedSecrets)
if err != nil {
return nil, api.GetServiceTokenDetailsResponse{}, err
}
}
return plainTextSecrets, serviceTokenDetails, nil return plainTextSecrets, serviceTokenDetails, nil
} }
func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, workspaceId string, environmentName string, tagSlugs string, secretsPath string) ([]models.SingleEnvironmentVariable, error) { func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, workspaceId string, environmentName string, tagSlugs string, secretsPath string, includeImports bool) ([]models.SingleEnvironmentVariable, error) {
httpClient := resty.New() httpClient := resty.New()
httpClient.SetAuthToken(JTWToken). httpClient.SetAuthToken(JTWToken).
SetHeader("Accept", "application/json") SetHeader("Accept", "application/json")
@@ -114,8 +122,9 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work
plainTextWorkspaceKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey) plainTextWorkspaceKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
getSecretsRequest := api.GetEncryptedSecretsV3Request{ getSecretsRequest := api.GetEncryptedSecretsV3Request{
WorkspaceId: workspaceId, WorkspaceId: workspaceId,
Environment: environmentName, Environment: environmentName,
IncludeImport: includeImports,
// TagSlugs: tagSlugs, // TagSlugs: tagSlugs,
} }
@@ -124,19 +133,53 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work
} }
encryptedSecrets, err := api.CallGetSecretsV3(httpClient, getSecretsRequest) encryptedSecrets, err := api.CallGetSecretsV3(httpClient, getSecretsRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets) plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets.Secrets)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err) return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
} }
if includeImports {
plainTextSecrets, err = InjectImportedSecret(plainTextWorkspaceKey, plainTextSecrets, encryptedSecrets.ImportedSecrets)
if err != nil {
return nil, err
}
}
return plainTextSecrets, nil return plainTextSecrets, nil
} }
func InjectImportedSecret(plainTextWorkspaceKey []byte, secrets []models.SingleEnvironmentVariable, importedSecrets []api.ImportedSecretV3) ([]models.SingleEnvironmentVariable, error) {
if importedSecrets == nil {
return secrets, nil
}
hasOverriden := make(map[string]bool)
for _, sec := range secrets {
hasOverriden[sec.Key] = true
}
for i := len(importedSecrets) - 1; i >= 0; i-- {
importSec := importedSecrets[i]
plainTextImportedSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, importSec.Secrets)
fmt.Println(plainTextImportedSecrets)
if err != nil {
return nil, fmt.Errorf("unable to decrypt your imported secrets [err=%v]", err)
}
for _, sec := range plainTextImportedSecrets {
if _, ok := hasOverriden[sec.Key]; !ok {
secrets = append(secrets, sec)
hasOverriden[sec.Key] = true
}
}
}
return secrets, nil
}
func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models.SingleEnvironmentVariable, error) { func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models.SingleEnvironmentVariable, error) {
var infisicalToken string var infisicalToken string
if params.InfisicalToken == "" { if params.InfisicalToken == "" {
@@ -179,7 +222,8 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
return nil, fmt.Errorf("unable to validate environment name because [err=%s]", err) return nil, fmt.Errorf("unable to validate environment name because [err=%s]", err)
} }
secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, params.Environment, params.TagSlugs, params.SecretsPath) secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId,
params.Environment, params.TagSlugs, params.SecretsPath, params.IncludeImport)
log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn) log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn)
backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32] backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32]
@@ -199,7 +243,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
} else { } else {
log.Debug().Msg("Trying to fetch secrets using service token") log.Debug().Msg("Trying to fetch secrets using service token")
secretsToReturn, _, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken, params.Environment, params.SecretsPath) secretsToReturn, _, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken, params.Environment, params.SecretsPath, params.IncludeImport)
} }
return secretsToReturn, errorToReturn return secretsToReturn, errorToReturn
@@ -427,9 +471,9 @@ func OverrideSecrets(secrets []models.SingleEnvironmentVariable, secretType stri
return secretsToReturn return secretsToReturn
} }
func GetPlainTextSecrets(key []byte, encryptedSecrets api.GetEncryptedSecretsV3Response) ([]models.SingleEnvironmentVariable, error) { func GetPlainTextSecrets(key []byte, encryptedSecrets []api.EncryptedSecretV3) ([]models.SingleEnvironmentVariable, error) {
plainTextSecrets := []models.SingleEnvironmentVariable{} plainTextSecrets := []models.SingleEnvironmentVariable{}
for _, secret := range encryptedSecrets.Secrets { for _, secret := range encryptedSecrets {
// Decrypt key // Decrypt key
key_iv, err := base64.StdEncoding.DecodeString(secret.SecretKeyIV) key_iv, err := base64.StdEncoding.DecodeString(secret.SecretKeyIV)
if err != nil { if err != nil {