Merge pull request #904 from Roasbeef/invoice-macaroon
rpc+lnd: add new invoice-only macaroon
This commit is contained in:
commit
7d14ed7a06
55
config.go
55
config.go
@ -35,6 +35,7 @@ const (
|
|||||||
defaultTLSKeyFilename = "tls.key"
|
defaultTLSKeyFilename = "tls.key"
|
||||||
defaultAdminMacFilename = "admin.macaroon"
|
defaultAdminMacFilename = "admin.macaroon"
|
||||||
defaultReadMacFilename = "readonly.macaroon"
|
defaultReadMacFilename = "readonly.macaroon"
|
||||||
|
defaultInvoiceMacFilename = "invoice.macaroon"
|
||||||
defaultLogLevel = "info"
|
defaultLogLevel = "info"
|
||||||
defaultLogDirname = "logs"
|
defaultLogDirname = "logs"
|
||||||
defaultLogFilename = "lnd.log"
|
defaultLogFilename = "lnd.log"
|
||||||
@ -57,14 +58,17 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultLndDir = btcutil.AppDataDir("lnd", false)
|
defaultLndDir = btcutil.AppDataDir("lnd", false)
|
||||||
defaultConfigFile = filepath.Join(defaultLndDir, defaultConfigFilename)
|
defaultConfigFile = filepath.Join(defaultLndDir, defaultConfigFilename)
|
||||||
defaultDataDir = filepath.Join(defaultLndDir, defaultDataDirname)
|
defaultDataDir = filepath.Join(defaultLndDir, defaultDataDirname)
|
||||||
defaultTLSCertPath = filepath.Join(defaultLndDir, defaultTLSCertFilename)
|
defaultLogDir = filepath.Join(defaultLndDir, defaultLogDirname)
|
||||||
defaultTLSKeyPath = filepath.Join(defaultLndDir, defaultTLSKeyFilename)
|
|
||||||
defaultAdminMacPath = filepath.Join(defaultLndDir, defaultAdminMacFilename)
|
defaultTLSCertPath = filepath.Join(defaultLndDir, defaultTLSCertFilename)
|
||||||
defaultReadMacPath = filepath.Join(defaultLndDir, defaultReadMacFilename)
|
defaultTLSKeyPath = filepath.Join(defaultLndDir, defaultTLSKeyFilename)
|
||||||
defaultLogDir = filepath.Join(defaultLndDir, defaultLogDirname)
|
|
||||||
|
defaultAdminMacPath = filepath.Join(defaultLndDir, defaultAdminMacFilename)
|
||||||
|
defaultReadMacPath = filepath.Join(defaultLndDir, defaultReadMacFilename)
|
||||||
|
defaultInvoiceMacPath = filepath.Join(defaultLndDir, defaultInvoiceMacFilename)
|
||||||
|
|
||||||
defaultBtcdDir = btcutil.AppDataDir("btcd", false)
|
defaultBtcdDir = btcutil.AppDataDir("btcd", false)
|
||||||
defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
|
defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
|
||||||
@ -151,6 +155,7 @@ type config struct {
|
|||||||
NoMacaroons bool `long:"no-macaroons" description:"Disable macaroon authentication"`
|
NoMacaroons bool `long:"no-macaroons" description:"Disable macaroon authentication"`
|
||||||
AdminMacPath string `long:"adminmacaroonpath" description:"Path to write the admin macaroon for lnd's RPC and REST services if it doesn't exist"`
|
AdminMacPath string `long:"adminmacaroonpath" description:"Path to write the admin macaroon for lnd's RPC and REST services if it doesn't exist"`
|
||||||
ReadMacPath string `long:"readonlymacaroonpath" description:"Path to write the read-only macaroon for lnd's RPC and REST services if it doesn't exist"`
|
ReadMacPath string `long:"readonlymacaroonpath" description:"Path to write the read-only macaroon for lnd's RPC and REST services if it doesn't exist"`
|
||||||
|
InvoiceMacPath string `long:"invoicemacaroonpath" description:"Path to the invoice-only macaroon for lnd's RPC and REST services if it doesn't exist"`
|
||||||
LogDir string `long:"logdir" description:"Directory to log output."`
|
LogDir string `long:"logdir" description:"Directory to log output."`
|
||||||
|
|
||||||
RPCListeners []string `long:"rpclisten" description:"Add an interface/port to listen for RPC connections"`
|
RPCListeners []string `long:"rpclisten" description:"Add an interface/port to listen for RPC connections"`
|
||||||
@ -206,15 +211,16 @@ type config struct {
|
|||||||
// 4) Parse CLI options and overwrite/add any specified options
|
// 4) Parse CLI options and overwrite/add any specified options
|
||||||
func loadConfig() (*config, error) {
|
func loadConfig() (*config, error) {
|
||||||
defaultCfg := config{
|
defaultCfg := config{
|
||||||
LndDir: defaultLndDir,
|
LndDir: defaultLndDir,
|
||||||
ConfigFile: defaultConfigFile,
|
ConfigFile: defaultConfigFile,
|
||||||
DataDir: defaultDataDir,
|
DataDir: defaultDataDir,
|
||||||
DebugLevel: defaultLogLevel,
|
DebugLevel: defaultLogLevel,
|
||||||
TLSCertPath: defaultTLSCertPath,
|
TLSCertPath: defaultTLSCertPath,
|
||||||
TLSKeyPath: defaultTLSKeyPath,
|
TLSKeyPath: defaultTLSKeyPath,
|
||||||
AdminMacPath: defaultAdminMacPath,
|
AdminMacPath: defaultAdminMacPath,
|
||||||
ReadMacPath: defaultReadMacPath,
|
InvoiceMacPath: defaultInvoiceMacPath,
|
||||||
LogDir: defaultLogDir,
|
ReadMacPath: defaultReadMacPath,
|
||||||
|
LogDir: defaultLogDir,
|
||||||
Bitcoin: &chainConfig{
|
Bitcoin: &chainConfig{
|
||||||
MinHTLC: defaultBitcoinMinHTLCMSat,
|
MinHTLC: defaultBitcoinMinHTLCMSat,
|
||||||
BaseFee: defaultBitcoinBaseFeeMSat,
|
BaseFee: defaultBitcoinBaseFeeMSat,
|
||||||
@ -285,6 +291,7 @@ func loadConfig() (*config, error) {
|
|||||||
defaultCfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
|
defaultCfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
|
||||||
defaultCfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
|
defaultCfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
|
||||||
defaultCfg.AdminMacPath = filepath.Join(lndDir, defaultAdminMacFilename)
|
defaultCfg.AdminMacPath = filepath.Join(lndDir, defaultAdminMacFilename)
|
||||||
|
defaultCfg.InvoiceMacPath = filepath.Join(lndDir, defaultInvoiceMacFilename)
|
||||||
defaultCfg.ReadMacPath = filepath.Join(lndDir, defaultReadMacFilename)
|
defaultCfg.ReadMacPath = filepath.Join(lndDir, defaultReadMacFilename)
|
||||||
defaultCfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
|
defaultCfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
|
||||||
}
|
}
|
||||||
@ -330,6 +337,7 @@ func loadConfig() (*config, error) {
|
|||||||
cfg.TLSKeyPath = cleanAndExpandPath(cfg.TLSKeyPath)
|
cfg.TLSKeyPath = cleanAndExpandPath(cfg.TLSKeyPath)
|
||||||
cfg.AdminMacPath = cleanAndExpandPath(cfg.AdminMacPath)
|
cfg.AdminMacPath = cleanAndExpandPath(cfg.AdminMacPath)
|
||||||
cfg.ReadMacPath = cleanAndExpandPath(cfg.ReadMacPath)
|
cfg.ReadMacPath = cleanAndExpandPath(cfg.ReadMacPath)
|
||||||
|
cfg.InvoiceMacPath = cleanAndExpandPath(cfg.InvoiceMacPath)
|
||||||
cfg.LogDir = cleanAndExpandPath(cfg.LogDir)
|
cfg.LogDir = cleanAndExpandPath(cfg.LogDir)
|
||||||
cfg.BtcdMode.Dir = cleanAndExpandPath(cfg.BtcdMode.Dir)
|
cfg.BtcdMode.Dir = cleanAndExpandPath(cfg.BtcdMode.Dir)
|
||||||
cfg.LtcdMode.Dir = cleanAndExpandPath(cfg.LtcdMode.Dir)
|
cfg.LtcdMode.Dir = cleanAndExpandPath(cfg.LtcdMode.Dir)
|
||||||
@ -630,10 +638,19 @@ func loadConfig() (*config, error) {
|
|||||||
// directory has changed from the default path, then we'll also update
|
// directory has changed from the default path, then we'll also update
|
||||||
// the path for the macaroons to be generated.
|
// the path for the macaroons to be generated.
|
||||||
if cfg.DataDir != defaultDataDir && cfg.AdminMacPath == defaultAdminMacPath {
|
if cfg.DataDir != defaultDataDir && cfg.AdminMacPath == defaultAdminMacPath {
|
||||||
cfg.AdminMacPath = filepath.Join(cfg.DataDir, defaultAdminMacFilename)
|
cfg.AdminMacPath = filepath.Join(
|
||||||
|
cfg.DataDir, defaultAdminMacFilename,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if cfg.DataDir != defaultDataDir && cfg.ReadMacPath == defaultReadMacPath {
|
if cfg.DataDir != defaultDataDir && cfg.ReadMacPath == defaultReadMacPath {
|
||||||
cfg.ReadMacPath = filepath.Join(cfg.DataDir, defaultReadMacFilename)
|
cfg.ReadMacPath = filepath.Join(
|
||||||
|
cfg.DataDir, defaultReadMacFilename,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if cfg.DataDir != defaultDataDir && cfg.InvoiceMacPath == defaultInvoiceMacPath {
|
||||||
|
cfg.InvoiceMacPath = filepath.Join(
|
||||||
|
cfg.DataDir, defaultInvoiceMacPath,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the network type to the log directory so it is "namespaced"
|
// Append the network type to the log directory so it is "namespaced"
|
||||||
|
46
lnd.go
46
lnd.go
@ -228,10 +228,15 @@ func lndMain() error {
|
|||||||
srvrLog.Error(err)
|
srvrLog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create macaroon files for lncli to use if they don't exist.
|
// Create macaroon files for lncli to use if they don't exist.
|
||||||
if !fileExists(cfg.AdminMacPath) && !fileExists(cfg.ReadMacPath) {
|
if !fileExists(cfg.AdminMacPath) && !fileExists(cfg.ReadMacPath) &&
|
||||||
err = genMacaroons(ctx, macaroonService,
|
!fileExists(cfg.InvoiceMacPath) {
|
||||||
cfg.AdminMacPath, cfg.ReadMacPath)
|
|
||||||
|
err = genMacaroons(
|
||||||
|
ctx, macaroonService, cfg.AdminMacPath,
|
||||||
|
cfg.ReadMacPath, cfg.InvoiceMacPath,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ltndLog.Errorf("unable to create macaroon "+
|
ltndLog.Errorf("unable to create macaroon "+
|
||||||
"files: %v", err)
|
"files: %v", err)
|
||||||
@ -751,12 +756,33 @@ func genCertPair(certFile, keyFile string) error {
|
|||||||
|
|
||||||
// genMacaroons generates a pair of macaroon files; one admin-level and one
|
// genMacaroons generates a pair of macaroon files; one admin-level and one
|
||||||
// read-only. These can also be used to generate more granular macaroons.
|
// read-only. These can also be used to generate more granular macaroons.
|
||||||
func genMacaroons(ctx context.Context, svc *macaroons.Service, admFile,
|
func genMacaroons(ctx context.Context, svc *macaroons.Service,
|
||||||
roFile string) error {
|
admFile, roFile, invoiceFile string) error {
|
||||||
|
|
||||||
|
// First, we'll generate a macaroon that only allows the caller to
|
||||||
|
// access invoice related calls. This is useful for merchants and other
|
||||||
|
// services to allow an isolated instance that can only query and
|
||||||
|
// modify invoices.
|
||||||
|
invoiceMac, err := svc.Oven.NewMacaroon(
|
||||||
|
ctx, bakery.LatestVersion, nil, invoicePermissions...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
invoiceMacBytes, err := invoiceMac.M().MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(invoiceFile, invoiceMacBytes, 0644)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(invoiceFile)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the read-only macaroon and write it to a file.
|
// Generate the read-only macaroon and write it to a file.
|
||||||
roMacaroon, err := svc.Oven.NewMacaroon(ctx, bakery.LatestVersion, nil,
|
roMacaroon, err := svc.Oven.NewMacaroon(
|
||||||
readPermissions...)
|
ctx, bakery.LatestVersion, nil, readPermissions...,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -770,8 +796,10 @@ func genMacaroons(ctx context.Context, svc *macaroons.Service, admFile,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate the admin macaroon and write it to a file.
|
// Generate the admin macaroon and write it to a file.
|
||||||
admMacaroon, err := svc.Oven.NewMacaroon(ctx, bakery.LatestVersion,
|
adminPermissions := append(readPermissions, writePermissions...)
|
||||||
nil, append(readPermissions, writePermissions...)...)
|
admMacaroon, err := svc.Oven.NewMacaroon(
|
||||||
|
ctx, bakery.LatestVersion, nil, adminPermissions...,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
38
rpcserver.go
38
rpcserver.go
@ -69,6 +69,10 @@ var (
|
|||||||
Entity: "info",
|
Entity: "info",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Entity: "invoices",
|
||||||
|
Action: "read",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// writePermissions is a slice of all entities that allow write
|
// writePermissions is a slice of all entities that allow write
|
||||||
@ -98,6 +102,32 @@ var (
|
|||||||
Entity: "info",
|
Entity: "info",
|
||||||
Action: "write",
|
Action: "write",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Entity: "invoices",
|
||||||
|
Action: "write",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoicePermissions is a slice of all the entities that allows a user
|
||||||
|
// to only access calls that are related to invoices, so: streaming
|
||||||
|
// RPC's, generating, and listening invoices.
|
||||||
|
invoicePermissions = []bakery.Op{
|
||||||
|
{
|
||||||
|
Entity: "invoices",
|
||||||
|
Action: "read",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Entity: "invoices",
|
||||||
|
Action: "write",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Entity: "address",
|
||||||
|
Action: "read",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Entity: "address",
|
||||||
|
Action: "write",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// permissions maps RPC calls to the permissions they require.
|
// permissions maps RPC calls to the permissions they require.
|
||||||
@ -188,19 +218,19 @@ var (
|
|||||||
Action: "write",
|
Action: "write",
|
||||||
}},
|
}},
|
||||||
"/lnrpc.Lightning/AddInvoice": {{
|
"/lnrpc.Lightning/AddInvoice": {{
|
||||||
Entity: "offchain",
|
Entity: "invoices",
|
||||||
Action: "write",
|
Action: "write",
|
||||||
}},
|
}},
|
||||||
"/lnrpc.Lightning/LookupInvoice": {{
|
"/lnrpc.Lightning/LookupInvoice": {{
|
||||||
Entity: "offchain",
|
Entity: "invoices",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
}},
|
}},
|
||||||
"/lnrpc.Lightning/ListInvoices": {{
|
"/lnrpc.Lightning/ListInvoices": {{
|
||||||
Entity: "offchain",
|
Entity: "invoices",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
}},
|
}},
|
||||||
"/lnrpc.Lightning/SubscribeInvoices": {{
|
"/lnrpc.Lightning/SubscribeInvoices": {{
|
||||||
Entity: "offchain",
|
Entity: "invoices",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
}},
|
}},
|
||||||
"/lnrpc.Lightning/SubscribeTransactions": {{
|
"/lnrpc.Lightning/SubscribeTransactions": {{
|
||||||
|
Loading…
Reference in New Issue
Block a user