From b2c62c4193910992631096a1a4182e0ad3929c98 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 14 Jul 2024 15:05:53 +0530 Subject: [PATCH 01/13] feat(cli): changed all secret endpoint to raw endpoint --- cli/packages/api/api.go | 122 +----------- cli/packages/cmd/agent.go | 2 +- cli/packages/cmd/secrets.go | 24 ++- cli/packages/util/config.go | 5 - cli/packages/util/credentials.go | 4 - cli/packages/util/secrets.go | 317 ++----------------------------- 6 files changed, 43 insertions(+), 431 deletions(-) diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index fd1dcc574..3aa07ad6f 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -225,25 +225,6 @@ func CallIsAuthenticated(httpClient *resty.Client) bool { return true } -func CallGetAccessibleEnvironments(httpClient *resty.Client, request GetAccessibleEnvironmentsRequest) (GetAccessibleEnvironmentsResponse, error) { - var accessibleEnvironmentsResponse GetAccessibleEnvironmentsResponse - response, err := httpClient. - R(). - SetResult(&accessibleEnvironmentsResponse). - SetHeader("User-Agent", USER_AGENT). - Get(fmt.Sprintf("%v/v2/workspace/%s/environments", config.INFISICAL_URL, request.WorkspaceId)) - - if err != nil { - return GetAccessibleEnvironmentsResponse{}, err - } - - if response.IsError() { - return GetAccessibleEnvironmentsResponse{}, fmt.Errorf("CallGetAccessibleEnvironments: Unsuccessful response: [response=%v] [response-code=%v] [url=%s]", response, response.StatusCode(), response.Request.URL) - } - - return accessibleEnvironmentsResponse, nil -} - func CallGetNewAccessTokenWithRefreshToken(httpClient *resty.Client, refreshToken string) (GetNewAccessTokenWithRefreshTokenResponse, error) { var newAccessToken GetNewAccessTokenWithRefreshTokenResponse response, err := httpClient. @@ -267,45 +248,6 @@ func CallGetNewAccessTokenWithRefreshToken(httpClient *resty.Client, refreshToke return newAccessToken, nil } -func CallGetSecretsV3(httpClient *resty.Client, request GetEncryptedSecretsV3Request) (GetEncryptedSecretsV3Response, error) { - var secretsResponse GetEncryptedSecretsV3Response - - httpRequest := httpClient. - R(). - SetResult(&secretsResponse). - SetHeader("User-Agent", USER_AGENT). - SetQueryParam("environment", request.Environment). - SetQueryParam("workspaceId", request.WorkspaceId) - - if request.Recursive { - httpRequest.SetQueryParam("recursive", "true") - } - - if request.IncludeImport { - httpRequest.SetQueryParam("include_imports", "true") - } - - if request.SecretPath != "" { - httpRequest.SetQueryParam("secretPath", request.SecretPath) - } - - response, err := httpRequest.Get(fmt.Sprintf("%v/v3/secrets", config.INFISICAL_URL)) - - if err != nil { - return GetEncryptedSecretsV3Response{}, fmt.Errorf("CallGetSecretsV3: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - if response.StatusCode() == 401 { - return GetEncryptedSecretsV3Response{}, fmt.Errorf("CallGetSecretsV3: Request to access secrets with [environment=%v] [path=%v] [workspaceId=%v] is denied. Please check if your authentication method has access to requested scope", request.Environment, request.SecretPath, request.WorkspaceId) - } else { - return GetEncryptedSecretsV3Response{}, fmt.Errorf("CallGetSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%v]", response.RawResponse) - } - } - - return secretsResponse, nil -} - func CallGetFoldersV1(httpClient *resty.Client, request GetFoldersV1Request) (GetFoldersV1Response, error) { var foldersResponse GetFoldersV1Response httpRequest := httpClient. @@ -370,27 +312,7 @@ func CallDeleteFolderV1(httpClient *resty.Client, request DeleteFolderV1Request) return folderResponse, nil } -func CallCreateSecretsV3(httpClient *resty.Client, request CreateSecretV3Request) error { - var secretsResponse GetEncryptedSecretsV3Response - response, err := httpClient. - R(). - SetResult(&secretsResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v3/secrets/%s", config.INFISICAL_URL, request.SecretName)) - - if err != nil { - return fmt.Errorf("CallCreateSecretsV3: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return fmt.Errorf("CallCreateSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) - } - - return nil -} - -func CallDeleteSecretsV3(httpClient *resty.Client, request DeleteSecretV3Request) error { +func CallDeleteSecretsRawV3(httpClient *resty.Client, request DeleteSecretV3Request) error { var secretsResponse GetEncryptedSecretsV3Response response, err := httpClient. @@ -398,7 +320,7 @@ func CallDeleteSecretsV3(httpClient *resty.Client, request DeleteSecretV3Request SetResult(&secretsResponse). SetHeader("User-Agent", USER_AGENT). SetBody(request). - Delete(fmt.Sprintf("%v/v3/secrets/%s", config.INFISICAL_URL, request.SecretName)) + Delete(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName)) if err != nil { return fmt.Errorf("CallDeleteSecretsV3: Unable to complete api request [err=%s]", err) @@ -411,46 +333,6 @@ func CallDeleteSecretsV3(httpClient *resty.Client, request DeleteSecretV3Request return nil } -func CallUpdateSecretsV3(httpClient *resty.Client, request UpdateSecretByNameV3Request, secretName string) error { - var secretsResponse GetEncryptedSecretsV3Response - response, err := httpClient. - R(). - SetResult(&secretsResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Patch(fmt.Sprintf("%v/v3/secrets/%s", config.INFISICAL_URL, secretName)) - - if err != nil { - return fmt.Errorf("CallUpdateSecretsV3: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return fmt.Errorf("CallUpdateSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) - } - - return nil -} - -func CallGetSingleSecretByNameV3(httpClient *resty.Client, request CreateSecretV3Request) error { - var secretsResponse GetEncryptedSecretsV3Response - response, err := httpClient. - R(). - SetResult(&secretsResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v3/secrets/%s", config.INFISICAL_URL, request.SecretName)) - - if err != nil { - return fmt.Errorf("CallGetSingleSecretByNameV3: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return fmt.Errorf("CallGetSingleSecretByNameV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) - } - - return nil -} - func CallCreateServiceToken(httpClient *resty.Client, request CreateServiceTokenRequest) (CreateServiceTokenResponse, error) { var createServiceTokenResponse CreateServiceTokenResponse response, err := httpClient. diff --git a/cli/packages/cmd/agent.go b/cli/packages/cmd/agent.go index f485f76a5..717a345c6 100644 --- a/cli/packages/cmd/agent.go +++ b/cli/packages/cmd/agent.go @@ -312,7 +312,7 @@ func ParseAgentConfig(configFile []byte) (*Config, error) { func secretTemplateFunction(accessToken string, existingEtag string, currentEtag *string) func(string, string, string) ([]models.SingleEnvironmentVariable, error) { return func(projectID, envSlug, secretPath string) ([]models.SingleEnvironmentVariable, error) { - res, err := util.GetPlainTextSecretsViaMachineIdentity(accessToken, projectID, envSlug, secretPath, false, false) + res, err := util.GetPlainTextSecretsV3(accessToken, projectID, envSlug, secretPath, false, false) if err != nil { return nil, err } diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index a56e002dc..52cad9747 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -187,12 +187,28 @@ var secretsSetCmd = &cobra.Command{ var secretOperations []models.SecretSetOperation if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { + fmt.Println(">>>", projectId) secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, token) } else { - util.RequireLogin() - util.RequireLocalWorkspaceFile() + // fmt.Println(">>>", projectId) + workspaceFile, err := util.GetWorkSpaceFromFile() + if err != nil { + util.HandleError(err, "unable to get your local config details [err=%v]") + } - secretOperations, err = util.SetEncryptedSecrets(args, secretType, environmentName, secretsPath) + loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() + if err != nil { + util.HandleError(err, "unable to authenticate [err=%v]") + } + + if loggedInUserDetails.LoginExpired { + util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") + } + + secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, workspaceFile.WorkspaceId, &models.TokenDetails{ + Type: "", + Token: loggedInUserDetails.UserCredentials.JTWToken, + }) } if err != nil { @@ -285,7 +301,7 @@ var secretsDeleteCmd = &cobra.Command{ SecretPath: secretsPath, } - err = api.CallDeleteSecretsV3(httpClient, request) + err = api.CallDeleteSecretsRawV3(httpClient, request) if err != nil { util.HandleError(err, "Unable to complete your delete request") } diff --git a/cli/packages/util/config.go b/cli/packages/util/config.go index 221609852..55c9df1b0 100644 --- a/cli/packages/util/config.go +++ b/cli/packages/util/config.go @@ -244,10 +244,5 @@ func WriteConfigFile(configFile *models.ConfigFile) error { return fmt.Errorf("writeConfigFile: Unable to write to file [err=%s]", err) } - if err != nil { - return fmt.Errorf("writeConfigFile: unable to write config file because an error occurred when write the config to file [err=%s]", err) - - } - return nil } diff --git a/cli/packages/util/credentials.go b/cli/packages/util/credentials.go index 4856de35a..be49e9267 100644 --- a/cli/packages/util/credentials.go +++ b/cli/packages/util/credentials.go @@ -52,10 +52,6 @@ func GetUserCredsFromKeyRing(userEmail string) (credentials models.UserCredentia return models.UserCredentials{}, fmt.Errorf("getUserCredsFromKeyRing: Something went wrong when unmarshalling user creds [err=%s]", err) } - if err != nil { - return models.UserCredentials{}, fmt.Errorf("GetUserCredsFromKeyRing: Unable to store user credentials [err=%s]", err) - } - return userCredentials, err } diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index dcb431cca..e9fc9a0ef 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -1,7 +1,6 @@ package util import ( - "crypto/sha256" "encoding/base64" "encoding/json" "errors" @@ -19,10 +18,10 @@ import ( "github.com/rs/zerolog/log" ) -func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool) ([]models.SingleEnvironmentVariable, api.GetServiceTokenDetailsResponse, error) { +func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool) ([]models.SingleEnvironmentVariable, error) { serviceTokenParts := strings.SplitN(fullServiceToken, ".", 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, 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]) @@ -34,19 +33,19 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment str serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient) if err != nil { - return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to get service token details. [err=%v]", err) + return nil, fmt.Errorf("unable to get service token details. [err=%v]", err) } // if multiple scopes are there then user needs to specify which environment and secret path if environment == "" { if len(serviceTokenDetails.Scopes) != 1 { - return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("you need to provide the --env for multiple environment scoped token") + return nil, fmt.Errorf("you need to provide the --env for multiple environment scoped token") } else { environment = serviceTokenDetails.Scopes[0].Environment } } - encryptedSecrets, err := api.CallGetSecretsV3(httpClient, api.GetEncryptedSecretsV3Request{ + rawSecrets, err := api.CallGetRawSecretsV3(httpClient, api.GetRawSecretsV3Request{ WorkspaceId: serviceTokenDetails.Workspace, Environment: environment, SecretPath: secretPath, @@ -54,109 +53,28 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment str Recursive: recursive, }) - if err != nil { - return nil, api.GetServiceTokenDetailsResponse{}, err - } - - decodedSymmetricEncryptionDetails, err := GetBase64DecodedSymmetricEncryptionDetails(serviceTokenParts[3], serviceTokenDetails.EncryptedKey, serviceTokenDetails.Iv, serviceTokenDetails.Tag) - if err != nil { - return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to decode symmetric encryption details [err=%v]", err) - } - - plainTextWorkspaceKey, err := crypto.DecryptSymmetric([]byte(serviceTokenParts[3]), decodedSymmetricEncryptionDetails.Cipher, decodedSymmetricEncryptionDetails.Tag, decodedSymmetricEncryptionDetails.IV) - if err != nil { - return nil, api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to decrypt the required workspace key") - } - - plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets.Secrets) - if err != nil { - 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 -} - -func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, workspaceId string, environmentName string, tagSlugs string, secretsPath string, includeImports bool, recursive bool) ([]models.SingleEnvironmentVariable, error) { - httpClient := resty.New() - httpClient.SetAuthToken(JTWToken). - SetHeader("Accept", "application/json") - - request := api.GetEncryptedWorkspaceKeyRequest{ - WorkspaceId: 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, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.EncryptedKey) - if err != nil { - HandleError(err, "Unable to get bytes represented by the base64 for encryptedWorkspaceKey") - } - - encryptedWorkspaceKeySenderPublicKey, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.Sender.PublicKey) - if err != nil { - HandleError(err, "Unable to get bytes represented by the base64 for encryptedWorkspaceKeySenderPublicKey") - } - - encryptedWorkspaceKeyNonce, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce) - if err != nil { - HandleError(err, "Unable to get bytes represented by the base64 for encryptedWorkspaceKeyNonce") - } - - currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(receiversPrivateKey) - if err != nil { - HandleError(err, "Unable to get bytes represented by the base64 for currentUsersPrivateKey") - } - - 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") - } - - plainTextWorkspaceKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey) - - getSecretsRequest := api.GetEncryptedSecretsV3Request{ - WorkspaceId: workspaceId, - Environment: environmentName, - IncludeImport: includeImports, - Recursive: recursive, - // TagSlugs: tagSlugs, - } - - if secretsPath != "" { - getSecretsRequest.SecretPath = secretsPath - } - - encryptedSecrets, err := api.CallGetSecretsV3(httpClient, getSecretsRequest) if err != nil { return nil, err } - plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecrets.Secrets) - if err != nil { - return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err) + plainTextSecrets := []models.SingleEnvironmentVariable{} + + for _, secret := range rawSecrets.Secrets { + plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, Type: secret.Type, WorkspaceId: secret.Workspace}) } if includeImports { - plainTextSecrets, err = InjectImportedSecret(plainTextWorkspaceKey, plainTextSecrets, encryptedSecrets.ImportedSecrets) + plainTextSecrets, err = InjectRawImportedSecret(plainTextSecrets, rawSecrets.Imports) if err != nil { return nil, err } } return plainTextSecrets, nil + } -func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId string, environmentName string, secretsPath string, includeImports bool, recursive bool) (models.PlaintextSecretResult, error) { +func GetPlainTextSecretsV3(accessToken string, workspaceId string, environmentName string, secretsPath string, includeImports bool, recursive bool) (models.PlaintextSecretResult, error) { httpClient := resty.New() httpClient.SetAuthToken(accessToken). SetHeader("Accept", "application/json") @@ -180,9 +98,6 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin } plainTextSecrets := []models.SingleEnvironmentVariable{} - if err != nil { - return models.PlaintextSecretResult{}, fmt.Errorf("unable to decrypt your secrets [err=%v]", err) - } for _, secret := range rawSecrets.Secrets { plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, Type: secret.Type, WorkspaceId: secret.Workspace}) @@ -361,8 +276,8 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo infisicalDotJson.WorkspaceId = params.WorkspaceId } - secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, infisicalDotJson.WorkspaceId, - params.Environment, params.TagSlugs, params.SecretsPath, params.IncludeImport, params.Recursive) + res, errorToReturn := GetPlainTextSecretsV3(loggedInUserDetails.UserCredentials.JTWToken, infisicalDotJson.WorkspaceId, + params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive) log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn) backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32] @@ -370,6 +285,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo WriteBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, backupSecretsEncryptionKey, secretsToReturn) } + secretsToReturn = res.Secrets // only attempt to serve cached secrets if no internet connection and if at least one secret cached if !isConnected { backedSecrets, err := ReadBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, backupSecretsEncryptionKey) @@ -383,7 +299,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo } else { if params.InfisicalToken != "" { log.Debug().Msg("Trying to fetch secrets using service token") - secretsToReturn, _, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive) + secretsToReturn, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive) } else if params.UniversalAuthAccessToken != "" { if params.WorkspaceId == "" { @@ -391,7 +307,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo } log.Debug().Msg("Trying to fetch secrets using universal auth") - res, err := GetPlainTextSecretsViaMachineIdentity(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive) + res, err := GetPlainTextSecretsV3(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive) errorToReturn = err secretsToReturn = res.Secrets @@ -591,7 +507,7 @@ func GetPlainTextSecrets(key []byte, encryptedSecrets []api.EncryptedSecretV3) ( return nil, fmt.Errorf("unable to decode secret authentication tag for secret value") } - value_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretValueCiphertext) + value_ciphertext, err := base64.StdEncoding.DecodeString(secret.SecretValueCiphertext) if err != nil { return nil, fmt.Errorf("unable to decode secret cipher text for secret key") } @@ -612,7 +528,7 @@ func GetPlainTextSecrets(key []byte, encryptedSecrets []api.EncryptedSecretV3) ( return nil, fmt.Errorf("unable to decode secret authentication tag for secret value") } - comment_ciphertext, _ := base64.StdEncoding.DecodeString(secret.SecretCommentCiphertext) + comment_ciphertext, err := base64.StdEncoding.DecodeString(secret.SecretCommentCiphertext) if err != nil { return nil, fmt.Errorf("unable to decode secret cipher text for secret key") } @@ -809,200 +725,6 @@ func GetPlainTextWorkspaceKey(authenticationToken string, receiverPrivateKey str 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 := strings.TrimSpace(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 { @@ -1116,6 +838,7 @@ func SetRawSecrets(secretArgs []string, secretType string, environmentName strin Environment: environmentName, } + fmt.Println(projectId, environmentName) err = api.CallCreateRawSecretsV3(httpClient, createSecretRequest) if err != nil { return nil, fmt.Errorf("unable to process new secret creations [err=%v]", err) From 5921dcaa519d0cff0c3f8721ad260d1155cdf2c4 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 15 Jul 2024 01:23:03 +0530 Subject: [PATCH 02/13] feat: switched operator to raw endpoints for secret management --- .../controllers/infisicalsecret_helper.go | 17 +- k8-operator/packages/api/api.go | 93 ----- k8-operator/packages/api/models.go | 1 + k8-operator/packages/util/secrets.go | 317 +++--------------- 4 files changed, 53 insertions(+), 375 deletions(-) diff --git a/k8-operator/controllers/infisicalsecret_helper.go b/k8-operator/controllers/infisicalsecret_helper.go index f92275af4..acb8b9e76 100644 --- a/k8-operator/controllers/infisicalsecret_helper.go +++ b/k8-operator/controllers/infisicalsecret_helper.go @@ -40,6 +40,10 @@ func (r *InfisicalSecretReconciler) HandleAuthentication(ctx context.Context, in if err != nil { return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err) } + if infisicalToken != "" { + infisicalClient.Auth().SetAccessToken(infisicalToken) + return AuthenticationDetails{authStrategy: AuthStrategy.SERVICE_TOKEN}, nil + } // ? Legacy support, service account auth serviceAccountCreds, err := r.GetInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret) @@ -48,9 +52,8 @@ func (r *InfisicalSecretReconciler) HandleAuthentication(ctx context.Context, in } if serviceAccountCreds.AccessKey != "" || serviceAccountCreds.PrivateKey != "" || serviceAccountCreds.PublicKey != "" { + infisicalClient.Auth().SetAccessToken(serviceAccountCreds.AccessKey) return AuthenticationDetails{authStrategy: AuthStrategy.SERVICE_ACCOUNT}, nil - } else if infisicalToken != "" { - return AuthenticationDetails{authStrategy: AuthStrategy.SERVICE_TOKEN}, nil } authStrategies := map[AuthStrategyType]func(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error){ @@ -69,7 +72,7 @@ func (r *InfisicalSecretReconciler) HandleAuthentication(ctx context.Context, in return authDetails, nil } - if err != nil && !errors.Is(err, ErrAuthNotApplicable) { + if !errors.Is(err, ErrAuthNotApplicable) { return AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err) } } @@ -360,11 +363,7 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context return fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err) } - if err != nil { - return fmt.Errorf("unable to load Infisical Token from the specified Kubernetes secret with error [%w]", err) - } - - plainTextSecretsFromApi, updateDetails, err = util.GetPlainTextSecretsViaServiceAccount(serviceAccountCreds, infisicalSecret.Spec.Authentication.ServiceAccount.ProjectId, infisicalSecret.Spec.Authentication.ServiceAccount.EnvironmentName, secretVersionBasedOnETag) + plainTextSecretsFromApi, updateDetails, err = util.GetPlainTextSecretsViaServiceAccount(infisicalClient, serviceAccountCreds, infisicalSecret.Spec.Authentication.ServiceAccount.ProjectId, infisicalSecret.Spec.Authentication.ServiceAccount.EnvironmentName, secretVersionBasedOnETag) if err != nil { return fmt.Errorf("\nfailed to get secrets because [err=%v]", err) } @@ -381,7 +380,7 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context secretsPath := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.SecretsPath recursive := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.Recursive - plainTextSecretsFromApi, updateDetails, err = util.GetPlainTextSecretsViaServiceToken(infisicalToken, secretVersionBasedOnETag, envSlug, secretsPath, recursive) + plainTextSecretsFromApi, updateDetails, err = util.GetPlainTextSecretsViaServiceToken(infisicalClient, infisicalToken, secretVersionBasedOnETag, envSlug, secretsPath, recursive) if err != nil { return fmt.Errorf("\nfailed to get secrets because [err=%v]", err) } diff --git a/k8-operator/packages/api/api.go b/k8-operator/packages/api/api.go index 5ca3bdba3..dd2af3353 100644 --- a/k8-operator/packages/api/api.go +++ b/k8-operator/packages/api/api.go @@ -8,26 +8,6 @@ import ( const USER_AGENT_NAME = "k8-operator" -func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncryptedWorkspaceKeyRequest) (GetEncryptedWorkspaceKeyResponse, error) { - endpoint := fmt.Sprintf("%v/v2/workspace/%v/encrypted-key", API_HOST_URL, request.WorkspaceId) - var result GetEncryptedWorkspaceKeyResponse - response, err := httpClient. - R(). - SetResult(&result). - SetHeader("User-Agent", USER_AGENT_NAME). - Get(endpoint) - - if err != nil { - return GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unsuccessful response: [response=%s]", response) - } - - return result, nil -} - func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDetailsResponse, error) { var tokenDetailsResponse GetServiceTokenDetailsResponse response, err := httpClient. @@ -51,43 +31,6 @@ func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDeta return tokenDetailsResponse, nil } -func CallGetSecretsV3(httpClient *resty.Client, request GetEncryptedSecretsV3Request) (GetEncryptedSecretsV3Response, error) { - var secretsResponse GetEncryptedSecretsV3Response - - httpRequest := httpClient. - R(). - SetResult(&secretsResponse). - SetHeader("User-Agent", USER_AGENT_NAME). - SetQueryParam("environment", request.Environment). - SetQueryParam("include_imports", "true"). // TODO needs to be set as a option - SetQueryParam("workspaceId", request.WorkspaceId) - - if request.SecretPath != "" { - httpRequest.SetQueryParam("secretPath", request.SecretPath) - } - - if request.Recursive { - httpRequest.SetQueryParam("recursive", "true") - } - - response, err := httpRequest.Get(fmt.Sprintf("%v/v3/secrets", API_HOST_URL)) - - if err != nil { - return GetEncryptedSecretsV3Response{}, fmt.Errorf("CallGetSecretsV3: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return GetEncryptedSecretsV3Response{}, fmt.Errorf("CallGetSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response) - } - - responseETag := response.Header().Get("etag") - - secretsResponse.Modified = request.ETag != responseETag - secretsResponse.ETag = responseETag - - return secretsResponse, nil -} - func CallGetServiceTokenAccountDetailsV2(httpClient *resty.Client) (ServiceAccountDetailsResponse, error) { var serviceAccountDetailsResponse ServiceAccountDetailsResponse response, err := httpClient. @@ -149,42 +92,6 @@ func CallUniversalMachineIdentityRefreshAccessToken(request MachineIdentityUnive return universalAuthRefreshResponse, nil } -func CallGetDecryptedSecretsV3(httpClient *resty.Client, request GetDecryptedSecretsV3Request) (GetDecryptedSecretsV3Response, error) { - var decryptedSecretsResponse GetDecryptedSecretsV3Response - - req := httpClient. - R(). - SetResult(&decryptedSecretsResponse). - SetHeader("User-Agent", USER_AGENT_NAME). - SetQueryParam("include_imports", "true"). - SetQueryParam("secretPath", request.SecretPath). - SetQueryParam("workspaceSlug", request.ProjectSlug). - SetQueryParam("environment", request.Environment) - - if request.Recursive { - req.SetQueryParam("recursive", "true") - } - if request.ExpandSecretReferences { - req.SetQueryParam("expandSecretReferences", "true") - } - - response, err := req.Get(fmt.Sprintf("%v/v3/secrets/raw", API_HOST_URL)) - - if err != nil { - return GetDecryptedSecretsV3Response{}, fmt.Errorf("CallGetDecryptedSecretsV3: Unable to complete api request [err=%s]", err) - } - if response.IsError() { - return GetDecryptedSecretsV3Response{}, fmt.Errorf("CallGetDecryptedSecretsV3: Unsuccessful response: [response=%s]", response) - } - - responseETag := response.Header().Get("etag") - - decryptedSecretsResponse.Modified = request.ETag != responseETag - decryptedSecretsResponse.ETag = responseETag - - return decryptedSecretsResponse, nil -} - func CallGetServiceAccountWorkspacePermissionsV2(httpClient *resty.Client) (ServiceAccountWorkspacePermissions, error) { var serviceAccountWorkspacePermissionsResponse ServiceAccountWorkspacePermissions response, err := httpClient. diff --git a/k8-operator/packages/api/models.go b/k8-operator/packages/api/models.go index 8439e4918..b2316117b 100644 --- a/k8-operator/packages/api/models.go +++ b/k8-operator/packages/api/models.go @@ -106,6 +106,7 @@ type GetDecryptedSecretsV3Response struct { } type GetDecryptedSecretsV3Request struct { + ProjectID string `json:"workspaceId"` ProjectSlug string `json:"workspaceSlug"` Environment string `json:"environment"` SecretPath string `json:"secretPath"` diff --git a/k8-operator/packages/util/secrets.go b/k8-operator/packages/util/secrets.go index c46617a23..9fb79c1de 100644 --- a/k8-operator/packages/util/secrets.go +++ b/k8-operator/packages/util/secrets.go @@ -1,10 +1,7 @@ package util import ( - "encoding/base64" "fmt" - "path" - "regexp" "strings" "github.com/Infisical/infisical/k8-operator/api/v1alpha1" @@ -87,7 +84,7 @@ func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalCl }, nil } -func GetPlainTextSecretsViaServiceToken(fullServiceToken string, etag string, envSlug string, secretPath string, recursive bool) ([]model.SingleEnvironmentVariable, model.RequestUpdateUpdateDetails, error) { +func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClientInterface, fullServiceToken string, etag string, envSlug string, secretPath string, recursive bool) ([]model.SingleEnvironmentVariable, model.RequestUpdateUpdateDetails, error) { serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4) if len(serviceTokenParts) < 4 { return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("invalid service token entered. Please double check your service token and try again") @@ -105,51 +102,44 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, etag string, en return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("unable to get service token details. [err=%v]", err) } - encryptedSecretsResponse, err := api.CallGetSecretsV3(httpClient, api.GetEncryptedSecretsV3Request{ - WorkspaceId: serviceTokenDetails.Workspace, - Environment: envSlug, - Recursive: recursive, - ETag: etag, - SecretPath: secretPath, + secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{ + ProjectID: serviceTokenDetails.Workspace, + Environment: envSlug, + Recursive: recursive, + SecretPath: secretPath, + IncludeImports: true, + ExpandSecretReferences: true, }) if err != nil { return nil, model.RequestUpdateUpdateDetails{}, err } - decodedSymmetricEncryptionDetails, err := GetBase64DecodedSymmetricEncryptionDetails(serviceTokenParts[3], serviceTokenDetails.EncryptedKey, serviceTokenDetails.Iv, serviceTokenDetails.Tag) - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("unable to decode symmetric encryption details [err=%v]", err) + var environmentVariables []model.SingleEnvironmentVariable + + for _, secret := range secrets { + + environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{ + Key: secret.SecretKey, + Value: secret.SecretValue, + Type: secret.Type, + ID: secret.ID, + }) } - plainTextWorkspaceKey, err := crypto.DecryptSymmetric([]byte(serviceTokenParts[3]), decodedSymmetricEncryptionDetails.Cipher, decodedSymmetricEncryptionDetails.Tag, decodedSymmetricEncryptionDetails.IV) - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("unable to decrypt the required workspace key") - } + newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", environmentVariables))) - plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecretsResponse.Secrets) - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("unable to decrypt your secrets [err=%v]", err) - } - - plainTextSecretsMergedWithImports, err := InjectImportedSecret(plainTextWorkspaceKey, plainTextSecrets, encryptedSecretsResponse.ImportedSecrets) - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, err - } - - // expand secrets that are referenced - expandedSecrets := ExpandSecrets(plainTextSecretsMergedWithImports, fullServiceToken) - - return expandedSecrets, model.RequestUpdateUpdateDetails{ - Modified: encryptedSecretsResponse.Modified, - ETag: encryptedSecretsResponse.ETag, + return environmentVariables, model.RequestUpdateUpdateDetails{ + Modified: etag != newEtag, + ETag: newEtag, }, nil + } // Fetches plaintext secrets from an API endpoint using a service account. // The function fetches the service account details and keys, decrypts the workspace key, fetches the encrypted secrets for the specified project and environment, and decrypts the secrets using the decrypted workspace key. // Returns the plaintext secrets, encrypted secrets response, and any errors that occurred during the process. -func GetPlainTextSecretsViaServiceAccount(serviceAccountCreds model.ServiceAccountDetails, projectId string, environmentName string, etag string) ([]model.SingleEnvironmentVariable, model.RequestUpdateUpdateDetails, error) { +func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalClientInterface, serviceAccountCreds model.ServiceAccountDetails, projectId string, environmentName string, etag string) ([]model.SingleEnvironmentVariable, model.RequestUpdateUpdateDetails, error) { httpClient := resty.New() httpClient.SetAuthToken(serviceAccountCreds.AccessKey). SetHeader("Accept", "application/json") @@ -176,226 +166,36 @@ func GetPlainTextSecretsViaServiceAccount(serviceAccountCreds model.ServiceAccou return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("unable to find key for [projectId=%s] [err=%v]. Ensure that the given service account has access to given projectId", projectId, err) } - cipherText, err := base64.StdEncoding.DecodeString(workspaceServiceAccountKey.EncryptedKey) - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to decode EncryptedKey secrets because [err=%v]", err) - } - - nonce, err := base64.StdEncoding.DecodeString(workspaceServiceAccountKey.Nonce) - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to decode nonce secrets because [err=%v]", err) - } - - publickey, err := base64.StdEncoding.DecodeString(serviceAccountCreds.PublicKey) - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to decode PublicKey secrets because [err=%v]", err) - } - - privateKey, err := base64.StdEncoding.DecodeString(serviceAccountCreds.PrivateKey) - - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to decode PrivateKey secrets because [err=%v]", err) - } - - plainTextWorkspaceKey := crypto.DecryptAsymmetric(cipherText, nonce, publickey, privateKey) - - encryptedSecretsResponse, err := api.CallGetSecretsV3(httpClient, api.GetEncryptedSecretsV3Request{ - WorkspaceId: projectId, - Environment: environmentName, - ETag: etag, + secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{ + ProjectID: projectId, + Environment: environmentName, + Recursive: false, + SecretPath: "/", + IncludeImports: true, + ExpandSecretReferences: true, }) if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("unable to fetch secrets because [err=%v]", err) + return nil, model.RequestUpdateUpdateDetails{}, err } - plainTextSecrets, err := GetPlainTextSecrets(plainTextWorkspaceKey, encryptedSecretsResponse.Secrets) - if err != nil { - return nil, model.RequestUpdateUpdateDetails{}, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get plain text secrets because [err=%v]", err) - } + var environmentVariables []model.SingleEnvironmentVariable - return plainTextSecrets, model.RequestUpdateUpdateDetails{ - Modified: encryptedSecretsResponse.Modified, - ETag: encryptedSecretsResponse.ETag, - }, nil -} - -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 GetPlainTextSecrets(key []byte, encryptedSecrets []api.EncryptedSecretV3) ([]model.SingleEnvironmentVariable, error) { - plainTextSecrets := []model.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 := model.SingleEnvironmentVariable{ - Key: string(plainTextKey), - Value: string(plainTextValue), - Type: string(secret.Type), + for _, secret := range secrets { + environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{ + Key: secret.SecretKey, + Value: secret.SecretValue, + Type: secret.Type, ID: secret.ID, - } - - plainTextSecrets = append(plainTextSecrets, plainTextSecret) + }) } - return plainTextSecrets, nil -} + newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", environmentVariables))) -var secRefRegex = regexp.MustCompile(`\${([^\}]*)}`) - -func recursivelyExpandSecret(expandedSecs map[string]string, interpolatedSecs map[string]string, crossSecRefFetch func(env string, path []string, key string) string, key string) string { - if v, ok := expandedSecs[key]; ok { - return v - } - - interpolatedVal, ok := interpolatedSecs[key] - if !ok { - return "" - // panic(fmt.Errorf("Could not find referred secret with key name %s", key), "Please check it refers a") - } - - refs := secRefRegex.FindAllStringSubmatch(interpolatedVal, -1) - for _, val := range refs { - // key: "${something}" val: [${something},something] - interpolatedExp, interpolationKey := val[0], val[1] - ref := strings.Split(interpolationKey, ".") - - // ${KEY1} => [key1] - if len(ref) == 1 { - val := recursivelyExpandSecret(expandedSecs, interpolatedSecs, crossSecRefFetch, interpolationKey) - interpolatedVal = strings.ReplaceAll(interpolatedVal, interpolatedExp, val) - continue - } - - // cross board reference ${env.folder.key1} => [env folder key1] - if len(ref) > 1 { - secEnv, tmpSecPath, secKey := ref[0], ref[1:len(ref)-1], ref[len(ref)-1] - interpolatedSecs[interpolationKey] = crossSecRefFetch(secEnv, tmpSecPath, secKey) // get the reference value - val := recursivelyExpandSecret(expandedSecs, interpolatedSecs, crossSecRefFetch, interpolationKey) - interpolatedVal = strings.ReplaceAll(interpolatedVal, interpolatedExp, val) - } - - } - expandedSecs[key] = interpolatedVal - return interpolatedVal -} - -func ExpandSecrets(secrets []model.SingleEnvironmentVariable, infisicalToken string) []model.SingleEnvironmentVariable { - expandedSecs := make(map[string]string) - interpolatedSecs := make(map[string]string) - // map[env.secret-path][keyname]Secret - crossEnvRefSecs := make(map[string]map[string]model.SingleEnvironmentVariable) // a cache to hold all cross board reference secrets - - for _, sec := range secrets { - // get all references in a secret - refs := secRefRegex.FindAllStringSubmatch(sec.Value, -1) - // nil means its a secret without reference - if refs == nil { - expandedSecs[sec.Key] = sec.Value // atomic secrets without any interpolation - } else { - interpolatedSecs[sec.Key] = sec.Value - } - } - - for i, sec := range secrets { - // already present pick that up - if expandedVal, ok := expandedSecs[sec.Key]; ok { - secrets[i].Value = expandedVal - continue - } - - expandedVal := recursivelyExpandSecret(expandedSecs, interpolatedSecs, func(env string, secPaths []string, secKey string) string { - secPaths = append([]string{"/"}, secPaths...) - secPath := path.Join(secPaths...) - - secPathDot := strings.Join(secPaths, ".") - uniqKey := fmt.Sprintf("%s.%s", env, secPathDot) - - if crossRefSec, ok := crossEnvRefSecs[uniqKey]; !ok { - // if not in cross reference cache, fetch it from server - refSecs, _, err := GetPlainTextSecretsViaServiceToken(infisicalToken, "", env, secPath, false) - if err != nil { - fmt.Printf("Could not fetch secrets in environment: %s secret-path: %s", env, secPath) - // HandleError(err, fmt.Sprintf("Could not fetch secrets in environment: %s secret-path: %s", env, secPath), "If you are using a service token to fetch secrets, please ensure it is valid") - } - refSecsByKey := getSecretsByKeys(refSecs) - // save it to avoid calling api again for same environment and folder path - crossEnvRefSecs[uniqKey] = refSecsByKey - return refSecsByKey[secKey].Value - } else { - return crossRefSec[secKey].Value - } - }, sec.Key) - - secrets[i].Value = expandedVal - } - return secrets + return environmentVariables, model.RequestUpdateUpdateDetails{ + Modified: etag != newEtag, + ETag: newEtag, + }, nil } func getSecretsByKeys(secrets []model.SingleEnvironmentVariable) map[string]model.SingleEnvironmentVariable { @@ -408,35 +208,6 @@ func getSecretsByKeys(secrets []model.SingleEnvironmentVariable) map[string]mode return secretMapByName } -func InjectImportedSecret(plainTextWorkspaceKey []byte, secrets []model.SingleEnvironmentVariable, importedSecrets []api.ImportedSecretV3) ([]model.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) - - 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 MergeRawImportedSecrets(secrets []model.SingleEnvironmentVariable, importedSecrets []api.ImportedRawSecretV3) []model.SingleEnvironmentVariable { if importedSecrets == nil { return secrets From 551450848210b86561ef85d3851756764ddcccc5 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 15 Jul 2024 01:23:47 +0530 Subject: [PATCH 03/13] refactor(cli): removed unused secret logic for e2ee used --- cli/packages/cmd/secrets.go | 2 - cli/packages/util/secrets.go | 109 ----------------------------------- 2 files changed, 111 deletions(-) diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 52cad9747..1f64ee9b6 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -187,10 +187,8 @@ var secretsSetCmd = &cobra.Command{ var secretOperations []models.SecretSetOperation if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - fmt.Println(">>>", projectId) secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, token) } else { - // fmt.Println(">>>", projectId) workspaceFile, err := util.GetWorkSpaceFromFile() if err != nil { util.HandleError(err, "unable to get your local config details [err=%v]") diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index e9fc9a0ef..6e4c9d862 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -141,34 +141,6 @@ func CreateDynamicSecretLease(accessToken string, projectSlug string, environmen }, 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) - - 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 InjectRawImportedSecret(secrets []models.SingleEnvironmentVariable, importedSecrets []api.ImportedRawSecretV3) ([]models.SingleEnvironmentVariable, error) { if importedSecrets == nil { return secrets, nil @@ -472,87 +444,6 @@ func OverrideSecrets(secrets []models.SingleEnvironmentVariable, secretType stri return secretsToReturn } -func GetPlainTextSecrets(key []byte, encryptedSecrets []api.EncryptedSecretV3) ([]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, err := 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") - } - - // Decrypt comment - comment_iv, err := base64.StdEncoding.DecodeString(secret.SecretCommentIV) - if err != nil { - return nil, fmt.Errorf("unable to decode secret IV for secret value") - } - - comment_tag, err := base64.StdEncoding.DecodeString(secret.SecretCommentTag) - if err != nil { - return nil, fmt.Errorf("unable to decode secret authentication tag for secret value") - } - - comment_ciphertext, err := base64.StdEncoding.DecodeString(secret.SecretCommentCiphertext) - if err != nil { - return nil, fmt.Errorf("unable to decode secret cipher text for secret key") - } - - plainTextComment, err := crypto.DecryptSymmetric(key, comment_ciphertext, comment_tag, comment_iv) - if err != nil { - return nil, fmt.Errorf("unable to symmetrically decrypt secret comment") - } - - plainTextSecret := models.SingleEnvironmentVariable{ - Key: string(plainTextKey), - Value: string(plainTextValue), - Type: string(secret.Type), - ID: secret.ID, - Tags: secret.Tags, - Comment: string(plainTextComment), - } - - plainTextSecrets = append(plainTextSecrets, plainTextSecret) - } - - return plainTextSecrets, nil -} - func WriteBackupSecrets(workspace string, environment string, secretsPath string, encryptionKey []byte, secrets []models.SingleEnvironmentVariable) error { formattedPath := strings.ReplaceAll(secretsPath, "/", "-") fileName := fmt.Sprintf("secrets_%s_%s_%s", workspace, environment, formattedPath) From c39ea130b10f9b64c7ab253f9026119f0309b5c1 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 16 Jul 2024 14:05:38 +0530 Subject: [PATCH 04/13] feat: changed backup secret to keyring and resolved backup not working in previous versions --- cli/packages/models/cli.go | 7 +++ cli/packages/util/constants.go | 2 + cli/packages/util/secrets.go | 111 ++++++++++++++------------------- 3 files changed, 55 insertions(+), 65 deletions(-) diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go index 6f996d667..4b02cb6f8 100644 --- a/cli/packages/models/cli.go +++ b/cli/packages/models/cli.go @@ -140,3 +140,10 @@ type SecretSetOperation struct { SecretValue string SecretOperation string } + +type BackupSecretKeyRing struct { + ProjectID string `json:"projectId"` + Environment string `json:"environment"` + SecretPath string `json:"secretPath"` + Secrets []SingleEnvironmentVariable +} diff --git a/cli/packages/util/constants.go b/cli/packages/util/constants.go index bff3c3ab0..a3cc2cfcb 100644 --- a/cli/packages/util/constants.go +++ b/cli/packages/util/constants.go @@ -30,6 +30,8 @@ const ( SERVICE_TOKEN_IDENTIFIER = "service-token" UNIVERSAL_AUTH_TOKEN_IDENTIFIER = "universal-auth-token" + + INFISICAL_BACKUP_SECRET = "infisical-backup-secrets" ) var ( diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index 6e4c9d862..a9d1ec905 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -8,6 +8,7 @@ import ( "os" "path" "regexp" + "slices" "strings" "unicode" @@ -16,6 +17,7 @@ import ( "github.com/Infisical/infisical-merge/packages/models" "github.com/go-resty/resty/v2" "github.com/rs/zerolog/log" + "github.com/zalando/go-keyring" ) func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool) ([]models.SingleEnvironmentVariable, error) { @@ -252,15 +254,14 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive) log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn) - backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32] if errorToReturn == nil { - WriteBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, backupSecretsEncryptionKey, secretsToReturn) + WriteBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, res.Secrets) } secretsToReturn = res.Secrets // only attempt to serve cached secrets if no internet connection and if at least one secret cached if !isConnected { - backedSecrets, err := ReadBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, backupSecretsEncryptionKey) + backedSecrets, err := ReadBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath) if len(backedSecrets) > 0 { PrintWarning("Unable to fetch latest secret(s) due to connection error, serving secrets from last successful fetch. For more info, run with --debug") secretsToReturn = backedSecrets @@ -444,93 +445,71 @@ func OverrideSecrets(secrets []models.SingleEnvironmentVariable, secretType stri return secretsToReturn } -func WriteBackupSecrets(workspace string, environment string, secretsPath string, encryptionKey []byte, secrets []models.SingleEnvironmentVariable) error { - formattedPath := strings.ReplaceAll(secretsPath, "/", "-") - fileName := fmt.Sprintf("secrets_%s_%s_%s", workspace, environment, formattedPath) - secrets_backup_folder_name := "secrets-backup" - - _, fullConfigFileDirPath, err := GetFullConfigFilePath() +func WriteBackupSecrets(workspace string, environment string, secretsPath string, secrets []models.SingleEnvironmentVariable) error { + var backedUpSecrets []models.BackupSecretKeyRing + secretValueInKeyRing, err := GetValueInKeyring(INFISICAL_BACKUP_SECRET) if err != nil { - return fmt.Errorf("WriteBackupSecrets: unable to get full config folder path [err=%s]", err) - } - - // create secrets backup directory - fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) - if _, err := os.Stat(fullPathToSecretsBackupFolder); errors.Is(err, os.ErrNotExist) { - err := os.Mkdir(fullPathToSecretsBackupFolder, os.ModePerm) - if err != nil { - return err + if err == keyring.ErrUnsupportedPlatform { + return errors.New("your OS does not support keyring. Consider using a service token https://infisical.com/docs/documentation/platform/token") + } else if err != keyring.ErrNotFound { + return fmt.Errorf("something went wrong, failed to retrieve value from system keyring [error=%v]", err) } } + _ = json.Unmarshal([]byte(secretValueInKeyRing), &backedUpSecrets) - var encryptedSecrets []models.SymmetricEncryptionResult - for _, secret := range secrets { - marshaledSecrets, _ := json.Marshal(secret) - result, err := crypto.EncryptSymmetric(marshaledSecrets, encryptionKey) - if err != nil { - return err - } + backedUpSecrets = slices.DeleteFunc(backedUpSecrets, func(e models.BackupSecretKeyRing) bool { + return e.SecretPath == secretsPath && e.ProjectID == workspace && e.Environment == environment + }) + newBackupSecret := models.BackupSecretKeyRing{ + ProjectID: workspace, + Environment: environment, + SecretPath: secretsPath, + Secrets: secrets, + } + backedUpSecrets = append(backedUpSecrets, newBackupSecret) - encryptedSecrets = append(encryptedSecrets, result) + listOfSecretsMarshalled, err := json.Marshal(backedUpSecrets) + if err != nil { + return err } - listOfSecretsMarshalled, _ := json.Marshal(encryptedSecrets) - err = os.WriteFile(fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName), listOfSecretsMarshalled, 0600) + err = SetValueInKeyring(INFISICAL_BACKUP_SECRET, string(listOfSecretsMarshalled)) if err != nil { - return fmt.Errorf("WriteBackupSecrets: Unable to write backup secrets to file [err=%s]", err) + return fmt.Errorf("StoreUserCredsInKeyRing: unable to store user credentials because [err=%s]", err) } return nil } -func ReadBackupSecrets(workspace string, environment string, secretsPath string, encryptionKey []byte) ([]models.SingleEnvironmentVariable, error) { - formattedPath := strings.ReplaceAll(secretsPath, "/", "-") - fileName := fmt.Sprintf("secrets_%s_%s_%s", workspace, environment, formattedPath) - secrets_backup_folder_name := "secrets-backup" - - _, fullConfigFileDirPath, err := GetFullConfigFilePath() +func ReadBackupSecrets(workspace string, environment string, secretsPath string) ([]models.SingleEnvironmentVariable, error) { + secretValueInKeyRing, err := GetValueInKeyring(INFISICAL_BACKUP_SECRET) if err != nil { - return nil, fmt.Errorf("ReadBackupSecrets: unable to write config file because an error occurred when getting config file path [err=%s]", err) + if err == keyring.ErrUnsupportedPlatform { + return nil, errors.New("your OS does not support keyring. Consider using a service token https://infisical.com/docs/documentation/platform/token") + } else if err == keyring.ErrNotFound { + return nil, errors.New("credentials not found in system keyring") + } else { + return nil, fmt.Errorf("something went wrong, failed to retrieve value from system keyring [error=%v]", err) + } } - fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) - if _, err := os.Stat(fullPathToSecretsBackupFolder); errors.Is(err, os.ErrNotExist) { - return nil, nil - } - - encryptedBackupSecretsFilePath := fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName) - - encryptedBackupSecretsAsBytes, err := os.ReadFile(encryptedBackupSecretsFilePath) + var backedUpSecrets []models.BackupSecretKeyRing + err = json.Unmarshal([]byte(secretValueInKeyRing), &backedUpSecrets) if err != nil { - return nil, err + return nil, fmt.Errorf("getUserCredsFromKeyRing: Something went wrong when unmarshalling user creds [err=%s]", err) } - var listOfEncryptedBackupSecrets []models.SymmetricEncryptionResult - - _ = json.Unmarshal(encryptedBackupSecretsAsBytes, &listOfEncryptedBackupSecrets) - - var plainTextSecrets []models.SingleEnvironmentVariable - for _, encryptedSecret := range listOfEncryptedBackupSecrets { - result, err := crypto.DecryptSymmetric(encryptionKey, encryptedSecret.CipherText, encryptedSecret.AuthTag, encryptedSecret.Nonce) - if err != nil { - return nil, err + for _, backupSecret := range backedUpSecrets { + if backupSecret.Environment == environment && backupSecret.ProjectID == workspace && backupSecret.SecretPath == secretsPath { + return backupSecret.Secrets, nil } - - var plainTextSecret models.SingleEnvironmentVariable - - err = json.Unmarshal(result, &plainTextSecret) - if err != nil { - return nil, err - } - - plainTextSecrets = append(plainTextSecrets, plainTextSecret) } - return plainTextSecrets, nil - + return nil, nil } func DeleteBackupSecrets() error { + // keeping this logic for now. Need to remove it later as more users migrate keyring would be used and this folder will be removed completely by then secrets_backup_folder_name := "secrets-backup" _, fullConfigFileDirPath, err := GetFullConfigFilePath() @@ -540,6 +519,8 @@ func DeleteBackupSecrets() error { fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) + DeleteValueInKeyring(INFISICAL_BACKUP_SECRET) + return os.RemoveAll(fullPathToSecretsBackupFolder) } From 1d6d424c911c792a727fb40e720314e684d356aa Mon Sep 17 00:00:00 2001 From: = Date: Tue, 16 Jul 2024 14:08:09 +0530 Subject: [PATCH 05/13] fix: removed print not used --- cli/packages/util/secrets.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index a9d1ec905..ec98a181f 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -710,7 +710,6 @@ func SetRawSecrets(secretArgs []string, secretType string, environmentName strin Environment: environmentName, } - fmt.Println(projectId, environmentName) err = api.CallCreateRawSecretsV3(httpClient, createSecretRequest) if err != nil { return nil, fmt.Errorf("unable to process new secret creations [err=%v]", err) From 1f6c33bdb87e636906576e6c820af5f9533ec2e7 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 18 Jul 2024 01:46:28 +0530 Subject: [PATCH 06/13] feat: updated bot not found error message --- backend/src/services/project-bot/project-bot-fns.ts | 6 +++--- cli/packages/api/api.go | 5 ++++- cli/packages/util/secrets.go | 7 ++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/backend/src/services/project-bot/project-bot-fns.ts b/backend/src/services/project-bot/project-bot-fns.ts index 00604b37f..0f5d229d7 100644 --- a/backend/src/services/project-bot/project-bot-fns.ts +++ b/backend/src/services/project-bot/project-bot-fns.ts @@ -24,10 +24,10 @@ export const getBotKeyFnFactory = ( const bot = await projectBotDAL.findOne({ projectId: project.id }); - if (!bot) throw new BadRequestError({ message: "Failed to find bot key" }); - if (!bot.isActive) throw new BadRequestError({ message: "Bot is not active" }); + if (!bot) throw new BadRequestError({ message: "Failed to find bot key", name: "bot_not_found_error" }); + if (!bot.isActive) throw new BadRequestError({ message: "Bot is not active", name: "bot_not_found_error" }); if (!bot.encryptedProjectKeyNonce || !bot.encryptedProjectKey) - throw new BadRequestError({ message: "Encryption key missing" }); + throw new BadRequestError({ message: "Encryption key missing", name: "bot_not_found_error" }); const botPrivateKey = getBotPrivateKey({ bot }); diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index 3aa07ad6f..c7827cdc9 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -418,7 +418,10 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques } if response.IsError() && strings.Contains(response.String(), "bot_not_found_error") { - return GetRawSecretsV3Response{}, fmt.Errorf("project with id %s is a legacy project type, please navigate to project settings and disable end to end encryption then try again", request.WorkspaceId) + return GetRawSecretsV3Response{}, fmt.Errorf(`project with id %s is a legacy project type, + Legacy infisical instance: Please disable end to end encryption inside project settings. + New infisical instances/cloud: Please upgrade your project by navigating to secret overview page and click on upgrade. + `, request.WorkspaceId) } if response.IsError() { diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index ec98a181f..18608c61e 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -250,15 +250,16 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo infisicalDotJson.WorkspaceId = params.WorkspaceId } - res, errorToReturn := GetPlainTextSecretsV3(loggedInUserDetails.UserCredentials.JTWToken, infisicalDotJson.WorkspaceId, + res, err := GetPlainTextSecretsV3(loggedInUserDetails.UserCredentials.JTWToken, infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive) - 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]", err) - if errorToReturn == nil { + if err == nil { WriteBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath, res.Secrets) } secretsToReturn = res.Secrets + errorToReturn = err // only attempt to serve cached secrets if no internet connection and if at least one secret cached if !isConnected { backedSecrets, err := ReadBackupSecrets(infisicalDotJson.WorkspaceId, params.Environment, params.SecretsPath) From 5360fb033ab95813dc878f372cb3ae3cf8d68151 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 18 Jul 2024 17:53:25 -0400 Subject: [PATCH 07/13] fix set secret --- cli/packages/util/secrets.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index 18608c61e..53c11abbf 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -607,7 +607,9 @@ func SetRawSecrets(secretArgs []string, secretType string, environmentName strin getAllEnvironmentVariablesRequest := models.GetAllSecretsParameters{Environment: environmentName, SecretsPath: secretsPath, WorkspaceId: projectId} if tokenDetails.Type == UNIVERSAL_AUTH_TOKEN_IDENTIFIER { getAllEnvironmentVariablesRequest.UniversalAuthAccessToken = tokenDetails.Token - } else { + } + + if tokenDetails.Type == SERVICE_TOKEN_IDENTIFIER { getAllEnvironmentVariablesRequest.InfisicalToken = tokenDetails.Token } From 4ba529f22dddb42d10086d54c596e50b2006f931 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 18 Jul 2024 18:05:01 -0400 Subject: [PATCH 08/13] print error message when projectId flag is not passed for set secret --- cli/packages/cmd/secrets.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 1f64ee9b6..743458727 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -187,6 +187,10 @@ var secretsSetCmd = &cobra.Command{ var secretOperations []models.SecretSetOperation if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { + if projectId == "" { + util.PrintErrorMessageAndExit("When using service tokens or machine identities, you must set the --projectId flag") + } + secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, token) } else { workspaceFile, err := util.GetWorkSpaceFromFile() From 6f0484f07448e1daa6ae20853ec0df897563c5a3 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 18 Jul 2024 18:29:39 -0400 Subject: [PATCH 09/13] update bot key message --- cli/packages/api/api.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index c7827cdc9..8f118651d 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -418,9 +418,8 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques } if response.IsError() && strings.Contains(response.String(), "bot_not_found_error") { - return GetRawSecretsV3Response{}, fmt.Errorf(`project with id %s is a legacy project type, - Legacy infisical instance: Please disable end to end encryption inside project settings. - New infisical instances/cloud: Please upgrade your project by navigating to secret overview page and click on upgrade. + return GetRawSecretsV3Response{}, fmt.Errorf(`Project with id %s is incompatible with your current CLI version. + If you're self-hosting, update your Infisical instance to the latest release. Then, upgrade your project by visiting its settings page. `, request.WorkspaceId) } From 538613dd4059b4bb9c10907dba5ed8fa46f98149 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 19 Jul 2024 19:59:16 +0530 Subject: [PATCH 10/13] feat: added bot error message for old instance --- cli/packages/api/api.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index 8f118651d..6e21fa837 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -417,7 +417,10 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unable to complete api request [err=%w]", err) } - if response.IsError() && strings.Contains(response.String(), "bot_not_found_error") { + if response.IsError() && + (strings.Contains(response.String(), "bot_not_found_error") || + strings.Contains(strings.ToLower(response.String()), "failed to find bot key") || + strings.Contains(strings.ToLower(response.String()), "bot is not active")) { return GetRawSecretsV3Response{}, fmt.Errorf(`Project with id %s is incompatible with your current CLI version. If you're self-hosting, update your Infisical instance to the latest release. Then, upgrade your project by visiting its settings page. `, request.WorkspaceId) From 72d2247bf2a7cf91c44ad7d0f76615ff5d041407 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 19 Jul 2024 12:55:52 -0400 Subject: [PATCH 11/13] add support for --projectId when user is logged in on secrets set --- cli/packages/cmd/secrets.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 743458727..2894765ae 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -193,9 +193,13 @@ var secretsSetCmd = &cobra.Command{ secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, token) } else { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.HandleError(err, "unable to get your local config details [err=%v]") + if projectId == "" { + workspaceFile, err := util.GetWorkSpaceFromFile() + if err != nil { + util.HandleError(err, "unable to get your local config details [err=%v]") + } + + projectId = workspaceFile.WorkspaceId } loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() @@ -207,7 +211,7 @@ var secretsSetCmd = &cobra.Command{ util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") } - secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, workspaceFile.WorkspaceId, &models.TokenDetails{ + secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, &models.TokenDetails{ Type: "", Token: loggedInUserDetails.UserCredentials.JTWToken, }) From 2f17f5e7df877537dff2d17690eedd1fac3a7315 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 19 Jul 2024 14:50:16 -0400 Subject: [PATCH 12/13] update bot not found message --- cli/packages/api/api.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index 6e21fa837..184874f8a 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -421,8 +421,7 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques (strings.Contains(response.String(), "bot_not_found_error") || strings.Contains(strings.ToLower(response.String()), "failed to find bot key") || strings.Contains(strings.ToLower(response.String()), "bot is not active")) { - return GetRawSecretsV3Response{}, fmt.Errorf(`Project with id %s is incompatible with your current CLI version. - If you're self-hosting, update your Infisical instance to the latest release. Then, upgrade your project by visiting its settings page. + return GetRawSecretsV3Response{}, fmt.Errorf(`Project with id %s is incompatible with your current CLI version. Upgrade your project by visiting the project settings page. If you're self hosting and project upgrade option isn't yet available, contact your administrator to upgrade your Infisical instance to the latest release. `, request.WorkspaceId) } From 754db67f1146f217c11d787914494219de70c6ae Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 19 Jul 2024 16:47:21 -0400 Subject: [PATCH 13/13] update chart version --- helm-charts/secrets-operator/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-charts/secrets-operator/Chart.yaml b/helm-charts/secrets-operator/Chart.yaml index d02c78fc9..2247b79b0 100644 --- a/helm-charts/secrets-operator/Chart.yaml +++ b/helm-charts/secrets-operator/Chart.yaml @@ -13,9 +13,9 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: v0.6.4 +version: v0.7.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.6.4" +appVersion: "v0.7.0"