Skip to content

Commit be018e8

Browse files
committed
firewall: extract session ID from metadata
1 parent e57e118 commit be018e8

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

firewall/privacy_mapper.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,11 @@ func (p *PrivacyMapper) Intercept(ctx context.Context,
106106
"interception request: %v", err)
107107
}
108108

109-
sessionID, err := session.IDFromMacaroon(ri.Macaroon)
109+
sessionID, ok, err := ri.extractSessionID()
110110
if err != nil {
111-
return nil, fmt.Errorf("could not extract ID from macaroon")
111+
return nil, fmt.Errorf("could not extract session ID: %v", err)
112+
} else if !ok {
113+
return nil, fmt.Errorf("no session ID found in request")
112114
}
113115

114116
log.Tracef("PrivacyMapper: Intercepting %v", ri)

firewall/request_info.go

+45
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"fmt"
55
"strings"
66

7+
"github.com/lightninglabs/lightning-terminal/session"
78
"github.com/lightningnetwork/lnd/lnrpc"
9+
"google.golang.org/grpc/metadata"
810
"gopkg.in/macaroon.v2"
911
)
1012

@@ -38,18 +40,27 @@ type RequestInfo struct {
3840
MetaInfo *InterceptMetaInfo
3941
Rules *InterceptRules
4042
WithPrivacy bool
43+
MDPairs metadata.MD
4144
}
4245

4346
// NewInfoFromRequest parses the given RPC middleware interception request and
4447
// returns a RequestInfo struct.
4548
func NewInfoFromRequest(req *lnrpc.RPCMiddlewareRequest) (*RequestInfo, error) {
49+
md := make(metadata.MD)
50+
for k, vs := range req.MetadataPairs {
51+
for _, v := range vs.Values {
52+
md.Append(k, v)
53+
}
54+
}
55+
4656
var ri *RequestInfo
4757
switch t := req.InterceptType.(type) {
4858
case *lnrpc.RPCMiddlewareRequest_StreamAuth:
4959
ri = &RequestInfo{
5060
MWRequestType: MWRequestTypeStreamAuth,
5161
URI: t.StreamAuth.MethodFullUri,
5262
Streaming: true,
63+
MDPairs: md,
5364
}
5465

5566
case *lnrpc.RPCMiddlewareRequest_Request:
@@ -60,6 +71,7 @@ func NewInfoFromRequest(req *lnrpc.RPCMiddlewareRequest) (*RequestInfo, error) {
6071
IsError: t.Request.IsError,
6172
Serialized: t.Request.Serialized,
6273
Streaming: t.Request.StreamRpc,
74+
MDPairs: md,
6375
}
6476

6577
case *lnrpc.RPCMiddlewareRequest_Response:
@@ -70,6 +82,7 @@ func NewInfoFromRequest(req *lnrpc.RPCMiddlewareRequest) (*RequestInfo, error) {
7082
IsError: t.Response.IsError,
7183
Serialized: t.Response.Serialized,
7284
Streaming: t.Response.StreamRpc,
85+
MDPairs: md,
7386
}
7487

7588
default:
@@ -134,3 +147,35 @@ func (ri *RequestInfo) String() string {
134147
ri.GRPCMessageType, ri.Streaming, strings.Join(ri.Caveats, ","),
135148
ri.MetaInfo, ri.Rules)
136149
}
150+
151+
func (ri *RequestInfo) extractSessionID() (session.ID, bool, error) {
152+
// First prize is to extract the session ID from the MD pairs.
153+
id, ok, err := session.FromGrpcMD(ri.MDPairs)
154+
if err != nil {
155+
return id, ok, err
156+
} else if ok {
157+
return id, ok, nil
158+
}
159+
160+
// TODO(elle): This is a temporary workaround to support older versions
161+
// of LND that don't attach metadata pairs to the request.
162+
// We should remove this once we have bumped our minimum compatible
163+
// LND version to one that always attaches metadata pairs.
164+
// This is because the macaroon root key ID is not a reliable way of
165+
// extracting the session ID since the macaroon root key ID may also
166+
// be the first 4 bytes of an account ID and so collisions are possible
167+
// here.
168+
169+
// Otherwise, fall back to extracting the session ID from the macaroon.
170+
if ri.Macaroon == nil {
171+
return session.ID{}, false, nil
172+
}
173+
174+
id, err = session.IDFromMacaroon(ri.Macaroon)
175+
if err != nil {
176+
return session.ID{}, false, fmt.Errorf("could not extract "+
177+
"ID from macaroon: %w", err)
178+
}
179+
180+
return id, true, nil
181+
}

firewall/request_logger.go

+24-9
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,30 @@ func (r *RequestLogger) Intercept(_ context.Context,
182182
func (r *RequestLogger) addNewAction(ri *RequestInfo,
183183
withPayloadData bool) error {
184184

185-
// If no macaroon is provided, then an empty 4-byte array is used as the
186-
// session ID. Otherwise, the macaroon is used to derive a session ID.
187-
var sessionID [4]byte
188-
if ri.Macaroon != nil {
189-
var err error
190-
sessionID, err = session.IDFromMacaroon(ri.Macaroon)
191-
if err != nil {
192-
return fmt.Errorf("could not extract ID from macaroon")
193-
}
185+
// NOTE: Here is where we determine if we are linking the action to a
186+
// known session OR a known account OR both. Right now (to not change
187+
// behaviour from before): we give preference to a linked session.
188+
// The original behaviour here was to purely use the macaroon's
189+
// root key ID (4 bytes) to extract the "session ID". BUT if this
190+
// action was triggered by an account call (that is not coupled to a
191+
// session), then the "session ID" we are extracting here is actually
192+
// the first 4 bytes of the account ID.
193+
//
194+
// What we actually need to do is:
195+
// 1) only use the ctx/metadata to extract the session ID.
196+
// 2) use the macaroon caveat to extract the account ID.
197+
// With the above complete, we will be able to link an action to:
198+
// a session OR an account OR both OR none.
199+
//
200+
// Given the above info, it is also clear that the name "session ID"
201+
// here is misleading as it is not necessarily the session ID. It could
202+
// be: a session ID, an account ID or None. So it is more like a
203+
// "macaroon identifier".
204+
var sessionID session.ID
205+
if sessID, ok, err := ri.extractSessionID(); err != nil {
206+
return fmt.Errorf("could not extract session ID: %v", err)
207+
} else if ok {
208+
sessionID = sessID
194209
}
195210

196211
action := &firewalldb.Action{

firewall/rule_enforcer.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,11 @@ func (r *RuleEnforcer) Intercept(ctx context.Context,
233233
func (r *RuleEnforcer) handleRequest(ctx context.Context,
234234
ri *RequestInfo) (proto.Message, error) {
235235

236-
sessionID, err := session.IDFromMacaroon(ri.Macaroon)
236+
sessionID, ok, err := ri.extractSessionID()
237237
if err != nil {
238-
return nil, fmt.Errorf("could not extract ID from macaroon")
238+
return nil, fmt.Errorf("could not extract session ID: %v", err)
239+
} else if !ok {
240+
return nil, fmt.Errorf("no session ID found in request")
239241
}
240242

241243
rules, err := r.collectEnforcers(ctx, ri, sessionID)

0 commit comments

Comments
 (0)