Merge pull request #78 from guggero/macaroons

Add macaroon authentication
This commit is contained in:
Wilmer Paulino 2020-09-17 15:17:24 -07:00 committed by GitHub
commit 7c7dfd0c36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 375 additions and 50 deletions

View file

@ -435,14 +435,21 @@ following command (assuming you have a local testnet `lnd` running):
The current server is reachable at `clm.testnet.lightningcluster.com:12010`,
this may change as the alpha version progresses.
## Transport security
## Authentication and transport security
The gRPC and REST connections of `poold` are encrypted with TLS the same way
`lnd` is.
The gRPC and REST connections of `poold` are encrypted with TLS and secured with
macaroon authentication the same way `lnd` is.
If no custom base directory is set then the TLS certificate is stored in
`~/.pool/<network>/tls.cert`.
`~/.pool/<network>/tls.cert` and the base macaroon in
`~/.pool/<network>/pool.macaroon`.
The `pool` command will pick up the file automatically on mainnet if no custom
The `pool` command will pick up these file automatically on mainnet if no custom
base directory is used. For other networks it should be sufficient to add the
`--network` flag to tell the CLI in what sub directory to look for the files.
For more information on macaroons,
[see the macaroon documentation of lnd.](https://github.com/lightningnetwork/lnd/blob/master/docs/macaroons.md)
**NOTE**: pool's macaroons are independent from `lnd`'s. The same macaroon
cannot be used for both `poold` and `lnd`.

View file

@ -52,9 +52,9 @@ var (
Value: pool.DefaultTLSCertPath,
}
macaroonPathFlag = cli.StringFlag{
Name: "macaroonpath",
Usage: "path to macaroon file, only needed if pool runs " +
"in the same process as lnd",
Name: "macaroonpath",
Usage: "path to macaroon file",
Value: pool.DefaultMacaroonPath,
}
)
@ -172,32 +172,45 @@ func extractPathArgs(ctx *cli.Context) (string, string, error) {
}
// We'll now fetch the basedir so we can make a decision on how to
// properly read the cert. This will either be the default, or will have
// been overwritten by the end user.
// properly read the cert and macaroon. This will either be the default,
// or will have been overwritten by the end user.
baseDir := lncfg.CleanAndExpandPath(ctx.GlobalString(baseDirFlag.Name))
tlsCertPath := lncfg.CleanAndExpandPath(ctx.GlobalString(
tlsCertFlag.Name,
))
macPath := lncfg.CleanAndExpandPath(ctx.GlobalString(
macaroonPathFlag.Name,
))
// If a custom base directory was set, we'll also check if custom paths
// for the TLS cert file were set as well. If not, we'll override their
// paths so they can be found within the custom base directory set. This
// allows us to set a custom base directory, along with custom paths to
// the TLS cert file.
// for the TLS cert and macaroon file were set as well. If not, we'll
// override their paths so they can be found within the custom base
// directory set. This allows us to set a custom base directory, along
// with custom paths to the TLS cert and macaroon file.
if baseDir != pool.DefaultBaseDir || networkStr != pool.DefaultNetwork {
tlsCertPath = filepath.Join(
baseDir, networkStr, pool.DefaultTLSCertFilename,
)
macPath = filepath.Join(
baseDir, networkStr, pool.DefaultMacaroonFilename,
)
}
return tlsCertPath, ctx.GlobalString(macaroonPathFlag.Name), nil
return tlsCertPath, macPath, nil
}
func getClientConn(address, tlsCertPath, macaroonPath string) (*grpc.ClientConn,
error) {
// We always need to send a macaroon.
macOption, err := readMacaroon(macaroonPath)
if err != nil {
return nil, err
}
opts := []grpc.DialOption{
grpc.WithDefaultCallOptions(maxMsgRecvSize),
macOption,
}
// TLS cannot be disabled, we'll always have a cert file to read.
@ -206,11 +219,6 @@ func getClientConn(address, tlsCertPath, macaroonPath string) (*grpc.ClientConn,
fatal(err)
}
// Macaroons are not yet enabled by default.
if macaroonPath != "" {
opts = append(opts, readMacaroon(macaroonPath))
}
opts = append(opts, grpc.WithTransportCredentials(creds))
conn, err := grpc.Dial(address, opts...)
@ -253,16 +261,16 @@ func parseUint64(ctx *cli.Context, argIdx int, flag, cmd string) (uint64, error)
// readMacaroon tries to read the macaroon file at the specified path and create
// gRPC dial options from it.
func readMacaroon(macPath string) grpc.DialOption {
func readMacaroon(macPath string) (grpc.DialOption, error) {
// Load the specified macaroon file.
macBytes, err := ioutil.ReadFile(macPath)
if err != nil {
fatal(fmt.Errorf("unable to read macaroon path : %v", err))
return nil, fmt.Errorf("unable to read macaroon path : %v", err)
}
mac := &macaroon.Macaroon{}
if err = mac.UnmarshalBinary(macBytes); err != nil {
fatal(fmt.Errorf("unable to decode macaroon: %v", err))
return nil, fmt.Errorf("unable to decode macaroon: %v", err)
}
macConstraints := []macaroons.Constraint{
@ -282,10 +290,10 @@ func readMacaroon(macPath string) grpc.DialOption {
// Apply constraints to the macaroon.
constrainedMac, err := macaroons.AddConstraints(mac, macConstraints...)
if err != nil {
fatal(err)
return nil, err
}
// Now we append the macaroon credentials to the dial options.
cred := macaroons.NewMacaroonCredential(constrainedMac)
return grpc.WithPerRPCCredentials(cred)
return grpc.WithPerRPCCredentials(cred), nil
}

View file

@ -62,6 +62,16 @@ var (
DefaultTLSKeyPath = filepath.Join(
DefaultBaseDir, DefaultNetwork, DefaultTLSKeyFilename,
)
// DefaultMacaroonFilename is the default file name for the
// autogenerated pool macaroon.
DefaultMacaroonFilename = "pool.macaroon"
// DefaultMacaroonPath is the default full path of the base pool
// macaroon.
DefaultMacaroonPath = filepath.Join(
DefaultBaseDir, DefaultNetwork, DefaultMacaroonFilename,
)
)
type LndConfig struct {
@ -78,7 +88,7 @@ type Config struct {
TLSPathAuctSrv string `long:"tlspathauctserver" description:"Path to auction server tls certificate"`
RPCListen string `long:"rpclisten" description:"Address to listen on for gRPC clients"`
RESTListen string `long:"restlisten" description:"Address to listen on for REST clients"`
BaseDir string `long:"basedir" description:"The base directory where pool stores all its data. If set, this option overwrites --logdir, --tlscertpath and --tlskeypath."`
BaseDir string `long:"basedir" description:"The base directory where pool stores all its data. If set, this option overwrites --logdir, --macaroonpath, --tlscertpath and --tlskeypath."`
LogDir string `long:"logdir" description:"Directory to log output."`
MaxLogFiles int `long:"maxlogfiles" description:"Maximum logfiles to keep (0 for no rotation)"`
@ -95,6 +105,8 @@ type Config struct {
TLSAutoRefresh bool `long:"tlsautorefresh" description:"Re-generate TLS certificate and key if the IPs or domains are changed."`
TLSDisableAutofill bool `long:"tlsdisableautofill" description:"Do not include the interface IPs or the system hostname in TLS certificate, use first --tlsextradomain as Common Name instead, if set."`
MacaroonPath string `long:"macaroonpath" description:"Path to write the macaroon for pool's RPC and REST services if it doesn't exist."`
NewNodesOnly bool `long:"newnodesonly" description:"Only accept channels from nodes that the connected lnd node doesn't already have open or pending channels with."`
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65535"`
@ -140,6 +152,7 @@ func DefaultConfig() Config {
DebugLevel: defaultLogLevel,
TLSCertPath: DefaultTLSCertPath,
TLSKeyPath: DefaultTLSKeyPath,
MacaroonPath: DefaultMacaroonPath,
Lnd: &LndConfig{
Host: "localhost:10009",
},
@ -153,6 +166,7 @@ func Validate(cfg *Config) error {
cfg.LogDir = lncfg.CleanAndExpandPath(cfg.LogDir)
cfg.TLSCertPath = lncfg.CleanAndExpandPath(cfg.TLSCertPath)
cfg.TLSKeyPath = lncfg.CleanAndExpandPath(cfg.TLSKeyPath)
cfg.MacaroonPath = lncfg.CleanAndExpandPath(cfg.MacaroonPath)
// Since our pool directory overrides our log and TLS dir values, make
// sure that they are not set when base dir is set. We hard here rather
@ -163,6 +177,7 @@ func Validate(cfg *Config) error {
logDirSet := cfg.LogDir != defaultLogDir
tlsCertPathSet := cfg.TLSCertPath != DefaultTLSCertPath
tlsKeyPathSet := cfg.TLSKeyPath != DefaultTLSKeyPath
macaroonPathSet := cfg.MacaroonPath != DefaultMacaroonPath
if logDirSet {
return fmt.Errorf("basedir overwrites logdir, please " +
@ -179,6 +194,11 @@ func Validate(cfg *Config) error {
"please only set one value")
}
if macaroonPathSet {
return fmt.Errorf("basedir overwrites macaroonpath, " +
"please only set one value")
}
// Once we are satisfied that no other config value was set, we
// replace them with our pool dir.
cfg.LogDir = filepath.Join(cfg.BaseDir, defaultLogDirname)
@ -189,9 +209,9 @@ func Validate(cfg *Config) error {
cfg.LogDir = filepath.Join(cfg.LogDir, cfg.Network)
cfg.BaseDir = filepath.Join(cfg.BaseDir, cfg.Network)
// We want the TLS files to also be in the "namespaced" sub directory.
// Replace the default values with actual values in case the user
// specified basedir.
// We want the TLS and macaroon files to also be in the "namespaced" sub
// directory. Replace the default values with actual values in case the
// user specified basedir.
if cfg.TLSCertPath == DefaultTLSCertPath {
cfg.TLSCertPath = filepath.Join(
cfg.BaseDir, DefaultTLSCertFilename,
@ -202,6 +222,11 @@ func Validate(cfg *Config) error {
cfg.BaseDir, DefaultTLSKeyFilename,
)
}
if cfg.MacaroonPath == DefaultMacaroonPath {
cfg.MacaroonPath = filepath.Join(
cfg.BaseDir, DefaultMacaroonFilename,
)
}
// If either of these directories do not exist, create them.
if err := os.MkdirAll(cfg.BaseDir, os.ModePerm); err != nil {

7
go.mod
View file

@ -15,8 +15,10 @@ require (
github.com/lightninglabs/aperture v0.1.1-beta.0.20200901205500-5237b07a6ef5
github.com/lightninglabs/lndclient v0.11.0-1
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d
// TODO(guggero): Bump to v0.11.1-beta once released.
github.com/lightningnetwork/lnd v0.11.0-beta.rc4.0.20200907194312-751b02361e53
// TODO(guggero): Bump lnd to the final v0.11.1-beta version once it's
// released.
github.com/lightningnetwork/lnd v0.11.0-beta.rc4.0.20200911014924-bc6e52888763
github.com/lightningnetwork/lnd/cert v1.0.3
github.com/stretchr/testify v1.5.1
github.com/urfave/cli v1.20.0
@ -24,5 +26,6 @@ require (
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884
google.golang.org/grpc v1.29.1
gopkg.in/macaroon-bakery.v2 v2.0.1
gopkg.in/macaroon.v2 v2.1.0
)

6
go.sum
View file

@ -207,11 +207,9 @@ github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce7
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI=
github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea h1:oCj48NQ8u7Vz+MmzHqt0db6mxcFZo3Ho7M5gCJauY/k=
github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4=
github.com/lightningnetwork/lnd v0.11.0-beta h1:pUAT7FMHqS+iarNxyRtgj96XKCGAWwmb6ZdiUBy78ts=
github.com/lightningnetwork/lnd v0.11.0-beta/go.mod h1:CzArvT7NFDLhVyW06+NJWSuWFmE6Ea+AjjA3txUBqTM=
github.com/lightningnetwork/lnd v0.11.0-beta/go.mod h1:CzArvT7NFDLhVyW06+NJWSuWFmE6Ea+AjjA3txUBqTM=
github.com/lightningnetwork/lnd v0.11.0-beta.rc4.0.20200907194312-751b02361e53 h1:2q0mdQRgA8uEblmZO+i9pqkNWVb9S3dyV1evVRRe/k4=
github.com/lightningnetwork/lnd v0.11.0-beta.rc4.0.20200907194312-751b02361e53/go.mod h1:IvrqVCc5tN2on6E7IHhrwyiM7FCHZ92LphZD+v88LXY=
github.com/lightningnetwork/lnd v0.11.0-beta.rc4.0.20200911014924-bc6e52888763 h1:OUWOTo2BAcsnEaMQIf4gLktU3zGytx6pXrmjUNpZpdg=
github.com/lightningnetwork/lnd v0.11.0-beta.rc4.0.20200911014924-bc6e52888763/go.mod h1:IvrqVCc5tN2on6E7IHhrwyiM7FCHZ92LphZD+v88LXY=
github.com/lightningnetwork/lnd/cert v1.0.2 h1:g2rEu+sM2Uyz0bpfuvwri/ks6R/26H5iY1NcGbpDJ+c=
github.com/lightningnetwork/lnd/cert v1.0.2/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo=
github.com/lightningnetwork/lnd/cert v1.0.3 h1:/K2gjzLgVI8we2IIPKc0ztWTEa85uds5sWXi1K6mOT0=

188
macaroons.go Normal file
View file

@ -0,0 +1,188 @@
package pool
import (
"context"
"fmt"
"io/ioutil"
"os"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
)
const (
// poolMacaroonLocation is the value we use for the pool macaroons'
// "Location" field when baking them.
poolMacaroonLocation = "pool"
)
var (
// RequiredPermissions is a map of all pool RPC methods and their
// required macaroon permissions to access poold.
RequiredPermissions = map[string][]bakery.Op{
"/poolrpc.Trader/QuoteAccount": {{
Entity: "account",
Action: "read",
}},
"/poolrpc.Trader/InitAccount": {{
Entity: "account",
Action: "write",
}},
"/poolrpc.Trader/ListAccounts": {{
Entity: "account",
Action: "read",
}},
"/poolrpc.Trader/CloseAccount": {{
Entity: "account",
Action: "write",
}},
"/poolrpc.Trader/WithdrawAccount": {{
Entity: "account",
Action: "write",
}},
"/poolrpc.Trader/DepositAccount": {{
Entity: "account",
Action: "write",
}},
"/poolrpc.Trader/BumpAccountFee": {{
Entity: "account",
Action: "write",
}},
"/poolrpc.Trader/RecoverAccounts": {{
Entity: "account",
Action: "write",
}},
"/poolrpc.Trader/SubmitOrder": {{
Entity: "order",
Action: "write",
}},
"/poolrpc.Trader/ListOrders": {{
Entity: "order",
Action: "read",
}},
"/poolrpc.Trader/CancelOrder": {{
Entity: "order",
Action: "write",
}},
"/poolrpc.Trader/AuctionFee": {{
Entity: "auction",
Action: "read",
}},
"/poolrpc.Trader/BatchSnapshot": {{
Entity: "auction",
Action: "read",
}},
"/poolrpc.Trader/GetLsatTokens": {{
Entity: "auth",
Action: "read",
}},
}
// allPermissions is the list of all existing permissions that exist
// for poold'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 pool.
allPermissions = []bakery.Op{{
Entity: "account",
Action: "read",
}, {
Entity: "account",
Action: "write",
}, {
Entity: "order",
Action: "read",
}, {
Entity: "order",
Action: "write",
}, {
Entity: "auction",
Action: "read",
}, {
Entity: "auth",
Action: "read",
}}
// macDbDefaultPw is the default encryption password used to encrypt the
// pool 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
// keys to be encrypted on disk but won't provide any security at all as
// the password is known to anyone.
//
// TODO(guggero): Allow the password to be specified by the user. Needs
// create/unlock calls in the RPC. Using a password should be optional
// 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 (s *Server) startMacaroonService() error {
// Create the macaroon authentication/authorization service.
var err error
s.macaroonService, err = macaroons.NewService(
s.cfg.BaseDir, poolMacaroonLocation, 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 = s.macaroonService.CreateUnlock(&macDbDefaultPw)
if err != nil {
return fmt.Errorf("unable to unlock macaroon DB: %v", err)
}
// Create macaroon files for pool CLI to use if they don't exist.
if !lnrpc.FileExists(s.cfg.MacaroonPath) {
ctx := context.Background()
// 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.
poolMac, err := s.macaroonService.NewMacaroon(
ctx, macaroons.DefaultRootKeyID,
allPermissions...,
)
if err != nil {
return err
}
poolMacBytes, err := poolMac.M().MarshalBinary()
if err != nil {
return err
}
err = ioutil.WriteFile(s.cfg.MacaroonPath, poolMacBytes, 0644)
if err != nil {
if err := os.Remove(s.cfg.MacaroonPath); err != nil {
log.Errorf("Unable to remove %s: %v",
s.cfg.MacaroonPath, err)
}
return err
}
}
return nil
}
// stopMacaroonService closes the macaroon database.
func (s *Server) stopMacaroonService() error {
return s.macaroonService.Close()
}
// macaroonInterceptor creates macaroon security interceptors.
func (s *Server) macaroonInterceptor() (grpc.UnaryServerInterceptor,
grpc.StreamServerInterceptor) {
unaryInterceptor := s.macaroonService.UnaryServerInterceptor(
RequiredPermissions,
)
streamInterceptor := s.macaroonService.StreamServerInterceptor(
RequiredPermissions,
)
return unaryInterceptor, streamInterceptor
}

126
server.go
View file

@ -22,9 +22,11 @@ import (
"github.com/lightninglabs/pool/poolrpc"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/verrpc"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/signal"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"gopkg.in/macaroon-bakery.v2/bakery"
)
var (
@ -63,16 +65,17 @@ type Server struct {
// client or an error if none has been established yet.
GetIdentity func() (*lsat.TokenID, error)
cfg *Config
db *clientdb.DB
lsatStore *lsat.FileStore
lndServices *lndclient.GrpcLndServices
lndClient lnrpc.LightningClient
grpcServer *grpc.Server
restProxy *http.Server
grpcListener net.Listener
restListener net.Listener
wg sync.WaitGroup
cfg *Config
db *clientdb.DB
lsatStore *lsat.FileStore
lndServices *lndclient.GrpcLndServices
lndClient lnrpc.LightningClient
grpcServer *grpc.Server
restProxy *http.Server
grpcListener net.Listener
restListener net.Listener
macaroonService *macaroons.Service
wg sync.WaitGroup
}
// NewServer creates a new trader server.
@ -92,11 +95,28 @@ func (s *Server) Start() error {
// Print the version before executing either primary directive.
log.Infof("Version: %v", Version())
// Depending on how far we got in initializing the server, we might need
// to clean up certain services that were already started. Keep track of
// them with this map of service name to shutdown function.
shutdownFuncs := make(map[string]func() error)
defer func() {
for serviceName, shutdownFn := range shutdownFuncs {
if err := shutdownFn(); err != nil {
log.Errorf("Error shutting down %s service: %v",
serviceName, err)
}
}
}()
var err error
s.lndServices, err = getLnd(s.cfg.Network, s.cfg.Lnd)
if err != nil {
return err
}
shutdownFuncs["lnd"] = func() error { // nolint:unparam
s.lndServices.Close()
return nil
}
// As there're some other lower-level operations we may need access to,
// we'll also make a connection for a "basic client".
@ -111,11 +131,20 @@ func (s *Server) Start() error {
return err
}
// Start the macaroon service and let it create its default macaroon in
// case it doesn't exist yet.
if err := s.startMacaroonService(); err != nil {
return err
}
shutdownFuncs["macaroon"] = s.stopMacaroonService
// Setup the auctioneer client and interceptor.
err = s.setupClient()
if err != nil {
return err
}
shutdownFuncs["clientdb"] = s.db.Close
shutdownFuncs["auctioneer"] = s.AuctioneerClient.Stop
// Instantiate the trader gRPC server and start it.
s.rpcServer = newRPCServer(s)
@ -123,13 +152,19 @@ func (s *Server) Start() error {
if err != nil {
return err
}
shutdownFuncs["rpcServer"] = s.rpcServer.Stop
// Let's create our interceptor chain, starting with the security
// interceptors that will check macaroons for their validity.
unaryMacIntercept, streamMacIntercept := s.macaroonInterceptor()
serverOpts := []grpc.ServerOption{
grpc.StreamInterceptor(
grpc.ChainStreamInterceptor(
errorLogStreamServerInterceptor(rpcLog),
streamMacIntercept,
),
grpc.UnaryInterceptor(
grpc.ChainUnaryInterceptor(
errorLogUnaryServerInterceptor(rpcLog),
unaryMacIntercept,
),
}
s.grpcServer = grpc.NewServer(serverOpts...)
@ -155,7 +190,6 @@ func (s *Server) Start() error {
if err != nil {
return fmt.Errorf("RPC server unable to listen on %s",
s.cfg.RPCListen)
}
// We'll also create and start an accompanying proxy to serve
@ -196,6 +230,8 @@ func (s *Server) Start() error {
s.cfg.RESTListen)
}
s.restListener = tls.NewListener(s.restListener, serverTLSCfg)
shutdownFuncs["restListener"] = s.restListener.Close
s.restProxy = &http.Server{Handler: mux}
s.wg.Add(1)
go func() {
@ -209,6 +245,7 @@ func (s *Server) Start() error {
}()
}
s.grpcListener = tls.NewListener(s.grpcListener, serverTLSCfg)
shutdownFuncs["rpcListener"] = s.grpcListener.Close
// Start the grpc server.
s.wg.Add(1)
@ -232,7 +269,16 @@ func (s *Server) Start() error {
// The final thing we'll do on start up is sync the order state of the
// auctioneer with what we have on disk.
return s.syncLocalOrderState()
err = s.syncLocalOrderState()
if err != nil {
return err
}
// If we got here successfully, there's no need to shutdown anything
// anymore.
shutdownFuncs = nil
return nil
}
// StartAsSubserver is an alternative start method where the RPC server does not
@ -250,11 +296,33 @@ func (s *Server) StartAsSubserver(lndClient lnrpc.LightningClient,
// Print the version before executing either primary directive.
log.Infof("Version: %v", Version())
// Depending on how far we got in initializing the server, we might need
// to clean up certain services that were already started. Keep track of
// them with this map of service name to shutdown function.
shutdownFuncs := make(map[string]func() error)
defer func() {
for serviceName, shutdownFn := range shutdownFuncs {
if err := shutdownFn(); err != nil {
log.Errorf("Error shutting down %s service: %v",
serviceName, err)
}
}
}()
// Start the macaroon service and let it create its default macaroon in
// case it doesn't exist yet.
if err := s.startMacaroonService(); err != nil {
return err
}
shutdownFuncs["macaroon"] = s.stopMacaroonService
// Setup the auctioneer client and interceptor.
err := s.setupClient()
if err != nil {
return err
}
shutdownFuncs["clientdb"] = s.db.Close
shutdownFuncs["auctioneer"] = s.AuctioneerClient.Stop
// Instantiate the trader gRPC server and start it.
s.rpcServer = newRPCServer(s)
@ -262,10 +330,35 @@ func (s *Server) StartAsSubserver(lndClient lnrpc.LightningClient,
if err != nil {
return err
}
shutdownFuncs["rpcServer"] = s.rpcServer.Stop
// The final thing we'll do on start up is sync the order state of the
// auctioneer with what we have on disk.
return s.syncLocalOrderState()
err = s.syncLocalOrderState()
if err != nil {
return err
}
// If we got here successfully, there's no need to shutdown anything
// anymore.
shutdownFuncs = nil
return nil
}
// ValidateMacaroon extracts the macaroon from the context's gRPC metadata,
// checks its signature, makes sure all specified permissions for the called
// method are contained within and finally ensures all caveat conditions are
// met. A non-nil error is returned if any of the checks fail. This method is
// needed to enable poold running as an external subserver in the same process
// as lnd but still validate its own macaroons.
func (s *Server) ValidateMacaroon(ctx context.Context,
requiredPermissions []bakery.Op, fullMethod string) error {
// Delegate the call to pool's own macaroon validator service.
return s.macaroonService.ValidateMacaroon(
ctx, requiredPermissions, fullMethod,
)
}
// setupClient initializes the auctioneer client and its interceptors.
@ -395,6 +488,9 @@ func (s *Server) Stop() error {
if err := s.db.Close(); err != nil {
log.Errorf("Error closing DB: %v", err)
}
if err := s.macaroonService.Close(); err != nil {
log.Errorf("Error stopping macaroon service: %v", err)
}
s.lndServices.Close()
s.wg.Wait()