multi: upgrade macaroons to v2, replace per-method auth with interceptors

This commit reworks the macaroon authentication framework to use the
v2 macaroon format and bakery API. It also replaces the code in each
RPC method which calls the macaroon verifier with interceptors which
call the macaroon verifier instead. In addition, the operation
permissions are reworked to fit the new format of "allow" commands
(specifically, entity/operation permissions instead of method
permissions).
This commit is contained in:
Alex 2018-01-16 09:18:41 -07:00 committed by Olaoluwa Osuntokun
parent 20098e8cb3
commit 21c29c33d7
11 changed files with 427 additions and 514 deletions

View File

@ -11,7 +11,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"gopkg.in/macaroon.v1" macaroon "gopkg.in/macaroon.v2"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"

12
glide.lock generated
View File

@ -1,5 +1,5 @@
hash: 70e2030c50a730ad0ad428176f12851f35178e31e262bae82cedd89579a14b40 hash: f72fdbb4d93ddd98991e830f978bfe9e08623e2a9976857b0f7668641486369c
updated: 2018-01-24T15:24:09.346726778-08:00 updated: 2018-01-29T15:55:37.905498329-07:00
imports: imports:
- name: github.com/aead/chacha20 - name: github.com/aead/chacha20
version: d31a916ded42d1640b9d89a26f8abd53cc96790c version: d31a916ded42d1640b9d89a26f8abd53cc96790c
@ -204,11 +204,11 @@ imports:
- transport - transport
- name: gopkg.in/errgo.v1 - name: gopkg.in/errgo.v1
version: 442357a80af5c6bf9b6d51ae791a39c3421004f3 version: 442357a80af5c6bf9b6d51ae791a39c3421004f3
- name: gopkg.in/macaroon-bakery.v1 - name: gopkg.in/macaroon-bakery.v2
version: 8e14f8b0f5e286ad100ca8d6ce5bde0f0dfb8b21 version: 04cf5ef151a211d929975161aea7c65f94c90446
subpackages: subpackages:
- bakery - bakery
- bakery/checkers - bakery/checkers
- name: gopkg.in/macaroon.v1 - name: gopkg.in/macaroon.v2
version: ab101776739ee61baab9bc50e4b33b5aeb3ac843 version: bed2a428da6e56d950bed5b41fcbae3141e5b0d0
testImports: [] testImports: []

View File

@ -74,8 +74,8 @@ import:
- chaincfg - chaincfg
- package: github.com/lightninglabs/neutrino - package: github.com/lightninglabs/neutrino
version: 6c8f30a130bb170348ceb754ce4204156efe2741 version: 6c8f30a130bb170348ceb754ce4204156efe2741
- package: gopkg.in/macaroon.v1 - package: gopkg.in/macaroon.v2
- package: gopkg.in/macaroon-bakery.v1 - package: gopkg.in/macaroon-bakery.v2
- package: github.com/juju/loggo - package: github.com/juju/loggo
- package: github.com/rogpeppe/fastuuid - package: github.com/rogpeppe/fastuuid
- package: gopkg.in/errgo.v1 - package: gopkg.in/errgo.v1

68
lnd.go
View File

