diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index 608b8d3f..1031c252 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -1767,6 +1767,16 @@ var unlockCommand = cli.Command{ "maximum number of consecutive, unused " + "addresses ever generated by the wallet.", }, + cli.BoolFlag{ + Name: "stdin", + Usage: "read password from standard input instead of " + + "prompting for it. THIS IS CONSIDERED TO " + + "BE DANGEROUS if the password is located in " + + "a file that can be read by another user. " + + "This flag should only be used in " + + "combination with some sort of password " + + "manager or secrets vault.", + }, }, Action: actionDecorator(unlock), } @@ -1776,12 +1786,33 @@ func unlock(ctx *cli.Context) error { client, cleanUp := getWalletUnlockerClient(ctx) defer cleanUp() - fmt.Printf("Input wallet password: ") - pw, err := terminal.ReadPassword(int(syscall.Stdin)) + var ( + pw []byte + err error + ) + switch { + // Read the password from standard in as if it were a file. This should + // only be used if the password is piped into lncli from some sort of + // password manager. If the user types the password instead, it will be + // echoed in the console. + case ctx.IsSet("stdin"): + reader := bufio.NewReader(os.Stdin) + pw, err = reader.ReadBytes('\n') + + // Remove carriage return and newline characters. + pw = bytes.Trim(pw, "\r\n") + + // Read the password from a terminal by default. This requires the + // terminal to be a real tty and will fail if a string is piped into + // lncli. + default: + fmt.Printf("Input wallet password: ") + pw, err = terminal.ReadPassword(syscall.Stdin) + fmt.Println() + } if err != nil { return err } - fmt.Println() args := ctx.Args()