@@ -3,8 +3,14 @@ package firewall
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "sync"
7
+ "time"
6
8
9
+ "github.com/golang/protobuf/proto"
10
+ "github.com/lightninglabs/lightning-terminal/firewalldb"
7
11
mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware"
12
+ "github.com/lightninglabs/lightning-terminal/session"
13
+ "github.com/lightninglabs/protobuf-hex-display/jsonpb"
8
14
"github.com/lightningnetwork/lnd/lnrpc"
9
15
)
10
16
@@ -13,18 +19,40 @@ const (
13
19
RequestLoggerName = "lit-macaroon-firewall-logger"
14
20
)
15
21
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
+ )
19
37
20
38
// RequestLogger is a RequestInterceptor that just logs incoming RPC requests.
21
39
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
+ }
28
56
}
29
57
30
58
// Name returns the name of the interceptor.
@@ -55,14 +83,113 @@ func (r *RequestLogger) Intercept(_ context.Context,
55
83
"interception request: %v" , err )
56
84
}
57
85
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
+ }
59
156
60
- // Persist the observed request if it is tagged with specific meta
61
- // information.
62
157
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
64
191
}
192
+ delete (r .reqIDToAction , reqID )
65
193
66
- // Send empty response, accepting the request.
67
- return mid .RPCOk (req )
194
+ return r .actionsDB .SetActionState (actionLocator , state , errReason )
68
195
}
0 commit comments