@ -24,7 +24,7 @@ import (
"sync" "sync"
"time" "time"
"gopkg.in/macaroon-bakery.v1/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -143,10 +143,15 @@ func lndMain() error {
defer chanDB.Close() defer chanDB.Close()
// Only process macaroons if --no-macaroons isn't set. // Only process macaroons if --no-macaroons isn't set.
var macaroonService *bakery.Service ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
var macaroonService *bakery.Bakery
if !cfg.NoMacaroons { if !cfg.NoMacaroons {
// Create the macaroon authentication/authorization service. // Create the macaroon authentication/authorization service.
macaroonService, err = macaroons.NewService(macaroonDatabaseDir) macaroonService, err = macaroons.NewService(macaroonDatabaseDir,
macaroons.IPLockChecker)
if err != nil { if err != nil {
srvrLog.Errorf("unable to create macaroon service: %v", err) srvrLog.Errorf("unable to create macaroon service: %v", err)
return err return err
@ -154,8 +159,8 @@ func lndMain() error {
// 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(macaroonService, cfg.AdminMacPath, err = genMacaroons(ctx, macaroonService,
cfg.ReadMacPath) cfg.AdminMacPath, cfg.ReadMacPath)
if err != nil { if err != nil {
ltndLog.Errorf("unable to create macaroon "+ ltndLog.Errorf("unable to create macaroon "+
"files: %v", err) "files: %v", err)
@ -368,9 +373,19 @@ func lndMain() error {
} }
server.fundingMgr = fundingMgr server.fundingMgr = fundingMgr
// Check macaroon authentication if macaroons aren't disabled.
if macaroonService != nil {
serverOpts = append(serverOpts,
grpc.UnaryInterceptor(macaroons.UnaryServerInterceptor(
macaroonService, permissions)),
grpc.StreamInterceptor(macaroons.StreamServerInterceptor(
macaroonService, permissions)),
)
}
// Initialize, and register our implementation of the gRPC interface // Initialize, and register our implementation of the gRPC interface
// exported by the rpcServer. // exported by the rpcServer.
rpcServer := newRPCServer(server, macaroonService) rpcServer := newRPCServer(server)
if err := rpcServer.Start(); err != nil { if err := rpcServer.Start(); err != nil {
return err return err
} }
@ -393,10 +408,6 @@ func lndMain() error {
} }
// Finally, start the REST proxy for our gRPC server above. // Finally, start the REST proxy for our gRPC server above.
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := proxy.NewServeMux() mux := proxy.NewServeMux()
err = lnrpc.RegisterLightningHandlerFromEndpoint(ctx, mux, err = lnrpc.RegisterLightningHandlerFromEndpoint(ctx, mux,
cfg.RPCListeners[0], proxyOpts) cfg.RPCListeners[0], proxyOpts)
@ -648,27 +659,16 @@ 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(svc *bakery.Service, admFile, roFile string) error { func genMacaroons(ctx context.Context, svc *bakery.Bakery, admFile,
// Generate the admin macaroon and write it to a file. roFile string) error {
admMacaroon, err := svc.NewMacaroon("", nil, nil)
if err != nil {
return err
}
admBytes, err := admMacaroon.MarshalBinary()
if err != nil {
return err
}
if err = ioutil.WriteFile(admFile, admBytes, 0600); err != nil {
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 := macaroons.AddConstraints(admMacaroon, roMacaroon, err := svc.Oven.NewMacaroon(ctx, bakery.LatestVersion, nil,
macaroons.AllowConstraint(roPermissions...)) readPermissions...)
if err != nil { if err != nil {
return err return err
} }
roBytes, err := roMacaroon.MarshalBinary() roBytes, err := roMacaroon.M().MarshalBinary()
if err != nil { if err != nil {
return err return err
} }
@ -677,6 +677,20 @@ func genMacaroons(svc *bakery.Service, admFile, roFile string) error {
return err return err
} }
// Generate the admin macaroon and write it to a file.
admMacaroon, err := svc.Oven.NewMacaroon(ctx, bakery.LatestVersion,
nil, append(readPermissions, writePermissions...)...)
if err != nil {
return err
}
admBytes, err := admMacaroon.M().MarshalBinary()
if err != nil {
return err
}
if err = ioutil.WriteFile(admFile, admBytes, 0600); err != nil {
return err
}
return nil return nil
} }
@ -685,7 +699,7 @@ func genMacaroons(svc *bakery.Service, admFile, roFile string) error {
// the user to this RPC server. // the user to this RPC server.
func waitForWalletPassword(grpcEndpoints, restEndpoints []string, func waitForWalletPassword(grpcEndpoints, restEndpoints []string,
serverOpts []grpc.ServerOption, proxyOpts []grpc.DialOption, serverOpts []grpc.ServerOption, proxyOpts []grpc.DialOption,
tlsConf *tls.Config, macaroonService *bakery.Service) ([]byte, []byte, error) { tlsConf *tls.Config, macaroonService *bakery.Bakery) ([]byte, []byte, error) {
// Set up a new PasswordService, which will listen // Set up a new PasswordService, which will listen
// for passwords provided over RPC. // for passwords provided over RPC.

View File

@ -18,7 +18,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
macaroon "gopkg.in/macaroon.v1" macaroon "gopkg.in/macaroon.v2"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"

View File

@ -3,15 +3,13 @@ package macaroons
import ( import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"net"
"google.golang.org/grpc/metadata"
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"gopkg.in/macaroon-bakery.v1/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon-bakery.v1/bakery/checkers" macaroon "gopkg.in/macaroon.v2"
macaroon "gopkg.in/macaroon.v1"
) )
// MacaroonCredential wraps a macaroon to implement the // MacaroonCredential wraps a macaroon to implement the
@ -54,13 +52,11 @@ func NewMacaroonCredential(m *macaroon.Macaroon) MacaroonCredential {
// bakery service, context, and uri. Within the passed context.Context, we // bakery service, context, and uri. Within the passed context.Context, we
// expect a macaroon to be encoded as request metadata using the key // expect a macaroon to be encoded as request metadata using the key
// "macaroon". // "macaroon".
func ValidateMacaroon(ctx context.Context, method string, func ValidateMacaroon(ctx context.Context, requiredPermissions []bakery.Op,
svc *bakery.Service) error { svc *bakery.Bakery) error {
// Get macaroon bytes from context and unmarshal into macaroon. // Get macaroon bytes from context and unmarshal into macaroon.
// md, ok := metadata.FromIncomingContext(ctx)
// TODO(aakselrod): use FromIncomingContext after grpc update in glide.
md, ok := metadata.FromContext(ctx)
if !ok { if !ok {
return fmt.Errorf("unable to get metadata from context") return fmt.Errorf("unable to get metadata from context")
} }
@ -69,16 +65,6 @@ func ValidateMacaroon(ctx context.Context, method string,
len(md["macaroon"])) len(md["macaroon"]))
} }
// Get peer info and extract IP address from it for macaroon check
pr, ok := peer.FromContext(ctx)
if !ok {
return fmt.Errorf("unable to get peer info from context")
}
peerAddr, _, err := net.SplitHostPort(pr.Addr.String())
if err != nil {
return fmt.Errorf("unable to parse peer address")
}
// With the macaroon obtained, we'll now decode the hex-string // With the macaroon obtained, we'll now decode the hex-string
// encoding, then unmarshal it from binary into its concrete struct // encoding, then unmarshal it from binary into its concrete struct
// representation. // representation.
@ -93,12 +79,8 @@ func ValidateMacaroon(ctx context.Context, method string,
} }
// Check the method being called against the permitted operation and // Check the method being called against the permitted operation and
// the expiration time and return the result. // the expiration time and IP address and return the result.
// authChecker := svc.Checker.Auth(macaroon.Slice{mac})
// TODO(aakselrod): Add more checks as required. _, err = authChecker.Allow(ctx, requiredPermissions...)
return svc.Check(macaroon.Slice{mac}, checkers.New( return err
AllowChecker(method),
TimeoutChecker(),
IPLockChecker(peerAddr),
))
} }

View File

@ -5,14 +5,22 @@ import (
"net" "net"
"time" "time"
"gopkg.in/macaroon-bakery.v1/bakery/checkers" "google.golang.org/grpc/peer"
macaroon "gopkg.in/macaroon.v1"
"gopkg.in/macaroon-bakery.v2/bakery/checkers"
macaroon "gopkg.in/macaroon.v2"
"golang.org/x/net/context"
) )
// Constraint type adds a layer of indirection over macaroon caveats and // Constraint type adds a layer of indirection over macaroon caveats.
// checkers.
type Constraint func(*macaroon.Macaroon) error type Constraint func(*macaroon.Macaroon) error
// Checker type adds a layer of indirection over macaroon checkers. A Checker
// returns the name of the checker and the checker function; these are used to
// register the function with the bakery service's compound checker.
type Checker func() (string, checkers.Func)
// AddConstraints returns new derived macaroon by applying every passed // AddConstraints returns new derived macaroon by applying every passed
// constraint and tightening its restrictions. // constraint and tightening its restrictions.
func AddConstraints(mac *macaroon.Macaroon, cs ...Constraint) (*macaroon.Macaroon, error) { func AddConstraints(mac *macaroon.Macaroon, cs ...Constraint) (*macaroon.Macaroon, error) {
@ -27,21 +35,7 @@ func AddConstraints(mac *macaroon.Macaroon, cs ...Constraint) (*macaroon.Macaroo
// Each *Constraint function is a functional option, which takes a pointer // Each *Constraint function is a functional option, which takes a pointer
// to the macaroon and adds another restriction to it. For each *Constraint, // to the macaroon and adds another restriction to it. For each *Constraint,
// the corresponding *Checker is provided. // the corresponding *Checker is provided if not provided by default.
// AllowConstraint restricts allowed operations set to the ones
// passed to it.
func AllowConstraint(ops ...string) func(*macaroon.Macaroon) error {
return func(mac *macaroon.Macaroon) error {
caveat := checkers.AllowCaveat(ops...)
return mac.AddFirstPartyCaveat(caveat.Condition)
}
}
// AllowChecker wraps default checkers.OperationChecker.
func AllowChecker(method string) checkers.Checker {
return checkers.OperationChecker(method)
}
// TimeoutConstraint restricts the lifetime of the macaroon // TimeoutConstraint restricts the lifetime of the macaroon
// to the amount of seconds given. // to the amount of seconds given.
@ -50,15 +44,10 @@ func TimeoutConstraint(seconds int64) func(*macaroon.Macaroon) error {
macaroonTimeout := time.Duration(seconds) macaroonTimeout := time.Duration(seconds)
requestTimeout := time.Now().Add(time.Second * macaroonTimeout) requestTimeout := time.Now().Add(time.Second * macaroonTimeout)
caveat := checkers.TimeBeforeCaveat(requestTimeout) caveat := checkers.TimeBeforeCaveat(requestTimeout)
return mac.AddFirstPartyCaveat(caveat.Condition) return mac.AddFirstPartyCaveat([]byte(caveat.Condition))
} }
} }
// TimeoutChecker wraps default checkers.TimeBefore checker.
func TimeoutChecker() checkers.Checker {
return checkers.TimeBefore
}
// IPLockConstraint locks macaroon to a specific IP address. // IPLockConstraint locks macaroon to a specific IP address.
// If address is an empty string, this constraint does nothing to // If address is an empty string, this constraint does nothing to
// accommodate default value's desired behavior. // accommodate default value's desired behavior.
@ -69,24 +58,33 @@ func IPLockConstraint(ipAddr string) func(*macaroon.Macaroon) error {
if macaroonIPAddr == nil { if macaroonIPAddr == nil {
return fmt.Errorf("incorrect macaroon IP-lock address") return fmt.Errorf("incorrect macaroon IP-lock address")
} }
caveat := checkers.ClientIPAddrCaveat(macaroonIPAddr) caveat := checkers.Condition("ipaddr",
return mac.AddFirstPartyCaveat(caveat.Condition) macaroonIPAddr.String())
return mac.AddFirstPartyCaveat([]byte(caveat))
} }
return nil return nil
} }
} }
// IPLockChecker accepts client IP from the validation context and compares it // IPLockChecker accepts client IP from the validation context and compares it
// with IP locked in the macaroon. // with IP locked in the macaroon. It is of the `Checker` type.
func IPLockChecker(clientIP string) checkers.Checker { func IPLockChecker() (string, checkers.Func) {
return checkers.CheckerFunc{ return "ipaddr", func(ctx context.Context, cond, arg string) error {
Condition_: checkers.CondClientIPAddr, // Get peer info and extract IP address from it for macaroon
Check_: func(_, cav string) error { // check.
if !net.ParseIP(cav).Equal(net.ParseIP(clientIP)) { pr, ok := peer.FromContext(ctx)
if !ok {
return fmt.Errorf("unable to get peer info from context")
}
peerAddr, _, err := net.SplitHostPort(pr.Addr.String())
if err != nil {
return fmt.Errorf("unable to parse peer address")
}
if !net.ParseIP(arg).Equal(net.ParseIP(peerAddr)) {
msg := "macaroon locked to different IP address" msg := "macaroon locked to different IP address"
return fmt.Errorf(msg) return fmt.Errorf(msg)
} }
return nil return nil
},
} }
} }

