feat: HSM support

This commit is contained in:
Daniel Hougaard
2024-10-31 17:59:41 +04:00
parent a807f0cf6c
commit 891a1ea2b9
26 changed files with 967 additions and 57 deletions

View File

@@ -525,3 +525,40 @@ func CallUpdateRawSecretsV3(httpClient *resty.Client, request UpdateRawSecretByN
return nil
}
func CallExportKmsRootEncryptionKey(httpClient *resty.Client) (ExportKmsRootKeyResponse, error) {
var exportKmsKeyResponse ExportKmsRootKeyResponse
response, err := httpClient.
R().
SetResult(&exportKmsKeyResponse).
SetHeader("User-Agent", USER_AGENT).
Post(fmt.Sprintf("%v/v1/admin/kms-export", config.INFISICAL_URL))
if err != nil {
return ExportKmsRootKeyResponse{}, fmt.Errorf("CallSuperAdminExportKmsKey: Unable to complete api request [err=%w]", err)
}
if response.IsError() {
return ExportKmsRootKeyResponse{}, fmt.Errorf("CallSuperAdminExportKmsKey: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
}
return exportKmsKeyResponse, nil
}
func CallImportKmsRootEncryptionKey(httpClient *resty.Client, request ImportKmsRootKeyRequest) error {
response, err := httpClient.
R().
SetHeader("User-Agent", USER_AGENT).
SetBody(request).
Post(fmt.Sprintf("%v/v1/admin/kms-import", config.INFISICAL_URL))
if err != nil {
return fmt.Errorf("CallSuperAdminImportKmsKey: Unable to complete api request [err=%w]", err)
}
if response.IsError() {
return fmt.Errorf("CallSuperAdminImportKmsKey: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
}
return nil
}

View File

@@ -136,8 +136,8 @@ type GetOrganizationsResponse struct {
}
type SelectOrganizationResponse struct {
Token string `json:"token"`
MfaEnabled bool `json:"isMfaEnabled"`
Token string `json:"token"`
MfaEnabled bool `json:"isMfaEnabled"`
}
type SelectOrganizationRequest struct {
@@ -617,3 +617,11 @@ type GetRawSecretV3ByNameResponse struct {
} `json:"secret"`
ETag string
}
type ExportKmsRootKeyResponse struct {
SecretParts []string `json:"secretParts"`
}
type ImportKmsRootKeyRequest struct {
SecretParts []string `json:"secretParts"`
}

128
cli/packages/cmd/kms.go Normal file
View File

@@ -0,0 +1,128 @@
/*
Copyright (c) 2023 Infisical Inc.
*/
package cmd
import (
"fmt"
"strings"
"time"
"github.com/Infisical/infisical-merge/packages/api"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/fatih/color"
"github.com/go-resty/resty/v2"
"github.com/spf13/cobra"
)
var kmsCmd = &cobra.Command{
Use: "kms",
Short: "Manage your Infisical KMS encryption keys",
DisableFlagsInUseLine: true,
Example: "infisical kms",
Args: cobra.ExactArgs(0),
PreRun: func(cmd *cobra.Command, args []string) {
util.RequireLogin()
},
Run: func(cmd *cobra.Command, args []string) {
},
}
// exportCmd represents the export command
var exportKeyCmd = &cobra.Command{
Use: "export",
Short: "Used to export your Infisical root encryption key parts, to be used for recovery (infisical import-key [...parts])",
DisableFlagsInUseLine: true,
Example: "infisical kms export",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
loggedInDetails, err := util.GetCurrentLoggedInUserDetails()
if err != nil {
util.HandleError(err)
}
if !loggedInDetails.IsUserLoggedIn || loggedInDetails.LoginExpired {
util.HandleError(fmt.Errorf("You must be logged in to run this command"))
}
httpClient := resty.New()
httpClient.SetAuthToken(loggedInDetails.UserCredentials.JTWToken).
SetHeader("Accept", "application/json")
res, err := api.CallExportKmsRootEncryptionKey(httpClient)
if err != nil {
if strings.Contains(err.Error(), "configuration already exported") {
util.HandleError(fmt.Errorf("This KMS encryption key has already been exported. You can only export the decryption key once."))
} else {
util.HandleError(err)
}
}
boldGreen := color.New(color.FgGreen).Add(color.Bold)
time.Sleep(time.Second * 1)
boldGreen.Printf(">>>> Successfully exported KMS encryption key\n\n")
plainBold := color.New(color.Bold)
for i, part := range res.SecretParts {
plainBold.Printf("Part %d: %v\n", i+1, part)
}
boldYellow := color.New(color.FgYellow).Add(color.Bold)
boldYellow.Printf("\nPlease store these parts in a secure location. You will need them to recover your KMS encryption key.\nYou will not be able to export these credentials again in the future.\n\n")
},
}
var importKeyCmd = &cobra.Command{
Use: "import",
Short: "Used to import your Infisical root encryption key parts, to be used for recovery (infisical import-key [...parts])",
DisableFlagsInUseLine: true,
Example: "infisical kms import",
Args: cobra.MinimumNArgs(6),
Run: func(cmd *cobra.Command, args []string) {
loggedInDetails, err := util.GetCurrentLoggedInUserDetails()
if err != nil {
util.HandleError(err)
}
if !loggedInDetails.IsUserLoggedIn || loggedInDetails.LoginExpired {
util.HandleError(fmt.Errorf("You must be logged in to run this command"))
}
httpClient := resty.New()
httpClient.SetAuthToken(loggedInDetails.UserCredentials.JTWToken).
SetHeader("Accept", "application/json")
err = api.CallImportKmsRootEncryptionKey(httpClient, api.ImportKmsRootKeyRequest{
SecretParts: args,
})
if err != nil {
if strings.Contains(err.Error(), "configuration was never exported") {
util.HandleError(fmt.Errorf("This KMS encryption key has not been exported yet. You must export the key first before you can import it."))
} else {
util.HandleError(err)
}
}
boldGreen := color.New(color.FgGreen).Add(color.Bold)
time.Sleep(time.Second * 1)
boldGreen.Printf(">>>> Successfully imported KMS encryption key\n\n")
boldYellow := color.New(color.FgYellow).Add(color.Bold)
boldYellow.Printf("Important: Make sure to set the `ROOT_KEY_ENCRYPTION_STRATEGY` environment variable to `BASIC` on your Infisical instance.\nNot doing this will likely result in having to re-import the key on the next instance restart.\n\n")
},
}
func init() {
kmsCmd.AddCommand(exportKeyCmd)
kmsCmd.AddCommand(importKeyCmd)
rootCmd.AddCommand(kmsCmd)
}