multi: refactor external subserver config
As a preparation for making more and more implementation details configurable, we add a new ImplementationCfg struct that houses all the interfaces that can be defined externally.
This commit is contained in:
6 files changed
+152
-100
No files matched your search
+2
-1
@@ -31,11 +31,12 @@ func main() {
|
|||||||
// Help was requested, exit normally.
|
// Help was requested, exit normally.
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
implCfg := loadedConfig.ImplementationConfig()
|
||||||
|
|
||||||
// Call the "real" main in a nested manner so the defers will properly
|
// Call the "real" main in a nested manner so the defers will properly
|
||||||
// be executed in the case of a graceful shutdown.
|
// be executed in the case of a graceful shutdown.
|
||||||
if err = lnd.Main(
|
if err = lnd.Main(
|
||||||
loadedConfig, lnd.ListenerCfg{}, shutdownInterceptor,
|
loadedConfig, lnd.ListenerCfg{}, implCfg, shutdownInterceptor,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
_, _ = fmt.Fprintln(os.Stderr, err)
|
_, _ = fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -1536,6 +1536,17 @@ func (c *Config) graphDatabaseDir() string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImplementationConfig returns the configuration of what actual implementations
|
||||||
|
// should be used when creating the main lnd instance.
|
||||||
|
func (c *Config) ImplementationConfig() *ImplementationCfg {
|
||||||
|
defaultImpl := &DefaultWalletImpl{}
|
||||||
|
return &ImplementationCfg{
|
||||||
|
GrpcRegistrar: defaultImpl,
|
||||||
|
RestRegistrar: defaultImpl,
|
||||||
|
ExternalValidator: defaultImpl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CleanAndExpandPath expands environment variables and leading ~ in the
|
// CleanAndExpandPath expands environment variables and leading ~ in the
|
||||||
// passed path, cleans the result, and returns it.
|
// passed path, cleans the result, and returns it.
|
||||||
// This function is taken from https://github.com/btcsuite/btcd
|
// This function is taken from https://github.com/btcsuite/btcd
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package lnd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GrpcRegistrar is an interface that must be satisfied by an external subserver
|
||||||
|
// that wants to be able to register its own gRPC server onto lnd's main
|
||||||
|
// grpc.Server instance.
|
||||||
|
type GrpcRegistrar interface {
|
||||||
|
// RegisterGrpcSubserver is called for each net.Listener on which lnd
|
||||||
|
// creates a grpc.Server instance. External subservers implementing this
|
||||||
|
// method can then register their own gRPC server structs to the main
|
||||||
|
// server instance.
|
||||||
|
RegisterGrpcSubserver(*grpc.Server) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestRegistrar is an interface that must be satisfied by an external subserver
|
||||||
|
// that wants to be able to register its own REST mux onto lnd's main
|
||||||
|
// proxy.ServeMux instance.
|
||||||
|
type RestRegistrar interface {
|
||||||
|
// RegisterRestSubserver is called after lnd creates the main
|
||||||
|
// proxy.ServeMux instance. External subservers implementing this method
|
||||||
|
// can then register their own REST proxy stubs to the main server
|
||||||
|
// instance.
|
||||||
|
RegisterRestSubserver(context.Context, *proxy.ServeMux, string,
|
||||||
|
[]grpc.DialOption) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalValidator is an interface that must be satisfied by an external
|
||||||
|
// macaroon validator.
|
||||||
|
type ExternalValidator interface {
|
||||||
|
macaroons.MacaroonValidator
|
||||||
|
|
||||||
|
// Permissions returns the permissions that the external validator is
|
||||||
|
// validating. It is a map between the full HTTP URI of each RPC and its
|
||||||
|
// required macaroon permissions. If multiple action/entity tuples are
|
||||||
|
// specified per URI, they are all required. See rpcserver.go for a list
|
||||||
|
// of valid action and entity values.
|
||||||
|
Permissions() map[string][]bakery.Op
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImplementationCfg is a struct that holds all configuration items for
|
||||||
|
// components that can be implemented outside lnd itself.
|
||||||
|
type ImplementationCfg struct {
|
||||||
|
// GrpcRegistrar is a type that can register additional gRPC subservers
|
||||||
|
// before the main gRPC server is started.
|
||||||
|
GrpcRegistrar
|
||||||
|
|
||||||
|
// RestRegistrar is a type that can register additional REST subservers
|
||||||
|
// before the main REST proxy is started.
|
||||||
|
RestRegistrar
|
||||||
|
|
||||||
|
// ExternalValidator is a type that can provide external macaroon
|
||||||
|
// validation.
|
||||||
|
ExternalValidator
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultWalletImpl is the default implementation of our normal, btcwallet
|
||||||
|
// backed configuration.
|
||||||
|
type DefaultWalletImpl struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterRestSubserver is called after lnd creates the main proxy.ServeMux
|
||||||
|
// instance. External subservers implementing this method can then register
|
||||||
|
// their own REST proxy stubs to the main server instance.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the GrpcRegistrar interface.
|
||||||
|
func (d *DefaultWalletImpl) RegisterRestSubserver(context.Context,
|
||||||
|
*proxy.ServeMux, string, []grpc.DialOption) error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterGrpcSubserver is called for each net.Listener on which lnd creates a
|
||||||
|
// grpc.Server instance. External subservers implementing this method can then
|
||||||
|
// register their own gRPC server structs to the main server instance.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the GrpcRegistrar interface.
|
||||||
|
func (d *DefaultWalletImpl) RegisterGrpcSubserver(*grpc.Server) error {
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the ExternalValidator interface.
|
||||||
|
func (d *DefaultWalletImpl) ValidateMacaroon(ctx context.Context,
|
||||||
|
requiredPermissions []bakery.Op, fullMethod string) error {
|
||||||
|
|
||||||
|
// Because the default implementation does not return any permissions,
|
||||||
|
// we shouldn't be registered as an external validator at all and this
|
||||||
|
// should never be invoked.
|
||||||
|
return fmt.Errorf("default implementation does not support external " +
|
||||||
|
"macaroon validation")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permissions returns the permissions that the external validator is
|
||||||
|
// validating. It is a map between the full HTTP URI of each RPC and its
|
||||||
|
// required macaroon permissions. If multiple action/entity tuples are specified
|
||||||
|
// per URI, they are all required. See rpcserver.go for a list of valid action
|
||||||
|
// and entity values.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the ExternalValidator interface.
|
||||||
|
func (d *DefaultWalletImpl) Permissions() map[string][]bakery.Op {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -122,51 +122,6 @@ func AdminAuthOptions(cfg *Config, skipMacaroons bool) ([]grpc.DialOption, error
|
|||||||
return opts, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GrpcRegistrar is an interface that must be satisfied by an external subserver
|
|
||||||
// that wants to be able to register its own gRPC server onto lnd's main
|
|
||||||
// grpc.Server instance.
|
|
||||||
type GrpcRegistrar interface {
|
|
||||||
// RegisterGrpcSubserver is called for each net.Listener on which lnd
|
|
||||||
// creates a grpc.Server instance. External subservers implementing this
|
|
||||||
// method can then register their own gRPC server structs to the main
|
|
||||||
// server instance.
|
|
||||||
RegisterGrpcSubserver(*grpc.Server) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestRegistrar is an interface that must be satisfied by an external subserver
|
|
||||||
// that wants to be able to register its own REST mux onto lnd's main
|
|
||||||
// proxy.ServeMux instance.
|
|
||||||
type RestRegistrar interface {
|
|
||||||
// RegisterRestSubserver is called after lnd creates the main
|
|
||||||
// proxy.ServeMux instance. External subservers implementing this method
|
|
||||||
// can then register their own REST proxy stubs to the main server
|
|
||||||
// instance.
|
|
||||||
RegisterRestSubserver(context.Context, *proxy.ServeMux, string,
|
|
||||||
[]grpc.DialOption) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCSubserverConfig is a struct that can be used to register an external
|
|
||||||
// subserver with the custom permissions that map to the gRPC server that is
|
|
||||||
// going to be registered with the GrpcRegistrar.
|
|
||||||
type RPCSubserverConfig struct {
|
|
||||||
// Registrar is a callback that is invoked for each net.Listener on
|
|
||||||
// which lnd creates a grpc.Server instance.
|
|
||||||
Registrar GrpcRegistrar
|
|
||||||
|
|
||||||
// Permissions is the permissions required for the external subserver.
|
|
||||||
// It is a map between the full HTTP URI of each RPC and its required
|
|
||||||
// macaroon permissions. If multiple action/entity tuples are specified
|
|
||||||
// per URI, they are all required. See rpcserver.go for a list of valid
|
|
||||||
// action and entity values.
|
|
||||||
Permissions map[string][]bakery.Op
|
|
||||||
|
|
||||||
// MacaroonValidator is a custom macaroon validator that should be used
|
|
||||||
// instead of the default lnd validator. If specified, the custom
|
|
||||||
// validator is used for all URIs specified in the above Permissions
|
|
||||||
// map.
|
|
||||||
MacaroonValidator macaroons.MacaroonValidator
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenerWithSignal is a net.Listener that has an additional Ready channel that
|
// ListenerWithSignal is a net.Listener that has an additional Ready channel that
|
||||||
// will be closed when a server starts listening.
|
// will be closed when a server starts listening.
|
||||||
type ListenerWithSignal struct {
|
type ListenerWithSignal struct {
|
||||||
@@ -187,14 +142,6 @@ type ListenerCfg struct {
|
|||||||
// RPCListeners can be set to the listeners to use for the RPC server.
|
// RPCListeners can be set to the listeners to use for the RPC server.
|
||||||
// If empty a regular network listener will be created.
|
// If empty a regular network listener will be created.
|
||||||
RPCListeners []*ListenerWithSignal
|
RPCListeners []*ListenerWithSignal
|
||||||
|
|
||||||
// ExternalRPCSubserverCfg is optional and specifies the registration
|
|
||||||
// callback and permissions to register external gRPC subservers.
|
|
||||||
ExternalRPCSubserverCfg *RPCSubserverConfig
|
|
||||||
|
|
||||||
// ExternalRestRegistrar is optional and specifies the registration
|
|
||||||
// callback to register external REST subservers.
|
|
||||||
ExternalRestRegistrar RestRegistrar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errStreamIsolationWithProxySkip = errors.New(
|
var errStreamIsolationWithProxySkip = errors.New(
|
||||||
@@ -205,7 +152,9 @@ var errStreamIsolationWithProxySkip = errors.New(
|
|||||||
// validated main configuration struct and an optional listener config struct.
|
// validated main configuration struct and an optional listener config struct.
|
||||||
// This function starts all main system components then blocks until a signal
|
// This function starts all main system components then blocks until a signal
|
||||||
// is received on the shutdownChan at which point everything is shut down again.
|
// is received on the shutdownChan at which point everything is shut down again.
|
||||||
func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error {
|
func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
|
||||||
|
interceptor signal.Interceptor) error {
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
ltndLog.Info("Shutdown complete\n")
|
ltndLog.Info("Shutdown complete\n")
|
||||||
err := cfg.LogWriter.Close()
|
err := cfg.LogWriter.Close()
|
||||||
@@ -387,12 +336,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, interceptor signal.Interceptor) error
|
|||||||
|
|
||||||
// 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(
|
rpcServer := newRPCServer(cfg, interceptorChain, implCfg, interceptor)
|
||||||
cfg, interceptorChain, lisCfg.ExternalRPCSubserverCfg,
|
|
||||||
lisCfg.ExternalRestRegistrar,
|
|
||||||
interceptor,
|
|
||||||
)
|
|
||||||
|
|
||||||
err = rpcServer.RegisterWithGrpcServer(grpcServer)
|
err = rpcServer.RegisterWithGrpcServer(grpcServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+2
-1
@@ -99,6 +99,7 @@ func Start(extraArgs string, rpcReady Callback) {
|
|||||||
Ready: rpcListening,
|
Ready: rpcListening,
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
implCfg := loadedConfig.ImplementationConfig()
|
||||||
|
|
||||||
// Call the "real" main in a nested manner so the defers will properly
|
// Call the "real" main in a nested manner so the defers will properly
|
||||||
// be executed in the case of a graceful shutdown.
|
// be executed in the case of a graceful shutdown.
|
||||||
@@ -107,7 +108,7 @@ func Start(extraArgs string, rpcReady Callback) {
|
|||||||
defer close(quit)
|
defer close(quit)
|
||||||
|
|
||||||
if err := lnd.Main(
|
if err := lnd.Main(
|
||||||
loadedConfig, cfg, shutdownInterceptor,
|
loadedConfig, cfg, implCfg, shutdownInterceptor,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
if e, ok := err.(*flags.Error); ok &&
|
if e, ok := err.(*flags.Error); ok &&
|
||||||
e.Type == flags.ErrHelp {
|
e.Type == flags.ErrHelp {
|
||||||
|
|||||||
+18
-38
@@ -611,16 +611,12 @@ type rpcServer struct {
|
|||||||
// selfNode is our own pubkey.
|
// selfNode is our own pubkey.
|
||||||
selfNode route.Vertex
|
selfNode route.Vertex
|
||||||
|
|
||||||
// interceptorChain is the the interceptor added to our gRPC server.
|
// interceptorChain is the interceptor added to our gRPC server.
|
||||||
interceptorChain *rpcperms.InterceptorChain
|
interceptorChain *rpcperms.InterceptorChain
|
||||||
|
|
||||||
// extSubserverCfg is optional and specifies the registration
|
// implCfg is the configuration for some of the interfaces that can be
|
||||||
// callback and permissions to register external gRPC subservers.
|
// provided externally.
|
||||||
extSubserverCfg *RPCSubserverConfig
|
implCfg *ImplementationCfg
|
||||||
|
|
||||||
// extRestRegistrar is optional and specifies the registration
|
|
||||||
// callback to register external REST subservers.
|
|
||||||
extRestRegistrar RestRegistrar
|
|
||||||
|
|
||||||
// interceptor is used to be able to request a shutdown
|
// interceptor is used to be able to request a shutdown
|
||||||
interceptor signal.Interceptor
|
interceptor signal.Interceptor
|
||||||
@@ -634,9 +630,7 @@ var _ lnrpc.LightningServer = (*rpcServer)(nil)
|
|||||||
// dependencies are added, this will be an non-functioning RPC server only to
|
// dependencies are added, this will be an non-functioning RPC server only to
|
||||||
// be used to register the LightningService with the gRPC server.
|
// be used to register the LightningService with the gRPC server.
|
||||||
func newRPCServer(cfg *Config, interceptorChain *rpcperms.InterceptorChain,
|
func newRPCServer(cfg *Config, interceptorChain *rpcperms.InterceptorChain,
|
||||||
extSubserverCfg *RPCSubserverConfig,
|
implCfg *ImplementationCfg, interceptor signal.Interceptor) *rpcServer {
|
||||||
extRestRegistrar RestRegistrar,
|
|
||||||
interceptor signal.Interceptor) *rpcServer {
|
|
||||||
|
|
||||||
// We go trhough the list of registered sub-servers, and create a gRPC
|
// We go trhough the list of registered sub-servers, and create a gRPC
|
||||||
// handler for each. These are used to register with the gRPC server
|
// handler for each. These are used to register with the gRPC server
|
||||||
@@ -654,8 +648,7 @@ func newRPCServer(cfg *Config, interceptorChain *rpcperms.InterceptorChain,
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
subGrpcHandlers: subServerHandlers,
|
subGrpcHandlers: subServerHandlers,
|
||||||
interceptorChain: interceptorChain,
|
interceptorChain: interceptorChain,
|
||||||
extSubserverCfg: extSubserverCfg,
|
implCfg: implCfg,
|
||||||
extRestRegistrar: extRestRegistrar,
|
|
||||||
quit: make(chan struct{}, 1),
|
quit: make(chan struct{}, 1),
|
||||||
interceptor: interceptor,
|
interceptor: interceptor,
|
||||||
}
|
}
|
||||||
@@ -785,30 +778,22 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
|||||||
|
|
||||||
// External subserver possibly need to register their own permissions
|
// External subserver possibly need to register their own permissions
|
||||||
// and macaroon validator.
|
// and macaroon validator.
|
||||||
if r.extSubserverCfg != nil {
|
for method, ops := range r.implCfg.ExternalValidator.Permissions() {
|
||||||
macValidator := r.extSubserverCfg.MacaroonValidator
|
|
||||||
for method, ops := range r.extSubserverCfg.Permissions {
|
|
||||||
err := r.interceptorChain.AddPermission(method, ops)
|
err := r.interceptorChain.AddPermission(method, ops)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give the external subservers the possibility
|
// Give the external subservers the possibility to also use
|
||||||
// to also use their own validator to check any
|
// their own validator to check any macaroons attached to calls
|
||||||
// macaroons attached to calls to this method.
|
// to this method. This allows them to have their own root key
|
||||||
// This allows them to have their own root key
|
|
||||||
// ID database and permission entities.
|
// ID database and permission entities.
|
||||||
if macValidator != nil {
|
err = macService.RegisterExternalValidator(
|
||||||
err := macService.RegisterExternalValidator(
|
method, r.implCfg.ExternalValidator,
|
||||||
method, macValidator,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could "+
|
return fmt.Errorf("could not register external "+
|
||||||
"not register "+
|
"macaroon validator: %v", err)
|
||||||
"external macaroon "+
|
|
||||||
"validator: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -846,14 +831,11 @@ func (r *rpcServer) RegisterWithGrpcServer(grpcServer *grpc.Server) error {
|
|||||||
// their own, we just let them register their services to the same
|
// their own, we just let them register their services to the same
|
||||||
// server instance so all of them can be exposed on the same
|
// server instance so all of them can be exposed on the same
|
||||||
// port/listener.
|
// port/listener.
|
||||||
if r.extSubserverCfg != nil && r.extSubserverCfg.Registrar != nil {
|
err := r.implCfg.RegisterGrpcSubserver(grpcServer)
|
||||||
registerer := r.extSubserverCfg.Registrar
|
|
||||||
err := registerer.RegisterGrpcSubserver(grpcServer)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("error registering external gRPC "+
|
rpcsLog.Errorf("error registering external gRPC "+
|
||||||
"subserver: %v", err)
|
"subserver: %v", err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -908,14 +890,12 @@ func (r *rpcServer) RegisterWithRestProxy(restCtx context.Context,
|
|||||||
// Before listening on any of the interfaces, we also want to give the
|
// Before listening on any of the interfaces, we also want to give the
|
||||||
// external subservers a chance to register their own REST proxy stub
|
// external subservers a chance to register their own REST proxy stub
|
||||||
// with our mux instance.
|
// with our mux instance.
|
||||||
if r.extRestRegistrar != nil {
|
err = r.implCfg.RegisterRestSubserver(
|
||||||
err := r.extRestRegistrar.RegisterRestSubserver(
|
|
||||||
restCtx, restMux, restProxyDest, restDialOpts,
|
restCtx, restMux, restProxyDest, restDialOpts,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rpcsLog.Errorf("error registering "+
|
rpcsLog.Errorf("error registering external REST subserver: %v",
|
||||||
"external REST subserver: %v", err)
|
err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user