View File

@ -1,9 +1,15 @@
package macaroons package macaroons
import ( import (
"fmt"
"path" "path"
"gopkg.in/macaroon-bakery.v1/bakery" "google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
"gopkg.in/macaroon-bakery.v2/bakery/checkers"
"golang.org/x/net/context"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )
@ -15,8 +21,13 @@ var (
) )
// NewService returns a service backed by the macaroon Bolt DB stored in the // NewService returns a service backed by the macaroon Bolt DB stored in the
// passed directory. // passed directory. The `checks` argument can be any of the `Checker` type
func NewService(dir string) (*bakery.Service, error) { // functions defined in this package, or a custom checker if desired. This
// constructor prevents double-registration of checkers to prevent panics, so
// listing the same checker more than once is not harmful. Default checkers,
// such as those for `allow`, `time-before`, `declared`, and `error` caveats
// are registered automatically and don't need to be added.
func NewService(dir string, checks ...Checker) (*bakery.Bakery, error) {
// Open the database that we'll use to store the primary macaroon key, // Open the database that we'll use to store the primary macaroon key,
// and all generated macaroons+caveats. // and all generated macaroons+caveats.
macaroonDB, err := bolt.Open(path.Join(dir, dbFilename), 0600, macaroonDB, err := bolt.Open(path.Join(dir, dbFilename), 0600,
@ -29,19 +40,90 @@ func NewService(dir string) (*bakery.Service, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
macaroonStore, err := NewStorage(macaroonDB)
if err != nil {
return nil, err
}
macaroonParams := bakery.NewServiceParams{ macaroonParams := bakery.BakeryParams{
Location: "lnd", Location: "lnd",
Store: macaroonStore,
RootKeyStore: rootKeyStore, RootKeyStore: rootKeyStore,
// No third-party caveat support for now. // No third-party caveat support for now.
// TODO(aakselrod): Add third-party caveat support. // TODO(aakselrod): Add third-party caveat support.
Locator: nil, Locator: nil,
Key: nil, Key: nil,
} }
return bakery.NewService(macaroonParams)
svc := bakery.New(macaroonParams)
// Register all custom caveat checkers with the bakery's checker.
// TODO(aakselrod): Add more checks as required.
checker := svc.Checker.FirstPartyCaveatChecker.(*checkers.Checker)
for _, check := range checks {
cond, fun := check()
if !isRegistered(checker, cond) {
checker.Register(cond, "std", fun)
}
}
return svc, nil
}
// isRegistered checks to see if the required checker has already been
// registered in order to avoid a panic caused by double registration.
func isRegistered(c *checkers.Checker, name string) bool {
if c == nil {
return false
}
for _, info := range c.Info() {
if info.Name == name && info.Prefix == "std" {
return true
}
}
return false
}
// UnaryServerInterceptor is a GRPC interceptor that checks whether the
// request is authorized by the included macaroons.
func UnaryServerInterceptor(svc *bakery.Bakery,
permissionMap map[string][]bakery.Op) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
if _, ok := permissionMap[info.FullMethod]; !ok {
return nil, fmt.Errorf("%s: unknown permissions "+
"required for method", info.FullMethod)
}
err := ValidateMacaroon(ctx, permissionMap[info.FullMethod],
svc)
if err != nil {
return nil, err
}
return handler(ctx, req)
}
}
// StreamServerInterceptor is a GRPC interceptor that checks whether the
// request is authorized by the included macaroons.
func StreamServerInterceptor(svc *bakery.Bakery,
permissionMap map[string][]bakery.Op) grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream,
info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
if _, ok := permissionMap[info.FullMethod]; !ok {
return fmt.Errorf("%s: unknown permissions required "+
"for method", info.FullMethod)
}
err := ValidateMacaroon(ss.Context(),
permissionMap[info.FullMethod], svc)
if err != nil {
return err
}
return handler(srv, ss)
}
} }

View File

