21c29c33d7
This commit reworks the macaroon authentication framework to use the v2 macaroon format and bakery API. It also replaces the code in each RPC method which calls the macaroon verifier with interceptors which call the macaroon verifier instead. In addition, the operation permissions are reworked to fit the new format of "allow" commands (specifically, entity/operation permissions instead of method permissions).
218 lines
5.6 KiB
Go
218 lines
5.6 KiB
Go
// Copyright (c) 2013-2017 The btcsuite developers
|
|
// Copyright (c) 2015-2016 The Decred developers
|
|
// Copyright (C) 2015-2017 The Lightning Network Developers
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
macaroon "gopkg.in/macaroon.v2"
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
"github.com/lightningnetwork/lnd/macaroons"
|
|
"github.com/roasbeef/btcutil"
|
|
"github.com/urfave/cli"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
)
|
|
|
|
const (
|
|
defaultTLSCertFilename = "tls.cert"
|
|
defaultMacaroonFilename = "admin.macaroon"
|
|
)
|
|
|
|
var (
|
|
lndHomeDir = btcutil.AppDataDir("lnd", false)
|
|
defaultTLSCertPath = filepath.Join(lndHomeDir, defaultTLSCertFilename)
|
|
defaultMacaroonPath = filepath.Join(lndHomeDir, defaultMacaroonFilename)
|
|
)
|
|
|
|
func fatal(err error) {
|
|
fmt.Fprintf(os.Stderr, "[lncli] %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func getWalletUnlockerClient(ctx *cli.Context) (lnrpc.WalletUnlockerClient, func()) {
|
|
conn := getClientConn(ctx)
|
|
|
|
cleanUp := func() {
|
|
conn.Close()
|
|
}
|
|
|
|
return lnrpc.NewWalletUnlockerClient(conn), cleanUp
|
|
}
|
|
|
|
func getClient(ctx *cli.Context) (lnrpc.LightningClient, func()) {
|
|
conn := getClientConn(ctx)
|
|
|
|
cleanUp := func() {
|
|
conn.Close()
|
|
}
|
|
|
|
return lnrpc.NewLightningClient(conn), cleanUp
|
|
}
|
|
|
|
func getClientConn(ctx *cli.Context) *grpc.ClientConn {
|
|
// Load the specified TLS certificate and build transport credentials
|
|
// with it.
|
|
tlsCertPath := cleanAndExpandPath(ctx.GlobalString("tlscertpath"))
|
|
creds, err := credentials.NewClientTLSFromFile(tlsCertPath, "")
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
// Create a dial options array.
|
|
opts := []grpc.DialOption{
|
|
grpc.WithTransportCredentials(creds),
|
|
}
|
|
|
|
// Only process macaroon credentials if --no-macaroons isn't set.
|
|
if !ctx.GlobalBool("no-macaroons") {
|
|
// Load the specified macaroon file.
|
|
macPath := cleanAndExpandPath(ctx.GlobalString("macaroonpath"))
|
|
macBytes, err := ioutil.ReadFile(macPath)
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
mac := &macaroon.Macaroon{}
|
|
if err = mac.UnmarshalBinary(macBytes); err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
macConstraints := []macaroons.Constraint{
|
|
// We add a time-based constraint to prevent replay of the
|
|
// macaroon. It's good for 60 seconds by default to make up for
|
|
// any discrepancy between client and server clocks, but leaking
|
|
// the macaroon before it becomes invalid makes it possible for
|
|
// an attacker to reuse the macaroon. In addition, the validity
|
|
// time of the macaroon is extended by the time the server clock
|
|
// is behind the client clock, or shortened by the time the
|
|
// server clock is ahead of the client clock (or invalid
|
|
// altogether if, in the latter case, this time is more than 60
|
|
// seconds).
|
|
// TODO(aakselrod): add better anti-replay protection.
|
|
macaroons.TimeoutConstraint(ctx.GlobalInt64("macaroontimeout")),
|
|
|
|
// Lock macaroon down to a specific IP address.
|
|
macaroons.IPLockConstraint(ctx.GlobalString("macaroonip")),
|
|
|
|
// ... Add more constraints if needed.
|
|
}
|
|
|
|
// Apply constraints to the macaroon.
|
|
constrainedMac, err := macaroons.AddConstraints(mac, macConstraints...)
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
// Now we append the macaroon credentials to the dial options.
|
|
cred := macaroons.NewMacaroonCredential(constrainedMac)
|
|
opts = append(opts, grpc.WithPerRPCCredentials(cred))
|
|
}
|
|
|
|
conn, err := grpc.Dial(ctx.GlobalString("rpcserver"), opts...)
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
|
|
return conn
|
|
}
|
|
|
|
func main() {
|
|
app := cli.NewApp()
|
|
app.Name = "lncli"
|
|
app.Version = "0.3"
|
|
app.Usage = "control plane for your Lightning Network Daemon (lnd)"
|
|
app.Flags = []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "rpcserver",
|
|
Value: "localhost:10009",
|
|
Usage: "host:port of ln daemon",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "tlscertpath",
|
|
Value: defaultTLSCertPath,
|
|
Usage: "path to TLS certificate",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "no-macaroons",
|
|
Usage: "disable macaroon authentication",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "macaroonpath",
|
|
Value: defaultMacaroonPath,
|
|
Usage: "path to macaroon file",
|
|
},
|
|
cli.Int64Flag{
|
|
Name: "macaroontimeout",
|
|
Value: 60,
|
|
Usage: "anti-replay macaroon validity time in seconds",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "macaroonip",
|
|
Usage: "if set, lock macaroon to specific IP address",
|
|
},
|
|
}
|
|
app.Commands = []cli.Command{
|
|
createCommand,
|
|
unlockCommand,
|
|
newAddressCommand,
|
|
sendManyCommand,
|
|
sendCoinsCommand,
|
|
connectCommand,
|
|
disconnectCommand,
|
|
openChannelCommand,
|
|
closeChannelCommand,
|
|
listPeersCommand,
|
|
walletBalanceCommand,
|
|
channelBalanceCommand,
|
|
getInfoCommand,
|
|
pendingChannelsCommand,
|
|
sendPaymentCommand,
|
|
payInvoiceCommand,
|
|
addInvoiceCommand,
|
|
lookupInvoiceCommand,
|
|
listInvoicesCommand,
|
|
listChannelsCommand,
|
|
listPaymentsCommand,
|
|
describeGraphCommand,
|
|
getChanInfoCommand,
|
|
getNodeInfoCommand,
|
|
queryRoutesCommand,
|
|
getNetworkInfoCommand,
|
|
debugLevelCommand,
|
|
decodePayReqComamnd,
|
|
listChainTxnsCommand,
|
|
stopCommand,
|
|
signMessageCommand,
|
|
verifyMessageCommand,
|
|
feeReportCommand,
|
|
updateChannelPolicyCommand,
|
|
}
|
|
|
|
if err := app.Run(os.Args); err != nil {
|
|
fatal(err)
|
|
}
|
|
}
|
|
|
|
// cleanAndExpandPath expands environment variables and leading ~ in the
|
|
// passed path, cleans the result, and returns it.
|
|
// This function is taken from https://github.com/btcsuite/btcd
|
|
func cleanAndExpandPath(path string) string {
|
|
// Expand initial ~ to OS specific home directory.
|
|
if strings.HasPrefix(path, "~") {
|
|
homeDir := filepath.Dir(lndHomeDir)
|
|
path = strings.Replace(path, "~", homeDir, 1)
|
|
}
|
|
|
|
// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
|
|
// but the variables can still be expanded via POSIX-style $VARIABLE.
|
|
return filepath.Clean(os.ExpandEnv(path))
|
|
}
|