diff --git a/cert/selfsigned.go b/cert/selfsigned.go index e8bd5710..e428e5eb 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 { diff --git a/lnd.go b/lnd.go index b0114a16..62e8c6c2 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 { @@ -81,12 +121,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 +284,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 +293,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 +307,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 +332,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 +549,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 +564,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 +842,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/mobile/bindings.go b/mobile/bindings.go index 7619837a..0945a17b 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{}) }() } diff --git a/mobile/gen_bindings.sh b/mobile/gen_bindings.sh index 1da7759a..ea0be7f1 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 diff --git a/mobile/sample_lnd.conf b/mobile/sample_lnd.conf index a1b93647..c2c87fad 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 diff --git a/rpcserver.go b/rpcserver.go index 7ae83dd0..fe8c8746 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 }