2022-04-28 11:58:34 +02:00
|
|
|
package firewall
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
|
2025-05-13 13:34:50 +02:00
|
|
|
"github.com/lightninglabs/lightning-terminal/accounts"
|
2025-05-13 08:53:03 +02:00
|
|
|
"github.com/lightninglabs/lightning-terminal/session"
|
|
|
|
|
"github.com/lightningnetwork/lnd/fn"
|
2022-04-28 11:58:34 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
2025-05-13 08:53:03 +02:00
|
|
|
"google.golang.org/grpc/metadata"
|
2022-04-28 11:58:34 +02:00
|
|
|
"gopkg.in/macaroon.v2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// MWRequestTypeStreamAuth represents the type name for a stream
|
|
|
|
|
// authentication interception message.
|
|
|
|
|
MWRequestTypeStreamAuth = "stream_auth"
|
|
|
|
|
|
|
|
|
|
// MWRequestTypeRequest represents the type name for a request
|
|
|
|
|
// interception message.
|
|
|
|
|
MWRequestTypeRequest = "request"
|
|
|
|
|
|
|
|
|
|
// MWRequestTypeResponse represents the type name for a response
|
|
|
|
|
// interception message.
|
|
|
|
|
MWRequestTypeResponse = "response"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// RequestInfo stores the parsed representation of an incoming RPC middleware
|
|
|
|
|
// request.
|
|
|
|
|
type RequestInfo struct {
|
2025-05-13 08:53:03 +02:00
|
|
|
SessionID fn.Option[session.ID]
|
2025-05-13 13:34:50 +02:00
|
|
|
AccountID fn.Option[accounts.AccountID]
|
2022-04-28 11:58:34 +02:00
|
|
|
MsgID uint64
|
|
|
|
|
RequestID uint64
|
|
|
|
|
MWRequestType string
|
|
|
|
|
URI string
|
|
|
|
|
GRPCMessageType string
|
2022-06-08 11:25:02 +02:00
|
|
|
IsError bool
|
|
|
|
|
Serialized []byte
|
2022-04-28 11:58:34 +02:00
|
|
|
Streaming bool
|
|
|
|
|
Macaroon *macaroon.Macaroon
|
|
|
|
|
Caveats []string
|
|
|
|
|
MetaInfo *InterceptMetaInfo
|
2022-06-08 11:25:02 +02:00
|
|
|
Rules *InterceptRules
|
2022-09-08 13:25:27 +02:00
|
|
|
WithPrivacy bool
|
2022-04-28 11:58:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewInfoFromRequest parses the given RPC middleware interception request and
|
|
|
|
|
// returns a RequestInfo struct.
|
|
|
|
|
func NewInfoFromRequest(req *lnrpc.RPCMiddlewareRequest) (*RequestInfo, error) {
|
|
|
|
|
var ri *RequestInfo
|
|
|
|
|
switch t := req.InterceptType.(type) {
|
|
|
|
|
case *lnrpc.RPCMiddlewareRequest_StreamAuth:
|
|
|
|
|
ri = &RequestInfo{
|
|
|
|
|
MWRequestType: MWRequestTypeStreamAuth,
|
|
|
|
|
URI: t.StreamAuth.MethodFullUri,
|
|
|
|
|
Streaming: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case *lnrpc.RPCMiddlewareRequest_Request:
|
|
|
|
|
ri = &RequestInfo{
|
|
|
|
|
MWRequestType: MWRequestTypeRequest,
|
|
|
|
|
URI: t.Request.MethodFullUri,
|
|
|
|
|
GRPCMessageType: t.Request.TypeName,
|
2022-06-08 11:25:02 +02:00
|
|
|
IsError: t.Request.IsError,
|
|
|
|
|
Serialized: t.Request.Serialized,
|
2022-04-28 11:58:34 +02:00
|
|
|
Streaming: t.Request.StreamRpc,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case *lnrpc.RPCMiddlewareRequest_Response:
|
|
|
|
|
ri = &RequestInfo{
|
|
|
|
|
MWRequestType: MWRequestTypeResponse,
|
|
|
|
|
URI: t.Response.MethodFullUri,
|
|
|
|
|
GRPCMessageType: t.Response.TypeName,
|
2022-06-08 11:25:02 +02:00
|
|
|
IsError: t.Response.IsError,
|
|
|
|
|
Serialized: t.Response.Serialized,
|
2022-04-28 11:58:34 +02:00
|
|
|
Streaming: t.Response.StreamRpc,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("invalid request type: %T", t)
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-13 08:53:03 +02:00
|
|
|
md := make(metadata.MD)
|
|
|
|
|
for k, vs := range req.MetadataPairs {
|
|
|
|
|
for _, v := range vs.Values {
|
|
|
|
|
md.Append(k, v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sessionID, err := session.FromGRPCMetadata(md)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error extracting session ID "+
|
|
|
|
|
"from request: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-28 11:58:34 +02:00
|
|
|
ri.MsgID = req.MsgId
|
|
|
|
|
ri.RequestID = req.RequestId
|
2025-05-13 08:53:03 +02:00
|
|
|
ri.SessionID = sessionID
|
2022-04-28 11:58:34 +02:00
|
|
|
|
2022-11-23 10:42:47 +07:00
|
|
|
// If there is no macaroon in the request, then there is nothing left
|
|
|
|
|
// to parse.
|
|
|
|
|
if len(req.RawMacaroon) == 0 {
|
|
|
|
|
return ri, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-28 11:58:34 +02:00
|
|
|
ri.Macaroon = &macaroon.Macaroon{}
|
|
|
|
|
if err := ri.Macaroon.UnmarshalBinary(req.RawMacaroon); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error parsing macaroon: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ri.Caveats = make([]string, len(ri.Macaroon.Caveats()))
|
|
|
|
|
for idx, cav := range ri.Macaroon.Caveats() {
|
|
|
|
|
ri.Caveats[idx] = string(cav.Id)
|
|
|
|
|
|
|
|
|
|
// Apply any meta information sent as a custom caveat. Only the
|
|
|
|
|
// last one will be considered if there are multiple caveats.
|
|
|
|
|
metaInfo, err := ParseMetaInfoCaveat(ri.Caveats[idx])
|
|
|
|
|
if err == nil {
|
|
|
|
|
ri.MetaInfo = metaInfo
|
|
|
|
|
|
2022-09-08 13:25:27 +02:00
|
|
|
// The same caveat can't be a meta info and a rule list
|
|
|
|
|
// or a privacy caveat.
|
2022-04-28 11:58:34 +02:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Also apply the rule list sent as a custom caveat. Only the
|
|
|
|
|
// last set of rules will be considered if there are multiple
|
|
|
|
|
// caveats.
|
|
|
|
|
rules, err := ParseRuleCaveat(ri.Caveats[idx])
|
|
|
|
|
if err == nil {
|
|
|
|
|
ri.Rules = rules
|
2022-09-08 13:25:27 +02:00
|
|
|
|
|
|
|
|
// The same caveat can't be a rule list and a privacy
|
|
|
|
|
// caveat.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if IsPrivacyCaveat(ri.Caveats[idx]) {
|
|
|
|
|
ri.WithPrivacy = true
|
2022-04-28 11:58:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-13 13:34:50 +02:00
|
|
|
ri.AccountID, err = accounts.IDFromCaveats(ri.Macaroon.Caveats())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error extracting account ID "+
|
|
|
|
|
"from macaroon: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-28 11:58:34 +02:00
|
|
|
return ri, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// String returns the string representation of the request info struct.
|
|
|
|
|
func (ri *RequestInfo) String() string {
|
|
|
|
|
return fmt.Sprintf("Request={msg_id=%d, request_id=%d, type=%v, "+
|
|
|
|
|
"uri=%v, grpc_message_type=%v, streaming=%v, caveats=[%v], "+
|
|
|
|
|
"meta_info=%v, rules=[%v]}",
|
|
|
|
|
ri.MsgID, ri.RequestID, ri.MWRequestType, ri.URI,
|
|
|
|
|
ri.GRPCMessageType, ri.Streaming, strings.Join(ri.Caveats, ","),
|
|
|
|
|
ri.MetaInfo, ri.Rules)
|
|
|
|
|
}
|