From 27337dc973f26f344cbc07f45a558bea0e3e9a19 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 29 Nov 2019 12:17:47 +0100 Subject: [PATCH 1/6] lnd+rpcserver: use same serverOpts for custom listeners Earlier we used emtpy grpc server options if custom listeners were set. This was done to disable TLS. Now, we reuse the same server options as for the regular listeners, in a move towards enabling TLS also here. --- lnd.go | 57 +++++++++++++++++++++------------------------------- rpcserver.go | 11 +++++----- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/lnd.go b/lnd.go index b0114a166..4fbe291d5 100644 --- a/lnd.go +++ b/lnd.go @@ -81,12 +81,11 @@ type ListenerCfg struct { } // rpcListeners is a function type used for closures that fetches a set of RPC -// listeners for the current configuration, and the GRPC server options to use -// with these listeners. If no custom listeners are present, this should return -// normal listeners from the RPC endpoints defined in the config, and server -// options specifying TLS. -type rpcListeners func() ([]*ListenerWithSignal, func(), []grpc.ServerOption, - error) +// listeners for the current configuration. If no custom listeners are present, +// this should return normal listeners from the RPC endpoints defined in the +// config. The second return value us a closure that will close the fetched +// listeners. +type rpcListeners func() ([]*ListenerWithSignal, func(), error) // Main is the true entry point for lnd. This function is required since defers // created in the top-level scope of a main method aren't executed if os.Exit() @@ -245,9 +244,7 @@ func Main(lisCfg ListenerCfg) error { // getListeners is a closure that creates listeners from the // RPCListeners defined in the config. It also returns a cleanup // closure and the server options to use for the GRPC server. - getListeners := func() ([]*ListenerWithSignal, func(), - []grpc.ServerOption, error) { - + getListeners := func() ([]*ListenerWithSignal, func(), error) { var grpcListeners []*ListenerWithSignal for _, grpcEndpoint := range cfg.RPCListeners { // Start a gRPC server listening for HTTP/2 @@ -256,7 +253,7 @@ func Main(lisCfg ListenerCfg) error { if err != nil { ltndLog.Errorf("unable to listen on %s", grpcEndpoint) - return nil, nil, nil, err + return nil, nil, err } grpcListeners = append( grpcListeners, &ListenerWithSignal{ @@ -270,23 +267,20 @@ func Main(lisCfg ListenerCfg) error { lis.Close() } } - return grpcListeners, cleanup, serverOpts, nil + return grpcListeners, cleanup, nil } // walletUnlockerListeners is a closure we'll hand to the wallet // unlocker, that will be called when it needs listeners for its GPRC // server. walletUnlockerListeners := func() ([]*ListenerWithSignal, func(), - []grpc.ServerOption, error) { + error) { // If we have chosen to start with a dedicated listener for the - // wallet unlocker, we return it directly, and empty server - // options to deactivate TLS. - // TODO(halseth): any point in adding TLS support for custom - // listeners? + // wallet unlocker, we return it directly. if lisCfg.WalletUnlocker != nil { return []*ListenerWithSignal{lisCfg.WalletUnlocker}, - func() {}, []grpc.ServerOption{}, nil + func() {}, nil } // Otherwise we'll return the regular listeners. @@ -298,8 +292,8 @@ func Main(lisCfg ListenerCfg) error { // for wallet encryption. if !cfg.NoSeedBackup { params, err := waitForWalletPassword( - cfg.RESTListeners, restDialOpts, restProxyDest, tlsCfg, - walletUnlockerListeners, + cfg.RESTListeners, serverOpts, restDialOpts, + restProxyDest, tlsCfg, walletUnlockerListeners, ) if err != nil { err := fmt.Errorf("Unable to set up wallet password "+ @@ -515,17 +509,12 @@ func Main(lisCfg ListenerCfg) error { // rpcListeners is a closure we'll hand to the rpc server, that will be // called when it needs listeners for its GPRC server. - rpcListeners := func() ([]*ListenerWithSignal, func(), - []grpc.ServerOption, error) { - + rpcListeners := func() ([]*ListenerWithSignal, func(), error) { // If we have chosen to start with a dedicated listener for the - // rpc server, we return it directly, and empty server options - // to deactivate TLS. - // TODO(halseth): any point in adding TLS support for custom - // listeners? + // rpc server, we return it directly. if lisCfg.RPCListener != nil { return []*ListenerWithSignal{lisCfg.RPCListener}, - func() {}, []grpc.ServerOption{}, nil + func() {}, nil } // Otherwise we'll return the regular listeners. @@ -535,9 +524,9 @@ func Main(lisCfg ListenerCfg) error { // Initialize, and register our implementation of the gRPC interface // exported by the rpcServer. rpcServer, err := newRPCServer( - server, macaroonService, cfg.SubRPCServers, restDialOpts, - restProxyDest, atplManager, server.invoices, tower, tlsCfg, - rpcListeners, chainedAcceptor, + server, macaroonService, cfg.SubRPCServers, serverOpts, + restDialOpts, restProxyDest, atplManager, server.invoices, + tower, tlsCfg, rpcListeners, chainedAcceptor, ) if err != nil { err := fmt.Errorf("Unable to create RPC server: %v", err) @@ -813,13 +802,13 @@ type WalletUnlockParams struct { // WalletUnlocker server, and block until a password is provided by // the user to this RPC server. func waitForWalletPassword(restEndpoints []net.Addr, - restDialOpts []grpc.DialOption, restProxyDest string, - tlsConf *tls.Config, getListeners rpcListeners) ( - *WalletUnlockParams, error) { + serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption, + restProxyDest string, tlsConf *tls.Config, + getListeners rpcListeners) (*WalletUnlockParams, error) { // Start a gRPC server listening for HTTP/2 connections, solely used // for getting the encryption password from the client. - listeners, cleanup, serverOpts, err := getListeners() + listeners, cleanup, err := getListeners() if err != nil { return nil, err } diff --git a/rpcserver.go b/rpcserver.go index 7ae83dd01..fe8c87464 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -508,10 +508,11 @@ var _ lnrpc.LightningServer = (*rpcServer)(nil) // base level options passed to the grPC server. This typically includes things // like requiring TLS, etc. func newRPCServer(s *server, macService *macaroons.Service, - subServerCgs *subRPCServerConfigs, restDialOpts []grpc.DialOption, - restProxyDest string, atpl *autopilot.Manager, - invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone, - tlsCfg *tls.Config, getListeners rpcListeners, + subServerCgs *subRPCServerConfigs, serverOpts []grpc.ServerOption, + restDialOpts []grpc.DialOption, restProxyDest string, + atpl *autopilot.Manager, invoiceRegistry *invoices.InvoiceRegistry, + tower *watchtower.Standalone, tlsCfg *tls.Config, + getListeners rpcListeners, chanPredicate *chanacceptor.ChainedAcceptor) (*rpcServer, error) { // Set up router rpc backend. @@ -643,7 +644,7 @@ func newRPCServer(s *server, macService *macaroons.Service, ) // Get the listeners and server options to use for this rpc server. - listeners, cleanup, serverOpts, err := getListeners() + listeners, cleanup, err := getListeners() if err != nil { return nil, err } From ca22001bab64063a0b758cfc05e116af6e7e8ca9 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 29 Nov 2019 12:20:13 +0100 Subject: [PATCH 2/6] cert: add bufconn to TLS hostnames --- cert/selfsigned.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cert/selfsigned.go b/cert/selfsigned.go index e8bd57101..e428e5eb5 100644 --- a/cert/selfsigned.go +++ b/cert/selfsigned.go @@ -109,6 +109,10 @@ func GenCertPair(org, certFile, keyFile string, tlsExtraIPs, // verification will fail in the client. dnsNames = append(dnsNames, "unix", "unixpacket") + // Also add hostnames for 'bufconn' which is the hostname used for the + // in-memory connections used on mobile. + dnsNames = append(dnsNames, "bufconn") + // Generate a private key for the certificate. priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { From 7077d1958917c09d9967621410b4087d92e23f6c Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 2 Dec 2019 13:23:20 +0100 Subject: [PATCH 3/6] lnd: add Authenticate method Adds a global Authenticate method that can be used to get the atuhentication options needed to call the grpc server. Currently meant only for used with the mobile bindings, so we use the admin macaroon. --- lnd.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lnd.go b/lnd.go index 4fbe291d5..62e8c6c24 100644 --- a/lnd.go +++ b/lnd.go @@ -22,6 +22,7 @@ import ( _ "net/http/pprof" "gopkg.in/macaroon-bakery.v2/bakery" + "gopkg.in/macaroon.v2" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -59,6 +60,45 @@ var ( networkDir string ) +// AdminAuthOptions returns a list of DialOptions that can be used to +// authenticate with the RPC server with admin capabilities. +// +// NOTE: This should only be called after the RPCListener has signaled it is +// ready. +func AdminAuthOptions() ([]grpc.DialOption, error) { + creds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "") + if err != nil { + return nil, fmt.Errorf("unable to read TLS cert: %v", err) + } + + // Create a dial options array. + opts := []grpc.DialOption{ + grpc.WithTransportCredentials(creds), + } + + // Get the admin macaroon if macaroons are active. + if !cfg.NoMacaroons { + // Load the adming macaroon file. + macBytes, err := ioutil.ReadFile(cfg.AdminMacPath) + if err != nil { + return nil, fmt.Errorf("unable to read macaroon "+ + "path (check the network setting!): %v", err) + } + + mac := &macaroon.Macaroon{} + if err = mac.UnmarshalBinary(macBytes); err != nil { + return nil, fmt.Errorf("unable to decode macaroon: %v", + err) + } + + // Now we append the macaroon credentials to the dial options. + cred := macaroons.NewMacaroonCredential(mac) + opts = append(opts, grpc.WithPerRPCCredentials(cred)) + } + + return opts, nil +} + // ListnerWithSignal is a net.Listner that has an additional Ready channel that // will be closed when a server starts listening. type ListenerWithSignal struct { From 200161e04c1faa5702cf859b94aa5b3758d926a8 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 2 Dec 2019 10:05:41 +0100 Subject: [PATCH 4/6] mobile: update to falafel 0.6 --- mobile/gen_bindings.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/gen_bindings.sh b/mobile/gen_bindings.sh index 1da7759a1..ea0be7f15 100755 --- a/mobile/gen_bindings.sh +++ b/mobile/gen_bindings.sh @@ -3,7 +3,7 @@ mkdir -p build # Check falafel version. -falafelVersion="0.5" +falafelVersion="0.6" falafel=$(which falafel) if [ $falafel ] then From 5be6e1c92e6b127fb0295d75d4d50dc921892cfc Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 2 Dec 2019 13:26:06 +0100 Subject: [PATCH 5/6] mobile: authenticate with rpc server This makes the mobile bindings work with TLS and macaroons enabled, which is supported from falafel 0.6. --- mobile/bindings.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mobile/bindings.go b/mobile/bindings.go index 7619837a4..0945a17b4 100644 --- a/mobile/bindings.go +++ b/mobile/bindings.go @@ -79,6 +79,19 @@ func Start(extraArgs string, unlockerReady, rpcReady Callback) { go func() { <-rpcListening + + // Now that the RPC server is ready, we can get the needed + // authentication options, and add them to the global dial + // options. + auth, err := lnd.AdminAuthOptions() + if err != nil { + rpcReady.OnError(err) + return + } + + // Add the auth options to the listener's dial options. + addLightningLisDialOption(auth...) + rpcReady.OnResponse([]byte{}) }() } From d196a4431ac77de93929c574e0fa9c1c59c01fae Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 18 Dec 2019 10:30:51 +0100 Subject: [PATCH 6/6] mobile: remove no-macaroons from sample config Now that macaroons are supported, encourage usage. --- mobile/sample_lnd.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/mobile/sample_lnd.conf b/mobile/sample_lnd.conf index a1b93647b..c2c87fad4 100644 --- a/mobile/sample_lnd.conf +++ b/mobile/sample_lnd.conf @@ -1,6 +1,5 @@ [Application Options] debuglevel=info -no-macaroons=1 maxbackoff=2s nolisten=1 norest=1