@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"io" "io"
"golang.org/x/net/context"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )
@ -21,7 +23,7 @@ var (
// just 0, to emulate the memory storage that comes with bakery. // just 0, to emulate the memory storage that comes with bakery.
// //
// TODO(aakselrod): Add support for key rotation. // TODO(aakselrod): Add support for key rotation.
defaultRootKeyID = "0" defaultRootKeyID = []byte("0")
// macaroonBucketName is the name of the macaroon store bucket. // macaroonBucketName is the name of the macaroon store bucket.
macaroonBucketName = []byte("macaroons") macaroonBucketName = []byte("macaroons")
@ -49,13 +51,13 @@ func NewRootKeyStorage(db *bolt.DB) (*RootKeyStorage, error) {
} }
// Get implements the Get method for the bakery.RootKeyStorage interface. // Get implements the Get method for the bakery.RootKeyStorage interface.
func (r *RootKeyStorage) Get(id string) ([]byte, error) { func (r *RootKeyStorage) Get(_ context.Context, id []byte) ([]byte, error) {
var rootKey []byte var rootKey []byte
err := r.View(func(tx *bolt.Tx) error { err := r.View(func(tx *bolt.Tx) error {
dbKey := tx.Bucket(rootKeyBucketName).Get([]byte(id)) dbKey := tx.Bucket(rootKeyBucketName).Get(id)
if len(dbKey) == 0 { if len(dbKey) == 0 {
return fmt.Errorf("root key with id %s doesn't exist", return fmt.Errorf("root key with id %s doesn't exist",
id) string(id))
} }
rootKey = make([]byte, len(dbKey)) rootKey = make([]byte, len(dbKey))
@ -72,12 +74,12 @@ func (r *RootKeyStorage) Get(id string) ([]byte, error) {
// RootKey implements the RootKey method for the bakery.RootKeyStorage // RootKey implements the RootKey method for the bakery.RootKeyStorage
// interface. // interface.
// TODO(aakselrod): Add support for key rotation. // TODO(aakselrod): Add support for key rotation.
func (r *RootKeyStorage) RootKey() ([]byte, string, error) { func (r *RootKeyStorage) RootKey(_ context.Context) ([]byte, []byte, error) {
var rootKey []byte var rootKey []byte
id := defaultRootKeyID id := defaultRootKeyID
err := r.Update(func(tx *bolt.Tx) error { err := r.Update(func(tx *bolt.Tx) error {
ns := tx.Bucket(rootKeyBucketName) ns := tx.Bucket(rootKeyBucketName)
rootKey = ns.Get([]byte(id)) rootKey = ns.Get(id)
// If there's no root key stored in the bucket yet, create one. // If there's no root key stored in the bucket yet, create one.
if len(rootKey) != 0 { if len(rootKey) != 0 {
@ -89,69 +91,11 @@ func (r *RootKeyStorage) RootKey() ([]byte, string, error) {
if _, err := io.ReadFull(rand.Reader, rootKey[:]); err != nil { if _, err := io.ReadFull(rand.Reader, rootKey[:]); err != nil {
return err return err
} }
return ns.Put([]byte(id), rootKey) return ns.Put(id, rootKey)
}) })
if err != nil { if err != nil {
return nil, "", err return nil, nil, err
} }
return rootKey, id, nil return rootKey, id, nil
} }
// Storage implements the bakery.Storage interface.
type Storage struct {
*bolt.DB
}
// NewStorage creates a Storage instance.
//
// TODO(aakselrod): Add support for encryption of data with passphrase.
func NewStorage(db *bolt.DB) (*Storage, error) {
// If the store's bucket doesn't exist, create it.
err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists(macaroonBucketName)
return err
})
if err != nil {
return nil, err
}
// Return the DB wrapped in a Storage object.
return &Storage{db}, nil
}
// Put implements the Put method for the bakery.Storage interface.
func (s *Storage) Put(location string, item string) error {
return s.Update(func(tx *bolt.Tx) error {
return tx.Bucket(macaroonBucketName).Put([]byte(location),
[]byte(item))
})
}
// Get implements the Get method for the bakery.Storage interface.
func (s *Storage) Get(location string) (string, error) {
var item []byte
err := s.View(func(tx *bolt.Tx) error {
itemBytes := tx.Bucket(macaroonBucketName).Get([]byte(location))
if len(itemBytes) == 0 {
return fmt.Errorf("couldn't get item for location %s",
location)
}
item = make([]byte, len(itemBytes))
copy(item, itemBytes)
return nil
})
if err != nil {
return "", err
}
return string(item), nil
}
// Del implements the Del method for the bakery.Storage interface.
func (s *Storage) Del(location string) error {
return s.Update(func(tx *bolt.Tx) error {
return tx.Bucket(macaroonBucketName).Delete([]byte(location))
})
}

View File

@ -13,7 +13,7 @@ import (
"strings" "strings"
"time" "time"
"gopkg.in/macaroon-bakery.v1/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -25,7 +25,6 @@ import (
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/zpay32" "github.com/lightningnetwork/lnd/zpay32"
"github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/blockchain"
@ -43,25 +42,227 @@ import (
var ( var (
defaultAccount uint32 = waddrmgr.DefaultAccountNum defaultAccount uint32 = waddrmgr.DefaultAccountNum
// roPermissions is a slice of method names that are considered "read-only" // readPermissions is a slice of all entities that allow read
// for authorization purposes, all lowercase. // permissions for authorization purposes, all lowercase.
roPermissions = []string{ readPermissions = []bakery.Op{
"verifymessage", {
"getinfo", Entity: "onchain",
"listpeers", Action: "read",
"walletbalance", },
"channelbalance", {
"listchannels", Entity: "offchain",
"readinvoices", Action: "read",
"gettransactions", },
"describegraph", {
"getchaninfo", Entity: "address",
"getnodeinfo", Action: "read",
"queryroutes", },
"getnetworkinfo", {
"listpayments", Entity: "message",
"decodepayreq", Action: "read",
"feereport", },
{
Entity: "peers",
Action: "read",
},
{
Entity: "info",
Action: "read",
},
}
// writePermissions is a slice of all entities that allow write
// permissions for authorization purposes, all lowercase.
writePermissions = []bakery.Op{
{
Entity: "onchain",
Action: "write",
},
{
Entity: "offchain",
Action: "write",
},
{
Entity: "address",
Action: "write",
},
{
Entity: "message",
Action: "write",
},
{
Entity: "peers",
Action: "write",
},
{
Entity: "info",
Action: "write",
},
}
// permissions maps RPC calls to the permissions they require.
permissions = map[string][]bakery.Op{
"/lnrpc.Lightning/SendCoins": {{
Entity: "onchain",
Action: "write",
}},
"/lnrpc.Lightning/SendMany": {{
Entity: "onchain",
Action: "write",
}},
"/lnrpc.Lightning/NewAddress": {{
Entity: "address",
Action: "write",
}},
"/lnrpc.Lightning/NewWitnessAddress": {{
Entity: "address",
Action: "write",
}},
"/lnrpc.Lightning/SignMessage": {{
Entity: "message",
Action: "write",
}},
"/lnrpc.Lightning/VerifyMessage": {{
Entity: "message",
Action: "read",
}},
"/lnrpc.Lightning/ConnectPeer": {{
Entity: "peers",
Action: "write",
}},
"/lnrpc.Lightning/DisconnectPeer": {{
Entity: "peers",
Action: "write",
}},
"/lnrpc.Lightning/OpenChannel": {{
Entity: "onchain",
Action: "write",
}, {
Entity: "offchain",
Action: "write",
}},
"/lnrpc.Lightning/OpenchannelSync": {{
Entity: "onchain",
Action: "write",
}, {
Entity: "offchain",
Action: "write",
}},
"/lnrpc.Lightning/CloseChannel": {{
Entity: "onchain",
Action: "write",
}, {
Entity: "offchain",
Action: "write",
}},
"/lnrpc.Lightning/GetInfo": {{
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/ListPeers": {{
Entity: "peers",
Action: "read",
}},
"/lnrpc.Lightning/WalletBalance": {{
Entity: "onchain",
Action: "read",
}},
"/lnrpc.Lightning/ChannelBalance": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/PendingChannels": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/ListChannels": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/SendPayment": {{
Entity: "offchain",
Action: "write",
}},
"/lnrpc.Lightning/SendPaymentSync": {{
Entity: "offchain",
Action: "write",
}},
"/lnrpc.Lightning/AddInvoice": {{
Entity: "offchain",
Action: "write",
}},
"/lnrpc.Lightning/LookupInvoice": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/ListInvoices": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/SubscribeInvoices": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/SubscribeTransactions": {{
Entity: "onchain",
Action: "read",
}},
"/lnrpc.Lightning/GetTransactions": {{
Entity: "onchain",
Action: "read",
}},
"/lnrpc.Lightning/DescribeGraph": {{
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/GetChanInfo": {{
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/GetNodeInfo": {{
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/QueryRoutes": {{
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/GetNetworkInfo": {{
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/StopDaemon": {{
Entity: "info",
Action: "write",
}},
"/lnrpc.Lightning/SubscribeChannelGraph": {{
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/ListPayments": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/DeleteAllPayments": {{
Entity: "offchain",
Action: "write",
}},
"/lnrpc.Lightning/DebugLevel": {{
Entity: "info",
Action: "write",
}},
"/lnrpc.Lightning/DecodePayReq": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/FeeReport": {{
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/UpdateChannelPolicy": {{
Entity: "offchain",
Action: "write",
}},
} }
) )
@ -77,10 +278,6 @@ type rpcServer struct {
started int32 // To be used atomically. started int32 // To be used atomically.
shutdown int32 // To be used atomically. shutdown int32 // To be used atomically.
// authSvc is the authentication/authorization service backed by
// macaroons.
authSvc *bakery.Service
server *server server *server
wg sync.WaitGroup wg sync.WaitGroup
@ -93,10 +290,9 @@ type rpcServer struct {
var _ lnrpc.LightningServer = (*rpcServer)(nil) var _ lnrpc.LightningServer = (*rpcServer)(nil)
// newRPCServer creates and returns a new instance of the rpcServer. // newRPCServer creates and returns a new instance of the rpcServer.
func newRPCServer(s *server, authSvc *bakery.Service) *rpcServer { func newRPCServer(s *server) *rpcServer {
return &rpcServer{ return &rpcServer{
server: s, server: s,
authSvc: authSvc,
quit: make(chan struct{}, 1), quit: make(chan struct{}, 1),
} }
} }
@ -201,14 +397,6 @@ func determineFeePerByte(feeEstimator lnwallet.FeeEstimator, targetConf int32,
func (r *rpcServer) SendCoins(ctx context.Context, func (r *rpcServer) SendCoins(ctx context.Context,
in *lnrpc.SendCoinsRequest) (*lnrpc.SendCoinsResponse, error) { in *lnrpc.SendCoinsRequest) (*lnrpc.SendCoinsResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "sendcoins",
r.authSvc); err != nil {
return nil, err
}
}
// Based on the passed fee related paramters, we'll determine an // Based on the passed fee related paramters, we'll determine an
// approriate fee rate for this transaction. // approriate fee rate for this transaction.
feePerByte, err := determineFeePerByte( feePerByte, err := determineFeePerByte(
@ -237,14 +425,6 @@ func (r *rpcServer) SendCoins(ctx context.Context,
func (r *rpcServer) SendMany(ctx context.Context, func (r *rpcServer) SendMany(ctx context.Context,
in *lnrpc.SendManyRequest) (*lnrpc.SendManyResponse, error) { in *lnrpc.SendManyRequest) (*lnrpc.SendManyResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "sendcoins",
r.authSvc); err != nil {
return nil, err
}
}
// Based on the passed fee related paramters, we'll determine an // Based on the passed fee related paramters, we'll determine an
// approriate fee rate for this transaction. // approriate fee rate for this transaction.
feePerByte, err := determineFeePerByte( feePerByte, err := determineFeePerByte(
@ -271,14 +451,6 @@ func (r *rpcServer) SendMany(ctx context.Context,
func (r *rpcServer) NewAddress(ctx context.Context, func (r *rpcServer) NewAddress(ctx context.Context,
in *lnrpc.NewAddressRequest) (*lnrpc.NewAddressResponse, error) { in *lnrpc.NewAddressRequest) (*lnrpc.NewAddressResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "newaddress",
r.authSvc); err != nil {
return nil, err
}
}
// Translate the gRPC proto address type to the wallet controller's // Translate the gRPC proto address type to the wallet controller's
// available address types. // available address types.
var addrType lnwallet.AddressType var addrType lnwallet.AddressType
@ -305,14 +477,6 @@ func (r *rpcServer) NewAddress(ctx context.Context,
func (r *rpcServer) NewWitnessAddress(ctx context.Context, func (r *rpcServer) NewWitnessAddress(ctx context.Context,
in *lnrpc.NewWitnessAddressRequest) (*lnrpc.NewAddressResponse, error) { in *lnrpc.NewWitnessAddressRequest) (*lnrpc.NewAddressResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "newaddress",
r.authSvc); err != nil {
return nil, err
}
}
addr, err := r.server.cc.wallet.NewAddress( addr, err := r.server.cc.wallet.NewAddress(
lnwallet.NestedWitnessPubKey, false, lnwallet.NestedWitnessPubKey, false,
) )
@ -331,14 +495,6 @@ func (r *rpcServer) NewWitnessAddress(ctx context.Context,
func (r *rpcServer) SignMessage(ctx context.Context, func (r *rpcServer) SignMessage(ctx context.Context,
in *lnrpc.SignMessageRequest) (*lnrpc.SignMessageResponse, error) { in *lnrpc.SignMessageRequest) (*lnrpc.SignMessageResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "signmessage",
r.authSvc); err != nil {
return nil, err
}
}
if in.Msg == nil { if in.Msg == nil {
return nil, fmt.Errorf("need a message to sign") return nil, fmt.Errorf("need a message to sign")
} }
@ -359,14 +515,6 @@ func (r *rpcServer) SignMessage(ctx context.Context,
func (r *rpcServer) VerifyMessage(ctx context.Context, func (r *rpcServer) VerifyMessage(ctx context.Context,
in *lnrpc.VerifyMessageRequest) (*lnrpc.VerifyMessageResponse, error) { in *lnrpc.VerifyMessageRequest) (*lnrpc.VerifyMessageResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "verifymessage",
r.authSvc); err != nil {
return nil, err
}
}
if in.Msg == nil { if in.Msg == nil {
return nil, fmt.Errorf("need a message to verify") return nil, fmt.Errorf("need a message to verify")
} }
@ -406,14 +554,6 @@ func (r *rpcServer) VerifyMessage(ctx context.Context,
func (r *rpcServer) ConnectPeer(ctx context.Context, func (r *rpcServer) ConnectPeer(ctx context.Context,
in *lnrpc.ConnectPeerRequest) (*lnrpc.ConnectPeerResponse, error) { in *lnrpc.ConnectPeerRequest) (*lnrpc.ConnectPeerResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "connectpeer",
r.authSvc); err != nil {
return nil, err
}
}
// The server hasn't yet started, so it won't be able to service any of // The server hasn't yet started, so it won't be able to service any of
// our requests, so we'll bail early here. // our requests, so we'll bail early here.
if !r.server.Started() { if !r.server.Started() {
@ -475,14 +615,6 @@ func (r *rpcServer) ConnectPeer(ctx context.Context,
func (r *rpcServer) DisconnectPeer(ctx context.Context, func (r *rpcServer) DisconnectPeer(ctx context.Context,
in *lnrpc.DisconnectPeerRequest) (*lnrpc.DisconnectPeerResponse, error) { in *lnrpc.DisconnectPeerRequest) (*lnrpc.DisconnectPeerResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "disconnectpeer",
r.authSvc); err != nil {
return nil, err
}
}
rpcsLog.Debugf("[disconnectpeer] from peer(%s)", in.PubKey) rpcsLog.Debugf("[disconnectpeer] from peer(%s)", in.PubKey)
if !r.server.Started() { if !r.server.Started() {
@ -532,14 +664,6 @@ func (r *rpcServer) DisconnectPeer(ctx context.Context,
func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
updateStream lnrpc.Lightning_OpenChannelServer) error { updateStream lnrpc.Lightning_OpenChannelServer) error {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(updateStream.Context(),
"openchannel", r.authSvc); err != nil {
return err
}
}
rpcsLog.Tracef("[openchannel] request to peerid(%v) "+ rpcsLog.Tracef("[openchannel] request to peerid(%v) "+
"allocation(us=%v, them=%v)", in.TargetPeerId, "allocation(us=%v, them=%v)", in.TargetPeerId,
in.LocalFundingAmount, in.PushSat) in.LocalFundingAmount, in.PushSat)
@ -686,14 +810,6 @@ out:
func (r *rpcServer) OpenChannelSync(ctx context.Context, func (r *rpcServer) OpenChannelSync(ctx context.Context,
in *lnrpc.OpenChannelRequest) (*lnrpc.ChannelPoint, error) { in *lnrpc.OpenChannelRequest) (*lnrpc.ChannelPoint, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "openchannel",
r.authSvc); err != nil {
return nil, err
}
}
rpcsLog.Tracef("[openchannel] request to peerid(%v) "+ rpcsLog.Tracef("[openchannel] request to peerid(%v) "+
"allocation(us=%v, them=%v)", in.TargetPeerId, "allocation(us=%v, them=%v)", in.TargetPeerId,
in.LocalFundingAmount, in.PushSat) in.LocalFundingAmount, in.PushSat)
@ -818,14 +934,6 @@ func getChanPointFundingTxid(chanPoint *lnrpc.ChannelPoint) ([]byte, error) {
func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
updateStream lnrpc.Lightning_CloseChannelServer) error { updateStream lnrpc.Lightning_CloseChannelServer) error {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(updateStream.Context(),
"closechannel", r.authSvc); err != nil {
return err
}
}
force := in.Force force := in.Force
index := in.ChannelPoint.OutputIndex index := in.ChannelPoint.OutputIndex
txidHash, err := getChanPointFundingTxid(in.GetChannelPoint()) txidHash, err := getChanPointFundingTxid(in.GetChannelPoint())
@ -1033,14 +1141,6 @@ func (r *rpcServer) fetchActiveChannel(chanPoint wire.OutPoint) (*lnwallet.Light
func (r *rpcServer) GetInfo(ctx context.Context, func (r *rpcServer) GetInfo(ctx context.Context,
in *lnrpc.GetInfoRequest) (*lnrpc.GetInfoResponse, error) { in *lnrpc.GetInfoRequest) (*lnrpc.GetInfoResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "getinfo",
r.authSvc); err != nil {
return nil, err
}
}
var activeChannels uint32 var activeChannels uint32
serverPeers := r.server.Peers() serverPeers := r.server.Peers()
for _, serverPeer := range serverPeers { for _, serverPeer := range serverPeers {
@ -1098,7 +1198,6 @@ func (r *rpcServer) GetInfo(ctx context.Context,
Testnet: activeNetParams.Params == &chaincfg.TestNet3Params, Testnet: activeNetParams.Params == &chaincfg.TestNet3Params,
Chains: activeChains, Chains: activeChains,
Uris: uris, Uris: uris,
Alias: nodeAnn.Alias.String(),
}, nil }, nil
} }
@ -1106,14 +1205,6 @@ func (r *rpcServer) GetInfo(ctx context.Context,
func (r *rpcServer) ListPeers(ctx context.Context, func (r *rpcServer) ListPeers(ctx context.Context,
in *lnrpc.ListPeersRequest) (*lnrpc.ListPeersResponse, error) { in *lnrpc.ListPeersRequest) (*lnrpc.ListPeersResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "listpeers",
r.authSvc); err != nil {
return nil, err
}
}
rpcsLog.Tracef("[listpeers] request") rpcsLog.Tracef("[listpeers] request")
serverPeers := r.server.Peers() serverPeers := r.server.Peers()
@ -1167,14 +1258,6 @@ func (r *rpcServer) ListPeers(ctx context.Context,
func (r *rpcServer) WalletBalance(ctx context.Context, func (r *rpcServer) WalletBalance(ctx context.Context,
in *lnrpc.WalletBalanceRequest) (*lnrpc.WalletBalanceResponse, error) { in *lnrpc.WalletBalanceRequest) (*lnrpc.WalletBalanceResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "walletbalance",
r.authSvc); err != nil {
return nil, err
}
}
// Get total balance, from txs that have >= 0 confirmations. // Get total balance, from txs that have >= 0 confirmations.
totalBal, err := r.server.cc.wallet.ConfirmedBalance(0, in.WitnessOnly) totalBal, err := r.server.cc.wallet.ConfirmedBalance(0, in.WitnessOnly)
if err != nil { if err != nil {
@ -1204,14 +1287,6 @@ func (r *rpcServer) WalletBalance(ctx context.Context,
func (r *rpcServer) ChannelBalance(ctx context.Context, func (r *rpcServer) ChannelBalance(ctx context.Context,
in *lnrpc.ChannelBalanceRequest) (*lnrpc.ChannelBalanceResponse, error) { in *lnrpc.ChannelBalanceRequest) (*lnrpc.ChannelBalanceResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "channelbalance",
r.authSvc); err != nil {
return nil, err
}
}
channels, err := r.server.chanDB.FetchAllChannels() channels, err := r.server.chanDB.FetchAllChannels()
if err != nil { if err != nil {
return nil, err return nil, err
@ -1234,14 +1309,6 @@ func (r *rpcServer) ChannelBalance(ctx context.Context,
func (r *rpcServer) PendingChannels(ctx context.Context, func (r *rpcServer) PendingChannels(ctx context.Context,
in *lnrpc.PendingChannelsRequest) (*lnrpc.PendingChannelsResponse, error) { in *lnrpc.PendingChannelsRequest) (*lnrpc.PendingChannelsResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "listchannels",
r.authSvc); err != nil {
return nil, err
}
}
rpcsLog.Debugf("[pendingchannels]") rpcsLog.Debugf("[pendingchannels]")
resp := &lnrpc.PendingChannelsResponse{} resp := &lnrpc.PendingChannelsResponse{}
@ -1402,14 +1469,6 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
func (r *rpcServer) ListChannels(ctx context.Context, func (r *rpcServer) ListChannels(ctx context.Context,
in *lnrpc.ListChannelsRequest) (*lnrpc.ListChannelsResponse, error) { in *lnrpc.ListChannelsRequest) (*lnrpc.ListChannelsResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "listchannels",
r.authSvc); err != nil {
return nil, err
}
}
resp := &lnrpc.ListChannelsResponse{} resp := &lnrpc.ListChannelsResponse{}
graph := r.server.chanDB.ChannelGraph() graph := r.server.chanDB.ChannelGraph()
@ -1552,14 +1611,6 @@ func validatePayReqExpiry(payReq *zpay32.Invoice) error {
// bi-directional stream allowing clients to rapidly send payments through the // bi-directional stream allowing clients to rapidly send payments through the
// Lightning Network with a single persistent connection. // Lightning Network with a single persistent connection.
func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) error { func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) error {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(paymentStream.Context(),
"sendpayment", r.authSvc); err != nil {
return err
}
}
// For each payment we need to know the msat amount, the destination // For each payment we need to know the msat amount, the destination
// public key, and the payment hash. // public key, and the payment hash.
type payment struct { type payment struct {
@ -1805,14 +1856,6 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
func (r *rpcServer) SendPaymentSync(ctx context.Context, func (r *rpcServer) SendPaymentSync(ctx context.Context,
nextPayment *lnrpc.SendRequest) (*lnrpc.SendResponse, error) { nextPayment *lnrpc.SendRequest) (*lnrpc.SendResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "sendpayment",
r.authSvc); err != nil {
return nil, err
}
}
// TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit // TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit
// * limit either a %, or absolute, or iff more than sending // * limit either a %, or absolute, or iff more than sending
@ -1939,14 +1982,6 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
func (r *rpcServer) AddInvoice(ctx context.Context, func (r *rpcServer) AddInvoice(ctx context.Context,
invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) { invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "addinvoice",
r.authSvc); err != nil {
return nil, err
}
}
var paymentPreimage [32]byte var paymentPreimage [32]byte
switch { switch {
@ -2164,14 +2199,6 @@ func createRPCInvoice(invoice *channeldb.Invoice) (*lnrpc.Invoice, error) {
func (r *rpcServer) LookupInvoice(ctx context.Context, func (r *rpcServer) LookupInvoice(ctx context.Context,
req *lnrpc.PaymentHash) (*lnrpc.Invoice, error) { req *lnrpc.PaymentHash) (*lnrpc.Invoice, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "readinvoices",
r.authSvc); err != nil {
return nil, err
}
}
var ( var (
payHash [32]byte payHash [32]byte
rHash []byte rHash []byte
@ -2221,14 +2248,6 @@ func (r *rpcServer) LookupInvoice(ctx context.Context,
func (r *rpcServer) ListInvoices(ctx context.Context, func (r *rpcServer) ListInvoices(ctx context.Context,
req *lnrpc.ListInvoiceRequest) (*lnrpc.ListInvoiceResponse, error) { req *lnrpc.ListInvoiceRequest) (*lnrpc.ListInvoiceResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "readinvoices",
r.authSvc); err != nil {
return nil, err
}
}
dbInvoices, err := r.server.chanDB.FetchAllInvoices(req.PendingOnly) dbInvoices, err := r.server.chanDB.FetchAllInvoices(req.PendingOnly)
if err != nil { if err != nil {
return nil, err return nil, err
@ -2255,14 +2274,6 @@ func (r *rpcServer) ListInvoices(ctx context.Context,
func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription, func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription,
updateStream lnrpc.Lightning_SubscribeInvoicesServer) error { updateStream lnrpc.Lightning_SubscribeInvoicesServer) error {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(updateStream.Context(),
"readinvoices", r.authSvc); err != nil {
return err
}
}
invoiceClient := r.server.invoices.SubscribeNotifications() invoiceClient := r.server.invoices.SubscribeNotifications()
defer invoiceClient.Cancel() defer invoiceClient.Cancel()
@ -2291,14 +2302,6 @@ func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription,
func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest, func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest,
updateStream lnrpc.Lightning_SubscribeTransactionsServer) error { updateStream lnrpc.Lightning_SubscribeTransactionsServer) error {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(updateStream.Context(),
"gettransactions", r.authSvc); err != nil {
return err
}
}
txClient, err := r.server.cc.wallet.SubscribeTransactions() txClient, err := r.server.cc.wallet.SubscribeTransactions()
if err != nil { if err != nil {
return err return err
@ -2340,14 +2343,6 @@ func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest,
func (r *rpcServer) GetTransactions(ctx context.Context, func (r *rpcServer) GetTransactions(ctx context.Context,
_ *lnrpc.GetTransactionsRequest) (*lnrpc.TransactionDetails, error) { _ *lnrpc.GetTransactionsRequest) (*lnrpc.TransactionDetails, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "gettransactions",
r.authSvc); err != nil {
return nil, err
}
}
// TODO(roasbeef): add pagination support // TODO(roasbeef): add pagination support
transactions, err := r.server.cc.wallet.ListTransactionDetails() transactions, err := r.server.cc.wallet.ListTransactionDetails()
if err != nil { if err != nil {
@ -2387,14 +2382,6 @@ func (r *rpcServer) GetTransactions(ctx context.Context,
func (r *rpcServer) DescribeGraph(ctx context.Context, func (r *rpcServer) DescribeGraph(ctx context.Context,
_ *lnrpc.ChannelGraphRequest) (*lnrpc.ChannelGraph, error) { _ *lnrpc.ChannelGraphRequest) (*lnrpc.ChannelGraph, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "describegraph",
r.authSvc); err != nil {
return nil, err
}
}
resp := &lnrpc.ChannelGraph{} resp := &lnrpc.ChannelGraph{}
// Obtain the pointer to the global singleton channel graph, this will // Obtain the pointer to the global singleton channel graph, this will
@ -2501,14 +2488,6 @@ func (r *rpcServer) GetChanInfo(ctx context.Context,
graph := r.server.chanDB.ChannelGraph() graph := r.server.chanDB.ChannelGraph()
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "getchaninfo",
r.authSvc); err != nil {
return nil, err
}
}
edgeInfo, edge1, edge2, err := graph.FetchChannelEdgesByID(in.ChanId) edgeInfo, edge1, edge2, err := graph.FetchChannelEdgesByID(in.ChanId)
if err != nil { if err != nil {
return nil, err return nil, err
@ -2527,14 +2506,6 @@ func (r *rpcServer) GetChanInfo(ctx context.Context,
func (r *rpcServer) GetNodeInfo(ctx context.Context, func (r *rpcServer) GetNodeInfo(ctx context.Context,
in *lnrpc.NodeInfoRequest) (*lnrpc.NodeInfo, error) { in *lnrpc.NodeInfoRequest) (*lnrpc.NodeInfo, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "getnodeinfo",
r.authSvc); err != nil {
return nil, err
}
}
graph := r.server.chanDB.ChannelGraph() graph := r.server.chanDB.ChannelGraph()
// First, parse the hex-encoded public key into a full in-memory public // First, parse the hex-encoded public key into a full in-memory public
@ -2608,14 +2579,6 @@ func (r *rpcServer) GetNodeInfo(ctx context.Context,
func (r *rpcServer) QueryRoutes(ctx context.Context, func (r *rpcServer) QueryRoutes(ctx context.Context,
in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) { in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "queryroutes",
r.authSvc); err != nil {
return nil, err
}
}
// First parse the hex-encdoed public key into a full public key objet // First parse the hex-encdoed public key into a full public key objet
// we can properly manipulate. // we can properly manipulate.
pubKeyBytes, err := hex.DecodeString(in.PubKey) pubKeyBytes, err := hex.DecodeString(in.PubKey)
@ -2682,14 +2645,6 @@ func marshallRoute(route *routing.Route) *lnrpc.Route {
func (r *rpcServer) GetNetworkInfo(ctx context.Context, func (r *rpcServer) GetNetworkInfo(ctx context.Context,
_ *lnrpc.NetworkInfoRequest) (*lnrpc.NetworkInfo, error) { _ *lnrpc.NetworkInfoRequest) (*lnrpc.NetworkInfo, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "getnetworkinfo",
r.authSvc); err != nil {
return nil, err
}
}
graph := r.server.chanDB.ChannelGraph() graph := r.server.chanDB.ChannelGraph()
var ( var (
@ -2805,14 +2760,6 @@ func (r *rpcServer) GetNetworkInfo(ctx context.Context,
func (r *rpcServer) StopDaemon(ctx context.Context, func (r *rpcServer) StopDaemon(ctx context.Context,
_ *lnrpc.StopRequest) (*lnrpc.StopResponse, error) { _ *lnrpc.StopRequest) (*lnrpc.StopResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "stopdaemon",
r.authSvc); err != nil {
return nil, err
}
}
shutdownRequestChannel <- struct{}{} shutdownRequestChannel <- struct{}{}
return &lnrpc.StopResponse{}, nil return &lnrpc.StopResponse{}, nil
} }
@ -2826,14 +2773,6 @@ func (r *rpcServer) StopDaemon(ctx context.Context,
func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription, func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription,
updateStream lnrpc.Lightning_SubscribeChannelGraphServer) error { updateStream lnrpc.Lightning_SubscribeChannelGraphServer) error {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(updateStream.Context(),
"describegraph", r.authSvc); err != nil {
return err
}
}
// First, we start by subscribing to a new intent to receive // First, we start by subscribing to a new intent to receive
// notifications from the channel router. // notifications from the channel router.
client, err := r.server.chanRouter.SubscribeTopology() client, err := r.server.chanRouter.SubscribeTopology()
@ -2951,14 +2890,6 @@ func marshallTopologyChange(topChange *routing.TopologyChange) *lnrpc.GraphTopol
func (r *rpcServer) ListPayments(ctx context.Context, func (r *rpcServer) ListPayments(ctx context.Context,
_ *lnrpc.ListPaymentsRequest) (*lnrpc.ListPaymentsResponse, error) { _ *lnrpc.ListPaymentsRequest) (*lnrpc.ListPaymentsResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "listpayments",
r.authSvc); err != nil {
return nil, err
}
}
rpcsLog.Debugf("[ListPayments]") rpcsLog.Debugf("[ListPayments]")
payments, err := r.server.chanDB.FetchAllPayments() payments, err := r.server.chanDB.FetchAllPayments()
@ -2992,14 +2923,6 @@ func (r *rpcServer) ListPayments(ctx context.Context,
func (r *rpcServer) DeleteAllPayments(ctx context.Context, func (r *rpcServer) DeleteAllPayments(ctx context.Context,
_ *lnrpc.DeleteAllPaymentsRequest) (*lnrpc.DeleteAllPaymentsResponse, error) { _ *lnrpc.DeleteAllPaymentsRequest) (*lnrpc.DeleteAllPaymentsResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "deleteallpayments",
r.authSvc); err != nil {
return nil, err
}
}
rpcsLog.Debugf("[DeleteAllPayments]") rpcsLog.Debugf("[DeleteAllPayments]")
if err := r.server.chanDB.DeleteAllPayments(); err != nil { if err := r.server.chanDB.DeleteAllPayments(); err != nil {
@ -3016,14 +2939,6 @@ func (r *rpcServer) DeleteAllPayments(ctx context.Context,
func (r *rpcServer) DebugLevel(ctx context.Context, func (r *rpcServer) DebugLevel(ctx context.Context,
req *lnrpc.DebugLevelRequest) (*lnrpc.DebugLevelResponse, error) { req *lnrpc.DebugLevelRequest) (*lnrpc.DebugLevelResponse, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "debuglevel",
r.authSvc); err != nil {
return nil, err
}
}
// If show is set, then we simply print out the list of available // If show is set, then we simply print out the list of available
// sub-systems. // sub-systems.
if req.Show { if req.Show {
@ -3049,14 +2964,6 @@ func (r *rpcServer) DebugLevel(ctx context.Context,
func (r *rpcServer) DecodePayReq(ctx context.Context, func (r *rpcServer) DecodePayReq(ctx context.Context,
req *lnrpc.PayReqString) (*lnrpc.PayReq, error) { req *lnrpc.PayReqString) (*lnrpc.PayReq, error) {
// Check macaroon to see if this is allowed.
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "decodepayreq",
r.authSvc); err != nil {
return nil, err
}
}
rpcsLog.Tracef("[decodepayreq] decoding: %v", req.PayReq) rpcsLog.Tracef("[decodepayreq] decoding: %v", req.PayReq)
// Fist we'll attempt to decode the payment request string, if the // Fist we'll attempt to decode the payment request string, if the
@ -3117,13 +3024,6 @@ const feeBase = 1000000
func (r *rpcServer) FeeReport(ctx context.Context, func (r *rpcServer) FeeReport(ctx context.Context,
_ *lnrpc.FeeReportRequest) (*lnrpc.FeeReportResponse, error) { _ *lnrpc.FeeReportRequest) (*lnrpc.FeeReportResponse, error) {
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "feereport",
r.authSvc); err != nil {
return nil, err
}
}
// TODO(roasbeef): use UnaryInterceptor to add automated logging // TODO(roasbeef): use UnaryInterceptor to add automated logging
channelGraph := r.server.chanDB.ChannelGraph() channelGraph := r.server.chanDB.ChannelGraph()
@ -3174,13 +3074,6 @@ const minFeeRate = 1e-6
func (r *rpcServer) UpdateChannelPolicy(ctx context.Context, func (r *rpcServer) UpdateChannelPolicy(ctx context.Context,
req *lnrpc.PolicyUpdateRequest) (*lnrpc.PolicyUpdateResponse, error) { req *lnrpc.PolicyUpdateRequest) (*lnrpc.PolicyUpdateResponse, error) {
if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "updatechannelpolicy",
r.authSvc); err != nil {
return nil, err
}
}
var targetChans []wire.OutPoint var targetChans []wire.OutPoint
switch scope := req.Scope.(type) { switch scope := req.Scope.(type) {
// If the request is targeting all active channels, then we don't need // If the request is targeting all active channels, then we don't need

View File

@ -8,7 +8,7 @@ import (
"github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcwallet/wallet" "github.com/roasbeef/btcwallet/wallet"
"golang.org/x/net/context" "golang.org/x/net/context"
"gopkg.in/macaroon-bakery.v1/bakery" "gopkg.in/macaroon-bakery.v2/bakery"
) )
// UnlockerService implements the WalletUnlocker service used to provide lnd // UnlockerService implements the WalletUnlocker service used to provide lnd
@ -29,7 +29,7 @@ type UnlockerService struct {
} }
// New creates and returns a new UnlockerService. // New creates and returns a new UnlockerService.
func New(authSvc *bakery.Service, chainDir string, func New(authSvc *bakery.Bakery, chainDir string,
params *chaincfg.Params) *UnlockerService { params *chaincfg.Params) *UnlockerService {
return &UnlockerService{ return &UnlockerService{
CreatePasswords: make(chan []byte, 1), CreatePasswords: make(chan []byte, 1),