lncli: add flags for stateless init to wallet unlocker commands
This commit is contained in:
parent
4c8d374007
commit
10673b9dfa
@ -4,16 +4,31 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lncfg"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/walletunlocker"
|
"github.com/lightningnetwork/lnd/walletunlocker"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
statelessInitFlag = cli.BoolFlag{
|
||||||
|
Name: "stateless_init",
|
||||||
|
Usage: "do not create any macaroon files in the file " +
|
||||||
|
"system of the daemon",
|
||||||
|
}
|
||||||
|
saveToFlag = cli.StringFlag{
|
||||||
|
Name: "save_to",
|
||||||
|
Usage: "save returned admin macaroon to this file",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
var createCommand = cli.Command{
|
var createCommand = cli.Command{
|
||||||
Name: "create",
|
Name: "create",
|
||||||
Category: "Startup",
|
Category: "Startup",
|
||||||
@ -37,6 +52,14 @@ var createCommand = cli.Command{
|
|||||||
to potentially recover all on-chain funds, and most off-chain funds as
|
to potentially recover all on-chain funds, and most off-chain funds as
|
||||||
well.
|
well.
|
||||||
|
|
||||||
|
If the --stateless_init flag is set, no macaroon files are created by
|
||||||
|
the daemon. Instead, the binary serialized admin macaroon is returned
|
||||||
|
in the answer. This answer MUST be stored somewhere, otherwise all
|
||||||
|
access to the RPC server will be lost and the wallet must be recreated
|
||||||
|
to re-gain access.
|
||||||
|
If the --save_to parameter is set, the macaroon is saved to this file,
|
||||||
|
otherwise it is printed to standard out.
|
||||||
|
|
||||||
Finally, it's also possible to use this command and a set of static
|
Finally, it's also possible to use this command and a set of static
|
||||||
channel backups to trigger a recover attempt for the provided Static
|
channel backups to trigger a recover attempt for the provided Static
|
||||||
Channel Backups. Only one of the three parameters will be accepted. See
|
Channel Backups. Only one of the three parameters will be accepted. See
|
||||||
@ -58,6 +81,8 @@ var createCommand = cli.Command{
|
|||||||
Name: "multi_file",
|
Name: "multi_file",
|
||||||
Usage: "the path to a multi-channel back up file",
|
Usage: "the path to a multi-channel back up file",
|
||||||
},
|
},
|
||||||
|
statelessInitFlag,
|
||||||
|
saveToFlag,
|
||||||
},
|
},
|
||||||
Action: actionDecorator(create),
|
Action: actionDecorator(create),
|
||||||
}
|
}
|
||||||
@ -171,7 +196,15 @@ func create(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should the daemon be initialized stateless? Then we expect an answer
|
||||||
|
// with the admin macaroon later. Because the --save_to is related to
|
||||||
|
// stateless init, it doesn't make sense to be set on its own.
|
||||||
|
statelessInit := ctx.Bool(statelessInitFlag.Name)
|
||||||
|
if !statelessInit && ctx.IsSet(saveToFlag.Name) {
|
||||||
|
return fmt.Errorf("cannot set save_to parameter without " +
|
||||||
|
"stateless_init")
|
||||||
}
|
}
|
||||||
|
|
||||||
walletPassword, err := capturePassword(
|
walletPassword, err := capturePassword(
|
||||||
@ -349,13 +382,19 @@ mnemonicCheck:
|
|||||||
AezeedPassphrase: aezeedPass,
|
AezeedPassphrase: aezeedPass,
|
||||||
RecoveryWindow: recoveryWindow,
|
RecoveryWindow: recoveryWindow,
|
||||||
ChannelBackups: chanBackups,
|
ChannelBackups: chanBackups,
|
||||||
|
StatelessInit: statelessInit,
|
||||||
}
|
}
|
||||||
if _, err := client.InitWallet(ctxb, req); err != nil {
|
response, err := client.InitWallet(ctxb, req)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("\nlnd successfully initialized!")
|
fmt.Println("\nlnd successfully initialized!")
|
||||||
|
|
||||||
|
if statelessInit {
|
||||||
|
return storeOrPrintAdminMac(ctx, response.AdminMacaroon)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,6 +449,12 @@ var unlockCommand = cli.Command{
|
|||||||
start up. This command MUST be run after booting up lnd before it's
|
start up. This command MUST be run after booting up lnd before it's
|
||||||
able to carry out its duties. An exception is if a user is running with
|
able to carry out its duties. An exception is if a user is running with
|
||||||
--noseedbackup, then a default passphrase will be used.
|
--noseedbackup, then a default passphrase will be used.
|
||||||
|
|
||||||
|
If the --stateless_init flag is set, no macaroon files are created by
|
||||||
|
the daemon. This should be set for every unlock if the daemon was
|
||||||
|
initially initialized stateless. Otherwise the daemon will create
|
||||||
|
unencrypted macaroon files which could leak information to the system
|
||||||
|
that the daemon runs on.
|
||||||
`,
|
`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.IntFlag{
|
cli.IntFlag{
|
||||||
@ -430,6 +475,7 @@ var unlockCommand = cli.Command{
|
|||||||
"combination with some sort of password " +
|
"combination with some sort of password " +
|
||||||
"manager or secrets vault.",
|
"manager or secrets vault.",
|
||||||
},
|
},
|
||||||
|
statelessInitFlag,
|
||||||
},
|
},
|
||||||
Action: actionDecorator(unlock),
|
Action: actionDecorator(unlock),
|
||||||
}
|
}
|
||||||
@ -485,6 +531,7 @@ func unlock(ctx *cli.Context) error {
|
|||||||
req := &lnrpc.UnlockWalletRequest{
|
req := &lnrpc.UnlockWalletRequest{
|
||||||
WalletPassword: pw,
|
WalletPassword: pw,
|
||||||
RecoveryWindow: recoveryWindow,
|
RecoveryWindow: recoveryWindow,
|
||||||
|
StatelessInit: ctx.Bool(statelessInitFlag.Name),
|
||||||
}
|
}
|
||||||
_, err = client.UnlockWallet(ctxb, req)
|
_, err = client.UnlockWallet(ctxb, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -511,7 +558,35 @@ var changePasswordCommand = cli.Command{
|
|||||||
--noseedbackup), one must restart their daemon without
|
--noseedbackup), one must restart their daemon without
|
||||||
--noseedbackup and use this command. The "current password" field
|
--noseedbackup and use this command. The "current password" field
|
||||||
should be left empty.
|
should be left empty.
|
||||||
|
|
||||||
|
If the daemon was originally initialized stateless, then the
|
||||||
|
--stateless_init flag needs to be set for the change password request
|
||||||
|
as well! Otherwise the daemon will generate unencrypted macaroon files
|
||||||
|
in its file system again and possibly leak sensitive information.
|
||||||
|
Changing the password will by default not change the macaroon root key
|
||||||
|
(just re-encrypt the macaroon database with the new password). So all
|
||||||
|
macaroons will still be valid.
|
||||||
|
If one wants to make sure that all previously created macaroons are
|
||||||
|
invalidated, a new macaroon root key can be generated by using the
|
||||||
|
--new_mac_root_key flag.
|
||||||
|
|
||||||
|
After a successful password change with the --stateless_init flag set,
|
||||||
|
the current or new admin macaroon is returned binary serialized in the
|
||||||
|
answer. This answer MUST then be stored somewhere, otherwise
|
||||||
|
all access to the RPC server will be lost and the wallet must be re-
|
||||||
|
created to re-gain access. If the --save_to parameter is set, the
|
||||||
|
macaroon is saved to this file, otherwise it is printed to standard out.
|
||||||
`,
|
`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
statelessInitFlag,
|
||||||
|
saveToFlag,
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "new_mac_root_key",
|
||||||
|
Usage: "rotate the macaroon root key resulting in " +
|
||||||
|
"all previously created macaroons to be " +
|
||||||
|
"invalidated",
|
||||||
|
},
|
||||||
|
},
|
||||||
Action: actionDecorator(changePassword),
|
Action: actionDecorator(changePassword),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,15 +614,53 @@ func changePassword(ctx *cli.Context) error {
|
|||||||
return fmt.Errorf("passwords don't match")
|
return fmt.Errorf("passwords don't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &lnrpc.ChangePasswordRequest{
|
// Should the daemon be initialized stateless? Then we expect an answer
|
||||||
CurrentPassword: currentPw,
|
// with the admin macaroon later. Because the --save_to is related to
|
||||||
NewPassword: newPw,
|
// stateless init, it doesn't make sense to be set on its own.
|
||||||
|
statelessInit := ctx.Bool(statelessInitFlag.Name)
|
||||||
|
if !statelessInit && ctx.IsSet(saveToFlag.Name) {
|
||||||
|
return fmt.Errorf("cannot set save_to parameter without " +
|
||||||
|
"stateless_init")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.ChangePassword(ctxb, req)
|
req := &lnrpc.ChangePasswordRequest{
|
||||||
|
CurrentPassword: currentPw,
|
||||||
|
NewPassword: newPw,
|
||||||
|
StatelessInit: statelessInit,
|
||||||
|
NewMacaroonRootKey: ctx.Bool("new_mac_root_key"),
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := client.ChangePassword(ctxb, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if statelessInit {
|
||||||
|
return storeOrPrintAdminMac(ctx, response.AdminMacaroon)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeOrPrintAdminMac either stores the admin macaroon to a file specified or
|
||||||
|
// prints it to standard out, depending on the user flags set.
|
||||||
|
func storeOrPrintAdminMac(ctx *cli.Context, adminMac []byte) error {
|
||||||
|
// The user specified the optional --save_to parameter. We'll save the
|
||||||
|
// macaroon to that file.
|
||||||
|
if ctx.IsSet("save_to") {
|
||||||
|
macSavePath := lncfg.CleanAndExpandPath(ctx.String("save_to"))
|
||||||
|
err := ioutil.WriteFile(macSavePath, adminMac, 0644)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.Remove(macSavePath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Admin macaroon saved to %s\n", macSavePath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we just print it. The user MUST store this macaroon
|
||||||
|
// somewhere so we either save it to a provided file path or just print
|
||||||
|
// it to standard output.
|
||||||
|
fmt.Printf("Admin macaroon: %s\n", hex.EncodeToString(adminMac))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user