From ad7cdc8ed236d386010c8a9b5c9a2e06d405d7c3 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Thu, 13 Jan 2022 14:12:19 +0200 Subject: [PATCH] multi: use lndclient MacaroonService Since the code for creating and using a macaroon service is the same for multiple projects (pool, loop, litd etc), the code has been unified in lndclient. So this commit removes the macaroon service code and instead uses the lndclient code. --- go.mod | 3 +- go.sum | 4 +- loopd/daemon.go | 77 ++++++++++---- loopd/macaroons.go | 168 ------------------------------- loopd/register_default.go | 6 +- loopd/swapclient_server_debug.go | 8 +- 6 files changed, 65 insertions(+), 201 deletions(-) diff --git a/go.mod b/go.mod index e34f187..c43f75f 100644 --- a/go.mod +++ b/go.mod @@ -10,13 +10,12 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 github.com/jessevdk/go-flags v1.4.0 github.com/lightninglabs/aperture v0.1.6-beta - github.com/lightninglabs/lndclient v0.14.0-5 + github.com/lightninglabs/lndclient v0.14.0-8 github.com/lightninglabs/loop/swapserverrpc v1.0.0 github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display github.com/lightningnetwork/lnd v0.14.1-beta github.com/lightningnetwork/lnd/cert v1.1.0 github.com/lightningnetwork/lnd/clock v1.1.0 - github.com/lightningnetwork/lnd/kvdb v1.2.1 github.com/lightningnetwork/lnd/queue v1.1.0 github.com/lightningnetwork/lnd/ticker v1.1.0 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index f9c9308..e100bf2 100644 --- a/go.sum +++ b/go.sum @@ -461,8 +461,8 @@ github.com/lightninglabs/aperture v0.1.6-beta/go.mod h1:9xl4mx778ZAzrB87nLHMqk+X github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= github.com/lightninglabs/lndclient v0.11.0-4/go.mod h1:8/cTKNwgL87NX123gmlv3Xh6p1a7pvzu+40Un3PhHiI= -github.com/lightninglabs/lndclient v0.14.0-5 h1:dI2/Y2fn9m5VuwMTd/DcF6y0DYdMy3pk0MPu4xNjj54= -github.com/lightninglabs/lndclient v0.14.0-5/go.mod h1:2kH9vNoc29ghIkfMjxwSeK8yCxsYfR80XAJ9PU/QWWk= +github.com/lightninglabs/lndclient v0.14.0-8 h1:vdwV6yFU4A7BjG2V8cpI8Kqdl2M0NSfsA+RWR+JGTko= +github.com/lightninglabs/lndclient v0.14.0-8/go.mod h1:YIE/Yac69hIMiq9cm/ZC2sP4F0Llv3tC4hZGfgOhdeY= github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg= github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0= github.com/lightninglabs/neutrino v0.12.1/go.mod h1:GlKninWpRBbL7b8G0oQ36/8downfnFwKsr0hbRA6E/E= diff --git a/loopd/daemon.go b/loopd/daemon.go index d2eb263..f846bdb 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -15,8 +15,8 @@ import ( proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" + "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/looprpc" - "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/macaroons" "google.golang.org/grpc" @@ -85,8 +85,7 @@ type Daemon struct { restListener net.Listener restCtxCancel func() - macaroonService *macaroons.Service - macaroonDB kvdb.Backend + macaroonService *lndclient.MacaroonService } // New creates a new instance of the loop client daemon. @@ -200,6 +199,10 @@ func (d *Daemon) StartAsSubserver(lndGrpc *lndclient.GrpcLndServices, func (d *Daemon) ValidateMacaroon(ctx context.Context, requiredPermissions []bakery.Op, fullMethod string) error { + if d.macaroonService == nil { + return fmt.Errorf("macaroon service has not been initialised") + } + // Delegate the call to loop's own macaroon validator service. return d.macaroonService.ValidateMacaroon( ctx, requiredPermissions, fullMethod, @@ -213,11 +216,14 @@ func (d *Daemon) startWebServers() error { // With our client created, let's now finish setting up and start our // RPC server. First we add the security interceptor to our gRPC server // options that checks the macaroons for validity. - serverOpts, err := d.macaroonInterceptor() + unaryInterceptor, streamInterceptor, err := d.macaroonService.Interceptors() if err != nil { return fmt.Errorf("error with macaroon interceptor: %v", err) } - d.grpcServer = grpc.NewServer(serverOpts...) + d.grpcServer = grpc.NewServer( + grpc.UnaryInterceptor(unaryInterceptor), + grpc.StreamInterceptor(streamInterceptor), + ) looprpc.RegisterSwapClientServer(d.grpcServer, d) // Register our debug server if it is compiled in. @@ -370,15 +376,43 @@ func (d *Daemon) initialize(createDefaultMacaroonFile bool) error { // stop on main context cancel. So we create it early and pass it down. d.mainCtx, d.mainCtxCancel = context.WithCancel(context.Background()) - // Start the macaroon service and let it create its default macaroon in - // case it doesn't exist yet. - err = d.startMacaroonService(createDefaultMacaroonFile) - if err != nil { - // The client is the only thing we started yet, so if we clean - // up its connection now, nothing else needs to be shut down at - // this point. - clientCleanup() - return err + // Add our debug permissions to our main set of required permissions + // if compiled in. + for endpoint, perm := range debugRequiredPermissions { + RequiredPermissions[endpoint] = perm + } + + if createDefaultMacaroonFile { + // Start the macaroon service and let it create its default + // macaroon in case it doesn't exist yet. + d.macaroonService, err = lndclient.NewMacaroonService( + &lndclient.MacaroonServiceConfig{ + DBPath: d.cfg.DataDir, + DBFileName: "macaroons.db", + DBTimeout: loopdb.DefaultLoopDBTimeout, + MacaroonLocation: loopMacaroonLocation, + MacaroonPath: d.cfg.MacaroonPath, + Checkers: []macaroons.Checker{ + macaroons.IPLockChecker, + }, + RequiredPerms: RequiredPermissions, + DBPassword: macDbDefaultPw, + LndClient: &d.lnd.LndServices, + EphemeralKey: lndclient.SharedKeyNUMS, + KeyLocator: lndclient.SharedKeyLocator, + }, + ) + if err != nil { + return err + } + + if err = d.macaroonService.Start(); err != nil { + // The client is the only thing we started yet, so if we + // clean up its connection now, nothing else needs to be + // shut down at this point. + clientCleanup() + return err + } } // Now finally fully initialize the swap client RPC server instance. @@ -396,10 +430,15 @@ func (d *Daemon) initialize(createDefaultMacaroonFile bool) error { // Retrieve all currently existing swaps from the database. swapsList, err := d.impl.FetchSwaps() if err != nil { + if d.macaroonService == nil { + clientCleanup() + return err + } + // The client and the macaroon service are the only things we // started yet, so if we clean that up now, nothing else needs // to be shut down at this point. - if err := d.StopMacaroonService(); err != nil { + if err := d.macaroonService.Stop(); err != nil { log.Errorf("Error shutting down macaroon service: %v", err) } @@ -520,9 +559,11 @@ func (d *Daemon) stop() { d.restCtxCancel() } - err := d.StopMacaroonService() - if err != nil { - log.Errorf("Error stopping macaroon service: %v", err) + if d.macaroonService != nil { + err := d.macaroonService.Stop() + if err != nil { + log.Errorf("Error stopping macaroon service: %v", err) + } } // Next, shut down the connections to lnd and the swap server. diff --git a/loopd/macaroons.go b/loopd/macaroons.go index 5ea4155..c312939 100644 --- a/loopd/macaroons.go +++ b/loopd/macaroons.go @@ -1,18 +1,6 @@ package loopd import ( - "context" - "fmt" - "io/ioutil" - "os" - - "github.com/coreos/bbolt" - "github.com/lightninglabs/loop/loopdb" - "github.com/lightningnetwork/lnd/kvdb" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/macaroons" - "github.com/lightningnetwork/lnd/rpcperms" - "google.golang.org/grpc" "gopkg.in/macaroon-bakery.v2/bakery" ) @@ -105,36 +93,6 @@ var ( }}, } - // allPermissions is the list of all existing permissions that exist - // for loopd's RPC. The default macaroon that is created on startup - // contains all these permissions and is therefore equivalent to lnd's - // admin.macaroon but for loop. - allPermissions = []bakery.Op{{ - Entity: "loop", - Action: "out", - }, { - Entity: "loop", - Action: "in", - }, { - Entity: "swap", - Action: "execute", - }, { - Entity: "swap", - Action: "read", - }, { - Entity: "terms", - Action: "read", - }, { - Entity: "auth", - Action: "read", - }, { - Entity: "suggestions", - Action: "read", - }, { - Entity: "suggestions", - Action: "write", - }} - // macDbDefaultPw is the default encryption password used to encrypt the // loop macaroon database. The macaroon service requires us to set a // non-nil password so we set it to an empty string. This will cause the @@ -146,129 +104,3 @@ var ( // though. macDbDefaultPw = []byte("") ) - -// startMacaroonService starts the macaroon validation service, creates or -// unlocks the macaroon database and creates the default macaroon if it doesn't -// exist yet. If macaroons are disabled in general in the configuration, none of -// these actions are taken. -func (d *Daemon) startMacaroonService(createDefaultMacaroonFile bool) error { - var err error - d.macaroonDB, err = kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{ - DBPath: d.cfg.DataDir, - DBFileName: "macaroons.db", - DBTimeout: loopdb.DefaultLoopDBTimeout, - }) - if err != nil { - return fmt.Errorf("unable to load macaroon db: %v", err) - } - if err == bbolt.ErrTimeout { - return fmt.Errorf("%w: couldn't obtain exclusive lock on "+ - "%s/%s, timed out after %v", bbolt.ErrTimeout, - d.cfg.DataDir, "macaroons.db", - loopdb.DefaultLoopDBTimeout) - } - - // Create the macaroon authentication/authorization service. - d.macaroonService, err = macaroons.NewService( - d.macaroonDB, loopMacaroonLocation, false, - macaroons.IPLockChecker, - ) - if err != nil { - return fmt.Errorf("unable to set up macaroon authentication: "+ - "%v", err) - } - - // Try to unlock the macaroon store with the private password. - err = d.macaroonService.CreateUnlock(&macDbDefaultPw) - if err != nil { - return fmt.Errorf("unable to unlock macaroon DB: %v", err) - } - - // There are situations in which we don't want a macaroon to be created - // on disk (for example when running inside LiT stateless integrated - // mode). For any other cases, we create macaroon files for the loop CLI - // in the default directory. - if createDefaultMacaroonFile && !lnrpc.FileExists(d.cfg.MacaroonPath) { - // We don't offer the ability to rotate macaroon root keys yet, - // so just use the default one since the service expects some - // value to be set. - idCtx := macaroons.ContextWithRootKeyID( - context.Background(), macaroons.DefaultRootKeyID, - ) - - // We only generate one default macaroon that contains all - // existing permissions (equivalent to the admin.macaroon in - // lnd). Custom macaroons can be created through the bakery - // RPC. Add our debug permissions if required. - allPermissions = append(allPermissions, debugPermissions...) - loopMac, err := d.macaroonService.Oven.NewMacaroon( - idCtx, bakery.LatestVersion, nil, allPermissions..., - ) - if err != nil { - return err - } - loopMacBytes, err := loopMac.M().MarshalBinary() - if err != nil { - return err - } - err = ioutil.WriteFile(d.cfg.MacaroonPath, loopMacBytes, 0644) - if err != nil { - if err := os.Remove(d.cfg.MacaroonPath); err != nil { - log.Errorf("Unable to remove %s: %v", - d.cfg.MacaroonPath, err) - } - return err - } - } - - return nil -} - -// StopMacaroonService closes the macaroon database. -func (d *Daemon) StopMacaroonService() error { - var shutdownErr error - if err := d.macaroonService.Close(); err != nil { - log.Errorf("Error closing macaroon service: %v", err) - shutdownErr = err - } - - if err := d.macaroonDB.Close(); err != nil { - log.Errorf("Error closing macaroon DB: %v", err) - shutdownErr = err - } - - return shutdownErr -} - -// macaroonInterceptor creates gRPC server options with the macaroon security -// interceptors. -func (d *Daemon) macaroonInterceptor() ([]grpc.ServerOption, error) { - // Add our debug permissions to our main set of required permissions - // if compiled in. - for endpoint, perm := range debugRequiredPermissions { - RequiredPermissions[endpoint] = perm - } - - interceptor := rpcperms.NewInterceptorChain(log, false, nil) - err := interceptor.Start() - if err != nil { - return nil, err - } - - interceptor.SetWalletUnlocked() - interceptor.AddMacaroonService(d.macaroonService) - - for method, permissions := range RequiredPermissions { - err := interceptor.AddPermission(method, permissions) - if err != nil { - return nil, err - } - } - - unaryInterceptor := interceptor.MacaroonUnaryServerInterceptor() - streamInterceptor := interceptor.MacaroonStreamServerInterceptor() - return []grpc.ServerOption{ - grpc.UnaryInterceptor(unaryInterceptor), - grpc.StreamInterceptor(streamInterceptor), - }, nil -} diff --git a/loopd/register_default.go b/loopd/register_default.go index 08f3572..27a0974 100644 --- a/loopd/register_default.go +++ b/loopd/register_default.go @@ -1,13 +1,11 @@ +//go:build !dev // +build !dev package loopd import "gopkg.in/macaroon-bakery.v2/bakery" -var ( - debugRequiredPermissions = map[string][]bakery.Op{} - debugPermissions []bakery.Op -) +var debugRequiredPermissions = map[string][]bakery.Op{} // registerDebugServer is our default debug server registration function, which // excludes debug functionality. diff --git a/loopd/swapclient_server_debug.go b/loopd/swapclient_server_debug.go index 81182b3..d1f9465 100644 --- a/loopd/swapclient_server_debug.go +++ b/loopd/swapclient_server_debug.go @@ -1,3 +1,4 @@ +//go:build dev // +build dev package loopd @@ -19,13 +20,6 @@ var ( Action: "write", }}, } - - debugPermissions = []bakery.Op{ - { - Entity: "debug", - Action: "write", - }, - } ) // registerDebugServer registers the debug server.