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.
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 {

97
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
}

@ -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{})
}()
}

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

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

@ -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
}