diff --git a/cmd/waku/flags.go b/cmd/waku/flags.go index 67b635ba..cef87e8c 100644 --- a/cmd/waku/flags.go +++ b/cmd/waku/flags.go @@ -115,16 +115,6 @@ var ( Destination: &options.KeyPasswd, EnvVars: []string{"WAKUNODE2_KEY_PASSWORD"}, }) - GenerateKey = altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "generate-key", - Usage: "Generate private key file at path specified in --key-file with the password defined by --key-password", - Destination: &options.GenerateKey, - }) - Overwrite = altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "overwrite", - Usage: "When generating a keyfile, overwrite the nodekey file if it already exists", - Destination: &options.Overwrite, - }) StaticNode = cliutils.NewGenericFlagMultiValue(&cli.GenericFlag{ Name: "staticnode", Usage: "Multiaddr of peer to directly connect with. Option may be repeated", diff --git a/cmd/waku/keygen/command.go b/cmd/waku/keygen/command.go new file mode 100644 index 00000000..5ebce53d --- /dev/null +++ b/cmd/waku/keygen/command.go @@ -0,0 +1,80 @@ +package keygen + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/crypto" + "github.com/urfave/cli/v2" + "github.com/waku-org/go-waku/waku/v2/utils" + "go.uber.org/zap" +) + +// Command generates a key file used to generate the node's peerID, encrypted with an optional password +var Command = cli.Command{ + Name: "generate-key", + Usage: "Generate private key file at path specified in --key-file with the password defined by --key-password", + Action: func(cCtx *cli.Context) error { + if err := generateKeyFile(Options.KeyFile, []byte(Options.KeyPasswd), Options.Overwrite); err != nil { + utils.Logger().Fatal("could not write keyfile", zap.Error(err)) + } + return nil + }, + Flags: []cli.Flag{ + KeyFile, + KeyPassword, + Overwrite, + }, +} + +func checkForFileExistence(path string, overwrite bool) error { + _, err := os.Stat(path) + + if err == nil && !overwrite { + return fmt.Errorf("%s already exists. Use --overwrite to overwrite the file", path) + } + + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + return err + } + + return nil +} + +func generatePrivateKey() ([]byte, error) { + key, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + + return key.D.Bytes(), nil +} + +func writeKeyFile(path string, key []byte, passwd []byte) error { + encryptedK, err := keystore.EncryptDataV3(key, passwd, keystore.StandardScryptN, keystore.StandardScryptP) + if err != nil { + return err + } + + output, err := json.Marshal(encryptedK) + if err != nil { + return err + } + + return os.WriteFile(path, output, 0600) +} + +func generateKeyFile(path string, passwd []byte, overwrite bool) error { + if err := checkForFileExistence(path, overwrite); err != nil { + return err + } + + key, err := generatePrivateKey() + if err != nil { + return err + } + + return writeKeyFile(path, key, passwd) +} diff --git a/cmd/waku/keygen/flags.go b/cmd/waku/keygen/flags.go new file mode 100644 index 00000000..579ed05d --- /dev/null +++ b/cmd/waku/keygen/flags.go @@ -0,0 +1,35 @@ +package keygen + +import ( + cli "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2/altsrc" +) + +// Options contain the settings used for generating a key file +var Options GenerateKeyOptions + +var ( + // KeyFile is a flag that contains the path where the node key will be written + KeyFile = altsrc.NewPathFlag(&cli.PathFlag{ + Name: "key-file", + Value: "./nodekey", + Usage: "Path to a file containing the private key for the P2P node", + Destination: &Options.KeyFile, + EnvVars: []string{"WAKUNODE2_KEY_FILE"}, + }) + // KeyPassword is a flag to set the password used to encrypt the file + KeyPassword = altsrc.NewStringFlag(&cli.StringFlag{ + Name: "key-password", + Value: "secret", + Usage: "Password used for the private key file", + Destination: &Options.KeyPasswd, + EnvVars: []string{"WAKUNODE2_KEY_PASSWORD"}, + }) + // Overwrite is a flag used to overwrite an existing key file + Overwrite = altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: "overwrite", + Value: false, + Usage: "Overwrite the nodekey file if it already exists", + Destination: &Options.Overwrite, + }) +) diff --git a/cmd/waku/keygen/options.go b/cmd/waku/keygen/options.go new file mode 100644 index 00000000..18e9f30d --- /dev/null +++ b/cmd/waku/keygen/options.go @@ -0,0 +1,9 @@ +package keygen + +// GenerateKeyOptions contains all the settings that can be used when generating +// a keyfile with the generate-key command +type GenerateKeyOptions struct { + KeyFile string + KeyPasswd string + Overwrite bool +} diff --git a/cmd/waku/main.go b/cmd/waku/main.go index 319838cb..805e620d 100644 --- a/cmd/waku/main.go +++ b/cmd/waku/main.go @@ -5,10 +5,11 @@ import ( cli "github.com/urfave/cli/v2" "github.com/urfave/cli/v2/altsrc" + "github.com/waku-org/go-waku/cmd/waku/keygen" "github.com/waku-org/go-waku/waku/v2/node" ) -var options Options +var options NodeOptions func main() { // Defaults @@ -31,8 +32,6 @@ func main() { NodeKey, KeyFile, KeyPassword, - GenerateKey, - Overwrite, StaticNode, KeepAlive, PersistPeers, @@ -111,6 +110,9 @@ func main() { Execute(options) return nil }, + Commands: []*cli.Command{ + &keygen.Command, + }, } err := app.Run(os.Args) diff --git a/cmd/waku/node.go b/cmd/waku/node.go index f837a27a..6035353c 100644 --- a/cmd/waku/node.go +++ b/cmd/waku/node.go @@ -63,7 +63,7 @@ func failOnErr(err error, msg string) { } } -func requiresDB(options Options) bool { +func requiresDB(options NodeOptions) bool { return options.Store.Enable || options.Rendezvous.Enable } @@ -82,14 +82,7 @@ func scalePerc(value float64) float64 { const dialTimeout = 7 * time.Second // Execute starts a go-waku node with settings determined by the Options parameter -func Execute(options Options) { - if options.GenerateKey { - if err := writePrivateKeyToFile(options.KeyFile, []byte(options.KeyPasswd), options.Overwrite); err != nil { - failOnErr(err, "nodekey error") - } - return - } - +func Execute(options NodeOptions) { hostAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", options.Address, options.Port)) failOnErr(err, "invalid host address") @@ -527,53 +520,7 @@ func loadPrivateKeyFromFile(path string, passwd string) (*ecdsa.PrivateKey, erro return crypto.ToECDSA(pKey) } -func checkForFileExistence(path string, overwrite bool) error { - _, err := os.Stat(path) - - if err == nil && !overwrite { - return fmt.Errorf("%s already exists. Use --overwrite to overwrite the file", path) - } - - if err := os.Remove(path); err != nil && !os.IsNotExist(err) { - return err - } - - return nil -} - -func generatePrivateKey() ([]byte, error) { - key, err := crypto.GenerateKey() - if err != nil { - return nil, err - } - - return key.D.Bytes(), nil -} - -func writePrivateKeyToFile(path string, passwd []byte, overwrite bool) error { - if err := checkForFileExistence(path, overwrite); err != nil { - return err - } - - key, err := generatePrivateKey() - if err != nil { - return err - } - - encryptedK, err := keystore.EncryptDataV3(key, passwd, keystore.StandardScryptN, keystore.StandardScryptP) - if err != nil { - return err - } - - output, err := json.Marshal(encryptedK) - if err != nil { - return err - } - - return os.WriteFile(path, output, 0600) -} - -func getPrivKey(options Options) (*ecdsa.PrivateKey, error) { +func getPrivKey(options NodeOptions) (*ecdsa.PrivateKey, error) { var prvKey *ecdsa.PrivateKey // get private key from nodeKey or keyFile if options.NodeKey != nil { @@ -596,7 +543,7 @@ func getPrivKey(options Options) (*ecdsa.PrivateKey, error) { return prvKey, nil } -func printListeningAddresses(ctx context.Context, nodeOpts []node.WakuNodeOption, options Options) { +func printListeningAddresses(ctx context.Context, nodeOpts []node.WakuNodeOption, options NodeOptions) { params := new(node.WakuNodeParameters) for _, opt := range nodeOpts { err := opt(params) diff --git a/cmd/waku/node_no_rln.go b/cmd/waku/node_no_rln.go index c29c3cab..92d64ff9 100644 --- a/cmd/waku/node_no_rln.go +++ b/cmd/waku/node_no_rln.go @@ -8,6 +8,6 @@ import ( "go.uber.org/zap" ) -func checkForRLN(logger *zap.Logger, options Options, nodeOpts *[]node.WakuNodeOption) { +func checkForRLN(logger *zap.Logger, options NodeOptions, nodeOpts *[]node.WakuNodeOption) { // Do nothing } diff --git a/cmd/waku/node_rln.go b/cmd/waku/node_rln.go index 4696ee79..3b588e18 100644 --- a/cmd/waku/node_rln.go +++ b/cmd/waku/node_rln.go @@ -12,7 +12,7 @@ import ( "go.uber.org/zap" ) -func checkForRLN(logger *zap.Logger, options Options, nodeOpts *[]node.WakuNodeOption) { +func checkForRLN(logger *zap.Logger, options NodeOptions, nodeOpts *[]node.WakuNodeOption) { if options.RLNRelay.Enable { if !options.Relay.Enable { failOnErr(errors.New("relay not available"), "Could not enable RLN Relay") diff --git a/cmd/waku/options.go b/cmd/waku/options.go index 8b834667..c5a23e61 100644 --- a/cmd/waku/options.go +++ b/cmd/waku/options.go @@ -142,17 +142,15 @@ type RendezvousOptions struct { Nodes []multiaddr.Multiaddr } -// Options contains all the available features and settings that can be +// NodeOptions contains all the available features and settings that can be // configured via flags when executing go-waku as a service. -type Options struct { +type NodeOptions struct { Port int Address string DNS4DomainName string NodeKey *ecdsa.PrivateKey KeyFile string KeyPasswd string - GenerateKey bool - Overwrite bool StaticNodes []multiaddr.Multiaddr KeepAlive time.Duration AdvertiseAddresses []multiaddr.Multiaddr