Merge pull request #3783 from halseth/mobile-macaroons

[mobile] mobile macaroons support
This commit is contained in:
Johan T. Halseth 2019-12-18 12:06:26 +01:00 committed by GitHub
commit 773212bdf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 41 deletions

@ -109,6 +109,10 @@ func GenCertPair(org, certFile, keyFile string, tlsExtraIPs,
// verification will fail in the client. // verification will fail in the client.
dnsNames = append(dnsNames, "unix", "unixpacket") 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. // Generate a private key for the certificate.
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil { if err != nil {

97
lnd.go

@ -22,6 +22,7 @@ import (
_ "net/http/pprof" _ "net/http/pprof"
"gopkg.in/macaroon-bakery.v2/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon.v2"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@ -59,6 +60,45 @@ var (
networkDir string 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 // ListnerWithSignal is a net.Listner that has an additional Ready channel that
// will be closed when a server starts listening. // will be closed when a server starts listening.
type ListenerWithSignal struct { type ListenerWithSignal struct {
@ -81,12 +121,11 @@ type ListenerCfg struct {
} }
// rpcListeners is a function type used for closures that fetches a set of RPC // 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 // listeners for the current configuration. If no custom listeners are present,
// with these listeners. If no custom listeners are present, this should return // this should return normal listeners from the RPC endpoints defined in the
// normal listeners from the RPC endpoints defined in the config, and server // config. The second return value us a closure that will close the fetched
// options specifying TLS. // listeners.
type rpcListeners func() ([]*ListenerWithSignal, func(), []grpc.ServerOption, type rpcListeners func() ([]*ListenerWithSignal, func(), error)
error)
// Main is the true entry point for lnd. This function is required since defers // 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() // 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 // getListeners is a closure that creates listeners from the
// RPCListeners defined in the config. It also returns a cleanup // RPCListeners defined in the config. It also returns a cleanup
// closure and the server options to use for the GRPC server. // closure and the server options to use for the GRPC server.
getListeners := func() ([]*ListenerWithSignal, func(), getListeners := func() ([]*ListenerWithSignal, func(), error) {
[]grpc.ServerOption, error) {
var grpcListeners []*ListenerWithSignal var grpcListeners []*ListenerWithSignal
for _, grpcEndpoint := range cfg.RPCListeners { for _, grpcEndpoint := range cfg.RPCListeners {
// Start a gRPC server listening for HTTP/2 // Start a gRPC server listening for HTTP/2
@ -256,7 +293,7 @@ func Main(lisCfg ListenerCfg) error {
if err != nil { if err != nil {
ltndLog.Errorf("unable to listen on %s", ltndLog.Errorf("unable to listen on %s",
grpcEndpoint) grpcEndpoint)
return nil, nil, nil, err return nil, nil, err
} }
grpcListeners = append( grpcListeners = append(
grpcListeners, &ListenerWithSignal{ grpcListeners, &ListenerWithSignal{
@ -270,23 +307,20 @@ func Main(lisCfg ListenerCfg) error {
lis.Close() lis.Close()
} }
} }
return grpcListeners, cleanup, serverOpts, nil return grpcListeners, cleanup, nil
} }
// walletUnlockerListeners is a closure we'll hand to the wallet // walletUnlockerListeners is a closure we'll hand to the wallet
// unlocker, that will be called when it needs listeners for its GPRC // unlocker, that will be called when it needs listeners for its GPRC
// server. // server.
walletUnlockerListeners := func() ([]*ListenerWithSignal, func(), walletUnlockerListeners := func() ([]*ListenerWithSignal, func(),
[]grpc.ServerOption, error) { error) {
// If we have chosen to start with a dedicated listener for the // If we have chosen to start with a dedicated listener for the
// wallet unlocker, we return it directly, and empty server // wallet unlocker, we return it directly.
// options to deactivate TLS.
// TODO(halseth): any point in adding TLS support for custom
// listeners?
if lisCfg.WalletUnlocker != nil { if lisCfg.WalletUnlocker != nil {
return []*ListenerWithSignal{lisCfg.WalletUnlocker}, return []*ListenerWithSignal{lisCfg.WalletUnlocker},
func() {}, []grpc.ServerOption{}, nil func() {}, nil
} }
// Otherwise we'll return the regular listeners. // Otherwise we'll return the regular listeners.
@ -298,8 +332,8 @@ func Main(lisCfg ListenerCfg) error {
// for wallet encryption. // for wallet encryption.
if !cfg.NoSeedBackup { if !cfg.NoSeedBackup {
params, err := waitForWalletPassword( params, err := waitForWalletPassword(
cfg.RESTListeners, restDialOpts, restProxyDest, tlsCfg, cfg.RESTListeners, serverOpts, restDialOpts,
walletUnlockerListeners, restProxyDest, tlsCfg, walletUnlockerListeners,
) )
if err != nil { if err != nil {
err := fmt.Errorf("Unable to set up wallet password "+ 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 // rpcListeners is a closure we'll hand to the rpc server, that will be
// called when it needs listeners for its GPRC server. // called when it needs listeners for its GPRC server.
rpcListeners := func() ([]*ListenerWithSignal, func(), rpcListeners := func() ([]*ListenerWithSignal, func(), error) {
[]grpc.ServerOption, error) {
// If we have chosen to start with a dedicated listener for the // If we have chosen to start with a dedicated listener for the
// rpc server, we return it directly, and empty server options // rpc server, we return it directly.
// to deactivate TLS.
// TODO(halseth): any point in adding TLS support for custom
// listeners?
if lisCfg.RPCListener != nil { if lisCfg.RPCListener != nil {
return []*ListenerWithSignal{lisCfg.RPCListener}, return []*ListenerWithSignal{lisCfg.RPCListener},
func() {}, []grpc.ServerOption{}, nil func() {}, nil
} }
// Otherwise we'll return the regular listeners. // 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 // Initialize, and register our implementation of the gRPC interface
// exported by the rpcServer. // exported by the rpcServer.
rpcServer, err := newRPCServer( rpcServer, err := newRPCServer(
server, macaroonService, cfg.SubRPCServers, restDialOpts, server, macaroonService, cfg.SubRPCServers, serverOpts,
restProxyDest, atplManager, server.invoices, tower, tlsCfg, restDialOpts, restProxyDest, atplManager, server.invoices,
rpcListeners, chainedAcceptor, tower, tlsCfg, rpcListeners, chainedAcceptor,
) )
if err != nil { if err != nil {
err := fmt.Errorf("Unable to create RPC server: %v", err) 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 // WalletUnlocker server, and block until a password is provided by
// the user to this RPC server. // the user to this RPC server.
func waitForWalletPassword(restEndpoints []net.Addr, func waitForWalletPassword(restEndpoints []net.Addr,
restDialOpts []grpc.DialOption, restProxyDest string, serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption,
tlsConf *tls.Config, getListeners rpcListeners) ( restProxyDest string, tlsConf *tls.Config,
*WalletUnlockParams, error) { getListeners rpcListeners) (*WalletUnlockParams, error) {
// Start a gRPC server listening for HTTP/2 connections, solely used // Start a gRPC server listening for HTTP/2 connections, solely used
// for getting the encryption password from the client. // for getting the encryption password from the client.
listeners, cleanup, serverOpts, err := getListeners() listeners, cleanup, err := getListeners()
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -79,6 +79,19 @@ func Start(extraArgs string, unlockerReady, rpcReady Callback) {
go func() { go func() {
<-rpcListening <-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{}) rpcReady.OnResponse([]byte{})
}() }()
} }

@ -3,7 +3,7 @@
mkdir -p build mkdir -p build
# Check falafel version. # Check falafel version.
falafelVersion="0.5" falafelVersion="0.6"
falafel=$(which falafel) falafel=$(which falafel)
if [ $falafel ] if [ $falafel ]
then then

@ -1,6 +1,5 @@
[Application Options] [Application Options]
debuglevel=info debuglevel=info
no-macaroons=1
maxbackoff=2s maxbackoff=2s
nolisten=1 nolisten=1
norest=1 norest=1

@ -508,10 +508,11 @@ var _ lnrpc.LightningServer = (*rpcServer)(nil)
// base level options passed to the grPC server. This typically includes things // base level options passed to the grPC server. This typically includes things
// like requiring TLS, etc. // like requiring TLS, etc.
func newRPCServer(s *server, macService *macaroons.Service, func newRPCServer(s *server, macService *macaroons.Service,
subServerCgs *subRPCServerConfigs, restDialOpts []grpc.DialOption, subServerCgs *subRPCServerConfigs, serverOpts []grpc.ServerOption,
restProxyDest string, atpl *autopilot.Manager, restDialOpts []grpc.DialOption, restProxyDest string,
invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone, atpl *autopilot.Manager, invoiceRegistry *invoices.InvoiceRegistry,
tlsCfg *tls.Config, getListeners rpcListeners, tower *watchtower.Standalone, tlsCfg *tls.Config,
getListeners rpcListeners,
chanPredicate *chanacceptor.ChainedAcceptor) (*rpcServer, error) { chanPredicate *chanacceptor.ChainedAcceptor) (*rpcServer, error) {
// Set up router rpc backend. // 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. // Get the listeners and server options to use for this rpc server.
listeners, cleanup, serverOpts, err := getListeners() listeners, cleanup, err := getListeners()
if err != nil { if err != nil {
return nil, err return nil, err
} }