mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-13 21:29:31 +00:00
add support for multiple keyring
This commit is contained in:
@@ -15,7 +15,7 @@ var rootCmd = &cobra.Command{
|
||||
Short: "Infisical CLI is used to inject environment variables into any process",
|
||||
Long: `Infisical is a simple, end-to-end encrypted service that enables teams to sync and manage their environment variables across their development life cycle.`,
|
||||
CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
|
||||
Version: "0.1.11",
|
||||
Version: "0.1.12",
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
@@ -31,4 +31,8 @@ func init() {
|
||||
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
rootCmd.PersistentFlags().BoolVarP(&debugLogging, "debug", "d", false, "Enable verbose logging")
|
||||
rootCmd.PersistentFlags().StringVar(&util.INFISICAL_URL, "domain", "https://app.infisical.com/api", "Point the CLI to your own backend")
|
||||
|
||||
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.InitKeyRingInstance()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/99designs/keyring"
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var vaultSetCmd = &cobra.Command{
|
||||
Example: `infisical vault set pass`,
|
||||
Use: "set [vault-name]",
|
||||
Short: "Used to set the vault backend to store your login details securely at rest",
|
||||
DisableFlagsInUseLine: true,
|
||||
PreRun: toggleDebug,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
wantedVaultTypeName := args[0]
|
||||
currentVaultBackend, err := util.GetCurrentVaultBackend()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to set vault to [%s] because of [err=%s]", wantedVaultTypeName, err)
|
||||
return
|
||||
}
|
||||
|
||||
if wantedVaultTypeName == string(currentVaultBackend) {
|
||||
log.Errorf("You are already on vault backend [%s]", currentVaultBackend)
|
||||
return
|
||||
}
|
||||
|
||||
if isVaultToSwitchToValid(wantedVaultTypeName) {
|
||||
configFile, err := util.GetConfigFile()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to set vault to [%s] because of [err=%s]", wantedVaultTypeName, err)
|
||||
return
|
||||
}
|
||||
|
||||
configFile.VaultBackendType = keyring.BackendType(wantedVaultTypeName) // save selected vault
|
||||
configFile.LoggedInUserEmail = "" // reset the logged in user to prompt them to re login
|
||||
|
||||
err = util.WriteConfigFile(&configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to set vault to [%s] because an error occurred when saving the config file [err=%s]")
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Successfully, switched vault backend from [%s] to [%s]. Please login in again to store your login details in the new vault with [infisical login]", currentVaultBackend, wantedVaultTypeName)
|
||||
} else {
|
||||
log.Errorf("The requested vault type [%s] is not available on this system. Only the following vault backends are available for you system: %s", wantedVaultTypeName, keyring.AvailableBackends())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// runCmd represents the run command
|
||||
var vaultCmd = &cobra.Command{
|
||||
Use: "vault",
|
||||
Short: "Used to manage where your Infisical login token is saved on your machine",
|
||||
DisableFlagsInUseLine: true,
|
||||
PreRun: toggleDebug,
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
printAvailableVaultBackends()
|
||||
},
|
||||
}
|
||||
|
||||
func printAvailableVaultBackends() {
|
||||
log.Infof("The following vaults are available on your system:")
|
||||
for _, backend := range keyring.AvailableBackends() {
|
||||
log.Infof("- %s", backend)
|
||||
}
|
||||
|
||||
currentVaultBackend, err := util.GetCurrentVaultBackend()
|
||||
if err != nil {
|
||||
log.Errorf("printAvailableVaultBackends: unable to print the available vault backend because of error [err=%s]", err)
|
||||
}
|
||||
|
||||
log.Infof("\nYou are currently using [%s] vault to store your login credentials", string(currentVaultBackend))
|
||||
}
|
||||
|
||||
// Checks if the vault that the user wants to switch to is a valid available vault
|
||||
func isVaultToSwitchToValid(vaultNameToSwitchTo string) bool {
|
||||
isFound := false
|
||||
for _, backend := range keyring.AvailableBackends() {
|
||||
if vaultNameToSwitchTo == string(backend) {
|
||||
isFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return isFound
|
||||
}
|
||||
|
||||
func init() {
|
||||
vaultCmd.AddCommand(vaultSetCmd)
|
||||
rootCmd.AddCommand(vaultCmd)
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package models
|
||||
|
||||
import "github.com/99designs/keyring"
|
||||
|
||||
type UserCredentials struct {
|
||||
Email string `json:"email"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
@@ -8,7 +10,8 @@ type UserCredentials struct {
|
||||
|
||||
// The file struct for Infisical config file
|
||||
type ConfigFile struct {
|
||||
LoggedInUserEmail string `json:"loggedInUserEmail"`
|
||||
LoggedInUserEmail string `json:"loggedInUserEmail"`
|
||||
VaultBackendType keyring.BackendType `json:"vaultBackendType"`
|
||||
}
|
||||
|
||||
type SingleEnvironmentVariable struct {
|
||||
|
||||
@@ -24,8 +24,15 @@ func WriteInitalConfig(userCredentials *models.UserCredentials) error {
|
||||
}
|
||||
}
|
||||
|
||||
// get existing config
|
||||
existingConfigFile, err := GetConfigFile()
|
||||
if err != nil {
|
||||
return fmt.Errorf("writeInitalConfig: unable to write config file because [err=%s]", err)
|
||||
}
|
||||
|
||||
configFile := models.ConfigFile{
|
||||
LoggedInUserEmail: userCredentials.Email,
|
||||
VaultBackendType: existingConfigFile.VaultBackendType,
|
||||
}
|
||||
|
||||
configFileMarshalled, err := json.Marshal(configFile)
|
||||
@@ -152,3 +159,50 @@ func GetAllWorkSpaceConfigsStartingFromCurrentPath() (workspaces []models.Worksp
|
||||
|
||||
return listOfWorkSpaceConfigs, nil
|
||||
}
|
||||
|
||||
// Get the infisical config file and if it doesn't exist, return empty config model, otherwise raise error
|
||||
func GetConfigFile() (models.ConfigFile, error) {
|
||||
fullConfigFilePath, _, err := GetFullConfigFilePath()
|
||||
if err != nil {
|
||||
return models.ConfigFile{}, err
|
||||
}
|
||||
|
||||
configFileAsBytes, err := os.ReadFile(fullConfigFilePath)
|
||||
if err != nil {
|
||||
if err, ok := err.(*os.PathError); ok {
|
||||
return models.ConfigFile{}, nil
|
||||
} else {
|
||||
return models.ConfigFile{}, err
|
||||
}
|
||||
}
|
||||
|
||||
var configFile models.ConfigFile
|
||||
err = json.Unmarshal(configFileAsBytes, &configFile)
|
||||
if err != nil {
|
||||
return models.ConfigFile{}, err
|
||||
}
|
||||
|
||||
return configFile, nil
|
||||
}
|
||||
|
||||
// Write a ConfigFile to disk. Raise error if unable to save the model to ask
|
||||
func WriteConfigFile(configFile *models.ConfigFile) error {
|
||||
fullConfigFilePath, _, err := GetFullConfigFilePath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("writeConfigFile: unable to write config file because an error occurred when getting config file path [err=%s]", err)
|
||||
}
|
||||
|
||||
configFileMarshalled, err := json.Marshal(configFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writeConfigFile: unable to write config file because an error occurred when marshalling the config file [err=%s]", err)
|
||||
}
|
||||
|
||||
// Create file in directory
|
||||
err = WriteToFile(fullConfigFilePath, configFileMarshalled, os.ModePerm)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@ package util
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/99designs/keyring"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
const SERVICE_NAME = "infisical"
|
||||
@@ -17,26 +16,30 @@ const SERVICE_NAME = "infisical"
|
||||
func StoreUserCredsInKeyRing(userCred *models.UserCredentials) error {
|
||||
userCredMarshalled, err := json.Marshal(userCred)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Something went wrong when marshalling user creds:", err)
|
||||
return fmt.Errorf("StoreUserCredsInKeyRing: something went wrong when marshalling user creds [err=%s]", err)
|
||||
}
|
||||
|
||||
err = keyring.Set(SERVICE_NAME, userCred.Email, string(userCredMarshalled))
|
||||
err = keyringInstance.Set(keyring.Item{
|
||||
Key: userCred.Email,
|
||||
Data: []byte(string(userCredMarshalled)),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to store user credentials:", err)
|
||||
return fmt.Errorf("StoreUserCredsInKeyRing: unable to store user credentials because [err=%s]", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetUserCredsFromKeyRing(userEmail string) (credentials models.UserCredentials, err error) {
|
||||
credentialsString, err := keyring.Get(SERVICE_NAME, userEmail)
|
||||
credentialsValue, err := keyringInstance.Get(userEmail)
|
||||
if err != nil {
|
||||
return models.UserCredentials{}, fmt.Errorf("Unable to get key from Keyring:", err)
|
||||
return models.UserCredentials{}, fmt.Errorf("Unable to get key from Keyring. could not find login credentials in your Keyring. This is common if you have switched vault backend recently. If so, please login in again and retry:", err)
|
||||
}
|
||||
|
||||
var userCredentials models.UserCredentials
|
||||
|
||||
err = json.Unmarshal([]byte(credentialsString), &userCredentials)
|
||||
err = json.Unmarshal([]byte(credentialsValue.Data), &userCredentials)
|
||||
if err != nil {
|
||||
return models.UserCredentials{}, fmt.Errorf("Something went wrong when unmarshalling user creds:", err)
|
||||
}
|
||||
@@ -50,23 +53,13 @@ func GetUserCredsFromKeyRing(userEmail string) (credentials models.UserCredentia
|
||||
|
||||
func IsUserLoggedIn() (hasUserLoggedIn bool, theUsersEmail string, err error) {
|
||||
if ConfigFileExists() {
|
||||
fullConfigFilePath, _, err := GetFullConfigFilePath()
|
||||
configFile, err := GetConfigFile()
|
||||
if err != nil {
|
||||
log.Debugln("Error gettting full path:", err)
|
||||
return false, "", err
|
||||
return false, "", fmt.Errorf("IsUserLoggedIn: unable to get logged in user from config file [err=%s]", err)
|
||||
}
|
||||
|
||||
configFileAsBytes, err := os.ReadFile(fullConfigFilePath)
|
||||
if err != nil {
|
||||
log.Debugln("Unable to read config file:", err)
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
var configFile models.ConfigFile
|
||||
err = json.Unmarshal(configFileAsBytes, &configFile)
|
||||
if err != nil {
|
||||
log.Debugln("Unable to unmarshal config file:", err)
|
||||
return false, "", err
|
||||
if configFile.LoggedInUserEmail == "" {
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
userCreds, err := GetUserCredsFromKeyRing(configFile.LoggedInUserEmail)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/99designs/keyring"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// Keyring instance
|
||||
var keyringInstance keyring.Keyring
|
||||
var keyringInstanceConfig keyring.Config
|
||||
|
||||
func GetCurrentVaultBackend() (keyring.BackendType, error) {
|
||||
configFile, err := GetConfigFile()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getCurrentVaultBackend: unable to get config file [err=%s]", err)
|
||||
}
|
||||
|
||||
if configFile.VaultBackendType == "" {
|
||||
if keyring.AvailableBackends()[0] == keyring.FileBackend {
|
||||
}
|
||||
return keyring.AvailableBackends()[0], nil
|
||||
}
|
||||
|
||||
return configFile.VaultBackendType, nil
|
||||
}
|
||||
|
||||
func InitKeyRingInstance() {
|
||||
currentVaultBackend, err := GetCurrentVaultBackend()
|
||||
if err != nil {
|
||||
log.Infof("InitKeyRingInstance: unable to get the current vault backend, [err=%s]", err)
|
||||
}
|
||||
|
||||
keyringInstanceConfig = keyring.Config{
|
||||
FilePasswordFunc: fileKeyringPassphrasePrompt,
|
||||
ServiceName: SERVICE_NAME,
|
||||
LibSecretCollectionName: SERVICE_NAME,
|
||||
KWalletAppID: SERVICE_NAME,
|
||||
KWalletFolder: SERVICE_NAME,
|
||||
KeychainTrustApplication: true,
|
||||
WinCredPrefix: SERVICE_NAME,
|
||||
FileDir: fmt.Sprintf("~/%s-file-vault", SERVICE_NAME),
|
||||
KeychainAccessibleWhenUnlocked: true,
|
||||
}
|
||||
|
||||
// if the user explicitly sets a vault backend, then only use that
|
||||
if currentVaultBackend != "" {
|
||||
keyringInstanceConfig.AllowedBackends = []keyring.BackendType{keyring.BackendType(currentVaultBackend)}
|
||||
}
|
||||
|
||||
keyringInstance, err = keyring.Open(keyringInstanceConfig)
|
||||
if err != nil {
|
||||
log.Errorf("InitKeyRingInstance: Unable to create instance of Keyring because of [err=%s]", err)
|
||||
}
|
||||
}
|
||||
|
||||
func fileKeyringPassphrasePrompt(prompt string) (string, error) {
|
||||
if password, ok := os.LookupEnv("INFISICAL_VAULT_FILE_PASSPHRASE"); ok {
|
||||
return password, nil
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "%s: ", prompt)
|
||||
b, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package visualize
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/jedib0t/go-pretty/table"
|
||||
)
|
||||
|
||||
// Given headers and rows, this function will print out a table
|
||||
func Table(headers []string, rows [][]string) {
|
||||
t := table.NewWriter()
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
t.SetStyle(table.StyleLight)
|
||||
|
||||
// t.SetTitle("Title")
|
||||
t.Style().Options.DrawBorder = true
|
||||
t.Style().Options.SeparateHeader = true
|
||||
t.Style().Options.SeparateColumns = true
|
||||
|
||||
tableHeaders := table.Row{}
|
||||
for _, header := range headers {
|
||||
tableHeaders = append(tableHeaders, header)
|
||||
}
|
||||
|
||||
t.AppendHeader(tableHeaders)
|
||||
for _, row := range rows {
|
||||
tableRow := table.Row{}
|
||||
for _, val := range row {
|
||||
tableRow = append(tableRow, val)
|
||||
}
|
||||
t.AppendRow(tableRow)
|
||||
}
|
||||
|
||||
t.Render()
|
||||
}
|
||||
Reference in New Issue
Block a user