From d6e7084aadd5f6f7b7cadbd84bc158bbd53450ab Mon Sep 17 00:00:00 2001 From: Shibly Meeran Date: Mon, 4 Mar 2024 02:14:54 +0530 Subject: [PATCH] renaming flags and performing some CLI refactoring --- cli/internal/commands/commands.go | 20 +- cli/internal/commands/flags.go | 41 ++-- cli/internal/commands/kms.go | 2 + cli/internal/commands/secret.go | 334 ++------------------------- cli/internal/commands/slv.go | 2 +- cli/internal/commands/vault.go | 310 ++++++++++++++++++++++++- core/config/const.go | 5 + operator/api/v1/groupversion_info.go | 6 +- 8 files changed, 352 insertions(+), 368 deletions(-) diff --git a/cli/internal/commands/commands.go b/cli/internal/commands/commands.go index cb0f8ac..abd6fec 100644 --- a/cli/internal/commands/commands.go +++ b/cli/internal/commands/commands.go @@ -34,16 +34,16 @@ var ( envSelfSetCmd *cobra.Command // Vault Commands - vaultCmd *cobra.Command - vaultInfoCmd *cobra.Command - vaultNewCmd *cobra.Command - vaultShareCmd *cobra.Command + vaultCmd *cobra.Command + vaultNewCmd *cobra.Command + vaultShareCmd *cobra.Command + vaultInfoCmd *cobra.Command + vaultPutCmd *cobra.Command + vaultGetCmd *cobra.Command + vaultExportCmd *cobra.Command + vaultRefCmd *cobra.Command + vaultDerefCmd *cobra.Command // Secret Commands - secretCmd *cobra.Command - secretPutCmd *cobra.Command - secretGetCmd *cobra.Command - secretExportCmd *cobra.Command - secretRefCmd *cobra.Command - secretDerefCmd *cobra.Command + // secretCmd *cobra.Command ) diff --git a/cli/internal/commands/flags.go b/cli/internal/commands/flags.go index a1fa7af..104461c 100644 --- a/cli/internal/commands/flags.go +++ b/cli/internal/commands/flags.go @@ -63,13 +63,12 @@ var ( } envAddFlag = FlagDef{ - name: "add", - shorthand: "a", - usage: "Adds environment to default profile", + name: "add", + usage: "Adds environment to default profile", } envSearchFlag = FlagDef{ - name: "search-env", + name: "search", shorthand: "s", usage: "Searches query to filter environments", } @@ -105,20 +104,20 @@ var ( // Vault Command Flags vaultFileFlag = FlagDef{ - name: "vault-file", + name: "vault", shorthand: "v", - usage: "Path to the vault file [Must end with .slv.yml or .slv.yaml]", + usage: "Path to the vault file [Should end with .slv.yml or .slv.yaml]", } vaultAccessPublicKeysFlag = FlagDef{ - name: "public-keys", + name: "pubkey", shorthand: "k", - usage: "Public keys of environments or groups that can access the vault", + usage: "Public keys of environments that can access the vault", } vaultEnableHashingFlag = FlagDef{ - name: "enable-hash", - usage: "Preserve a partial secret hash for the purpose of validating secret rotation [Not recommended, though it might be resilient from brute-forcing]", + name: "hash", + usage: "Enables hashing by preserving a partial hash of the actual secret for the purpose of validating secret rotation [Not recommended, though it might be difficult to brute-force]", } vaultK8sFlag = FlagDef{ @@ -135,9 +134,8 @@ var ( } secretValueFlag = FlagDef{ - name: "secret", - shorthand: "s", - usage: "Secret to be added to the vault", + name: "secret", + usage: "Secret to be added to the vault", } secretForceUpdateFlag = FlagDef{ @@ -147,7 +145,7 @@ var ( secretListFormatFlag = FlagDef{ name: "format", - usage: "List secrets as one of [json, yaml, table, envars]. Defaults to table.", + usage: "List secrets as one of [json, yaml, table, envar]. Defaults to envar", } secretEncodeBase64Flag = FlagDef{ @@ -156,20 +154,17 @@ var ( } secretRefFileFlag = FlagDef{ - name: "file", - shorthand: "f", - usage: "Path to the YAML/JSON file", + name: "ref-file", + usage: "Path to the YAML/JSON file to be referenced", } secretRefTypeFlag = FlagDef{ - name: "type", - shorthand: "t", - usage: "Data format to be considered for the file to be referenced", + name: "ref-format", + usage: "Data serialization format of the referenced file", } secretRefPreviewOnlyFlag = FlagDef{ - name: "preview", - shorthand: "p", - usage: "Preview only mode", + name: "preview", + usage: "Preview only mode", } ) diff --git a/cli/internal/commands/kms.go b/cli/internal/commands/kms.go index e4f7a61..6604eba 100644 --- a/cli/internal/commands/kms.go +++ b/cli/internal/commands/kms.go @@ -13,6 +13,8 @@ func newKMSEnvCommand(kmsName, kmsProviderDesc string, keyIdFlag FlagDef) *cobra newKMSEnvCmd := &cobra.Command{ Use: kmsName, Short: kmsProviderDesc, + Long: kmsProviderDesc + + " - Uses RSA 4096 key with SHA-256 hashing in case of asymmetric binding. Create a KMS key accordingly.", Run: func(cmd *cobra.Command, args []string) { envName, _ := cmd.Flags().GetString(envNameFlag.name) envEmail, _ := cmd.Flags().GetString(envEmailFlag.name) diff --git a/cli/internal/commands/secret.go b/cli/internal/commands/secret.go index bf2f9a3..511a6b9 100644 --- a/cli/internal/commands/secret.go +++ b/cli/internal/commands/secret.go @@ -1,321 +1,17 @@ package commands -import ( - "encoding/base64" - "encoding/json" - "fmt" - "os" - "strings" - "text/tabwriter" - - "github.com/fatih/color" - "github.com/spf13/cobra" - "gopkg.in/yaml.v3" - "savesecrets.org/slv" - "savesecrets.org/slv/core/input" -) - -func toBase64(data []byte) string { - return base64.StdEncoding.EncodeToString(data) -} - -func secretCommand() *cobra.Command { - if secretCmd != nil { - return secretCmd - } - secretCmd = &cobra.Command{ - Use: "secret", - Aliases: []string{"secrets"}, - Short: "Working with secrets", - Long: `Working with secrets in SLV`, - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() - }, - } - secretCmd.AddCommand(secretPutCommand()) - secretCmd.AddCommand(secretGetCommand()) - secretCmd.AddCommand(secretExportCommand()) - secretCmd.AddCommand(secretRefCommand()) - secretCmd.AddCommand(secretDerefCommand()) - return secretCmd -} - -func secretPutCommand() *cobra.Command { - if secretPutCmd != nil { - return secretPutCmd - } - secretPutCmd = &cobra.Command{ - Use: "put", - Aliases: []string{"add", "set", "create"}, - Short: "Adds a secret to the vault", - Run: func(cmd *cobra.Command, args []string) { - vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() - name := cmd.Flag(secretNameFlag.name).Value.String() - secretStr := cmd.Flag(secretValueFlag.name).Value.String() - vault, err := getVault(vaultFile) - if err != nil { - exitOnError(err) - } - forceUpdate, _ := cmd.Flags().GetBool(secretForceUpdateFlag.name) - if !forceUpdate && vault.SecretExists(name) { - confirmation, err := input.GetVisibleInput("Secret already exists. Do you wish to overwrite it? (y/n): ") - if err != nil { - exitOnError(err) - } - if confirmation != "y" { - fmt.Println(color.YellowString("Operation aborted")) - safeExit() - } - } - var secret []byte - if secretStr == "" { - secret, err = input.GetHiddenInput("Enter the secret value for " + name + ": ") - if err != nil { - exitOnError(err) - } - } else { - secret = []byte(secretStr) - } - err = vault.PutSecret(name, secret) - if err != nil { - exitOnError(err) - } - fmt.Println("Updated secret: ", color.GreenString(name), " to vault: ", color.GreenString(vaultFile)) - safeExit() - }, - } - secretPutCmd.Flags().StringP(vaultFileFlag.name, vaultFileFlag.shorthand, "", vaultFileFlag.usage) - secretPutCmd.Flags().StringP(secretNameFlag.name, secretNameFlag.shorthand, "", secretNameFlag.usage) - secretPutCmd.Flags().StringP(secretValueFlag.name, secretValueFlag.shorthand, "", secretValueFlag.usage) - secretPutCmd.Flags().Bool(secretForceUpdateFlag.name, false, secretForceUpdateFlag.usage) - secretPutCmd.MarkFlagRequired(vaultFileFlag.name) - secretPutCmd.MarkFlagRequired(secretNameFlag.name) - return secretPutCmd -} - -func secretGetCommand() *cobra.Command { - if secretGetCmd != nil { - return secretGetCmd - } - secretGetCmd = &cobra.Command{ - Use: "get", - Aliases: []string{"show", "view", "read"}, - Short: "Gets a secret from the vault", - Run: func(cmd *cobra.Command, args []string) { - envSecretKey, err := slv.GetSecretKey() - if err != nil { - exitOnError(err) - } - vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() - name := cmd.Flag(secretNameFlag.name).Value.String() - vault, err := getVault(vaultFile) - if err != nil { - exitOnError(err) - } - err = vault.Unlock(*envSecretKey) - if err != nil { - exitOnError(err) - } - secret, err := vault.GetSecret(name) - if err != nil { - exitOnError(err) - } - encodeToBase64, _ := cmd.Flags().GetBool(secretEncodeBase64Flag.name) - if encodeToBase64 { - fmt.Println(toBase64(secret)) - } else { - fmt.Println(string(secret)) - } - safeExit() - }, - } - secretGetCmd.Flags().StringP(vaultFileFlag.name, vaultFileFlag.shorthand, "", vaultFileFlag.usage) - secretGetCmd.Flags().StringP(secretNameFlag.name, secretNameFlag.shorthand, "", secretNameFlag.usage) - secretGetCmd.Flags().BoolP(secretEncodeBase64Flag.name, secretEncodeBase64Flag.shorthand, false, secretEncodeBase64Flag.usage) - secretGetCmd.MarkFlagRequired(vaultFileFlag.name) - secretGetCmd.MarkFlagRequired(secretNameFlag.name) - return secretGetCmd -} - -func secretExportCommand() *cobra.Command { - if secretExportCmd != nil { - return secretExportCmd - } - secretExportCmd = &cobra.Command{ - Use: "export", - Aliases: []string{"dump", "get-all", "show-all", "view-all", "read-all"}, - Short: "Exports all secrets from the vault", - Run: func(cmd *cobra.Command, args []string) { - envSecretKey, err := slv.GetSecretKey() - if err != nil { - exitOnError(err) - } - vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() - vault, err := getVault(vaultFile) - if err != nil { - exitOnError(err) - } - err = vault.Unlock(*envSecretKey) - if err != nil { - exitOnError(err) - } - secrets, err := vault.GetAllSecrets() - if err != nil { - exitOnError(err) - } - secretOutputMap := make(map[string]string) - encodeToBase64, _ := cmd.Flags().GetBool(secretEncodeBase64Flag.name) - for name, secret := range secrets { - if encodeToBase64 { - secretOutputMap[name] = toBase64(secret) - } else { - secretOutputMap[name] = string(secret) - } - } - exportFormat := cmd.Flag(secretListFormatFlag.name).Value.String() - if exportFormat == "" { - exportFormat = "env" - } - switch exportFormat { - case "table": - tw := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0) - for key, value := range secretOutputMap { - fmt.Fprintf(tw, "%s\t%s\n", key, value) - } - tw.Flush() - case "json": - jsonData, err := json.MarshalIndent(secretOutputMap, "", " ") - if err != nil { - exitOnError(err) - } - fmt.Println(string(jsonData)) - case "yaml", "yml": - yamlData, err := yaml.Marshal(secretOutputMap) - if err != nil { - exitOnError(err) - } - fmt.Println(string(yamlData)) - case "envars", "envar", "env": - for key, value := range secretOutputMap { - value = strings.ReplaceAll(value, "\\", "\\\\") - value = strings.ReplaceAll(value, "\"", "\\\"") - fmt.Printf("%s=\"%s\"\n", key, value) - } - default: - exitOnErrorWithMessage("invalid format: " + exportFormat) - } - safeExit() - }, - } - secretExportCmd.Flags().StringP(vaultFileFlag.name, vaultFileFlag.shorthand, "", vaultFileFlag.usage) - secretExportCmd.Flags().StringP(secretListFormatFlag.name, secretListFormatFlag.shorthand, "", secretListFormatFlag.usage) - secretExportCmd.Flags().BoolP(secretEncodeBase64Flag.name, secretEncodeBase64Flag.shorthand, false, secretEncodeBase64Flag.usage) - secretExportCmd.MarkFlagRequired(vaultFileFlag.name) - return secretExportCmd -} - -func secretRefCommand() *cobra.Command { - if secretRefCmd != nil { - return secretRefCmd - } - secretRefCmd = &cobra.Command{ - Use: "ref", - Aliases: []string{"reference"}, - Short: "References and updates secrets to a vault from a given yaml or json file", - Run: func(cmd *cobra.Command, args []string) { - vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() - vault, err := getVault(vaultFile) - if err != nil { - exitOnError(err) - } - refFile := cmd.Flag(secretRefFileFlag.name).Value.String() - secretNamePrefix := cmd.Flag(secretNameFlag.name).Value.String() - refType := strings.ToLower(cmd.Flag(secretRefTypeFlag.name).Value.String()) - previewOnly, _ := cmd.Flags().GetBool(secretRefPreviewOnlyFlag.name) - forceUpdate, _ := cmd.Flags().GetBool(secretForceUpdateFlag.name) - if secretNamePrefix == "" && refType == "" { - exitOnErrorWithMessage("please provide at least one of --" + secretNameFlag.name + " or --" + secretRefTypeFlag.name + " flag") - } - if refType != "" && refType != "yaml" { - exitOnErrorWithMessage("only yaml auto reference is supported at the moment") - } - result, conflicting, err := vault.RefSecrets(refType, refFile, secretNamePrefix, forceUpdate, previewOnly) - if conflicting { - exitOnErrorWithMessage("conflict found. please use the --" + secretNameFlag.name + " flag to set a different name or --" + secretForceUpdateFlag.name + " flag to overwrite them.") - } else if err != nil { - exitOnError(err) - } - if previewOnly { - fmt.Println(result) - } else { - fmt.Println("Auto referenced", color.GreenString(refFile), "with vault", color.GreenString(vaultFile)) - } - safeExit() - }, - } - secretRefCmd.Flags().StringP(vaultFileFlag.name, vaultFileFlag.shorthand, "", vaultFileFlag.usage) - secretRefCmd.Flags().StringP(secretRefFileFlag.name, secretRefFileFlag.shorthand, "", secretRefFileFlag.usage) - secretRefCmd.Flags().StringP(secretNameFlag.name, secretNameFlag.shorthand, "", secretNameFlag.usage) - secretRefCmd.Flags().StringP(secretRefTypeFlag.name, secretRefTypeFlag.shorthand, "", secretRefTypeFlag.usage) - secretRefCmd.Flags().BoolP(secretRefPreviewOnlyFlag.name, secretRefPreviewOnlyFlag.shorthand, false, secretRefPreviewOnlyFlag.usage) - secretRefCmd.Flags().BoolP(secretForceUpdateFlag.name, secretForceUpdateFlag.shorthand, false, secretForceUpdateFlag.usage) - secretRefCmd.MarkFlagRequired(vaultFileFlag.name) - secretRefCmd.MarkFlagRequired(secretRefFileFlag.name) - return secretRefCmd -} - -func secretDerefCommand() *cobra.Command { - if secretDerefCmd != nil { - return secretDerefCmd - } - secretDerefCmd = &cobra.Command{ - Use: "deref", - Short: "Dereferences and updates secrets from a vault to a given yaml or json file", - Run: func(cmd *cobra.Command, args []string) { - envSecretKey, err := slv.GetSecretKey() - if err != nil { - exitOnError(err) - } - vaultFiles, err := cmd.Flags().GetStringSlice(vaultFileFlag.name) - if err != nil { - exitOnError(err) - } - files, err := cmd.Flags().GetStringSlice(secretRefFileFlag.name) - if err != nil { - exitOnError(err) - } - previewOnly := false - if len(vaultFiles) > 1 || len(files) > 1 { - previewOnly, _ = cmd.Flags().GetBool(secretRefPreviewOnlyFlag.name) - } - for _, vaultFile := range vaultFiles { - vault, err := getVault(vaultFile) - if err != nil { - exitOnError(err) - } - err = vault.Unlock(*envSecretKey) - if err != nil { - exitOnError(err) - } - for _, file := range files { - result, err := vault.DeRefSecrets(file, previewOnly) - if err != nil { - exitOnError(err) - } - if previewOnly { - fmt.Println(result) - } else { - fmt.Println("Dereferenced ", color.GreenString(file), "with the vault", color.GreenString(vaultFile)) - } - } - } - safeExit() - }, - } - secretDerefCmd.Flags().StringSliceP(vaultFileFlag.name, vaultFileFlag.shorthand, []string{}, vaultFileFlag.usage) - secretDerefCmd.Flags().StringSliceP(secretRefFileFlag.name, secretRefFileFlag.shorthand, []string{}, secretRefFileFlag.usage) - secretDerefCmd.Flags().BoolP(secretRefPreviewOnlyFlag.name, secretRefPreviewOnlyFlag.shorthand, false, secretRefPreviewOnlyFlag.usage) - secretDerefCmd.MarkFlagRequired(vaultFileFlag.name) - secretDerefCmd.MarkFlagRequired(secretRefFileFlag.name) - return secretDerefCmd -} +// func secretCommand() *cobra.Command { +// if secretCmd != nil { +// return secretCmd +// } +// secretCmd = &cobra.Command{ +// Use: "secret", +// Aliases: []string{"secrets"}, +// Short: "Sharing secrets using SLV", +// Long: `This command helps you to share secrets to other user/service environments using SLV.`, +// Run: func(cmd *cobra.Command, args []string) { +// cmd.Help() +// }, +// } +// return secretCmd +// } diff --git a/cli/internal/commands/slv.go b/cli/internal/commands/slv.go index ce427ba..1b12887 100644 --- a/cli/internal/commands/slv.go +++ b/cli/internal/commands/slv.go @@ -29,6 +29,6 @@ func SlvCommand() *cobra.Command { slvCmd.AddCommand(envCommand()) slvCmd.AddCommand(profileCommand()) slvCmd.AddCommand(vaultCommand()) - slvCmd.AddCommand(secretCommand()) + // slvCmd.AddCommand(secretCommand()) return slvCmd } diff --git a/cli/internal/commands/vault.go b/cli/internal/commands/vault.go index 1122b78..4e3d0d0 100644 --- a/cli/internal/commands/vault.go +++ b/cli/internal/commands/vault.go @@ -1,24 +1,30 @@ package commands import ( + "encoding/base64" + "encoding/json" "fmt" "os" + "strings" "text/tabwriter" "github.com/fatih/color" "github.com/spf13/cobra" + "gopkg.in/yaml.v3" "savesecrets.org/slv" "savesecrets.org/slv/core/commons" + "savesecrets.org/slv/core/config" "savesecrets.org/slv/core/crypto" "savesecrets.org/slv/core/environments" + "savesecrets.org/slv/core/input" "savesecrets.org/slv/core/profiles" "savesecrets.org/slv/core/vaults" ) const ( - k8sApiVersion = "slv.savesecrets.org/v1" - k8sKind = "SLV" - k8sVaultField = "spec" + k8sApiVersion = config.K8SLVGroup + "/" + config.K8SLVVersion + k8sKind = config.K8SLVKind + k8sVaultField = config.K8SLVVaultField ) func getVault(filePath string) (*vaults.Vault, error) { @@ -58,9 +64,16 @@ func vaultCommand() *cobra.Command { cmd.Help() }, } - vaultCmd.AddCommand(vaultInfoCommand()) + vaultCmd.PersistentFlags().StringP(vaultFileFlag.name, vaultFileFlag.shorthand, "", vaultFileFlag.usage) + vaultCmd.MarkPersistentFlagRequired(vaultFileFlag.name) vaultCmd.AddCommand(vaultNewCommand()) vaultCmd.AddCommand(vaultShareCommand()) + vaultCmd.AddCommand(vaultInfoCommand()) + vaultCmd.AddCommand(vaultPutCommand()) + vaultCmd.AddCommand(vaultGetCommand()) + vaultCmd.AddCommand(vaultExportCommand()) + vaultCmd.AddCommand(vaultRefCommand()) + vaultCmd.AddCommand(vaultDerefCommand()) return vaultCmd } @@ -86,18 +99,27 @@ func vaultInfoCommand() *cobra.Command { exitOnError(err) } profile, _ := profiles.GetDefaultProfile() + self := environments.GetSelf() envMap := make(map[string]string, len(accessors)) for _, accessor := range accessors { var env *environments.Environment envId := accessor.String() - if profile != nil { + selfEnv := false + if self != nil && self.PublicKey == accessor.String() { + env = self + selfEnv = true + } else if profile != nil { env, err = profile.GetEnv(envId) if err != nil { exitOnError(err) } } if env != nil { - envMap[envId] = envId + "\t(" + env.Name + ")" + if selfEnv { + envMap[envId] = envId + "\t(" + color.CyanString("Self"+": "+env.Name) + ")" + } else { + envMap[envId] = envId + "\t(" + env.Name + ")" + } } else { envMap[envId] = envId } @@ -121,8 +143,6 @@ func vaultInfoCommand() *cobra.Command { safeExit() }, } - vaultInfoCmd.Flags().StringP(vaultFileFlag.name, vaultFileFlag.shorthand, "", vaultFileFlag.usage) - vaultInfoCmd.MarkFlagRequired(vaultFileFlag.name) return vaultInfoCmd } @@ -218,13 +238,11 @@ func vaultNewCommand() *cobra.Command { safeExit() }, } - vaultNewCmd.Flags().StringP(vaultFileFlag.name, vaultFileFlag.shorthand, "", vaultFileFlag.usage) vaultNewCmd.Flags().StringSliceP(vaultAccessPublicKeysFlag.name, vaultAccessPublicKeysFlag.shorthand, []string{}, vaultAccessPublicKeysFlag.usage) vaultNewCmd.Flags().StringP(envSearchFlag.name, envSearchFlag.shorthand, "", envSearchFlag.usage) vaultNewCmd.Flags().BoolP(envSelfFlag.name, envSelfFlag.shorthand, false, envSelfFlag.usage) vaultNewCmd.Flags().StringP(vaultK8sFlag.name, vaultK8sFlag.shorthand, "", vaultK8sFlag.usage) vaultNewCmd.Flags().BoolP(vaultEnableHashingFlag.name, vaultEnableHashingFlag.shorthand, false, vaultEnableHashingFlag.usage) - vaultNewCmd.MarkFlagRequired(vaultFileFlag.name) return vaultNewCmd } @@ -269,10 +287,278 @@ func vaultShareCommand() *cobra.Command { exitOnError(err) }, } - vaultShareCmd.Flags().StringP(vaultFileFlag.name, vaultFileFlag.shorthand, "", vaultFileFlag.usage) vaultShareCmd.Flags().StringSliceP(vaultAccessPublicKeysFlag.name, vaultAccessPublicKeysFlag.shorthand, []string{}, vaultAccessPublicKeysFlag.usage) vaultShareCmd.Flags().StringP(envSearchFlag.name, envSearchFlag.shorthand, "", envSearchFlag.usage) vaultShareCmd.Flags().BoolP(envSelfFlag.name, envSelfFlag.shorthand, false, envSelfFlag.usage) - vaultShareCmd.MarkFlagRequired(vaultFileFlag.name) return vaultShareCmd } + +func vaultPutCommand() *cobra.Command { + if vaultPutCmd != nil { + return vaultPutCmd + } + vaultPutCmd = &cobra.Command{ + Use: "put", + Aliases: []string{"add", "set", "create"}, + Short: "Adds a secret to the vault", + Run: func(cmd *cobra.Command, args []string) { + vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() + name := cmd.Flag(secretNameFlag.name).Value.String() + secretStr := cmd.Flag(secretValueFlag.name).Value.String() + vault, err := getVault(vaultFile) + if err != nil { + exitOnError(err) + } + forceUpdate, _ := cmd.Flags().GetBool(secretForceUpdateFlag.name) + if !forceUpdate && vault.SecretExists(name) { + confirmation, err := input.GetVisibleInput("Secret already exists. Do you wish to overwrite it? (y/n): ") + if err != nil { + exitOnError(err) + } + if confirmation != "y" { + fmt.Println(color.YellowString("Operation aborted")) + safeExit() + } + } + var secret []byte + if secretStr == "" { + secret, err = input.GetHiddenInput("Enter the secret value for " + name + ": ") + if err != nil { + exitOnError(err) + } + } else { + secret = []byte(secretStr) + } + err = vault.PutSecret(name, secret) + if err != nil { + exitOnError(err) + } + fmt.Println("Updated secret: ", color.GreenString(name), " to vault: ", color.GreenString(vaultFile)) + safeExit() + }, + } + vaultPutCmd.Flags().StringP(secretNameFlag.name, secretNameFlag.shorthand, "", secretNameFlag.usage) + vaultPutCmd.Flags().StringP(secretValueFlag.name, secretValueFlag.shorthand, "", secretValueFlag.usage) + vaultPutCmd.Flags().Bool(secretForceUpdateFlag.name, false, secretForceUpdateFlag.usage) + vaultPutCmd.MarkFlagRequired(secretNameFlag.name) + return vaultPutCmd +} + +func vaultGetCommand() *cobra.Command { + if vaultGetCmd != nil { + return vaultGetCmd + } + vaultGetCmd = &cobra.Command{ + Use: "get", + Aliases: []string{"show", "view", "read"}, + Short: "Get a secret from the vault", + Run: func(cmd *cobra.Command, args []string) { + envSecretKey, err := slv.GetSecretKey() + if err != nil { + exitOnError(err) + } + vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() + name := cmd.Flag(secretNameFlag.name).Value.String() + vault, err := getVault(vaultFile) + if err != nil { + exitOnError(err) + } + err = vault.Unlock(*envSecretKey) + if err != nil { + exitOnError(err) + } + secret, err := vault.GetSecret(name) + if err != nil { + exitOnError(err) + } + encodeToBase64, _ := cmd.Flags().GetBool(secretEncodeBase64Flag.name) + if encodeToBase64 { + fmt.Println(base64.StdEncoding.EncodeToString(secret)) + } else { + fmt.Println(string(secret)) + } + safeExit() + }, + } + vaultGetCmd.Flags().StringP(secretNameFlag.name, secretNameFlag.shorthand, "", secretNameFlag.usage) + vaultGetCmd.Flags().BoolP(secretEncodeBase64Flag.name, secretEncodeBase64Flag.shorthand, false, secretEncodeBase64Flag.usage) + vaultGetCmd.MarkFlagRequired(secretNameFlag.name) + return vaultGetCmd +} + +func vaultExportCommand() *cobra.Command { + if vaultExportCmd != nil { + return vaultExportCmd + } + vaultExportCmd = &cobra.Command{ + Use: "export", + Aliases: []string{"dump", "get-all", "show-all", "view-all", "read-all"}, + Short: "Exports all secrets from the vault", + Run: func(cmd *cobra.Command, args []string) { + envSecretKey, err := slv.GetSecretKey() + if err != nil { + exitOnError(err) + } + vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() + vault, err := getVault(vaultFile) + if err != nil { + exitOnError(err) + } + err = vault.Unlock(*envSecretKey) + if err != nil { + exitOnError(err) + } + secrets, err := vault.GetAllSecrets() + if err != nil { + exitOnError(err) + } + secretOutputMap := make(map[string]string) + encodeToBase64, _ := cmd.Flags().GetBool(secretEncodeBase64Flag.name) + for name, secret := range secrets { + if encodeToBase64 { + secretOutputMap[name] = base64.StdEncoding.EncodeToString(secret) + } else { + secretOutputMap[name] = string(secret) + } + } + exportFormat := cmd.Flag(secretListFormatFlag.name).Value.String() + if exportFormat == "" { + exportFormat = "env" + } + switch exportFormat { + case "table": + tw := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0) + for key, value := range secretOutputMap { + fmt.Fprintf(tw, "%s\t%s\n", key, value) + } + tw.Flush() + case "json": + jsonData, err := json.MarshalIndent(secretOutputMap, "", " ") + if err != nil { + exitOnError(err) + } + fmt.Println(string(jsonData)) + case "yaml", "yml": + yamlData, err := yaml.Marshal(secretOutputMap) + if err != nil { + exitOnError(err) + } + fmt.Println(string(yamlData)) + case "envars", "envar", "env": + for key, value := range secretOutputMap { + value = strings.ReplaceAll(value, "\\", "\\\\") + value = strings.ReplaceAll(value, "\"", "\\\"") + fmt.Printf("%s=\"%s\"\n", key, value) + } + default: + exitOnErrorWithMessage("invalid format: " + exportFormat) + } + safeExit() + }, + } + vaultExportCmd.Flags().StringP(secretListFormatFlag.name, secretListFormatFlag.shorthand, "", secretListFormatFlag.usage) + vaultExportCmd.Flags().BoolP(secretEncodeBase64Flag.name, secretEncodeBase64Flag.shorthand, false, secretEncodeBase64Flag.usage) + return vaultExportCmd +} + +func vaultRefCommand() *cobra.Command { + if vaultRefCmd != nil { + return vaultRefCmd + } + vaultRefCmd = &cobra.Command{ + Use: "ref", + Aliases: []string{"reference"}, + Short: "References and updates secrets to a vault from a given yaml or json file", + Run: func(cmd *cobra.Command, args []string) { + vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() + vault, err := getVault(vaultFile) + if err != nil { + exitOnError(err) + } + refFile := cmd.Flag(secretRefFileFlag.name).Value.String() + secretNamePrefix := cmd.Flag(secretNameFlag.name).Value.String() + refType := strings.ToLower(cmd.Flag(secretRefTypeFlag.name).Value.String()) + previewOnly, _ := cmd.Flags().GetBool(secretRefPreviewOnlyFlag.name) + forceUpdate, _ := cmd.Flags().GetBool(secretForceUpdateFlag.name) + if secretNamePrefix == "" && refType == "" { + exitOnErrorWithMessage("please provide at least one of --" + secretNameFlag.name + " or --" + secretRefTypeFlag.name + " flag") + } + if refType != "" && refType != "yaml" { + exitOnErrorWithMessage("only yaml auto reference is supported at the moment") + } + result, conflicting, err := vault.RefSecrets(refType, refFile, secretNamePrefix, forceUpdate, previewOnly) + if conflicting { + exitOnErrorWithMessage("conflict found. please use the --" + secretNameFlag.name + " flag to set a different name or --" + secretForceUpdateFlag.name + " flag to overwrite them.") + } else if err != nil { + exitOnError(err) + } + if previewOnly { + fmt.Println(result) + } else { + fmt.Println("Auto referenced", color.GreenString(refFile), "with vault", color.GreenString(vaultFile)) + } + safeExit() + }, + } + vaultRefCmd.Flags().StringP(secretRefFileFlag.name, secretRefFileFlag.shorthand, "", secretRefFileFlag.usage) + vaultRefCmd.Flags().StringP(secretNameFlag.name, secretNameFlag.shorthand, "", secretNameFlag.usage) + vaultRefCmd.Flags().StringP(secretRefTypeFlag.name, secretRefTypeFlag.shorthand, "", secretRefTypeFlag.usage) + vaultRefCmd.Flags().BoolP(secretRefPreviewOnlyFlag.name, secretRefPreviewOnlyFlag.shorthand, false, secretRefPreviewOnlyFlag.usage) + vaultRefCmd.Flags().BoolP(secretForceUpdateFlag.name, secretForceUpdateFlag.shorthand, false, secretForceUpdateFlag.usage) + vaultRefCmd.MarkFlagRequired(secretRefFileFlag.name) + return vaultRefCmd +} + +func vaultDerefCommand() *cobra.Command { + if vaultDerefCmd != nil { + return vaultDerefCmd + } + vaultDerefCmd = &cobra.Command{ + Use: "deref", + Short: "Dereferences and updates secrets from a vault to a given yaml or json file", + Run: func(cmd *cobra.Command, args []string) { + envSecretKey, err := slv.GetSecretKey() + if err != nil { + exitOnError(err) + } + vaultFiles, err := cmd.Flags().GetStringSlice(vaultFileFlag.name) + if err != nil { + exitOnError(err) + } + files, err := cmd.Flags().GetStringSlice(secretRefFileFlag.name) + if err != nil { + exitOnError(err) + } + previewOnly := false + if len(vaultFiles) > 1 || len(files) > 1 { + previewOnly, _ = cmd.Flags().GetBool(secretRefPreviewOnlyFlag.name) + } + for _, vaultFile := range vaultFiles { + vault, err := getVault(vaultFile) + if err != nil { + exitOnError(err) + } + err = vault.Unlock(*envSecretKey) + if err != nil { + exitOnError(err) + } + for _, file := range files { + result, err := vault.DeRefSecrets(file, previewOnly) + if err != nil { + exitOnError(err) + } + if previewOnly { + fmt.Println(result) + } else { + fmt.Println("Dereferenced ", color.GreenString(file), "with the vault", color.GreenString(vaultFile)) + } + } + } + safeExit() + }, + } + vaultDerefCmd.Flags().StringSliceP(secretRefFileFlag.name, secretRefFileFlag.shorthand, []string{}, secretRefFileFlag.usage) + vaultDerefCmd.Flags().BoolP(secretRefPreviewOnlyFlag.name, secretRefPreviewOnlyFlag.shorthand, false, secretRefPreviewOnlyFlag.usage) + vaultDerefCmd.MarkFlagRequired(secretRefFileFlag.name) + return vaultDerefCmd +} diff --git a/core/config/const.go b/core/config/const.go index e9bcf93..f1bfc2c 100644 --- a/core/config/const.go +++ b/core/config/const.go @@ -21,6 +21,11 @@ const ( ___) | |__\ V / |____/|_____\_/ ` + + K8SLVGroup = "slv.savesecrets.org" + K8SLVVersion = "v1" + K8SLVKind = AppNameUpperCase + K8SLVVaultField = "spec" ) var ( diff --git a/operator/api/v1/groupversion_info.go b/operator/api/v1/groupversion_info.go index 6822fab..1df16bb 100644 --- a/operator/api/v1/groupversion_info.go +++ b/operator/api/v1/groupversion_info.go @@ -26,9 +26,9 @@ import ( ) const ( - Group = "slv.savesecrets.org" - Version = "v1" - Kind = config.AppNameUpperCase + Group = config.K8SLVGroup + Version = config.K8SLVVersion + Kind = config.K8SLVKind ) var (