Skip to content

Commit 44e734c

Browse files
authored
Merge pull request #453 from ellemouton/macaroonService
multi: use lndclient MacaroonService
2 parents 0d1cb3e + 2d7745e commit 44e734c

File tree

6 files changed

+68
-204
lines changed

6 files changed

+68
-204
lines changed

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ require (
1010
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
1111
github.com/jessevdk/go-flags v1.4.0
1212
github.com/lightninglabs/aperture v0.1.6-beta
13-
github.com/lightninglabs/lndclient v0.14.0-5
13+
github.com/lightninglabs/lndclient v0.14.0-8
1414
github.com/lightninglabs/loop/swapserverrpc v1.0.0
1515
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display
1616
github.com/lightningnetwork/lnd v0.14.1-beta
1717
github.com/lightningnetwork/lnd/cert v1.1.0
1818
github.com/lightningnetwork/lnd/clock v1.1.0
19-
github.com/lightningnetwork/lnd/kvdb v1.2.1
2019
github.com/lightningnetwork/lnd/queue v1.1.0
2120
github.com/lightningnetwork/lnd/ticker v1.1.0
2221
github.com/stretchr/testify v1.7.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,8 @@ github.com/lightninglabs/aperture v0.1.6-beta/go.mod h1:9xl4mx778ZAzrB87nLHMqk+X
461461
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc=
462462
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
463463
github.com/lightninglabs/lndclient v0.11.0-4/go.mod h1:8/cTKNwgL87NX123gmlv3Xh6p1a7pvzu+40Un3PhHiI=
464-
github.com/lightninglabs/lndclient v0.14.0-5 h1:dI2/Y2fn9m5VuwMTd/DcF6y0DYdMy3pk0MPu4xNjj54=
465-
github.com/lightninglabs/lndclient v0.14.0-5/go.mod h1:2kH9vNoc29ghIkfMjxwSeK8yCxsYfR80XAJ9PU/QWWk=
464+
github.com/lightninglabs/lndclient v0.14.0-8 h1:vdwV6yFU4A7BjG2V8cpI8Kqdl2M0NSfsA+RWR+JGTko=
465+
github.com/lightninglabs/lndclient v0.14.0-8/go.mod h1:YIE/Yac69hIMiq9cm/ZC2sP4F0Llv3tC4hZGfgOhdeY=
466466
github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg=
467467
github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0=
468468
github.com/lightninglabs/neutrino v0.12.1/go.mod h1:GlKninWpRBbL7b8G0oQ36/8downfnFwKsr0hbRA6E/E=

loopd/daemon.go

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import (
1515
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
1616
"github.com/lightninglabs/lndclient"
1717
"github.com/lightninglabs/loop"
18+
"github.com/lightninglabs/loop/loopdb"
1819
"github.com/lightninglabs/loop/looprpc"
19-
"github.com/lightningnetwork/lnd/kvdb"
2020
"github.com/lightningnetwork/lnd/lntypes"
2121
"github.com/lightningnetwork/lnd/macaroons"
2222
"google.golang.org/grpc"
@@ -85,8 +85,7 @@ type Daemon struct {
8585
restListener net.Listener
8686
restCtxCancel func()
8787

88-
macaroonService *macaroons.Service
89-
macaroonDB kvdb.Backend
88+
macaroonService *lndclient.MacaroonService
9089
}
9190

9291
// New creates a new instance of the loop client daemon.
@@ -164,7 +163,7 @@ func (d *Daemon) Start() error {
164163
// for REST (if enabled), instead of creating an own mux and HTTP server, we
165164
// register to an existing one.
166165
func (d *Daemon) StartAsSubserver(lndGrpc *lndclient.GrpcLndServices,
167-
createDefaultMacaroonFile bool) error {
166+
withMacaroonService bool) error {
168167

169168
// There should be no reason to start the daemon twice. Therefore return
170169
// an error if that's tried. This is mostly to guard against Start and
@@ -181,7 +180,7 @@ func (d *Daemon) StartAsSubserver(lndGrpc *lndclient.GrpcLndServices,
181180
// the swap server client, the RPC server instance and our main swap
182181
// handlers. If this fails, then nothing has been started yet and we can
183182
// just return the error.
184-
err := d.initialize(createDefaultMacaroonFile)
183+
err := d.initialize(withMacaroonService)
185184
if errors.Is(err, bbolt.ErrTimeout) {
186185
// We're trying to be started inside LiT so there most likely is
187186
// another standalone Loop process blocking the DB.
@@ -200,6 +199,10 @@ func (d *Daemon) StartAsSubserver(lndGrpc *lndclient.GrpcLndServices,
200199
func (d *Daemon) ValidateMacaroon(ctx context.Context,
201200
requiredPermissions []bakery.Op, fullMethod string) error {
202201

202+
if d.macaroonService == nil {
203+
return fmt.Errorf("macaroon service has not been initialised")
204+
}
205+
203206
// Delegate the call to loop's own macaroon validator service.
204207
return d.macaroonService.ValidateMacaroon(
205208
ctx, requiredPermissions, fullMethod,
@@ -213,11 +216,14 @@ func (d *Daemon) startWebServers() error {
213216
// With our client created, let's now finish setting up and start our
214217
// RPC server. First we add the security interceptor to our gRPC server
215218
// options that checks the macaroons for validity.
216-
serverOpts, err := d.macaroonInterceptor()
219+
unaryInterceptor, streamInterceptor, err := d.macaroonService.Interceptors()
217220
if err != nil {
218221
return fmt.Errorf("error with macaroon interceptor: %v", err)
219222
}
220-
d.grpcServer = grpc.NewServer(serverOpts...)
223+
d.grpcServer = grpc.NewServer(
224+
grpc.UnaryInterceptor(unaryInterceptor),
225+
grpc.StreamInterceptor(streamInterceptor),
226+
)
221227
looprpc.RegisterSwapClientServer(d.grpcServer, d)
222228

223229
// Register our debug server if it is compiled in.
@@ -341,7 +347,7 @@ func (d *Daemon) startWebServers() error {
341347
// the swap client RPC server instance and our main swap and error handlers. If
342348
// this method fails with an error then no goroutine was started yet and no
343349
// cleanup is necessary. If it succeeds, then goroutines have been spawned.
344-
func (d *Daemon) initialize(createDefaultMacaroonFile bool) error {
350+
func (d *Daemon) initialize(withMacaroonService bool) error {
345351
// If no swap server is specified, use the default addresses for mainnet
346352
// and testnet.
347353
if d.cfg.Server.Host == "" {
@@ -370,15 +376,43 @@ func (d *Daemon) initialize(createDefaultMacaroonFile bool) error {
370376
// stop on main context cancel. So we create it early and pass it down.
371377
d.mainCtx, d.mainCtxCancel = context.WithCancel(context.Background())
372378

373-
// Start the macaroon service and let it create its default macaroon in
374-
// case it doesn't exist yet.
375-
err = d.startMacaroonService(createDefaultMacaroonFile)
376-
if err != nil {
377-
// The client is the only thing we started yet, so if we clean
378-
// up its connection now, nothing else needs to be shut down at
379-
// this point.
380-
clientCleanup()
381-
return err
379+
// Add our debug permissions to our main set of required permissions
380+
// if compiled in.
381+
for endpoint, perm := range debugRequiredPermissions {
382+
RequiredPermissions[endpoint] = perm
383+
}
384+
385+
if withMacaroonService {
386+
// Start the macaroon service and let it create its default
387+
// macaroon in case it doesn't exist yet.
388+
d.macaroonService, err = lndclient.NewMacaroonService(
389+
&lndclient.MacaroonServiceConfig{
390+
DBPath: d.cfg.DataDir,
391+
DBFileName: "macaroons.db",
392+
DBTimeout: loopdb.DefaultLoopDBTimeout,
393+
MacaroonLocation: loopMacaroonLocation,
394+
MacaroonPath: d.cfg.MacaroonPath,
395+
Checkers: []macaroons.Checker{
396+
macaroons.IPLockChecker,
397+
},
398+
RequiredPerms: RequiredPermissions,
399+
DBPassword: macDbDefaultPw,
400+
LndClient: &d.lnd.LndServices,
401+
EphemeralKey: lndclient.SharedKeyNUMS,
402+
KeyLocator: lndclient.SharedKeyLocator,
403+
},
404+
)
405+
if err != nil {
406+
return err
407+
}
408+
409+
if err = d.macaroonService.Start(); err != nil {
410+
// The client is the only thing we started yet, so if we
411+
// clean up its connection now, nothing else needs to be
412+
// shut down at this point.
413+
clientCleanup()
414+
return err
415+
}
382416
}
383417

384418
// Now finally fully initialize the swap client RPC server instance.
@@ -396,10 +430,15 @@ func (d *Daemon) initialize(createDefaultMacaroonFile bool) error {
396430
// Retrieve all currently existing swaps from the database.
397431
swapsList, err := d.impl.FetchSwaps()
398432
if err != nil {
433+
if d.macaroonService == nil {
434+
clientCleanup()
435+
return err
436+
}
437+
399438
// The client and the macaroon service are the only things we
400439
// started yet, so if we clean that up now, nothing else needs
401440
// to be shut down at this point.
402-
if err := d.StopMacaroonService(); err != nil {
441+
if err := d.macaroonService.Stop(); err != nil {
403442
log.Errorf("Error shutting down macaroon service: %v",
404443
err)
405444
}
@@ -520,9 +559,11 @@ func (d *Daemon) stop() {
520559
d.restCtxCancel()
521560
}
522561

523-
err := d.StopMacaroonService()
524-
if err != nil {
525-
log.Errorf("Error stopping macaroon service: %v", err)
562+
if d.macaroonService != nil {
563+
err := d.macaroonService.Stop()
564+
if err != nil {
565+
log.Errorf("Error stopping macaroon service: %v", err)
566+
}
526567
}
527568

528569
// Next, shut down the connections to lnd and the swap server.

loopd/macaroons.go

Lines changed: 0 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
package loopd
22

33
import (
4-
"context"
5-
"fmt"
6-
"io/ioutil"
7-
"os"
8-
9-
"github.com/coreos/bbolt"
10-
"github.com/lightninglabs/loop/loopdb"
11-
"github.com/lightningnetwork/lnd/kvdb"
12-
"github.com/lightningnetwork/lnd/lnrpc"
13-
"github.com/lightningnetwork/lnd/macaroons"
14-
"github.com/lightningnetwork/lnd/rpcperms"
15-
"google.golang.org/grpc"
164
"gopkg.in/macaroon-bakery.v2/bakery"
175
)
186

@@ -105,36 +93,6 @@ var (
10593
}},
10694
}
10795

108-
// allPermissions is the list of all existing permissions that exist
109-
// for loopd's RPC. The default macaroon that is created on startup
110-
// contains all these permissions and is therefore equivalent to lnd's
111-
// admin.macaroon but for loop.
112-
allPermissions = []bakery.Op{{
113-
Entity: "loop",
114-
Action: "out",
115-
}, {
116-
Entity: "loop",
117-
Action: "in",
118-
}, {
119-
Entity: "swap",
120-
Action: "execute",
121-
}, {
122-
Entity: "swap",
123-
Action: "read",
124-
}, {
125-
Entity: "terms",
126-
Action: "read",
127-
}, {
128-
Entity: "auth",
129-
Action: "read",
130-
}, {
131-
Entity: "suggestions",
132-
Action: "read",
133-
}, {
134-
Entity: "suggestions",
135-
Action: "write",
136-
}}
137-
13896
// macDbDefaultPw is the default encryption password used to encrypt the
13997
// loop macaroon database. The macaroon service requires us to set a
14098
// non-nil password so we set it to an empty string. This will cause the
@@ -146,129 +104,3 @@ var (
146104
// though.
147105
macDbDefaultPw = []byte("")
148106
)
149-
150-
// startMacaroonService starts the macaroon validation service, creates or
151-
// unlocks the macaroon database and creates the default macaroon if it doesn't
152-
// exist yet. If macaroons are disabled in general in the configuration, none of
153-
// these actions are taken.
154-
func (d *Daemon) startMacaroonService(createDefaultMacaroonFile bool) error {
155-
var err error
156-
d.macaroonDB, err = kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{
157-
DBPath: d.cfg.DataDir,
158-
DBFileName: "macaroons.db",
159-
DBTimeout: loopdb.DefaultLoopDBTimeout,
160-
})
161-
if err != nil {
162-
return fmt.Errorf("unable to load macaroon db: %v", err)
163-
}
164-
if err == bbolt.ErrTimeout {
165-
return fmt.Errorf("%w: couldn't obtain exclusive lock on "+
166-
"%s/%s, timed out after %v", bbolt.ErrTimeout,
167-
d.cfg.DataDir, "macaroons.db",
168-
loopdb.DefaultLoopDBTimeout)
169-
}
170-
171-
// Create the macaroon authentication/authorization service.
172-
d.macaroonService, err = macaroons.NewService(
173-
d.macaroonDB, loopMacaroonLocation, false,
174-
macaroons.IPLockChecker,
175-
)
176-
if err != nil {
177-
return fmt.Errorf("unable to set up macaroon authentication: "+
178-
"%v", err)
179-
}
180-
181-
// Try to unlock the macaroon store with the private password.
182-
err = d.macaroonService.CreateUnlock(&macDbDefaultPw)
183-
if err != nil {
184-
return fmt.Errorf("unable to unlock macaroon DB: %v", err)
185-
}
186-
187-
// There are situations in which we don't want a macaroon to be created
188-
// on disk (for example when running inside LiT stateless integrated
189-
// mode). For any other cases, we create macaroon files for the loop CLI
190-
// in the default directory.
191-
if createDefaultMacaroonFile && !lnrpc.FileExists(d.cfg.MacaroonPath) {
192-
// We don't offer the ability to rotate macaroon root keys yet,
193-
// so just use the default one since the service expects some
194-
// value to be set.
195-
idCtx := macaroons.ContextWithRootKeyID(
196-
context.Background(), macaroons.DefaultRootKeyID,
197-
)
198-
199-
// We only generate one default macaroon that contains all
200-
// existing permissions (equivalent to the admin.macaroon in
201-
// lnd). Custom macaroons can be created through the bakery
202-
// RPC. Add our debug permissions if required.
203-
allPermissions = append(allPermissions, debugPermissions...)
204-
loopMac, err := d.macaroonService.Oven.NewMacaroon(
205-
idCtx, bakery.LatestVersion, nil, allPermissions...,
206-
)
207-
if err != nil {
208-
return err
209-
}
210-
loopMacBytes, err := loopMac.M().MarshalBinary()
211-
if err != nil {
212-
return err
213-
}
214-
err = ioutil.WriteFile(d.cfg.MacaroonPath, loopMacBytes, 0644)
215-
if err != nil {
216-
if err := os.Remove(d.cfg.MacaroonPath); err != nil {
217-
log.Errorf("Unable to remove %s: %v",
218-
d.cfg.MacaroonPath, err)
219-
}
220-
return err
221-
}
222-
}
223-
224-
return nil
225-
}
226-
227-
// StopMacaroonService closes the macaroon database.
228-
func (d *Daemon) StopMacaroonService() error {
229-
var shutdownErr error
230-
if err := d.macaroonService.Close(); err != nil {
231-
log.Errorf("Error closing macaroon service: %v", err)
232-
shutdownErr = err
233-
}
234-
235-
if err := d.macaroonDB.Close(); err != nil {
236-
log.Errorf("Error closing macaroon DB: %v", err)
237-
shutdownErr = err
238-
}
239-
240-
return shutdownErr
241-
}
242-
243-
// macaroonInterceptor creates gRPC server options with the macaroon security
244-
// interceptors.
245-
func (d *Daemon) macaroonInterceptor() ([]grpc.ServerOption, error) {
246-
// Add our debug permissions to our main set of required permissions
247-
// if compiled in.
248-
for endpoint, perm := range debugRequiredPermissions {
249-
RequiredPermissions[endpoint] = perm
250-
}
251-
252-
interceptor := rpcperms.NewInterceptorChain(log, false, nil)
253-
err := interceptor.Start()
254-
if err != nil {
255-
return nil, err
256-
}
257-
258-
interceptor.SetWalletUnlocked()
259-
interceptor.AddMacaroonService(d.macaroonService)
260-
261-
for method, permissions := range RequiredPermissions {
262-
err := interceptor.AddPermission(method, permissions)
263-
if err != nil {
264-
return nil, err
265-
}
266-
}
267-
268-
unaryInterceptor := interceptor.MacaroonUnaryServerInterceptor()
269-
streamInterceptor := interceptor.MacaroonStreamServerInterceptor()
270-
return []grpc.ServerOption{
271-
grpc.UnaryInterceptor(unaryInterceptor),
272-
grpc.StreamInterceptor(streamInterceptor),
273-
}, nil
274-
}

loopd/register_default.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1+
//go:build !dev
12
// +build !dev
23

34
package loopd
45

56
import "gopkg.in/macaroon-bakery.v2/bakery"
67

7-
var (
8-
debugRequiredPermissions = map[string][]bakery.Op{}
9-
debugPermissions []bakery.Op
10-
)
8+
var debugRequiredPermissions = map[string][]bakery.Op{}
119

1210
// registerDebugServer is our default debug server registration function, which
1311
// excludes debug functionality.

0 commit comments

Comments
 (0)