@@ -4,13 +4,16 @@ import (
4
4
"context"
5
5
"crypto/tls"
6
6
"crypto/x509"
7
+ "encoding/base64"
7
8
"encoding/hex"
8
9
"fmt"
9
10
"io/ioutil"
11
+ "strings"
10
12
"testing"
11
13
12
14
"github.com/btcsuite/btcutil"
13
15
"github.com/lightninglabs/faraday/frdrpc"
16
+ "github.com/lightninglabs/lightning-terminal/litrpc"
14
17
"github.com/lightninglabs/loop/looprpc"
15
18
"github.com/lightninglabs/pool/poolrpc"
16
19
"github.com/lightningnetwork/lnd/lnrpc"
@@ -94,32 +97,69 @@ var (
94
97
poolMacaroonFn = func (cfg * LitNodeConfig ) string {
95
98
return cfg .PoolMacPath
96
99
}
100
+ litRequestFn = func (ctx context.Context ,
101
+ c grpc.ClientConnInterface ) (proto.Message , error ) {
102
+
103
+ litConn := litrpc .NewSessionsClient (c )
104
+ return litConn .ListSessions (
105
+ ctx , & litrpc.ListSessionsRequest {},
106
+ )
107
+ }
97
108
98
109
endpoints = []struct {
99
- name string
100
- macaroonFn macaroonFn
101
- requestFn requestFn
102
- successPattern string
110
+ name string
111
+ macaroonFn macaroonFn
112
+ requestFn requestFn
113
+ successPattern string
114
+ supportsMacAuthOnLndPort bool
115
+ supportsMacAuthOnLitPort bool
116
+ supportsUIPasswordOnLndPort bool
117
+ supportsUIPasswordOnLitPort bool
103
118
}{{
104
- name : "lnrpc" ,
105
- macaroonFn : lndMacaroonFn ,
106
- requestFn : lndRequestFn ,
107
- successPattern : "\" identity_pubkey\" :\" 0" ,
119
+ name : "lnrpc" ,
120
+ macaroonFn : lndMacaroonFn ,
121
+ requestFn : lndRequestFn ,
122
+ successPattern : "\" identity_pubkey\" :\" 0" ,
123
+ supportsMacAuthOnLndPort : true ,
124
+ supportsMacAuthOnLitPort : true ,
125
+ supportsUIPasswordOnLndPort : false ,
126
+ supportsUIPasswordOnLitPort : true ,
127
+ }, {
128
+ name : "frdrpc" ,
129
+ macaroonFn : faradayMacaroonFn ,
130
+ requestFn : faradayRequestFn ,
131
+ successPattern : "\" reports\" :[]" ,
132
+ supportsMacAuthOnLndPort : true ,
133
+ supportsMacAuthOnLitPort : true ,
134
+ supportsUIPasswordOnLndPort : false ,
135
+ supportsUIPasswordOnLitPort : true ,
108
136
}, {
109
- name : "frdrpc" ,
110
- macaroonFn : faradayMacaroonFn ,
111
- requestFn : faradayRequestFn ,
112
- successPattern : "\" reports\" :[]" ,
137
+ name : "looprpc" ,
138
+ macaroonFn : loopMacaroonFn ,
139
+ requestFn : loopRequestFn ,
140
+ successPattern : "\" swaps\" :[]" ,
141
+ supportsMacAuthOnLndPort : true ,
142
+ supportsMacAuthOnLitPort : true ,
143
+ supportsUIPasswordOnLndPort : false ,
144
+ supportsUIPasswordOnLitPort : true ,
113
145
}, {
114
- name : "looprpc" ,
115
- macaroonFn : loopMacaroonFn ,
116
- requestFn : loopRequestFn ,
117
- successPattern : "\" swaps\" :[]" ,
146
+ name : "poolrpc" ,
147
+ macaroonFn : poolMacaroonFn ,
148
+ requestFn : poolRequestFn ,
149
+ successPattern : "\" accounts_active\" :0" ,
150
+ supportsMacAuthOnLndPort : true ,
151
+ supportsMacAuthOnLitPort : true ,
152
+ supportsUIPasswordOnLndPort : false ,
153
+ supportsUIPasswordOnLitPort : true ,
118
154
}, {
119
- name : "poolrpc" ,
120
- macaroonFn : poolMacaroonFn ,
121
- requestFn : poolRequestFn ,
122
- successPattern : "\" accounts_active\" :0" ,
155
+ name : "litrpc" ,
156
+ macaroonFn : nil ,
157
+ requestFn : litRequestFn ,
158
+ successPattern : "\" sessions\" :[]" ,
159
+ supportsMacAuthOnLndPort : false ,
160
+ supportsMacAuthOnLitPort : false ,
161
+ supportsUIPasswordOnLndPort : true ,
162
+ supportsUIPasswordOnLitPort : true ,
123
163
}}
124
164
)
125
165
@@ -147,6 +187,10 @@ func testModeIntegrated(net *NetworkHarness, t *harnessTest) {
147
187
for _ , endpoint := range endpoints {
148
188
endpoint := endpoint
149
189
tt .Run (endpoint .name + " lnd port" , func (ttt * testing.T ) {
190
+ if ! endpoint .supportsMacAuthOnLndPort {
191
+ return
192
+ }
193
+
150
194
runGRPCAuthTest (
151
195
ttt , cfg .RPCAddr (), cfg .TLSCertPath ,
152
196
endpoint .macaroonFn (cfg ),
@@ -156,6 +200,10 @@ func testModeIntegrated(net *NetworkHarness, t *harnessTest) {
156
200
})
157
201
158
202
tt .Run (endpoint .name + " lit port" , func (ttt * testing.T ) {
203
+ if ! endpoint .supportsMacAuthOnLitPort {
204
+ return
205
+ }
206
+
159
207
runGRPCAuthTest (
160
208
ttt , cfg .LitAddr (), cfg .TLSCertPath ,
161
209
endpoint .macaroonFn (cfg ),
@@ -165,6 +213,33 @@ func testModeIntegrated(net *NetworkHarness, t *harnessTest) {
165
213
})
166
214
}
167
215
})
216
+
217
+ t .t .Run ("UI password auth check" , func (tt * testing.T ) {
218
+ cfg := net .Alice .Cfg
219
+
220
+ for _ , endpoint := range endpoints {
221
+ endpoint := endpoint
222
+ tt .Run (endpoint .name + " lnd port" , func (ttt * testing.T ) {
223
+ runUIPasswordCheck (
224
+ ttt , cfg .RPCAddr (), cfg .TLSCertPath ,
225
+ cfg .UIPassword ,
226
+ endpoint .requestFn , true ,
227
+ ! endpoint .supportsUIPasswordOnLndPort ,
228
+ endpoint .successPattern ,
229
+ )
230
+ })
231
+
232
+ tt .Run (endpoint .name + " lit port" , func (ttt * testing.T ) {
233
+ runUIPasswordCheck (
234
+ ttt , cfg .LitAddr (), cfg .TLSCertPath ,
235
+ cfg .UIPassword ,
236
+ endpoint .requestFn , false ,
237
+ ! endpoint .supportsUIPasswordOnLitPort ,
238
+ endpoint .successPattern ,
239
+ )
240
+ })
241
+ }
242
+ })
168
243
}
169
244
170
245
// runCertificateCheck checks that the TLS certificates presented to clients are
@@ -232,6 +307,74 @@ func runGRPCAuthTest(t *testing.T, hostPort, tlsCertPath, macPath string,
232
307
require .Contains (t , string (json ), successContent )
233
308
}
234
309
310
+ // runUIPasswordCheck tests UI password authentication.
311
+ func runUIPasswordCheck (t * testing.T , hostPort , tlsCertPath , uiPassword string ,
312
+ makeRequest requestFn , shouldFailWithoutMacaroon ,
313
+ shouldFailWithDummyMacaroon bool , successContent string ) {
314
+
315
+ ctxb := context .Background ()
316
+ ctxt , cancel := context .WithTimeout (ctxb , defaultTimeout )
317
+ defer cancel ()
318
+
319
+ rawConn , err := connectRPC (ctxt , hostPort , tlsCertPath )
320
+ require .NoError (t , err )
321
+
322
+ // Make sure that a call without any metadata results in an error.
323
+ _ , err = makeRequest (ctxt , rawConn )
324
+ require .Error (t , err )
325
+ require .Contains (t , err .Error (), "expected 1 macaroon, got 0" )
326
+
327
+ // We can do the same calls by providing a UI password. Make sure that
328
+ // sending an incorrect one is ignored.
329
+ ctxm := uiPasswordContext (ctxt , "foobar" , false )
330
+ _ , err = makeRequest (ctxm , rawConn )
331
+ require .Error (t , err )
332
+ require .Contains (t , err .Error (), "expected 1 macaroon, got 0" )
333
+
334
+ // Sending a dummy macaroon along with the incorrect UI password also
335
+ // shouldn't be allowed and result in an error.
336
+ ctxm = uiPasswordContext (ctxt , "foobar" , true )
337
+ _ , err = makeRequest (ctxm , rawConn )
338
+ require .Error (t , err )
339
+ errStr := err .Error ()
340
+ err1 := strings .Contains (errStr , "invalid auth: invalid basic auth" )
341
+ err2 := strings .Contains (errStr , "cannot get macaroon: root key with" )
342
+ require .True (t , err1 || err2 , "wrong UI password and dummy mac" )
343
+
344
+ // Using the correct UI password should work for all requests.
345
+ ctxm = uiPasswordContext (ctxt , uiPassword , false )
346
+ resp , err := makeRequest (ctxm , rawConn )
347
+
348
+ // On lnd's gRPC interface we don't support using the UI password.
349
+ if shouldFailWithoutMacaroon {
350
+ require .Error (t , err )
351
+ require .Contains (t , err .Error (), "expected 1 macaroon, got 0" )
352
+
353
+ // Sending a dummy macaroon will allow us to not get an error in
354
+ // case of the litrpc calls, where we don't support macaroons
355
+ // but have the extraction call in the validator anyway. So we
356
+ // provide a dummy macaroon but still the UI password must be
357
+ // correct to pass.
358
+ ctxm = uiPasswordContext (ctxt , uiPassword , true )
359
+ resp , err = makeRequest (ctxm , rawConn )
360
+
361
+ if shouldFailWithDummyMacaroon {
362
+ require .Error (t , err )
363
+ require .Contains (
364
+ t , err .Error (), "cannot get macaroon: root" ,
365
+ )
366
+ return
367
+ }
368
+ }
369
+
370
+ // We expect the call to succeed.
371
+ require .NoError (t , err )
372
+
373
+ json , err := marshalOptions .Marshal (resp )
374
+ require .NoError (t , err )
375
+ require .Contains (t , string (json ), successContent )
376
+ }
377
+
235
378
// getServerCertificates returns the TLS certificates that a server presents to
236
379
// clients.
237
380
func getServerCertificates (hostPort string ) ([]* x509.Certificate , error ) {
@@ -256,6 +399,23 @@ func macaroonContext(ctx context.Context, macBytes []byte) context.Context {
256
399
return metadata .NewOutgoingContext (ctx , md )
257
400
}
258
401
402
+ func uiPasswordContext (ctx context.Context , password string ,
403
+ withDummyMac bool ) context.Context {
404
+
405
+ basicAuth := base64 .StdEncoding .EncodeToString (
406
+ []byte (fmt .Sprintf ("%s:%s" , password , password )),
407
+ )
408
+
409
+ md := metadata.MD {}
410
+ md ["authorization" ] = []string {fmt .Sprintf ("Basic %s" , basicAuth )}
411
+
412
+ if withDummyMac {
413
+ md ["macaroon" ] = []string {hex .EncodeToString (dummyMacBytes )}
414
+ }
415
+
416
+ return metadata .NewOutgoingContext (ctx , md )
417
+ }
418
+
259
419
func makeMac () * macaroon.Macaroon {
260
420
dummyMac , err := macaroon .New (
261
421
[]byte ("aabbccddeeff00112233445566778899" ), []byte ("AA==" ),
0 commit comments