Skip to content

Commit b59888c

Browse files
committed
multi: let request logger persist Actions
1 parent fbb42e8 commit b59888c

File tree

4 files changed

+147
-30
lines changed

4 files changed

+147
-30
lines changed

firewall/request_logger.go

Lines changed: 142 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ package firewall
33
import (
44
"context"
55
"fmt"
6+
"sync"
7+
"time"
68

9+
"github.com/golang/protobuf/proto"
10+
"github.com/lightninglabs/lightning-terminal/firewalldb"
711
mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware"
12+
"github.com/lightninglabs/lightning-terminal/session"
13+
"github.com/lightninglabs/protobuf-hex-display/jsonpb"
814
"github.com/lightningnetwork/lnd/lnrpc"
915
)
1016

@@ -13,18 +19,40 @@ const (
1319
RequestLoggerName = "lit-macaroon-firewall-logger"
1420
)
1521

16-
// A compile-time assertion that RuleEnforcer is a
17-
// rpcmiddleware.RequestInterceptor.
18-
var _ mid.RequestInterceptor = (*RequestLogger)(nil)
22+
var (
23+
// uriSkipList is a map of URIs that we don't want to log or persist
24+
// actions for.
25+
uriSkipList = map[string]bool{
26+
// We skip the CheckMacaroonPermissions uri since this method is
27+
// called each time a call to a non-Litd endpoint needs to be
28+
// validated and persisting the macaroon each time bloats the
29+
// DB.
30+
"/lnrpc.Lightning/CheckMacaroonPermissions": true,
31+
}
32+
33+
// A compile-time assertion that RuleEnforcer is a
34+
// rpcmiddleware.RequestInterceptor.
35+
_ mid.RequestInterceptor = (*RequestLogger)(nil)
36+
)
1937

2038
// RequestLogger is a RequestInterceptor that just logs incoming RPC requests.
2139
type RequestLogger struct {
22-
// ObservedRequests is a list of all requests that were observed because
23-
// they contained additional meta information about their intent.
24-
//
25-
// TODO(guggero): Replace by persistent storage to keep a history for
26-
// the user.
27-
ObservedRequests []*RequestInfo
40+
actionsDB firewalldb.ActionsWriteDB
41+
42+
// reqIDToAction is a map from request ID to an ActionLocator that can
43+
// be used to find the corresponding action. This is used so that
44+
// requests and responses can be easily linked. The mu mutex must be
45+
// used when accessing this map.
46+
reqIDToAction map[uint64]*firewalldb.ActionLocator
47+
mu sync.Mutex
48+
}
49+
50+
// NewRequestLogger creates a new RequestLogger.
51+
func NewRequestLogger(actionsDB firewalldb.ActionsWriteDB) *RequestLogger {
52+
return &RequestLogger{
53+
actionsDB: actionsDB,
54+
reqIDToAction: make(map[uint64]*firewalldb.ActionLocator),
55+
}
2856
}
2957

3058
// Name returns the name of the interceptor.
@@ -55,14 +83,113 @@ func (r *RequestLogger) Intercept(_ context.Context,
5583
"interception request: %v", err)
5684
}
5785

