From 922b065de5ed8b120dabfa36eecb535c988bdce0 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 17 Aug 2017 19:50:57 -0600 Subject: [PATCH] main: integrate macaroons into config, startup, and RPC server --- config.go | 31 +++-- lnd.go | 64 +++++++++- networktest.go | 28 ++++- rpcserver.go | 324 ++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 414 insertions(+), 33 deletions(-) diff --git a/config.go b/config.go index e9f8aad8..7bce9cc5 100644 --- a/config.go +++ b/config.go @@ -24,6 +24,8 @@ const ( defaultDataDirname = "data" defaultTLSCertFilename = "tls.cert" defaultTLSKeyFilename = "tls.key" + defaultAdminMacFilename = "admin.macaroon" + defaultReadMacFilename = "readonly.macaroon" defaultLogLevel = "info" defaultLogDirname = "logs" defaultLogFilename = "lnd.log" @@ -36,12 +38,14 @@ const ( ) var ( - lndHomeDir = btcutil.AppDataDir("lnd", false) - defaultConfigFile = filepath.Join(lndHomeDir, defaultConfigFilename) - defaultDataDir = filepath.Join(lndHomeDir, defaultDataDirname) - defaultTLSCertPath = filepath.Join(lndHomeDir, defaultTLSCertFilename) - defaultTLSKeyPath = filepath.Join(lndHomeDir, defaultTLSKeyFilename) - defaultLogDir = filepath.Join(lndHomeDir, defaultLogDirname) + lndHomeDir = btcutil.AppDataDir("lnd", false) + defaultConfigFile = filepath.Join(lndHomeDir, defaultConfigFilename) + defaultDataDir = filepath.Join(lndHomeDir, defaultDataDirname) + defaultTLSCertPath = filepath.Join(lndHomeDir, defaultTLSCertFilename) + defaultTLSKeyPath = filepath.Join(lndHomeDir, defaultTLSKeyFilename) + defaultAdminMacPath = filepath.Join(lndHomeDir, defaultAdminMacFilename) + defaultReadMacPath = filepath.Join(lndHomeDir, defaultReadMacFilename) + defaultLogDir = filepath.Join(lndHomeDir, defaultLogDirname) btcdHomeDir = btcutil.AppDataDir("btcd", false) defaultBtcdRPCCertFile = filepath.Join(btcdHomeDir, "rpc.cert") @@ -88,11 +92,14 @@ type autoPilotConfig struct { type config struct { ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` - ConfigFile string `long:"C" long:"configfile" description:"Path to configuration file"` - DataDir string `short:"b" long:"datadir" description:"The directory to store lnd's data within"` - TLSCertPath string `long:"tlscertpath" description:"Path to TLS certificate for lnd's RPC and REST services"` - TLSKeyPath string `long:"tlskeypath" description:"Path to TLS private key for lnd's RPC and REST services"` - LogDir string `long:"logdir" description:"Directory to log output."` + ConfigFile string `long:"C" long:"configfile" description:"Path to configuration file"` + DataDir string `short:"b" long:"datadir" description:"The directory to store lnd's data within"` + TLSCertPath string `long:"tlscertpath" description:"Path to TLS certificate for lnd's RPC and REST services"` + TLSKeyPath string `long:"tlskeypath" description:"Path to TLS private key for lnd's RPC and REST services"` + NoMacaroons bool `long:"no-macaroons" description:"Disable macaroon authentication"` + AdminMacPath string `long:"adminmacaroonpath" description:"Path to write the admin macaroon for lnd's RPC and REST services if it doesn't exist"` + ReadMacPath string `long:"readonlymacaroonpath" description:"Path to write the read-only macaroon for lnd's RPC and REST services if it doesn't exist"` + LogDir string `long:"logdir" description:"Directory to log output."` Listeners []string `long:"listen" description:"Add an interface/port to listen for connections (default all interfaces port: 5656)"` ExternalIPs []string `long:"externalip" description:"Add an ip to the list of local addresses we claim to listen on to peers"` @@ -132,6 +139,8 @@ func loadConfig() (*config, error) { DebugLevel: defaultLogLevel, TLSCertPath: defaultTLSCertPath, TLSKeyPath: defaultTLSKeyPath, + AdminMacPath: defaultAdminMacPath, + ReadMacPath: defaultReadMacPath, LogDir: defaultLogDir, PeerPort: defaultPeerPort, RPCPort: defaultRPCPort, diff --git a/lnd.go b/lnd.go index 38ad62a4..93b27e34 100644 --- a/lnd.go +++ b/lnd.go @@ -12,6 +12,9 @@ import ( "strconv" "time" + "gopkg.in/macaroon-bakery.v1/bakery" + "gopkg.in/macaroon-bakery.v1/bakery/checkers" + "golang.org/x/net/context" "google.golang.org/grpc" @@ -24,6 +27,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/macaroons" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcutil" ) @@ -78,6 +82,30 @@ func lndMain() error { } defer chanDB.Close() + // Only process macaroons if --no-macaroons isn't set. + var macaroonService *bakery.Service + if !cfg.NoMacaroons { + // Create the macaroon authentication/authorization service. + macaroonService, err = macaroons.NewService(cfg.DataDir) + if err != nil { + srvrLog.Errorf("unable to create macaroon service: %v", + err) + return err + } + + // Create macaroon files for lncli to use if they don't exist. + if !fileExists(cfg.AdminMacPath) && + !fileExists(cfg.ReadMacPath) { + err = genMacaroons(macaroonService, cfg.AdminMacPath, + cfg.ReadMacPath) + if err != nil { + ltndLog.Errorf("unable to create macaroon "+ + "files: %v", err) + return err + } + } + } + // With the information parsed from the configuration, create valid // instances of the paertinent interfaces required to operate the // Lightning Network Daemon. @@ -197,7 +225,7 @@ func lndMain() error { // Initialize, and register our implementation of the gRPC interface // exported by the rpcServer. - rpcServer := newRPCServer(server) + rpcServer := newRPCServer(server, macaroonService) if err := rpcServer.Start(); err != nil { return err } @@ -375,3 +403,37 @@ func genCertPair(certFile, keyFile string) error { rpcsLog.Infof("Done generating TLS certificates") return nil } + +// genMacaroons generates a pair of macaroon files; one admin-level and one +// read-only. These can also be used to generate more granular macaroons. +func genMacaroons(svc *bakery.Service, admFile, roFile string) error { + // Generate the admin macaroon and write it to a file. + admMacaroon, err := svc.NewMacaroon("", nil, nil) + if err != nil { + return err + } + admBytes, err := admMacaroon.MarshalBinary() + if err != nil { + return err + } + if err = ioutil.WriteFile(admFile, admBytes, 0600); err != nil { + return err + } + + // Generate the read-only macaroon and write it to a file. + caveat := checkers.AllowCaveat(roPermissions...) + roMacaroon := admMacaroon.Clone() + if err = svc.AddCaveat(roMacaroon, caveat); err != nil { + return err + } + roBytes, err := roMacaroon.MarshalBinary() + if err != nil { + return err + } + if err = ioutil.WriteFile(roFile, roBytes, 0644); err != nil { + os.Remove(admFile) + return err + } + + return nil +} diff --git a/networktest.go b/networktest.go index 9f6d8f0d..870b8390 100644 --- a/networktest.go +++ b/networktest.go @@ -15,6 +15,8 @@ import ( "sync" "time" + macaroon "gopkg.in/macaroon.v1" + "golang.org/x/net/context" "google.golang.org/grpc" @@ -25,6 +27,7 @@ import ( "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/macaroons" "github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/rpctest" @@ -137,6 +140,8 @@ func newLightningNode(btcrpcConfig *btcrpcclient.ConnConfig, lndArgs []string) ( } cfg.TLSCertPath = filepath.Join(cfg.DataDir, "tls.cert") cfg.TLSKeyPath = filepath.Join(cfg.DataDir, "tls.key") + cfg.AdminMacPath = filepath.Join(cfg.DataDir, "admin.macaroon") + cfg.ReadMacPath = filepath.Join(cfg.DataDir, "readonly.macaroon") cfg.PeerPort, cfg.RPCPort = generateListeningPorts() @@ -177,6 +182,8 @@ func (l *lightningNode) genArgs() []string { args = append(args, fmt.Sprintf("--tlscertpath=%v", l.cfg.TLSCertPath)) args = append(args, fmt.Sprintf("--tlskeypath=%v", l.cfg.TLSKeyPath)) args = append(args, fmt.Sprintf("--configfile=%v", l.cfg.DataDir)) + args = append(args, fmt.Sprintf("--adminmacaroonpath=%v", l.cfg.AdminMacPath)) + args = append(args, fmt.Sprintf("--readonlymacaroonpath=%v", l.cfg.ReadMacPath)) if l.extraArgs != nil { args = append(args, l.extraArgs...) @@ -247,23 +254,34 @@ func (l *lightningNode) Start(lndError chan error) error { return err } - // Wait until TLS certificate is created before using it, up to 20 sec. + // Wait until TLS certificate and admin macaroon are created before + // using them, up to 20 sec. tlsTimeout := time.After(20 * time.Second) - for !fileExists(l.cfg.TLSCertPath) { + for !fileExists(l.cfg.TLSCertPath) || !fileExists(l.cfg.AdminMacPath) { time.Sleep(100 * time.Millisecond) select { case <-tlsTimeout: panic(fmt.Errorf("timeout waiting for TLS cert file " + - "to be created after 20 seconds")) + "and admin macaroon file to be created after " + + "20 seconds")) default: } } - creds, err := credentials.NewClientTLSFromFile(l.cfg.TLSCertPath, "") + tlsCreds, err := credentials.NewClientTLSFromFile(l.cfg.TLSCertPath, "") if err != nil { return err } + macBytes, err := ioutil.ReadFile(l.cfg.AdminMacPath) + if err != nil { + return err + } + mac := &macaroon.Macaroon{} + if err = mac.UnmarshalBinary(macBytes); err != nil { + return err + } opts := []grpc.DialOption{ - grpc.WithTransportCredentials(creds), + grpc.WithTransportCredentials(tlsCreds), + grpc.WithPerRPCCredentials(macaroons.NewMacaroonCredential(mac)), grpc.WithBlock(), grpc.WithTimeout(time.Second * 20), } diff --git a/rpcserver.go b/rpcserver.go index 7fa37bb7..8caa05be 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -13,6 +13,8 @@ import ( "strings" "time" + "gopkg.in/macaroon-bakery.v1/bakery" + "sync" "sync/atomic" @@ -23,6 +25,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/zpay32" "github.com/roasbeef/btcd/blockchain" @@ -39,6 +42,26 @@ import ( var ( defaultAccount uint32 = waddrmgr.DefaultAccountNum + + // roPermissions is a slice of method names that are considered "read-only" + // for authorization purposes, all lowercase. + roPermissions = []string{ + "verifymessage", + "getinfo", + "listpeers", + "walletbalance", + "channelbalance", + "listchannels", + "readinvoices", + "gettransactions", + "describegraph", + "getchaninfo", + "getnodeinfo", + "queryroutes", + "getnetworkinfo", + "listpayments", + "decodepayreq", + } ) // rpcServer is a gRPC, RPC front end to the lnd daemon. @@ -49,6 +72,10 @@ type rpcServer struct { server *server + // authSvc is the authentication/authorization service backed by + // macaroons. + authSvc *bakery.Service + wg sync.WaitGroup quit chan struct{} @@ -59,8 +86,12 @@ type rpcServer struct { var _ lnrpc.LightningServer = (*rpcServer)(nil) // newRPCServer creates and returns a new instance of the rpcServer. -func newRPCServer(s *server) *rpcServer { - return &rpcServer{server: s, quit: make(chan struct{}, 1)} +func newRPCServer(s *server, authSvc *bakery.Service) *rpcServer { + return &rpcServer{ + server: s, + authSvc: authSvc, + quit: make(chan struct{}, 1), + } } // Start launches any helper goroutines required for the rpcServer @@ -123,6 +154,13 @@ func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64) (*chainhash.Ha // SendMany, this RPC call only allows creating a single output at a time. func (r *rpcServer) SendCoins(ctx context.Context, in *lnrpc.SendCoinsRequest) (*lnrpc.SendCoinsResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "sendcoins", + r.authSvc); err != nil { + return nil, err + } + } rpcsLog.Infof("[sendcoins] addr=%v, amt=%v", in.Addr, btcutil.Amount(in.Amount)) @@ -141,6 +179,13 @@ func (r *rpcServer) SendCoins(ctx context.Context, // outputs in parallel. func (r *rpcServer) SendMany(ctx context.Context, in *lnrpc.SendManyRequest) (*lnrpc.SendManyResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "sendcoins", + r.authSvc); err != nil { + return nil, err + } + } txid, err := r.sendCoinsOnChain(in.AddrToAmount) if err != nil { @@ -155,6 +200,13 @@ func (r *rpcServer) SendMany(ctx context.Context, // NewAddress creates a new address under control of the local wallet. func (r *rpcServer) NewAddress(ctx context.Context, in *lnrpc.NewAddressRequest) (*lnrpc.NewAddressResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "newaddress", + r.authSvc); err != nil { + return nil, err + } + } // Translate the gRPC proto address type to the wallet controller's // available address types. @@ -181,6 +233,13 @@ func (r *rpcServer) NewAddress(ctx context.Context, // the local wallet. func (r *rpcServer) NewWitnessAddress(ctx context.Context, in *lnrpc.NewWitnessAddressRequest) (*lnrpc.NewAddressResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "newaddress", + r.authSvc); err != nil { + return nil, err + } + } addr, err := r.server.cc.wallet.NewAddress(lnwallet.WitnessPubKey, false) if err != nil { @@ -197,6 +256,13 @@ func (r *rpcServer) NewWitnessAddress(ctx context.Context, // verification. func (r *rpcServer) SignMessage(ctx context.Context, in *lnrpc.SignMessageRequest) (*lnrpc.SignMessageResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "signmessage", + r.authSvc); err != nil { + return nil, err + } + } if in.Msg == nil { return nil, fmt.Errorf("need a message to sign") @@ -217,6 +283,13 @@ func (r *rpcServer) SignMessage(ctx context.Context, // VerifyMessage also returns the recovered pubkey from the signature. func (r *rpcServer) VerifyMessage(ctx context.Context, in *lnrpc.VerifyMessageRequest) (*lnrpc.VerifyMessageResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "verifymessage", + r.authSvc); err != nil { + return nil, err + } + } if in.Msg == nil { return nil, fmt.Errorf("need a message to verify") @@ -256,6 +329,13 @@ func (r *rpcServer) VerifyMessage(ctx context.Context, // ConnectPeer attempts to establish a connection to a remote peer. func (r *rpcServer) ConnectPeer(ctx context.Context, in *lnrpc.ConnectPeerRequest) (*lnrpc.ConnectPeerResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "connectpeer", + r.authSvc); err != nil { + return nil, err + } + } // The server hasn't yet started, so it won't be able to service any of // our requests, so we'll bail early here. @@ -317,6 +397,13 @@ func (r *rpcServer) ConnectPeer(ctx context.Context, // with the target peer, this action will be disallowed. func (r *rpcServer) DisconnectPeer(ctx context.Context, in *lnrpc.DisconnectPeerRequest) (*lnrpc.DisconnectPeerResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "disconnectpeer", + r.authSvc); err != nil { + return nil, err + } + } rpcsLog.Debugf("[disconnectpeer] from peer(%s)", in.PubKey) @@ -366,6 +453,13 @@ func (r *rpcServer) DisconnectPeer(ctx context.Context, // request to a remote peer. func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, updateStream lnrpc.Lightning_OpenChannelServer) error { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(updateStream.Context(), + "openchannel", r.authSvc); err != nil { + return err + } + } rpcsLog.Tracef("[openchannel] request to peerid(%v) "+ "allocation(us=%v, them=%v)", in.TargetPeerId, @@ -486,6 +580,13 @@ out: // strings. func (r *rpcServer) OpenChannelSync(ctx context.Context, in *lnrpc.OpenChannelRequest) (*lnrpc.ChannelPoint, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "openchannel", + r.authSvc); err != nil { + return nil, err + } + } rpcsLog.Tracef("[openchannel] request to peerid(%v) "+ "allocation(us=%v, them=%v)", in.TargetPeerId, @@ -569,6 +670,13 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context, // a force close after a timeout period in the case of an inactive peer. func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, updateStream lnrpc.Lightning_CloseChannelServer) error { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(updateStream.Context(), + "closechannel", r.authSvc); err != nil { + return err + } + } force := in.Force index := in.ChannelPoint.OutputIndex @@ -815,6 +923,13 @@ func (r *rpcServer) forceCloseChan(channel *lnwallet.LightningChannel) (*chainha // concerning the number of open+pending channels. func (r *rpcServer) GetInfo(ctx context.Context, in *lnrpc.GetInfoRequest) (*lnrpc.GetInfoResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "getinfo", + r.authSvc); err != nil { + return nil, err + } + } var activeChannels uint32 serverPeers := r.server.Peers() @@ -864,6 +979,13 @@ func (r *rpcServer) GetInfo(ctx context.Context, // ListPeers returns a verbose listing of all currently active peers. func (r *rpcServer) ListPeers(ctx context.Context, in *lnrpc.ListPeersRequest) (*lnrpc.ListPeersResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "listpeers", + r.authSvc); err != nil { + return nil, err + } + } rpcsLog.Tracef("[listpeers] request") @@ -919,6 +1041,13 @@ func (r *rpcServer) ListPeers(ctx context.Context, // TODO(roasbeef): add async hooks into wallet balance changes func (r *rpcServer) WalletBalance(ctx context.Context, in *lnrpc.WalletBalanceRequest) (*lnrpc.WalletBalanceResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "walletbalance", + r.authSvc); err != nil { + return nil, err + } + } balance, err := r.server.cc.wallet.ConfirmedBalance(1, in.WitnessOnly) if err != nil { @@ -936,6 +1065,13 @@ func (r *rpcServer) WalletBalance(ctx context.Context, // channels in satoshis. func (r *rpcServer) ChannelBalance(ctx context.Context, in *lnrpc.ChannelBalanceRequest) (*lnrpc.ChannelBalanceResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "channelbalance", + r.authSvc); err != nil { + return nil, err + } + } channels, err := r.server.chanDB.FetchAllChannels() if err != nil { @@ -958,6 +1094,13 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, // process of closure, either initiated cooperatively or non-cooperatively. func (r *rpcServer) PendingChannels(ctx context.Context, in *lnrpc.PendingChannelRequest) (*lnrpc.PendingChannelResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "listchannels", + r.authSvc); err != nil { + return nil, err + } + } rpcsLog.Debugf("[pendingchannels]") @@ -1091,6 +1234,13 @@ func (r *rpcServer) PendingChannels(ctx context.Context, // is a participant in. func (r *rpcServer) ListChannels(ctx context.Context, in *lnrpc.ListChannelsRequest) (*lnrpc.ListChannelsResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "listchannels", + r.authSvc); err != nil { + return nil, err + } + } resp := &lnrpc.ListChannelsResponse{} @@ -1196,6 +1346,14 @@ func (r *rpcServer) savePayment(route *routing.Route, amount btcutil.Amount, // bi-directional stream allowing clients to rapidly send payments through the // Lightning Network with a single persistent connection. func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) error { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(paymentStream.Context(), + "sendpayment", r.authSvc); err != nil { + return err + } + } + errChan := make(chan error, 1) payChan := make(chan *lnrpc.SendRequest) @@ -1355,6 +1513,13 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) // hash (if any) to be encoded as hex strings. func (r *rpcServer) SendPaymentSync(ctx context.Context, nextPayment *lnrpc.SendRequest) (*lnrpc.SendResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "sendpayment", + r.authSvc); err != nil { + return nil, err + } + } // We don't allow payments to be sent while the daemon itself is still // syncing as we may be trying to sent a payment over a "stale" @@ -1439,6 +1604,13 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context, // unique payment preimage. func (r *rpcServer) AddInvoice(ctx context.Context, invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "addinvoice", + r.authSvc); err != nil { + return nil, err + } + } var paymentPreimage [32]byte @@ -1521,6 +1693,13 @@ func (r *rpcServer) AddInvoice(ctx context.Context, // returned. func (r *rpcServer) LookupInvoice(ctx context.Context, req *lnrpc.PaymentHash) (*lnrpc.Invoice, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "readinvoices", + r.authSvc); err != nil { + return nil, err + } + } var ( payHash [32]byte @@ -1579,6 +1758,13 @@ func (r *rpcServer) LookupInvoice(ctx context.Context, // database. Any active debug invoices are ignored. func (r *rpcServer) ListInvoices(ctx context.Context, req *lnrpc.ListInvoiceRequest) (*lnrpc.ListInvoiceResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "readinvoices", + r.authSvc); err != nil { + return nil, err + } + } dbInvoices, err := r.server.chanDB.FetchAllInvoices(req.PendingOnly) if err != nil { @@ -1618,6 +1804,13 @@ func (r *rpcServer) ListInvoices(ctx context.Context, // notifying the client of newly added/settled invoices. func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription, updateStream lnrpc.Lightning_SubscribeInvoicesServer) error { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(updateStream.Context(), + "readinvoices", r.authSvc); err != nil { + return err + } + } invoiceClient := r.server.invoices.SubscribeNotifications() defer invoiceClient.Cancel() @@ -1650,6 +1843,13 @@ func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription, // over. func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest, updateStream lnrpc.Lightning_SubscribeTransactionsServer) error { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(updateStream.Context(), + "gettransactions", r.authSvc); err != nil { + return err + } + } txClient, err := r.server.cc.wallet.SubscribeTransactions() if err != nil { @@ -1689,8 +1889,15 @@ func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest, // GetTransactions returns a list of describing all the known transactions // relevant to the wallet. -func (r *rpcServer) GetTransactions(context.Context, - *lnrpc.GetTransactionsRequest) (*lnrpc.TransactionDetails, error) { +func (r *rpcServer) GetTransactions(ctx context.Context, + _ *lnrpc.GetTransactionsRequest) (*lnrpc.TransactionDetails, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "gettransactions", + r.authSvc); err != nil { + return nil, err + } + } // TODO(btcsuite): add pagination support transactions, err := r.server.cc.wallet.ListTransactionDetails() @@ -1722,8 +1929,15 @@ func (r *rpcServer) GetTransactions(context.Context, // As this is a directed graph, the edges also contain the node directional // specific routing policy which includes: the time lock delta, fee // information, etc. -func (r *rpcServer) DescribeGraph(context.Context, - *lnrpc.ChannelGraphRequest) (*lnrpc.ChannelGraph, error) { +func (r *rpcServer) DescribeGraph(ctx context.Context, + _ *lnrpc.ChannelGraphRequest) (*lnrpc.ChannelGraph, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "describegraph", + r.authSvc); err != nil { + return nil, err + } + } resp := &lnrpc.ChannelGraph{} @@ -1823,8 +2037,15 @@ func marshalDbEdge(edgeInfo *channeldb.ChannelEdgeInfo, // given channel identified by its channel ID: an 8-byte integer which uniquely // identifies the location of transaction's funding output within the block // chain. -func (r *rpcServer) GetChanInfo(_ context.Context, in *lnrpc.ChanInfoRequest) (*lnrpc.ChannelEdge, error) { +func (r *rpcServer) GetChanInfo(ctx context.Context, in *lnrpc.ChanInfoRequest) (*lnrpc.ChannelEdge, error) { graph := r.server.chanDB.ChannelGraph() + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "getchaninfo", + r.authSvc); err != nil { + return nil, err + } + } edgeInfo, edge1, edge2, err := graph.FetchChannelEdgesByID(in.ChanId) if err != nil { @@ -1841,7 +2062,14 @@ func (r *rpcServer) GetChanInfo(_ context.Context, in *lnrpc.ChanInfoRequest) (* // GetNodeInfo returns the latest advertised and aggregate authenticated // channel information for the specified node identified by its public key. -func (r *rpcServer) GetNodeInfo(_ context.Context, in *lnrpc.NodeInfoRequest) (*lnrpc.NodeInfo, error) { +func (r *rpcServer) GetNodeInfo(ctx context.Context, in *lnrpc.NodeInfoRequest) (*lnrpc.NodeInfo, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "getnodeinfo", + r.authSvc); err != nil { + return nil, err + } + } graph := r.server.chanDB.ChannelGraph() @@ -1910,8 +2138,15 @@ func (r *rpcServer) GetNodeInfo(_ context.Context, in *lnrpc.NodeInfoRequest) (* // // TODO(roasbeef): should return a slice of routes in reality // * create separate PR to send based on well formatted route -func (r *rpcServer) QueryRoutes(_ context.Context, +func (r *rpcServer) QueryRoutes(ctx context.Context, in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "queryroutes", + r.authSvc); err != nil { + return nil, err + } + } // First parse the hex-encdoed public key into a full public key objet // we can properly manipulate. @@ -1967,7 +2202,14 @@ func marshalRoute(route *routing.Route) *lnrpc.Route { // GetNetworkInfo returns some basic stats about the known channel graph from // the PoV of the node. -func (r *rpcServer) GetNetworkInfo(context.Context, *lnrpc.NetworkInfoRequest) (*lnrpc.NetworkInfo, error) { +func (r *rpcServer) GetNetworkInfo(ctx context.Context, _ *lnrpc.NetworkInfoRequest) (*lnrpc.NetworkInfo, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "getnetworkinfo", + r.authSvc); err != nil { + return nil, err + } + } graph := r.server.chanDB.ChannelGraph() @@ -2081,7 +2323,14 @@ func (r *rpcServer) GetNetworkInfo(context.Context, *lnrpc.NetworkInfoRequest) ( // StopDaemon will send a shutdown request to the interrupt handler, triggering // a graceful shutdown of the daemon. -func (r *rpcServer) StopDaemon(context.Context, *lnrpc.StopRequest) (*lnrpc.StopResponse, error) { +func (r *rpcServer) StopDaemon(ctx context.Context, _ *lnrpc.StopRequest) (*lnrpc.StopResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "stopdaemon", + r.authSvc); err != nil { + return nil, err + } + } shutdownRequestChannel <- struct{}{} return &lnrpc.StopResponse{}, nil @@ -2095,6 +2344,13 @@ func (r *rpcServer) StopDaemon(context.Context, *lnrpc.StopRequest) (*lnrpc.Stop // and finally when prior channels are closed on-chain. func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription, updateStream lnrpc.Lightning_SubscribeChannelGraphServer) error { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(updateStream.Context(), + "describegraph", r.authSvc); err != nil { + return err + } + } // First, we start by subscribing to a new intent to receive // notifications from the channel router. @@ -2206,8 +2462,15 @@ func marshallTopologyChange(topChange *routing.TopologyChange) *lnrpc.GraphTopol } // ListPayments returns a list of all outgoing payments. -func (r *rpcServer) ListPayments(context.Context, - *lnrpc.ListPaymentsRequest) (*lnrpc.ListPaymentsResponse, error) { +func (r *rpcServer) ListPayments(ctx context.Context, + _ *lnrpc.ListPaymentsRequest) (*lnrpc.ListPaymentsResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "listpayments", + r.authSvc); err != nil { + return nil, err + } + } rpcsLog.Debugf("[ListPayments]") @@ -2237,8 +2500,15 @@ func (r *rpcServer) ListPayments(context.Context, } // DeleteAllPayments deletes all outgoing payments from DB. -func (r *rpcServer) DeleteAllPayments(context.Context, - *lnrpc.DeleteAllPaymentsRequest) (*lnrpc.DeleteAllPaymentsResponse, error) { +func (r *rpcServer) DeleteAllPayments(ctx context.Context, + _ *lnrpc.DeleteAllPaymentsRequest) (*lnrpc.DeleteAllPaymentsResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "deleteallpayments", + r.authSvc); err != nil { + return nil, err + } + } rpcsLog.Debugf("[DeleteAllPayments]") @@ -2250,7 +2520,15 @@ func (r *rpcServer) DeleteAllPayments(context.Context, } // SetAlias... -func (r *rpcServer) SetAlias(context.Context, *lnrpc.SetAliasRequest) (*lnrpc.SetAliasResponse, error) { +func (r *rpcServer) SetAlias(ctx context.Context, _ *lnrpc.SetAliasRequest) (*lnrpc.SetAliasResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "setalias", + r.authSvc); err != nil { + return nil, err + } + } + return nil, nil } @@ -2260,6 +2538,13 @@ func (r *rpcServer) SetAlias(context.Context, *lnrpc.SetAliasRequest) (*lnrpc.Se // sub-system. func (r *rpcServer) DebugLevel(ctx context.Context, req *lnrpc.DebugLevelRequest) (*lnrpc.DebugLevelResponse, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "debuglevel", + r.authSvc); err != nil { + return nil, err + } + } // If show is set, then we simply print out the list of available // sub-systems. @@ -2285,6 +2570,13 @@ func (r *rpcServer) DebugLevel(ctx context.Context, // payment request. func (r *rpcServer) DecodePayReq(ctx context.Context, req *lnrpc.PayReqString) (*lnrpc.PayReq, error) { + // Check macaroon to see if this is allowed. + if r.authSvc != nil { + if err := macaroons.ValidateMacaroon(ctx, "decodepayreq", + r.authSvc); err != nil { + return nil, err + } + } // Fist we'll attempt to decode the payment request string, if the // request is invalid or the checksum doesn't match, then we'll exit