From db94d55d21be94496bb63214a80901974f48580b Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 13:44:29 +0200 Subject: [PATCH 01/10] perms: hold the mutex for any public call --- perms/manager.go | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/perms/manager.go b/perms/manager.go index 5d94d340c..49fd725f6 100644 --- a/perms/manager.go +++ b/perms/manager.go @@ -33,10 +33,10 @@ type Manager struct { // are available for use. This map will start out not including any of // lnd's sub-server permissions. Only when the LND build tags are // obtained and OnLNDBuildTags is called will this map include the - // available LND sub-server permissions. This map must only be accessed - // once the permsMu mutex is held. - perms map[string][]bakery.Op - permsMu sync.RWMutex + // available LND sub-server permissions. + perms map[string][]bakery.Op + + mu sync.RWMutex } // NewManager constructs a new Manager instance and collects any of the @@ -105,8 +105,8 @@ func NewManager(withAllSubServers bool) (*Manager, error) { // that it does not require a macaroon for validation. A URL is considered // white-listed if it has no operations associated with a URL. func (pm *Manager) IsWhiteListedURL(url string) bool { - pm.permsMu.Lock() - defer pm.permsMu.Unlock() + pm.mu.Lock() + defer pm.mu.Unlock() ops, ok := pm.perms[url] @@ -118,8 +118,8 @@ func (pm *Manager) IsWhiteListedURL(url string) bool { func (pm *Manager) RegisterSubServer(name string, permissions map[string][]bakery.Op, whiteListURLs map[string]struct{}) { - pm.permsMu.Lock() - defer pm.permsMu.Unlock() + pm.mu.Lock() + defer pm.mu.Unlock() pm.fixedPerms[name] = permissions @@ -142,8 +142,8 @@ func (pm *Manager) RegisterSubServer(name string, // permissions to add to the main permissions list. This method should only // be called once. func (pm *Manager) OnLNDBuildTags(lndBuildTags []string) { - pm.permsMu.Lock() - defer pm.permsMu.Unlock() + pm.mu.Lock() + defer pm.mu.Unlock() tagLookup := make(map[string]bool) for _, t := range lndBuildTags { @@ -170,8 +170,8 @@ func (pm *Manager) OnLNDBuildTags(lndBuildTags []string) { // the uri is known to the manager. The second return parameter will be false // if the URI is unknown to the manager. func (pm *Manager) URIPermissions(uri string) ([]bakery.Op, bool) { - pm.permsMu.RLock() - defer pm.permsMu.RUnlock() + pm.mu.RLock() + defer pm.mu.RUnlock() ops, ok := pm.perms[uri] return ops, ok @@ -182,8 +182,8 @@ func (pm *Manager) URIPermissions(uri string) ([]bakery.Op, bool) { // are a list of URIs that match the regex and the boolean represents whether // the given uri is in fact a regex. func (pm *Manager) MatchRegexURI(uriRegex string) ([]string, bool) { - pm.permsMu.RLock() - defer pm.permsMu.RUnlock() + pm.mu.RLock() + defer pm.mu.RUnlock() // If the given uri string is one of our permissions, then it is not // a regex. @@ -215,8 +215,8 @@ func (pm *Manager) MatchRegexURI(uriRegex string) ([]string, bool) { // manager is aware of. Optionally, readOnly can be set to true if only the // read-only permissions should be returned. func (pm *Manager) ActivePermissions(readOnly bool) []bakery.Op { - pm.permsMu.RLock() - defer pm.permsMu.RUnlock() + pm.mu.RLock() + defer pm.mu.RUnlock() // De-dup the permissions and optionally apply the read-only filter. dedupMap := make(map[string]map[string]bool) @@ -256,6 +256,9 @@ func (pm *Manager) ActivePermissions(readOnly bool) []bakery.Op { // _except_ for any LND permissions. In other words, this returns permissions // for which the external validator of Lit is responsible. func (pm *Manager) GetLitPerms() map[string][]bakery.Op { + pm.mu.Lock() + defer pm.mu.Unlock() + result := make(map[string][]bakery.Op) for subserver, ops := range pm.fixedPerms { if subserver == lndPerms { @@ -271,6 +274,9 @@ func (pm *Manager) GetLitPerms() map[string][]bakery.Op { // IsSubServerURI if the given URI belongs to the RPC of the given server. func (pm *Manager) IsSubServerURI(name string, uri string) bool { + pm.mu.Lock() + defer pm.mu.Unlock() + if name == lndPerms { return pm.isLndURI(uri) } From 5430e6d4e9e9bbbb12c464c74f9777ba6d249227 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 13:55:36 +0200 Subject: [PATCH 02/10] perms: add a ForceWhiteListURL Which will result in a specific URL returning `true` for `IsWhiteListedURL` while still returning the original permissions in `URIPermissions`. This will be used for situations where we want to explicitly handle the verification of a call to a URL in a code path that happens after path that hits `IsWhiteListed`. --- perms/manager.go | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/perms/manager.go b/perms/manager.go index 49fd725f6..2d83e6ece 100644 --- a/perms/manager.go +++ b/perms/manager.go @@ -1,6 +1,7 @@ package perms import ( + "fmt" "regexp" "strings" "sync" @@ -36,6 +37,13 @@ type Manager struct { // available LND sub-server permissions. perms map[string][]bakery.Op + // forcedWhiteListPerms holds a set of URIs that should be considered + // white listed even if they do have associated required permissions. + // IsWhiteListedURL will return true for any of these URIs but + // URIPermissions will continue to return the real permissions of the + // URI. + forcedWhiteListPerms map[string]struct{} + mu sync.RWMutex } @@ -95,19 +103,48 @@ func NewManager(withAllSubServers bool) (*Manager, error) { } return &Manager{ - lndSubServerPerms: lndSubServerPerms, - fixedPerms: permissions, - perms: allPerms, + lndSubServerPerms: lndSubServerPerms, + fixedPerms: permissions, + perms: allPerms, + forcedWhiteListPerms: make(map[string]struct{}), }, nil } +// ForceWhiteListURL will whitelist the given URL resulting in +// IsWhiteListedURL returning true for this URL in future calls. It will return +// an error if this URL is unknown to the Manager. URIPermissions will continue +// to return the real set of permissions for the URL. +// +// NOTE: URLs should be whitelisted with caution. This should only be done if +// the caller will explicitly handle the verification of a URL in a location +// other than where the usual permission verification is done (ie, in a code +// path other than the one calling IsWhiteListedURL. +func (pm *Manager) ForceWhiteListURL(url string) error { + pm.mu.Lock() + defer pm.mu.Unlock() + + _, ok := pm.perms[url] + if !ok { + return fmt.Errorf("only known URLs can be white listed") + } + + pm.forcedWhiteListPerms[url] = struct{}{} + + return nil +} + // IsWhiteListedURL returns true if the given URL has been whitelisted meaning // that it does not require a macaroon for validation. A URL is considered -// white-listed if it has no operations associated with a URL. +// white-listed if it has no operations associated with a URL or if it has +// been explicitly whitelisted. func (pm *Manager) IsWhiteListedURL(url string) bool { pm.mu.Lock() defer pm.mu.Unlock() + if _, ok := pm.forcedWhiteListPerms[url]; ok { + return true + } + ops, ok := pm.perms[url] return ok && len(ops) == 0 From b12d226f79ff2bc584d09b401a9aa21bb787ad50 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 14:04:55 +0200 Subject: [PATCH 03/10] lit: create litMacValidator a type which implements the macaroons.MacaroonValidator interface which purely authenticates a call against LiT's macaroon service. Then, make this available to the rpc proxy. It is not used yet. --- rpc_proxy.go | 5 ++++- terminal.go | 55 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/rpc_proxy.go b/rpc_proxy.go index 374226f22..d57c352d9 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -66,7 +66,8 @@ func (e *proxyErr) Unwrap() error { // newRpcProxy creates a new RPC proxy that can take any native gRPC, grpc-web // or REST request and delegate (and convert if necessary) it to the correct // component. -func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator, +func newRpcProxy(cfg *Config, validator, + litMacValidator macaroons.MacaroonValidator, superMacValidator session.SuperMacaroonValidator, permsMgr *perms.Manager, subServerMgr *subservers.Manager, statusMgr *litstatus.Manager) *rpcProxy { @@ -87,6 +88,7 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator, cfg: cfg, basicAuth: basicAuth, permsMgr: permsMgr, + litMacValidator: litMacValidator, macValidator: validator, superMacValidator: superMacValidator, subServerMgr: subServerMgr, @@ -169,6 +171,7 @@ type rpcProxy struct { bakeSuperMac bakeSuperMac + litMacValidator macaroons.MacaroonValidator macValidator macaroons.MacaroonValidator superMacValidator session.SuperMacaroonValidator diff --git a/terminal.go b/terminal.go index a474e124a..3f49a3be0 100644 --- a/terminal.go +++ b/terminal.go @@ -296,8 +296,8 @@ func (g *LightningTerminal) Run() error { // Construct the rpcProxy. It must be initialised before the main web // server is started. g.rpcProxy = newRpcProxy( - g.cfg, g, g.validateSuperMacaroon, g.permsMgr, g.subServerMgr, - g.statusMgr, + g.cfg, g, &litMacValidator{g}, g.validateSuperMacaroon, + g.permsMgr, g.subServerMgr, g.statusMgr, ) // Register any gRPC services that should be served using LiT's @@ -1212,19 +1212,12 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context, } if g.permsMgr.IsSubServerURI(subservers.LIT, fullMethod) { - if !g.macaroonServiceStarted { - return fmt.Errorf("the macaroon service has not " + - "started yet") - } - - if err := g.macaroonService.ValidateMacaroon( + validator := &litMacValidator{g} + err = validator.ValidateMacaroon( ctx, requiredPermissions, fullMethod, - ); err != nil { - return &proxyErr{ - proxyContext: "lit", - wrapped: fmt.Errorf("invalid macaroon: %w", - err), - } + ) + if err != nil { + return err } } @@ -1236,6 +1229,40 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context, return nil } +// litMacValidator wraps the LightningTerminal struct and uses it to implement +// the macaroons.ValidateMacaroon interface. Unlike the LightningTerminal's +// ValidateMacaroon method which does whitelist checks and possibly uses a +// different sub-server's macaroon validator, this implementation uses only +// LiT's own macaroon service to verify the call. +type litMacValidator struct { + *LightningTerminal +} + +// ValidateMacaroon checks that the given call is properly authenticated +// according to LiT's macaroon service. +// +// NOTE: This is part of the macaroons.ValidateMacaroon interface. +func (g *litMacValidator) ValidateMacaroon(ctx context.Context, + requiredPermissions []bakery.Op, fullMethod string) error { + + if !g.macaroonServiceStarted { + return fmt.Errorf("the macaroon service has not " + + "started yet") + } + + if err := g.macaroonService.ValidateMacaroon( + ctx, requiredPermissions, fullMethod, + ); err != nil { + return &proxyErr{ + proxyContext: "lit", + wrapped: fmt.Errorf("invalid macaroon: %w", + err), + } + } + + return nil +} + // Permissions returns all permissions for which the external validator of the // terminal is responsible. // From 744dd3a41cc2824e3a051bc5bc5fe85729835630 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 14:12:38 +0200 Subject: [PATCH 04/10] lit: make the basicClient available to the rpcProxy --- rpc_proxy.go | 11 +++++++++++ terminal.go | 1 + 2 files changed, 12 insertions(+) diff --git a/rpc_proxy.go b/rpc_proxy.go index d57c352d9..65c19e97b 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -18,6 +18,7 @@ import ( litstatus "github.com/lightninglabs/lightning-terminal/status" "github.com/lightninglabs/lightning-terminal/subservers" "github.com/lightningnetwork/lnd/lncfg" + "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/macaroons" grpcProxy "github.com/mwitkow/grpc-proxy/proxy" "google.golang.org/grpc" @@ -179,6 +180,10 @@ type rpcProxy struct { lndConn *grpc.ClientConn + // basicClient is an interface to an LND node. Note that this may be nil + // until it is set via setBasicLNDClient. + basicClient lnrpc.LightningClient + grpcServer *grpc.Server grpcWebProxy *grpcweb.WrappedGrpcServer } @@ -211,6 +216,12 @@ func (p *rpcProxy) Stop() error { return nil } +// setBasicLNDClient provides the rpcProxy with a connection to LND +// implementing the lnrpc.LightningClient interface. +func (p *rpcProxy) setBasicLNDClient(client lnrpc.LightningClient) { + p.basicClient = client +} + // StopDaemon will send a shutdown request to the interrupt handler, triggering // a graceful shutdown of the daemon. // diff --git a/terminal.go b/terminal.go index 3f49a3be0..cd223c3eb 100644 --- a/terminal.go +++ b/terminal.go @@ -805,6 +805,7 @@ func (g *LightningTerminal) setUpLNDClients(lndQuit chan struct{}) error { log.Infof("Retrying to connect basic lnd client") } + g.rpcProxy.setBasicLNDClient(g.basicClient) // Now we know that the connection itself is ready. But we also need to // wait for two things: The chain notifier to be ready and the lnd From 313262d1c3b829757afe894ed7ffa229eb31827a Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 14:14:19 +0200 Subject: [PATCH 05/10] lit: change the signature of bakeSuperMac to take an LND client So that we have control over which LND client the rpcProxy should use for various calls. --- rpc_proxy.go | 5 +++-- terminal.go | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/rpc_proxy.go b/rpc_proxy.go index 65c19e97b..42f01a63b 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -189,7 +189,8 @@ type rpcProxy struct { } // bakeSuperMac can be used to bake a new super macaroon. -type bakeSuperMac func(ctx context.Context, rootKeyID uint32) (string, error) +type bakeSuperMac func(ctx context.Context, lndClient lnrpc.LightningClient, + rootKeyID uint32) (string, error) // Start creates initial connection to lnd. func (p *rpcProxy) Start(lndConn *grpc.ClientConn, @@ -259,7 +260,7 @@ func (p *rpcProxy) BakeSuperMacaroon(ctx context.Context, return nil, ErrWaitingToStart } - superMac, err := p.bakeSuperMac(ctx, req.RootKeyIdSuffix) + superMac, err := p.bakeSuperMac(ctx, p.basicClient, req.RootKeyIdSuffix) if err != nil { return nil, err } diff --git a/terminal.go b/terminal.go index cd223c3eb..ca81c6bbc 100644 --- a/terminal.go +++ b/terminal.go @@ -579,8 +579,9 @@ func (g *LightningTerminal) start() error { // bakeSuperMac is a closure that can be used to bake a new super // macaroon that contains all active permissions. - bakeSuperMac := func(ctx context.Context, rootKeyIDSuffix uint32) ( - string, error) { + bakeSuperMac := func(ctx context.Context, + lndClient lnrpc.LightningClient, + rootKeyIDSuffix uint32) (string, error) { var suffixBytes [4]byte binary.BigEndian.PutUint32(suffixBytes[:], rootKeyIDSuffix) @@ -588,7 +589,7 @@ func (g *LightningTerminal) start() error { rootKeyID := session.NewSuperMacaroonRootKeyID(suffixBytes) return BakeSuperMacaroon( - ctx, g.basicClient, rootKeyID, + ctx, lndClient, rootKeyID, g.permsMgr.ActivePermissions(false), nil, ) } From 7635079490dfbc70434d7b6281083bbf77326299 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 14:21:06 +0200 Subject: [PATCH 06/10] config: extract LND dial address helper so that we can use it elsewhere. --- config.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/config.go b/config.go index e1dd5185d..ca922cda0 100644 --- a/config.go +++ b/config.go @@ -235,21 +235,32 @@ type Config struct { func (c *Config) lndConnectParams() (string, lndclient.Network, string, string, []byte) { + lndDialAddr := c.lndDialAddr() + // In remote lnd mode, we just pass along what was configured in the // remote section of the lnd config. - if c.LndMode == ModeRemote { - return c.Remote.Lnd.RPCServer, + if c.lndRemote { + return lndDialAddr, lndclient.Network(c.Network), lncfg.CleanAndExpandPath(c.Remote.Lnd.TLSCertPath), lncfg.CleanAndExpandPath(c.Remote.Lnd.MacaroonPath), nil } - // When we start lnd internally, we take the listen address as - // the client dial address. But with TLS enabled by default, we - // cannot call 0.0.0.0 internally when dialing lnd as that IP - // address isn't in the cert. We need to rewrite it to the - // loopback address. + return lndDialAddr, lndclient.Network(c.Network), "", "", + c.lndAdminMacaroon +} + +// lndDialAddr returns the address to connect to LND on. +func (c *Config) lndDialAddr() string { + if c.lndRemote { + return c.Remote.Lnd.RPCServer + } + + // When we start lnd internally, we take the listen address as the + // client dial address. But with TLS enabled by default, we cannot call + // 0.0.0.0 internally when dialing lnd as that IP address isn't in the + // cert. We need to rewrite it to the loopback address. lndDialAddr := c.Lnd.RPCListeners[0].String() switch { case strings.Contains(lndDialAddr, "0.0.0.0"): @@ -258,13 +269,10 @@ func (c *Config) lndConnectParams() (string, lndclient.Network, string, ) case strings.Contains(lndDialAddr, "[::]"): - lndDialAddr = strings.Replace( - lndDialAddr, "[::]", "[::1]", 1, - ) + lndDialAddr = strings.Replace(lndDialAddr, "[::]", "[::1]", 1) } - return lndDialAddr, lndclient.Network(c.Network), "", "", - c.lndAdminMacaroon + return lndDialAddr } // defaultConfig returns a configuration struct with all default values set. From 4bdde68581ae62e0ccea133b56ee9adca9b4cf18 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 14:37:52 +0200 Subject: [PATCH 07/10] litrpc: add new stateless_init field to BakeSuperMacaroon message --- app/src/types/generated/proxy_pb.d.ts | 4 ++ app/src/types/generated/proxy_pb.js | 31 ++++++++++- litrpc/firewall.pb.gw.go | 2 +- litrpc/firewall.swagger.json | 2 + litrpc/lit-accounts.pb.gw.go | 4 +- litrpc/lit-accounts.swagger.json | 4 ++ litrpc/lit-autopilot.pb.gw.go | 2 +- litrpc/lit-autopilot.swagger.json | 5 ++ litrpc/lit-sessions.pb.gw.go | 2 +- litrpc/lit-sessions.swagger.json | 4 ++ litrpc/lit-status.pb.gw.go | 2 +- litrpc/lit-status.swagger.json | 1 + litrpc/proxy.pb.go | 74 ++++++++++++++++----------- litrpc/proxy.pb.gw.go | 2 +- litrpc/proxy.proto | 6 +++ litrpc/proxy.swagger.json | 5 ++ proto/proxy.proto | 6 +++ 17 files changed, 117 insertions(+), 39 deletions(-) diff --git a/app/src/types/generated/proxy_pb.d.ts b/app/src/types/generated/proxy_pb.d.ts index 4399a2954..26118a21e 100644 --- a/app/src/types/generated/proxy_pb.d.ts +++ b/app/src/types/generated/proxy_pb.d.ts @@ -7,6 +7,9 @@ export class BakeSuperMacaroonRequest extends jspb.Message { getRootKeyIdSuffix(): number; setRootKeyIdSuffix(value: number): void; + getStatelessInit(): boolean; + setStatelessInit(value: boolean): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): BakeSuperMacaroonRequest.AsObject; static toObject(includeInstance: boolean, msg: BakeSuperMacaroonRequest): BakeSuperMacaroonRequest.AsObject; @@ -20,6 +23,7 @@ export class BakeSuperMacaroonRequest extends jspb.Message { export namespace BakeSuperMacaroonRequest { export type AsObject = { rootKeyIdSuffix: number, + statelessInit: boolean, } } diff --git a/app/src/types/generated/proxy_pb.js b/app/src/types/generated/proxy_pb.js index 3aa964de7..e5b873e14 100644 --- a/app/src/types/generated/proxy_pb.js +++ b/app/src/types/generated/proxy_pb.js @@ -67,7 +67,8 @@ proto.litrpc.BakeSuperMacaroonRequest.prototype.toObject = function(opt_includeI */ proto.litrpc.BakeSuperMacaroonRequest.toObject = function(includeInstance, msg) { var f, obj = { - rootKeyIdSuffix: jspb.Message.getFieldWithDefault(msg, 1, 0) + rootKeyIdSuffix: jspb.Message.getFieldWithDefault(msg, 1, 0), + statelessInit: jspb.Message.getFieldWithDefault(msg, 2, false) }; if (includeInstance) { @@ -108,6 +109,10 @@ proto.litrpc.BakeSuperMacaroonRequest.deserializeBinaryFromReader = function(msg var value = /** @type {number} */ (reader.readUint32()); msg.setRootKeyIdSuffix(value); break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setStatelessInit(value); + break; default: reader.skipField(); break; @@ -144,6 +149,13 @@ proto.litrpc.BakeSuperMacaroonRequest.serializeBinaryToWriter = function(message f ); } + f = message.getStatelessInit(); + if (f) { + writer.writeBool( + 2, + f + ); + } }; @@ -162,6 +174,23 @@ proto.litrpc.BakeSuperMacaroonRequest.prototype.setRootKeyIdSuffix = function(va }; +/** + * optional bool stateless_init = 2; + * Note that Boolean fields may be set to 0/1 when serialized from a Java server. + * You should avoid comparisons like {@code val === true/false} in those cases. + * @return {boolean} + */ +proto.litrpc.BakeSuperMacaroonRequest.prototype.getStatelessInit = function() { + return /** @type {boolean} */ (jspb.Message.getFieldWithDefault(this, 2, false)); +}; + + +/** @param {boolean} value */ +proto.litrpc.BakeSuperMacaroonRequest.prototype.setStatelessInit = function(value) { + jspb.Message.setProto3BooleanField(this, 2, value); +}; + + /** * Generated by JsPbCodeGenerator. diff --git a/litrpc/firewall.pb.gw.go b/litrpc/firewall.pb.gw.go index 1bbc19886..420e2a4ee 100644 --- a/litrpc/firewall.pb.gw.go +++ b/litrpc/firewall.pb.gw.go @@ -161,7 +161,7 @@ func RegisterFirewallHandlerServer(ctx context.Context, mux *runtime.ServeMux, s // RegisterFirewallHandlerFromEndpoint is same as RegisterFirewallHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterFirewallHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } diff --git a/litrpc/firewall.swagger.json b/litrpc/firewall.swagger.json index a5c41b9db..d76caa9ba 100644 --- a/litrpc/firewall.swagger.json +++ b/litrpc/firewall.swagger.json @@ -211,6 +211,7 @@ "actions": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcAction" }, "description": "A list of actions performed by the autopilot server." @@ -281,6 +282,7 @@ "details": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/protobufAny" } } diff --git a/litrpc/lit-accounts.pb.gw.go b/litrpc/lit-accounts.pb.gw.go index 7d38b74d7..4e095eea5 100644 --- a/litrpc/lit-accounts.pb.gw.go +++ b/litrpc/lit-accounts.pb.gw.go @@ -152,7 +152,7 @@ func local_request_Accounts_ListAccounts_0(ctx context.Context, marshaler runtim } var ( - filter_Accounts_RemoveAccount_0 = &utilities.DoubleArray{Encoding: map[string]int{"id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} + filter_Accounts_RemoveAccount_0 = &utilities.DoubleArray{Encoding: map[string]int{"id": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) func request_Accounts_RemoveAccount_0(ctx context.Context, marshaler runtime.Marshaler, client AccountsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -333,7 +333,7 @@ func RegisterAccountsHandlerServer(ctx context.Context, mux *runtime.ServeMux, s // RegisterAccountsHandlerFromEndpoint is same as RegisterAccountsHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterAccountsHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } diff --git a/litrpc/lit-accounts.swagger.json b/litrpc/lit-accounts.swagger.json index 799b2477b..4889c71ff 100644 --- a/litrpc/lit-accounts.swagger.json +++ b/litrpc/lit-accounts.swagger.json @@ -196,6 +196,7 @@ "invoices": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcAccountInvoice" }, "description": "The list of invoices created by the account. An invoice created by an\naccount will credit the account balance if it is settled." @@ -203,6 +204,7 @@ "payments": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcAccountPayment" }, "description": "The list of payments made by the account. A payment made by an account will\ndebit the account balance if it is settled." @@ -281,6 +283,7 @@ "accounts": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcAccount" }, "description": "All accounts in the account database." @@ -312,6 +315,7 @@ "details": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/protobufAny" } } diff --git a/litrpc/lit-autopilot.pb.gw.go b/litrpc/lit-autopilot.pb.gw.go index 53d8c1cd5..20ce9efa1 100644 --- a/litrpc/lit-autopilot.pb.gw.go +++ b/litrpc/lit-autopilot.pb.gw.go @@ -265,7 +265,7 @@ func RegisterAutopilotHandlerServer(ctx context.Context, mux *runtime.ServeMux, // RegisterAutopilotHandlerFromEndpoint is same as RegisterAutopilotHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterAutopilotHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } diff --git a/litrpc/lit-autopilot.swagger.json b/litrpc/lit-autopilot.swagger.json index 406055995..e1342634d 100644 --- a/litrpc/lit-autopilot.swagger.json +++ b/litrpc/lit-autopilot.swagger.json @@ -295,6 +295,7 @@ "permissions_list": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcPermissions" }, "description": "A list of URI permissions required by the feature." @@ -361,6 +362,7 @@ "sessions": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcSession" }, "description": "A list of the Autopilot sessions." @@ -386,6 +388,7 @@ "permissions": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcMacaroonPermission" }, "description": "A list of permissions that should be included in the macaroon." @@ -451,6 +454,7 @@ "operations": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcMacaroonPermission" }, "description": "A list of the permissions required for this method." @@ -696,6 +700,7 @@ "details": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/protobufAny" } } diff --git a/litrpc/lit-sessions.pb.gw.go b/litrpc/lit-sessions.pb.gw.go index a52b2fb99..3559339a6 100644 --- a/litrpc/lit-sessions.pb.gw.go +++ b/litrpc/lit-sessions.pb.gw.go @@ -222,7 +222,7 @@ func RegisterSessionsHandlerServer(ctx context.Context, mux *runtime.ServeMux, s // RegisterSessionsHandlerFromEndpoint is same as RegisterSessionsHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterSessionsHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } diff --git a/litrpc/lit-sessions.swagger.json b/litrpc/lit-sessions.swagger.json index d233c56c4..2300f69c0 100644 --- a/litrpc/lit-sessions.swagger.json +++ b/litrpc/lit-sessions.swagger.json @@ -132,6 +132,7 @@ "macaroon_custom_permissions": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcMacaroonPermission" }, "description": "Any custom permissions to add the session's macaroon." @@ -258,6 +259,7 @@ "sessions": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcSession" }, "description": "A list of sessions." @@ -283,6 +285,7 @@ "permissions": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/litrpcMacaroonPermission" }, "description": "A list of permissions that should be included in the macaroon." @@ -556,6 +559,7 @@ "details": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/protobufAny" } } diff --git a/litrpc/lit-status.pb.gw.go b/litrpc/lit-status.pb.gw.go index 251b21e5c..8edf5c1b9 100644 --- a/litrpc/lit-status.pb.gw.go +++ b/litrpc/lit-status.pb.gw.go @@ -86,7 +86,7 @@ func RegisterStatusHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser // RegisterStatusHandlerFromEndpoint is same as RegisterStatusHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterStatusHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } diff --git a/litrpc/lit-status.swagger.json b/litrpc/lit-status.swagger.json index 532fb220c..d8331522a 100644 --- a/litrpc/lit-status.swagger.json +++ b/litrpc/lit-status.swagger.json @@ -95,6 +95,7 @@ "details": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/protobufAny" } } diff --git a/litrpc/proxy.pb.go b/litrpc/proxy.pb.go index 63aba949c..e1ae7ebc1 100644 --- a/litrpc/proxy.pb.go +++ b/litrpc/proxy.pb.go @@ -28,6 +28,9 @@ type BakeSuperMacaroonRequest struct { // The root key ID suffix is the 4-byte suffix of the root key ID that will // be used to create the macaroon. RootKeyIdSuffix uint32 `protobuf:"varint,1,opt,name=root_key_id_suffix,json=rootKeyIdSuffix,proto3" json:"root_key_id_suffix,omitempty"` + // Whether LND and LiT are running in stateless_init mode. If this is true, + // then call's macaroon will be authenticated against LND's macaroon service. + StatelessInit bool `protobuf:"varint,2,opt,name=stateless_init,json=statelessInit,proto3" json:"stateless_init,omitempty"` } func (x *BakeSuperMacaroonRequest) Reset() { @@ -69,6 +72,13 @@ func (x *BakeSuperMacaroonRequest) GetRootKeyIdSuffix() uint32 { return 0 } +func (x *BakeSuperMacaroonRequest) GetStatelessInit() bool { + if x != nil { + return x.StatelessInit + } + return false +} + type BakeSuperMacaroonResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -283,40 +293,42 @@ var File_proxy_proto protoreflect.FileDescriptor var file_proxy_proto_rawDesc = []byte{ 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x6c, - 0x69, 0x74, 0x72, 0x70, 0x63, 0x22, 0x47, 0x0a, 0x18, 0x42, 0x61, 0x6b, 0x65, 0x53, 0x75, 0x70, + 0x69, 0x74, 0x72, 0x70, 0x63, 0x22, 0x6e, 0x0a, 0x18, 0x42, 0x61, 0x6b, 0x65, 0x53, 0x75, 0x70, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x72, - 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x22, 0x37, - 0x0a, 0x19, 0x42, 0x61, 0x6b, 0x65, 0x53, 0x75, 0x70, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x61, 0x72, - 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, - 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, - 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x74, 0x6f, 0x70, 0x44, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x14, 0x0a, 0x12, - 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x32, 0xe2, 0x01, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x3a, 0x0a, 0x07, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1a, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, - 0x42, 0x61, 0x6b, 0x65, 0x53, 0x75, 0x70, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x12, 0x20, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x53, - 0x75, 0x70, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, - 0x65, 0x53, 0x75, 0x70, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, - 0x62, 0x73, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x65, 0x72, - 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x25, + 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x69, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, + 0x73, 0x49, 0x6e, 0x69, 0x74, 0x22, 0x37, 0x0a, 0x19, 0x42, 0x61, 0x6b, 0x65, 0x53, 0x75, 0x70, + 0x65, 0x72, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x13, + 0x0a, 0x11, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x32, 0xe2, 0x01, 0x0a, 0x05, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x12, 0x3a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, + 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, + 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x6c, + 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x42, 0x61, 0x6b, 0x65, 0x53, 0x75, 0x70, 0x65, 0x72, + 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x53, 0x75, 0x70, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x69, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x53, 0x75, 0x70, 0x65, 0x72, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x34, 0x5a, + 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, + 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x74, + 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/litrpc/proxy.pb.gw.go b/litrpc/proxy.pb.gw.go index 7fb93bc28..c2e46dd90 100644 --- a/litrpc/proxy.pb.gw.go +++ b/litrpc/proxy.pb.gw.go @@ -204,7 +204,7 @@ func RegisterProxyHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv // RegisterProxyHandlerFromEndpoint is same as RegisterProxyHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterProxyHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } diff --git a/litrpc/proxy.proto b/litrpc/proxy.proto index 503989a6f..75a5e7f3e 100644 --- a/litrpc/proxy.proto +++ b/litrpc/proxy.proto @@ -30,6 +30,12 @@ message BakeSuperMacaroonRequest { be used to create the macaroon. */ uint32 root_key_id_suffix = 1; + + /* + Whether LND and LiT are running in stateless_init mode. If this is true, + then call's macaroon will be authenticated against LND's macaroon service. + */ + bool stateless_init = 2; } message BakeSuperMacaroonResponse { diff --git a/litrpc/proxy.swagger.json b/litrpc/proxy.swagger.json index 5d26c3aae..a9d54cd91 100644 --- a/litrpc/proxy.swagger.json +++ b/litrpc/proxy.swagger.json @@ -114,6 +114,10 @@ "type": "integer", "format": "int64", "description": "The root key ID suffix is the 4-byte suffix of the root key ID that will\nbe used to create the macaroon." + }, + "stateless_init": { + "type": "boolean", + "description": "Whether LND and LiT are running in stateless_init mode. If this is true,\nthen call's macaroon will be authenticated against LND's macaroon service." } } }, @@ -163,6 +167,7 @@ "details": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/protobufAny" } } diff --git a/proto/proxy.proto b/proto/proxy.proto index 503989a6f..75a5e7f3e 100644 --- a/proto/proxy.proto +++ b/proto/proxy.proto @@ -30,6 +30,12 @@ message BakeSuperMacaroonRequest { be used to create the macaroon. */ uint32 root_key_id_suffix = 1; + + /* + Whether LND and LiT are running in stateless_init mode. If this is true, + then call's macaroon will be authenticated against LND's macaroon service. + */ + bool stateless_init = 2; } message BakeSuperMacaroonResponse { From 5aabbdedcfdfbf869402f80add30b02c600d3402 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 14:46:47 +0200 Subject: [PATCH 08/10] lit: add a statelessInit bool in the config So that it is easy to check elsewhere if we are in this mode. --- config.go | 3 +++ terminal.go | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/config.go b/config.go index ca922cda0..48b96db1f 100644 --- a/config.go +++ b/config.go @@ -224,6 +224,9 @@ type Config struct { poolRemote bool tapRemote bool + // Whether or not LND and LiT are running in stateless init mode. + statelessInitMode bool + // lndAdminMacaroon is the admin macaroon that is given to us by lnd // over an in-memory connection on startup. This is only set in // integrated lnd mode. diff --git a/terminal.go b/terminal.go index ca81c6bbc..7613ddd0d 100644 --- a/terminal.go +++ b/terminal.go @@ -680,7 +680,6 @@ func (g *LightningTerminal) start() error { // If we're in integrated and stateless init mode, we won't create // macaroon files in any of the subserver daemons. - createDefaultMacaroons := true if g.cfg.LndMode == ModeIntegrated && g.lndInterceptorChain != nil && g.lndInterceptorChain.MacaroonService() != nil { @@ -690,16 +689,16 @@ func (g *LightningTerminal) start() error { // daemons. In all other cases we want default macaroons so we // can use the CLI tools to interact with loop/pool/faraday. macService := g.lndInterceptorChain.MacaroonService() - createDefaultMacaroons = !macService.StatelessInit + g.cfg.statelessInitMode = macService.StatelessInit } // Both connection types are ready now, let's start our sub-servers if // they should be started locally as an integrated service. g.subServerMgr.StartIntegratedServers( - g.basicClient, g.lndClient, createDefaultMacaroons, + g.basicClient, g.lndClient, !g.cfg.statelessInitMode, ) - err = g.startInternalSubServers(createDefaultMacaroons) + err = g.startInternalSubServers(!g.cfg.statelessInitMode) if err != nil { return fmt.Errorf("could not start litd sub-servers: %v", err) } From 20504ed304874037f0c7f84009d9e0d0763c8e39 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 14:52:22 +0200 Subject: [PATCH 09/10] rpcproxy: pass the bake-mac call directly to LND if in stateless mode In this commit, we force the permissions manager to see the BakeSuperMacaroon call of LiT as whitelisted. This means that when the initial call comes in, the `LightningTerminal.ValidateMacaroon` method will return early and not validate the call (which would fail in stateless init mode since LiT does not have a macaroon service in that case). So the call ends up going through to the rpcProxy's BakeSuperMacaroon method. Here we now have the following flow: - if the request does not have stateless_init set, then we keep the flow as it was: 1) Use the lit mac validator to check that the call is allowed given the required permissions of `BakeSuperMacaroon`. 2) if it is, then use the existing connection to LND that Lit has to do the macaroon baking call. - if stateless_init mode is set, then we do the following: 1) we extract the macaroon from the call (this should be an LND macaroon) 2) we create a new connection to LND using this provided macaroon. 3) use this connection to LND to bake the macaroon. --- rpc_proxy.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++- terminal.go | 5 ++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/rpc_proxy.go b/rpc_proxy.go index 42f01a63b..39129cfd1 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -17,6 +17,7 @@ import ( "github.com/lightninglabs/lightning-terminal/session" litstatus "github.com/lightninglabs/lightning-terminal/status" "github.com/lightninglabs/lightning-terminal/subservers" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/macaroons" @@ -34,6 +35,10 @@ const ( // HeaderMacaroon is the HTTP header field name that is used to send // the macaroon. HeaderMacaroon = "Macaroon" + + // bakeSuperMacURI is the LiT service URI that can be used to bake a + // super macaroon. + bakeSuperMacURI = "/litrpc.Proxy/BakeSuperMacaroon" ) var ( @@ -251,6 +256,14 @@ func (p *rpcProxy) GetInfo(_ context.Context, _ *litrpc.GetInfoRequest) ( // BakeSuperMacaroon bakes a new macaroon that includes permissions for // all the active daemons that LiT is connected to. // +// NOTE that we must always explicitly check the provided macaroon for this +// method. If req.StatelessInit is false, then we check the macaroon against +// LiT's macaroon validator, and then we just use LiT's existing, authenticated, +// connection to LND to bake the macaroon. Otherwise, when StatelessInit is +// true, we don't verify the macaroon here. We instead create a new connection +// to LND using the provided macaroon for authentication, and we use this new +// connection to bake the macaroon. +// // NOTE: this is part of the litrpc.ProxyServiceServer interface. func (p *rpcProxy) BakeSuperMacaroon(ctx context.Context, req *litrpc.BakeSuperMacaroonRequest) ( @@ -260,7 +273,59 @@ func (p *rpcProxy) BakeSuperMacaroon(ctx context.Context, return nil, ErrWaitingToStart } - superMac, err := p.bakeSuperMac(ctx, p.basicClient, req.RootKeyIdSuffix) + // Error out early if we are not in integrated mode and the + // stateless_init flag was set. Note that if we pass this check, we + // also know that LND is running in integrated mode + if req.StatelessInit && !p.cfg.statelessInitMode { + return nil, fmt.Errorf("LiT is not running in " + + "stateless-init mode") + } + + var lndClient lnrpc.LightningClient + if !req.StatelessInit { + perms, ok := p.permsMgr.URIPermissions(bakeSuperMacURI) + if !ok { + return nil, fmt.Errorf("unknown perms for %s", + bakeSuperMacURI) + } + + // Verify the macaroon using LiT's macaroon validator. + err := p.litMacValidator.ValidateMacaroon( + ctx, perms, bakeSuperMacURI, + ) + if err != nil { + return nil, err + } + + // Use LiT's authenticated connection to LND to bake the + // macaroon. + lndClient = p.basicClient + } else { + // If we are in stateless Init mode, then this call was made + // unauthenticated into LiT. However, it should have an LND + // macaroon attached which can be used to bake the macaroon. + // So we create a new connection to LND using this macaroon. + + // Extract the macaroon from the context. This should be an + // LND macaroon. + macHex, err := macaroons.RawMacaroonFromContext(ctx) + if err != nil { + return nil, err + } + + // Create a new connection to LND using the macaroon provided + // in the context. + lndClient, err = lndclient.NewBasicClient( + p.cfg.lndDialAddr(), "", "", p.cfg.Network, + lndclient.MacaroonData(macHex), + lndclient.Insecure(), + ) + if err != nil { + return nil, err + } + } + + superMac, err := p.bakeSuperMac(ctx, lndClient, req.RootKeyIdSuffix) if err != nil { return nil, err } diff --git a/terminal.go b/terminal.go index 7613ddd0d..c3917274a 100644 --- a/terminal.go +++ b/terminal.go @@ -251,6 +251,11 @@ func (g *LightningTerminal) Run() error { err) } + // We will check the BakeSuperMacaroon auth within the handler itself. + if err = g.permsMgr.ForceWhiteListURL(bakeSuperMacURI); err != nil { + return err + } + // The litcli status command will call the "/lnrpc.State/GetState" RPC. // As the status command is available to the user before the macaroons // have been loaded/created, and before the lnd clients have been From ad0336a9cc61a7d7350d14dd8539cfcbacba908e Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 13 Sep 2024 15:05:21 +0200 Subject: [PATCH 10/10] litcli: expose new stateless init option to cli bakesupermacaroon command --- cmd/litcli/proxy.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmd/litcli/proxy.go b/cmd/litcli/proxy.go index 87ec03b8e..9548f8514 100644 --- a/cmd/litcli/proxy.go +++ b/cmd/litcli/proxy.go @@ -32,6 +32,17 @@ var litCommands = []cli.Command{ "specified as a hex string using a " + "maximum of 8 characters.", }, + cli.BoolFlag{ + Name: "stateless_init", + Usage: "When set, it will be assumed that " + + "the provided macaroon is one " + + "created by LND. It is required " + + "that the macaroon has the " + + "permissions required to bake a " + + "macaroon via LND. This option may " + + "only be set if LND is running in " + + "stateless-init mode within LiT.", + }, cli.StringFlag{ Name: "save_to", Usage: "Save returned admin macaroon to " + @@ -125,6 +136,7 @@ func bakeSuperMacaroon(ctx *cli.Context) error { resp, err := client.BakeSuperMacaroon( ctxb, &litrpc.BakeSuperMacaroonRequest{ RootKeyIdSuffix: suffix, + StatelessInit: ctx.IsSet("stateless_init"), }, ) if err != nil {