58-
log.Infof("RequestLogger: Intercepting %v", ri)
86+
// If this request is for any URI in the uriSkipList map, then we do not
87+
// log or persist it.
88+
if uriSkipList[ri.URI] {
89+
return mid.RPCOk(req)
90+
}
91+
92+
log.Tracef("RequestLogger: Intercepting %v", ri)
93+
94+
switch ri.MWRequestType {
95+
case MWRequestTypeStreamAuth:
96+
return mid.RPCOk(req)
97+
98+
// Parse incoming requests and act on them.
99+
case MWRequestTypeRequest:
100+
return mid.RPCErr(req, r.addNewAction(ri))
101+
102+
// Parse and possibly manipulate outgoing responses.
103+
case MWRequestTypeResponse:
104+
var (
105+
state = firewalldb.ActionStateDone
106+
errReason string
107+
)
108+
if ri.IsError {
109+
state = firewalldb.ActionStateError
110+
errReason = mid.ParseResponseErr(ri.Serialized).Error()
111+
}
112+
113+
return mid.RPCErr(
114+
req, r.MarkAction(ri.RequestID, state, errReason),
115+
)
116+
117+
default:
118+
return mid.RPCErrString(req, "invalid intercept type: %v", r)
119+
}
120+
}
121+
122+
// addNewAction persists the new action to the db.
123+
func (r *RequestLogger) addNewAction(ri *RequestInfo) error {
124+
// If no macaroon is provided, then an empty 4-byte array is used as the
125+
// session ID. Otherwise, the macaroon is used to derive a session ID.
126+
var sessionID [4]byte
127+
if ri.Macaroon != nil {
128+
var err error
129+
sessionID, err = session.IDFromMacaroon(ri.Macaroon)
130+
if err != nil {
131+
return fmt.Errorf("could not extract ID from macaroon")
132+
}
133+
}
134+
135+
msg, err := mid.ParseProtobuf(ri.GRPCMessageType, ri.Serialized)
136+
if err != nil {
137+
return err
138+
}
139+
140+
jsonMarshaler := &jsonpb.Marshaler{
141+
EmitDefaults: true,
142+
OrigName: true,
143+
}
144+
145+
jsonStr, err := jsonMarshaler.MarshalToString(proto.MessageV1(msg))
146+
if err != nil {
147+
return fmt.Errorf("unable to decode response: %v", err)
148+
}
149+
150+
action := &firewalldb.Action{
151+
RPCMethod: ri.URI,
152+
RPCParamsJson: []byte(jsonStr),
153+
AttemptedAt: time.Now(),
154+
State: firewalldb.ActionStateInit,
155+
}
59156

60-
// Persist the observed request if it is tagged with specific meta
61-
// information.
62157
if ri.MetaInfo != nil {
63-
r.ObservedRequests = append(r.ObservedRequests, ri)
158+
action.ActorName = ri.MetaInfo.ActorName
159+
action.FeatureName = ri.MetaInfo.Feature
160+
action.Trigger = ri.MetaInfo.Trigger
161+
action.Intent = ri.MetaInfo.Intent
162+
action.StructuredJsonData = ri.MetaInfo.StructuredJsonData
163+
}
164+
165+
id, err := r.actionsDB.AddAction(sessionID, action)
166+
if err != nil {
167+
return err
168+
}
169+
170+
r.mu.Lock()
171+
r.reqIDToAction[ri.RequestID] = &firewalldb.ActionLocator{
172+
SessionID: sessionID,
173+
ActionID: id,
174+
}
175+
r.mu.Unlock()
176+
177+
return nil
178+
}
179+
180+
// MarkAction can be used to set the state of an action identified by the given
181+
// requestID.
182+
func (r *RequestLogger) MarkAction(reqID uint64,
183+
state firewalldb.ActionState, errReason string) error {
184+
185+
r.mu.Lock()
186+
defer r.mu.Unlock()
187+
188+
actionLocator, ok := r.reqIDToAction[reqID]
189+
if !ok {
190+
return nil
64191
}
192+
delete(r.reqIDToAction, reqID)
65193

66-
// Send empty response, accepting the request.
67-
return mid.RPCOk(req)
194+
return r.actionsDB.SetActionState(actionLocator, state, errReason)
68195
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
99
github.com/btcsuite/btcwallet/walletdb v1.4.0
1010
github.com/go-errors/errors v1.0.1
11+
github.com/golang/protobuf v1.5.2
1112
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
1213
github.com/improbable-eng/grpc-web v0.12.0
1314
github.com/jessevdk/go-flags v1.4.0
@@ -73,7 +74,6 @@ require (
7374
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
7475
github.com/gogo/protobuf v1.3.2 // indirect
7576
github.com/golang/mock v1.6.0 // indirect
76-
github.com/golang/protobuf v1.5.2 // indirect
7777
github.com/golang/snappy v0.0.4 // indirect
7878
github.com/google/btree v1.0.1 // indirect
7979
github.com/gorilla/websocket v1.4.2 // indirect

session/macaroon.go

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,7 @@ func IsSuperMacaroon(macHex string) bool {
6060
return false
6161
}
6262

63-
rawID := mac.Id()
64-
if rawID[0] != byte(bakery.LatestVersion) {
65-
return false
66-
}
67-
decodedID := &lnrpc.MacaroonId{}
68-
idProto := rawID[1:]
69-
err = proto.Unmarshal(idProto, decodedID)
70-
if err != nil {
71-
return false
72-
}
73-
74-
// The storage ID is a string representation of a 64bit unsigned number.
75-
rootKeyID, err := strconv.ParseUint(string(decodedID.StorageId), 10, 64)
63+
rootKeyID, err := RootKeyIDFromMacaroon(mac)
7664
if err != nil {
7765
return false
7866
}

terminal.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,13 +673,15 @@ func (g *LightningTerminal) startSubservers() error {
673673
}
674674
g.accountServiceStarted = true
675675

676+
requestLogger := firewall.NewRequestLogger(g.firewallDB)
677+
676678
// Start the middleware manager.
677679
log.Infof("Starting LiT middleware manager")
678680
g.middleware = mid.NewManager(
679681
g.cfg.RPCMiddleware.InterceptTimeout,
680682
g.lndClient.Client, g.errQueue.ChanIn(),
681683
g.accountService,
682-
&firewall.RequestLogger{},
684+
requestLogger,
683685
&firewall.RuleEnforcer{},
684686
)
685687

0 commit comments

Comments
 (0)