diff --git a/README.md b/README.md index 6388e51..c940e2f 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,10 @@ make && make install ``` ## Usage -Faraday connects to a single instance of lnd. It requires access to `lnd`'s `readonly.macaroon` and a valid TLS certificate. It will attempt to use the default `lnd` values if no command line flags are specified. +Faraday connects to a single instance of lnd. It requires access to `lnd`'s `admin.macaroon` and a valid TLS certificate. It will attempt to use the default `lnd` values if no command line flags are specified. ``` ./faraday \ ---lnd.macaroonpath={full path to lnd's readonly.macaroon} \ +--lnd.macaroonpath={full path to lnd's admin.macaroon} \ --lnd.tlscertpath={path to lnd cert} \ --lnd.rpcserver={host:port of lnd's rpcserver} ``` diff --git a/config.go b/config.go index afa0b92..f27787d 100644 --- a/config.go +++ b/config.go @@ -76,7 +76,7 @@ var ( // defaultLndMacaroon is the default macaroon file we use to connect to // lnd. - defaultLndMacaroon = "readonly.macaroon" + defaultLndMacaroon = "admin.macaroon" // DefaultLndDir is the default location where we look for lnd's tls and // macaroon files. @@ -103,7 +103,7 @@ type LndConfig struct { // all of lnd's macaroons. The specified macaroon MUST have all // permissions that all the subservers use, otherwise permission errors // will occur. - MacaroonPath string `long:"macaroonpath" description:"The full path to the single macaroon to use, either the readonly.macaroon or a custom baked one. Cannot be specified at the same time as macaroondir. A custom macaroon must contain ALL permissions required for all subservers to work, otherwise permission errors will occur."` + MacaroonPath string `long:"macaroonpath" description:"The full path to the single macaroon to use, either the admin.macaroon or a custom baked one. Cannot be specified at the same time as macaroondir. A custom macaroon must contain ALL permissions required for all subservers to work, otherwise permission errors will occur."` // TLSCertPath is the path to the tls cert that faraday should use. TLSCertPath string `long:"tlscertpath" description:"Path to TLS cert"` diff --git a/frdrpcserver/macaroons.go b/frdrpcserver/macaroons.go index b44b397..71627b1 100644 --- a/frdrpcserver/macaroons.go +++ b/frdrpcserver/macaroons.go @@ -1,21 +1,7 @@ package frdrpcserver import ( - "context" - "fmt" - "io/ioutil" - "os" "time" - - "github.com/lightningnetwork/lnd/kvdb" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/macaroons" - "github.com/lightningnetwork/lnd/rpcperms" - "go.etcd.io/bbolt" - "google.golang.org/grpc" - "gopkg.in/macaroon-bakery.v2/bakery" - - "github.com/lightninglabs/faraday/frdrpcserver/perms" ) const ( @@ -30,27 +16,6 @@ const ( var ( - // allPermissions is the list of all existing permissions that exist - // for faraday'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 faraday. - allPermissions = []bakery.Op{{ - Entity: "recommendation", - Action: "read", - }, { - Entity: "report", - Action: "read", - }, { - Entity: "audit", - Action: "read", - }, { - Entity: "insights", - Action: "read", - }, { - Entity: "rates", - Action: "read", - }} - // macDbDefaultPw is the default encryption password used to encrypt the // faraday 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 @@ -62,124 +27,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. -func (s *RPCServer) startMacaroonService(createDefaultMacaroonFile bool) error { - var err error - s.macaroonDB, err = kvdb.GetBoltBackend(&kvdb.BoltBackendConfig{ - DBPath: s.cfg.FaradayDir, - DBFileName: "macaroons.db", - DBTimeout: macDatabaseOpenTimeout, - }) - if err == bbolt.ErrTimeout { - return fmt.Errorf("error while trying to open %s/%s: "+ - "timed out after %v when trying to obtain exclusive "+ - "lock - make sure no other faraday daemon process "+ - "(standalone or embedded in lightning-terminal) is "+ - "running", s.cfg.FaradayDir, "macaroons.db", - macDatabaseOpenTimeout) - } - if err != nil { - return fmt.Errorf("unable to load macaroon db: %v", err) - } - - // Create the macaroon authentication/authorization service. - s.macaroonService, err = macaroons.NewService( - s.macaroonDB, faradayMacaroonLocation, 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 = s.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 faraday - // CLI in the default directory. - if createDefaultMacaroonFile && !lnrpc.FileExists(s.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. - faradayMac, err := s.macaroonService.Oven.NewMacaroon( - idCtx, bakery.LatestVersion, nil, allPermissions..., - ) - if err != nil { - return err - } - frdMacBytes, err := faradayMac.M().MarshalBinary() - if err != nil { - return err - } - err = ioutil.WriteFile(s.cfg.MacaroonPath, frdMacBytes, 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 *RPCServer) stopMacaroonService() error { - var shutdownErr error - if err := s.macaroonService.Close(); err != nil { - log.Errorf("Error closing macaroon service: %v", err) - shutdownErr = err - } - - if err := s.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 (s *RPCServer) macaroonInterceptor() ([]grpc.ServerOption, error) { - interceptor := rpcperms.NewInterceptorChain(log, false, nil) - - err := interceptor.Start() - if err != nil { - return nil, err - } - - interceptor.SetWalletUnlocked() - interceptor.AddMacaroonService(s.macaroonService) - - for method, permissions := range perms.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/frdrpcserver/rpcserver.go b/frdrpcserver/rpcserver.go index 1d7dace..3072e9b 100644 --- a/frdrpcserver/rpcserver.go +++ b/frdrpcserver/rpcserver.go @@ -22,7 +22,6 @@ import ( proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightninglabs/lndclient" - "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/macaroons" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -33,6 +32,7 @@ import ( "github.com/lightninglabs/faraday/chain" "github.com/lightninglabs/faraday/fiat" "github.com/lightninglabs/faraday/frdrpc" + "github.com/lightninglabs/faraday/frdrpcserver/perms" "github.com/lightninglabs/faraday/recommend" "github.com/lightninglabs/faraday/resolutions" "github.com/lightninglabs/faraday/revenue" @@ -107,8 +107,7 @@ type RPCServer struct { // restServer is the REST proxy server. restServer *http.Server - macaroonService *macaroons.Service - macaroonDB kvdb.Backend + macaroonService *lndclient.MacaroonService restCancel func() wg sync.WaitGroup @@ -181,16 +180,38 @@ func (s *RPCServer) Start() error { } }() + // Set up the macaroon service. + var err error + s.macaroonService, err = lndclient.NewMacaroonService( + &lndclient.MacaroonServiceConfig{ + DBPath: s.cfg.FaradayDir, + DBFileName: "macaroons.db", + DBTimeout: macDatabaseOpenTimeout, + MacaroonLocation: faradayMacaroonLocation, + MacaroonPath: s.cfg.MacaroonPath, + Checkers: []macaroons.Checker{ + macaroons.IPLockChecker, + }, + RequiredPerms: perms.RequiredPermissions, + DBPassword: macDbDefaultPw, + LndClient: &s.cfg.Lnd, + EphemeralKey: lndclient.SharedKeyNUMS, + KeyLocator: lndclient.SharedKeyLocator, + }) + if err != nil { + return fmt.Errorf("error creating macroon service: %v", err) + } + // Start the macaroon service and let it create its default macaroon in // case it doesn't exist yet. - if err := s.startMacaroonService(true); err != nil { + if err := s.macaroonService.Start(); err != nil { return fmt.Errorf("error starting macaroon service: %v", err) } - shutdownFuncs["macaroon"] = s.stopMacaroonService + shutdownFuncs["macaroon"] = s.macaroonService.Stop // First we add the security interceptor to our gRPC server options that // checks the macaroons for validity. - serverOpts, err := s.macaroonInterceptor() + unaryInterceptor, streamInterceptor, err := s.macaroonService.Interceptors() if err != nil { return fmt.Errorf("error with macaroon interceptor: %v", err) } @@ -200,8 +221,11 @@ func (s *RPCServer) Start() error { // use tls.NewListener(). Otherwise we run into the ALPN error with non- // golang clients. tlsCredentials := credentials.NewTLS(s.cfg.TLSServerConfig) - serverOpts = append(serverOpts, grpc.Creds(tlsCredentials)) - s.grpcServer = grpc.NewServer(serverOpts...) + s.grpcServer = grpc.NewServer( + grpc.UnaryInterceptor(unaryInterceptor), + grpc.StreamInterceptor(streamInterceptor), + grpc.Creds(tlsCredentials), + ) // Start the gRPC RPCServer listening for HTTP/2 connections. log.Info("Starting gRPC listener") @@ -303,16 +327,18 @@ func (s *RPCServer) Start() error { // for REST (if enabled), instead of creating an own mux and HTTP server, we // register to an existing one. func (s *RPCServer) StartAsSubserver(lndClient lndclient.LndServices, - createDefaultMacaroonFile bool) error { + withMacaroonService bool) error { if atomic.AddInt32(&s.started, 1) != 1 { return errServerAlreadyStarted } - // Start the macaroon service and let it create its default macaroon in - // case it doesn't exist yet. - if err := s.startMacaroonService(createDefaultMacaroonFile); err != nil { - return fmt.Errorf("error starting macaroon service: %v", err) + if withMacaroonService { + // Start the macaroon service and let it create its default + // macaroon in case it doesn't exist yet. + if err := s.macaroonService.Start(); err != nil { + return fmt.Errorf("error starting macaroon service: %v", err) + } } s.cfg.Lnd = lndClient @@ -328,6 +354,10 @@ func (s *RPCServer) StartAsSubserver(lndClient lndclient.LndServices, func (s *RPCServer) ValidateMacaroon(ctx context.Context, requiredPermissions []bakery.Op, fullMethod string) error { + if s.macaroonService == nil { + return fmt.Errorf("macaroon service not yet initialised") + } + // Delegate the call to faraday's own macaroon validator service. return s.macaroonService.ValidateMacaroon( ctx, requiredPermissions, fullMethod, @@ -348,8 +378,10 @@ func (s *RPCServer) Stop() error { } } - if err := s.stopMacaroonService(); err != nil { - log.Errorf("Error stopping macaroon service: %v", err) + if s.macaroonService != nil { + if err := s.macaroonService.Stop(); err != nil { + log.Errorf("Error stopping macaroon service: %v", err) + } } // Stop the grpc server and wait for all go routines to terminate. diff --git a/go.mod b/go.mod index b2c026c..16c5f29 100644 --- a/go.mod +++ b/go.mod @@ -10,11 +10,9 @@ require ( github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display github.com/lightningnetwork/lnd v0.14.1-beta.0.20220324135938-0dcaa511a249 github.com/lightningnetwork/lnd/cert v1.1.1 - github.com/lightningnetwork/lnd/kvdb v1.3.1 github.com/shopspring/decimal v1.2.0 github.com/stretchr/testify v1.7.0 github.com/urfave/cli v1.22.4 - go.etcd.io/bbolt v1.3.6 google.golang.org/grpc v1.38.0 google.golang.org/protobuf v1.26.0 gopkg.in/macaroon-bakery.v2 v2.0.1 diff --git a/itest/test_context.go b/itest/test_context.go index 11762d7..98b690d 100644 --- a/itest/test_context.go +++ b/itest/test_context.go @@ -39,7 +39,7 @@ var ( faradayArgs = []string{ "--rpclisten=localhost:8465", "--network=regtest", - "--lnd.macaroonpath=lnd-alice/data/chain/bitcoin/regtest/readonly.macaroon", + "--lnd.macaroonpath=lnd-alice/data/chain/bitcoin/regtest/admin.macaroon", "--lnd.tlscertpath=lnd-alice/tls.cert", "--debuglevel=debug", "--connect_bitcoin",