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.
pull/453/head
Elle Mouton 2 years ago
parent 0d1cb3e5d6
commit ad7cdc8ed2

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

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

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

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

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

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

Loading…
Cancel
Save