mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-16 13:00:39 +02:00
Merge pull request #78 from guggero/macaroons
Add macaroon authentication
This commit is contained in:
commit
7c7dfd0c36
7 changed files with 375 additions and 50 deletions
17
README.md
17
README.md
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
33
config.go
33
config.go
|
|
@ -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
7
go.mod
|
|
@ -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
6
go.sum
|
|
@ -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
188
macaroons.go
Normal 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
126
server.go
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue