diff --git a/cmd/admin/key/key.go b/cmd/admin/key/key.go index 5b0793b39c..f5fb492499 100644 --- a/cmd/admin/key/key.go +++ b/cmd/admin/key/key.go @@ -17,8 +17,7 @@ import ( ) const ( - flagMasterKey = "masterkey" - flagKeyFile = "file" + flagKeyFile = "file" ) type Config struct { @@ -30,7 +29,7 @@ func New() *cobra.Command { Use: "keys", Short: "manage encryption keys", } - cmd.PersistentFlags().String(flagMasterKey, "", "masterkey for en/decryption keys") + AddMasterKeyFlag(cmd) cmd.AddCommand(newKey()) return cmd } @@ -67,7 +66,10 @@ new -f keys.yaml key2=anotherkey`, if err := viper.Unmarshal(config); err != nil { return err } - masterKey, _ := cmd.Flags().GetString(flagMasterKey) + masterKey, err := MasterKey(cmd) + if err != nil { + return err + } storage, err := keyStorage(config.Database, masterKey) if err != nil { return err @@ -113,7 +115,7 @@ func keysFromYAML(file io.Reader) ([]*crypto.Key, error) { return keys, nil } -func openFile(fileName string) (*os.File, error) { +func openFile(fileName string) (io.Reader, error) { file, err := os.Open(fileName) if err != nil { return nil, caos_errs.ThrowInternalf(err, "KEY-asGr2", "failed to open file: %s", fileName) diff --git a/cmd/admin/key/masterkey.go b/cmd/admin/key/masterkey.go new file mode 100644 index 0000000000..cae2535661 --- /dev/null +++ b/cmd/admin/key/masterkey.go @@ -0,0 +1,64 @@ +package key + +import ( + "errors" + "io/ioutil" + "os" + + "github.com/spf13/cobra" +) + +const ( + flagMasterKey = "masterkeyFile" + flagMasterKeyShort = "m" + flagMasterKeyArg = "masterkey" + flagMasterKeyEnv = "masterkeyFromEnv" + envMasterKey = "ZITADEL_MASTERKEY" +) + +var ( + ErrNotSingleFlag = errors.New("masterkey must either be provided by file path, value or environment variable") +) + +func AddMasterKeyFlag(cmd *cobra.Command) { + cmd.PersistentFlags().StringP(flagMasterKey, flagMasterKeyShort, "", "path to the masterkey for en/decryption keys") + cmd.PersistentFlags().String(flagMasterKeyArg, "", "masterkey as argument for en/decryption keys") + cmd.PersistentFlags().Bool(flagMasterKeyEnv, false, "read masterkey for en/decryption keys from environment variable (ZITADEL_MASTERKEY)") +} + +func MasterKey(cmd *cobra.Command) (string, error) { + masterKeyFile, _ := cmd.Flags().GetString(flagMasterKey) + masterKeyFromArg, _ := cmd.Flags().GetString(flagMasterKeyArg) + masterKeyFromEnv, _ := cmd.Flags().GetBool(flagMasterKeyEnv) + if err := checkSingleFlag(masterKeyFile, masterKeyFromArg, masterKeyFromEnv); err != nil { + return "", err + } + if masterKeyFromArg != "" { + return masterKeyFromArg, nil + } + if masterKeyFromEnv { + return os.Getenv(envMasterKey), nil + } + data, err := ioutil.ReadFile(masterKeyFile) + if err != nil { + return "", err + } + return string(data), nil +} + +func checkSingleFlag(masterKeyFile, masterKeyFromArg string, masterKeyFromEnv bool) error { + var flags int + if masterKeyFile != "" { + flags++ + } + if masterKeyFromArg != "" { + flags++ + } + if masterKeyFromEnv { + flags++ + } + if flags != 1 { + return ErrNotSingleFlag + } + return nil +} diff --git a/cmd/admin/key/masterkey_test.go b/cmd/admin/key/masterkey_test.go new file mode 100644 index 0000000000..6be3aab56d --- /dev/null +++ b/cmd/admin/key/masterkey_test.go @@ -0,0 +1,72 @@ +package key + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_checkSingleFlag(t *testing.T) { + type args struct { + masterKeyFile string + masterKeyFromArg string + masterKeyFromEnv bool + } + tests := []struct { + name string + args args + wantErr assert.ErrorAssertionFunc + }{ + { + "no values, error", + args{ + masterKeyFile: "", + masterKeyFromArg: "", + masterKeyFromEnv: false, + }, + assert.Error, + }, + { + "multiple values, error", + args{ + masterKeyFile: "file", + masterKeyFromArg: "masterkey", + masterKeyFromEnv: true, + }, + assert.Error, + }, + { + "only file, ok", + args{ + masterKeyFile: "file", + masterKeyFromArg: "", + masterKeyFromEnv: false, + }, + assert.NoError, + }, + { + "only argument, ok", + args{ + masterKeyFile: "", + masterKeyFromArg: "masterkey", + masterKeyFromEnv: false, + }, + assert.NoError, + }, + { + "only env, ok", + args{ + masterKeyFile: "", + masterKeyFromArg: "", + masterKeyFromEnv: true, + }, + assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.wantErr(t, checkSingleFlag(tt.args.masterKeyFile, tt.args.masterKeyFromArg, tt.args.masterKeyFromEnv), fmt.Sprintf("checkSingleFlag(%v, %v)", tt.args.masterKeyFile, tt.args.masterKeyFromArg)) + }) + } +} diff --git a/cmd/admin/start/flags.go b/cmd/admin/start/flags.go index b09d462cb1..5768401ea5 100644 --- a/cmd/admin/start/flags.go +++ b/cmd/admin/start/flags.go @@ -3,6 +3,8 @@ package start import ( "github.com/spf13/cobra" "github.com/spf13/viper" + + "github.com/caos/zitadel/cmd/admin/key" ) func startFlags(cmd *cobra.Command) { @@ -11,7 +13,7 @@ func startFlags(cmd *cobra.Command) { bindStringFlag(cmd, "externalPort", "port ZITADEL will be exposed on") bindBoolFlag(cmd, "externalSecure", "if ZITADEL will be served on HTTPS") - cmd.PersistentFlags().String(flagMasterKey, "", "masterkey for en/decryption keys") + key.AddMasterKeyFlag(cmd) } diff --git a/cmd/admin/start/start.go b/cmd/admin/start/start.go index b66d48ee94..41ef7883c9 100644 --- a/cmd/admin/start/start.go +++ b/cmd/admin/start/start.go @@ -19,6 +19,8 @@ import ( "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" + "github.com/caos/zitadel/cmd/admin/key" + admin_es "github.com/caos/zitadel/internal/admin/repository/eventsourcing" "github.com/caos/zitadel/internal/api" "github.com/caos/zitadel/internal/api/assets" @@ -59,7 +61,10 @@ Requirements: - cockroachdb`, RunE: func(cmd *cobra.Command, args []string) error { config := MustNewConfig(viper.GetViper()) - masterKey, _ := cmd.Flags().GetString(flagMasterKey) + masterKey, err := key.MasterKey(cmd) + if err != nil { + return err + } return startZitadel(config, masterKey) },