feat(cli): CLI Keyring improvements

This commit is contained in:
Daniel Hougaard
2024-07-26 19:14:21 +02:00
parent b39b5bd1a1
commit ff0e7feeee
6 changed files with 101 additions and 18 deletions

View File

@@ -4,6 +4,7 @@ Copyright (c) 2023 Infisical Inc.
package cmd package cmd
import ( import (
"encoding/base64"
"fmt" "fmt"
"strings" "strings"
@@ -16,10 +17,49 @@ import (
var AvailableVaultsAndDescriptions = []string{"auto (automatically select native vault on system)", "file (encrypted file vault)"} var AvailableVaultsAndDescriptions = []string{"auto (automatically select native vault on system)", "file (encrypted file vault)"}
var AvailableVaults = []string{"auto", "file"} var AvailableVaults = []string{"auto", "file"}
var vaultSetPassphraseCmd = &cobra.Command{
Example: `infisical vault set-passphrase [your-passphrase]`,
Use: "set-passphrase [your-passphrase]",
Short: "Used to set the passphrase for the file vault",
DisableFlagsInUseLine: true,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
log.Error().Msgf("Please provide a passphrase to set for the file vault")
return
}
passphrase := args[0]
configFile, err := util.GetConfigFile()
if err != nil {
log.Error().Msgf("Unable to set passphrase for file vault because of [err=%s]", err)
return
}
if configFile.VaultBackendType != "file" {
log.Error().Msgf("You are not using file vault to store your login details. You can only set passphrase for file vault")
return
}
// encode with base64
encodedPassphrase := base64.StdEncoding.EncodeToString([]byte(passphrase))
configFile.VaultBackendPassphrase = encodedPassphrase
err = util.WriteConfigFile(&configFile)
if err != nil {
log.Error().Msgf("Unable to set passphrase for file vault because of [err=%s]", err)
return
}
fmt.Printf("\nSuccessfully, set passphrase for file vault. You can now store your login details securely at rest\n")
},
}
var vaultSetCmd = &cobra.Command{ var vaultSetCmd = &cobra.Command{
Example: `infisical vault set pass`, Example: `infisical vault set [file|auto]`,
Use: "set [vault-name]", Use: "set [file|auto]",
Short: "Used to set the vault backend to store your login details securely at rest", Short: "Used to set the type of vault backend to store your login details securely at rest",
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
@@ -89,5 +129,6 @@ func printAvailableVaultBackends() {
func init() { func init() {
vaultCmd.AddCommand(vaultSetCmd) vaultCmd.AddCommand(vaultSetCmd)
vaultCmd.AddCommand(vaultSetPassphraseCmd)
rootCmd.AddCommand(vaultCmd) rootCmd.AddCommand(vaultCmd)
} }

View File

@@ -11,10 +11,11 @@ type UserCredentials struct {
// The file struct for Infisical config file // The file struct for Infisical config file
type ConfigFile struct { type ConfigFile struct {
LoggedInUserEmail string `json:"loggedInUserEmail"` LoggedInUserEmail string `json:"loggedInUserEmail"`
LoggedInUserDomain string `json:"LoggedInUserDomain,omitempty"` LoggedInUserDomain string `json:"LoggedInUserDomain,omitempty"`
LoggedInUsers []LoggedInUser `json:"loggedInUsers,omitempty"` LoggedInUsers []LoggedInUser `json:"loggedInUsers,omitempty"`
VaultBackendType string `json:"vaultBackendType,omitempty"` VaultBackendType string `json:"vaultBackendType,omitempty"`
VaultBackendPassphrase string `json:"vaultBackendPassphrase,omitempty"`
} }
type LoggedInUser struct { type LoggedInUser struct {

View File

@@ -1,6 +1,7 @@
package util package util
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -50,10 +51,11 @@ func WriteInitalConfig(userCredentials *models.UserCredentials) error {
} }
configFile := models.ConfigFile{ configFile := models.ConfigFile{
LoggedInUserEmail: userCredentials.Email, LoggedInUserEmail: userCredentials.Email,
LoggedInUserDomain: config.INFISICAL_URL, LoggedInUserDomain: config.INFISICAL_URL,
LoggedInUsers: existingConfigFile.LoggedInUsers, LoggedInUsers: existingConfigFile.LoggedInUsers,
VaultBackendType: existingConfigFile.VaultBackendType, VaultBackendType: existingConfigFile.VaultBackendType,
VaultBackendPassphrase: existingConfigFile.VaultBackendPassphrase,
} }
configFileMarshalled, err := json.Marshal(configFile) configFileMarshalled, err := json.Marshal(configFile)
@@ -215,6 +217,14 @@ func GetConfigFile() (models.ConfigFile, error) {
return models.ConfigFile{}, err return models.ConfigFile{}, err
} }
if configFile.VaultBackendPassphrase != "" {
decodedPassphrase, err := base64.StdEncoding.DecodeString(configFile.VaultBackendPassphrase)
if err != nil {
return models.ConfigFile{}, fmt.Errorf("GetConfigFile: Unable to decode base64 passphrase [err=%s]", err)
}
os.Setenv("INFISICAL_VAULT_FILE_PASSPHRASE", string(decodedPassphrase))
}
return configFile, nil return configFile, nil
} }

View File

@@ -8,6 +8,10 @@ const (
INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json" INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN" INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN"
INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME = "INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN" INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME = "INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN"
INFISICAL_VAULT_FILE_PASSPHRASE_ENV_NAME = "INFISICAL_VAULT_FILE_PASSPHRASE" // This works because we've forked the keyring package and added support for this env variable. This explains why you won't find any occurrences of it in the CLI codebase.
VAULT_BACKEND_AUTO_MODE = "auto"
VAULT_BACKEND_FILE_MODE = "file"
// Universal Auth // Universal Auth
INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_ID" INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_ID"

View File

@@ -1,11 +1,18 @@
package util package util
import ( import (
"strings"
"github.com/fatih/color"
"github.com/zalando/go-keyring" "github.com/zalando/go-keyring"
) )
const MAIN_KEYRING_SERVICE = "infisical-cli" const MAIN_KEYRING_SERVICE = "infisical-cli"
func keyringNotConfigured(err error) bool {
return err != nil && strings.Contains(err.Error(), "was not provided by any .service files")
}
type TimeoutError struct { type TimeoutError struct {
message string message string
} }
@@ -20,16 +27,30 @@ func SetValueInKeyring(key, value string) error {
PrintErrorAndExit(1, err, "Unable to get current vault. Tip: run [infisical rest] then try again") PrintErrorAndExit(1, err, "Unable to get current vault. Tip: run [infisical rest] then try again")
} }
return keyring.Set(currentVaultBackend, MAIN_KEYRING_SERVICE, key, value) err = keyring.Set(currentVaultBackend, MAIN_KEYRING_SERVICE, key, value)
if err == keyring.ErrUnsupportedPlatform || keyringNotConfigured(err) {
boldGreen := color.New(color.FgGreen).Add(color.Bold)
boldGreen.Printf("Warning: Fallback file keyring is being used")
err = keyring.Set(VAULT_BACKEND_FILE_MODE, MAIN_KEYRING_SERVICE, key, value)
}
return err
} }
func GetValueInKeyring(key string) (string, error) { func GetValueInKeyring(key string) (string, error) {
currentVaultBackend, err := GetCurrentVaultBackend() currentVaultBackend, err := GetCurrentVaultBackend()
if err != nil { if err != nil {
PrintErrorAndExit(1, err, "Unable to get current vault. Tip: run [infisical rest] then try again") PrintErrorAndExit(1, err, "Unable to get current vault. Tip: run [infisical reset] then try again")
} }
return keyring.Get(currentVaultBackend, MAIN_KEYRING_SERVICE, key) value, err := keyring.Get(currentVaultBackend, MAIN_KEYRING_SERVICE, key)
if err == keyring.ErrUnsupportedPlatform || keyringNotConfigured(err) {
value, err = keyring.Get(currentVaultBackend, MAIN_KEYRING_SERVICE, key)
}
return value, err
} }
func DeleteValueInKeyring(key string) error { func DeleteValueInKeyring(key string) error {
@@ -38,5 +59,11 @@ func DeleteValueInKeyring(key string) error {
return err return err
} }
return keyring.Delete(currentVaultBackend, MAIN_KEYRING_SERVICE, key) err = keyring.Delete(currentVaultBackend, MAIN_KEYRING_SERVICE, key)
if err == keyring.ErrUnsupportedPlatform || keyringNotConfigured(err) {
err = keyring.Delete(currentVaultBackend, MAIN_KEYRING_SERVICE, key)
}
return err
} }

View File

@@ -11,11 +11,11 @@ func GetCurrentVaultBackend() (string, error) {
} }
if configFile.VaultBackendType == "" { if configFile.VaultBackendType == "" {
return "auto", nil return VAULT_BACKEND_AUTO_MODE, nil
} }
if configFile.VaultBackendType != "auto" && configFile.VaultBackendType != "file" { if configFile.VaultBackendType != VAULT_BACKEND_AUTO_MODE && configFile.VaultBackendType != VAULT_BACKEND_FILE_MODE {
return "auto", nil return VAULT_BACKEND_AUTO_MODE, nil
} }
return configFile.VaultBackendType, nil return configFile.VaultBackendType, nil