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/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 { diff --git a/config.go b/config.go index e1dd5185d..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. @@ -235,21 +238,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 +272,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. 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/perms/manager.go b/perms/manager.go index 5d94d340c..2d83e6ece 100644 --- a/perms/manager.go +++ b/perms/manager.go @@ -1,6 +1,7 @@ package perms import ( + "fmt" "regexp" "strings" "sync" @@ -33,10 +34,17 @@ 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 + + // 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 } // NewManager constructs a new Manager instance and collects any of the @@ -95,18 +103,47 @@ 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.permsMu.Lock() - defer pm.permsMu.Unlock() + pm.mu.Lock() + defer pm.mu.Unlock() + + if _, ok := pm.forcedWhiteListPerms[url]; ok { + return true + } ops, ok := pm.perms[url] @@ -118,8 +155,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 +179,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 +207,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 +219,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 +252,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 +293,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 +311,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) } 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 { diff --git a/rpc_proxy.go b/rpc_proxy.go index 374226f22..39129cfd1 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -17,7 +17,9 @@ 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" grpcProxy "github.com/mwitkow/grpc-proxy/proxy" "google.golang.org/grpc" @@ -33,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 ( @@ -66,7 +72,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 +94,7 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator, cfg: cfg, basicAuth: basicAuth, permsMgr: permsMgr, + litMacValidator: litMacValidator, macValidator: validator, superMacValidator: superMacValidator, subServerMgr: subServerMgr, @@ -169,6 +177,7 @@ type rpcProxy struct { bakeSuperMac bakeSuperMac + litMacValidator macaroons.MacaroonValidator macValidator macaroons.MacaroonValidator superMacValidator session.SuperMacaroonValidator @@ -176,12 +185,17 @@ 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 } // 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, @@ -208,6 +222,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. // @@ -236,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) ( @@ -245,7 +273,59 @@ func (p *rpcProxy) BakeSuperMacaroon(ctx context.Context, return nil, ErrWaitingToStart } - superMac, err := p.bakeSuperMac(ctx, 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 a474e124a..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 @@ -296,8 +301,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 @@ -579,8 +584,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 +594,7 @@ func (g *LightningTerminal) start() error { rootKeyID := session.NewSuperMacaroonRootKeyID(suffixBytes) return BakeSuperMacaroon( - ctx, g.basicClient, rootKeyID, + ctx, lndClient, rootKeyID, g.permsMgr.ActivePermissions(false), nil, ) } @@ -679,7 +685,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 { @@ -689,16 +694,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) } @@ -805,6 +810,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 @@ -1212,19 +1218,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 +1235,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. //