1
1
package itest
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"crypto/tls"
6
7
"crypto/x509"
@@ -22,6 +23,7 @@ import (
22
23
"github.com/stretchr/testify/require"
23
24
"golang.org/x/net/http2"
24
25
"google.golang.org/grpc"
26
+ "google.golang.org/grpc/codes"
25
27
"google.golang.org/grpc/credentials"
26
28
"google.golang.org/grpc/metadata"
27
29
"google.golang.org/protobuf/encoding/protojson"
66
68
Timeout : 1 * time .Second ,
67
69
}
68
70
71
+ // emptyGrpcWebRequest is the binary serialized POST content of an empty
72
+ // gRPC request. One byte version and then 4 bytes content length.
73
+ emptyGrpcWebRequest = []byte {0 , 0 , 0 , 0 , 0 }
74
+
69
75
lndRequestFn = func (ctx context.Context ,
70
76
c grpc.ClientConnInterface ) (proto.Message , error ) {
71
77
@@ -128,6 +134,7 @@ var (
128
134
supportsMacAuthOnLitPort bool
129
135
supportsUIPasswordOnLndPort bool
130
136
supportsUIPasswordOnLitPort bool
137
+ grpcWebURI string
131
138
}{{
132
139
name : "lnrpc" ,
133
140
macaroonFn : lndMacaroonFn ,
@@ -137,6 +144,7 @@ var (
137
144
supportsMacAuthOnLitPort : true ,
138
145
supportsUIPasswordOnLndPort : false ,
139
146
supportsUIPasswordOnLitPort : true ,
147
+ grpcWebURI : "/lnrpc.Lightning/GetInfo" ,
140
148
}, {
141
149
name : "frdrpc" ,
142
150
macaroonFn : faradayMacaroonFn ,
@@ -146,6 +154,7 @@ var (
146
154
supportsMacAuthOnLitPort : true ,
147
155
supportsUIPasswordOnLndPort : false ,
148
156
supportsUIPasswordOnLitPort : true ,
157
+ grpcWebURI : "/frdrpc.FaradayServer/RevenueReport" ,
149
158
}, {
150
159
name : "looprpc" ,
151
160
macaroonFn : loopMacaroonFn ,
@@ -155,6 +164,7 @@ var (
155
164
supportsMacAuthOnLitPort : true ,
156
165
supportsUIPasswordOnLndPort : false ,
157
166
supportsUIPasswordOnLitPort : true ,
167
+ grpcWebURI : "/looprpc.SwapClient/ListSwaps" ,
158
168
}, {
159
169
name : "poolrpc" ,
160
170
macaroonFn : poolMacaroonFn ,
@@ -164,6 +174,7 @@ var (
164
174
supportsMacAuthOnLitPort : true ,
165
175
supportsUIPasswordOnLndPort : false ,
166
176
supportsUIPasswordOnLitPort : true ,
177
+ grpcWebURI : "/poolrpc.Trader/GetInfo" ,
167
178
}, {
168
179
name : "litrpc" ,
169
180
macaroonFn : nil ,
@@ -173,6 +184,7 @@ var (
173
184
supportsMacAuthOnLitPort : false ,
174
185
supportsUIPasswordOnLndPort : true ,
175
186
supportsUIPasswordOnLitPort : true ,
187
+ grpcWebURI : "/litrpc.Sessions/ListSessions" ,
176
188
}}
177
189
)
178
190
@@ -257,6 +269,20 @@ func testModeIntegrated(net *NetworkHarness, t *harnessTest) {
257
269
t .t .Run ("UI index page fallback" , func (tt * testing.T ) {
258
270
runIndexPageCheck (tt , net .Alice .Cfg .LitAddr ())
259
271
})
272
+
273
+ t .t .Run ("grpc-web auth" , func (tt * testing.T ) {
274
+ cfg := net .Alice .Cfg
275
+
276
+ for _ , endpoint := range endpoints {
277
+ endpoint := endpoint
278
+ tt .Run (endpoint .name + " lit port" , func (ttt * testing.T ) {
279
+ runGRPCWebAuthTest (
280
+ ttt , cfg .LitAddr (), cfg .UIPassword ,
281
+ endpoint .grpcWebURI ,
282
+ )
283
+ })
284
+ }
285
+ })
260
286
}
261
287
262
288
// runCertificateCheck checks that the TLS certificates presented to clients are
@@ -406,6 +432,44 @@ func runIndexPageCheck(t *testing.T, hostPort string) {
406
432
require .Contains (t , body , indexHtmlMarker )
407
433
}
408
434
435
+ // runGRPCWebAuthTest tests authentication of the given gRPC interface.
436
+ func runGRPCWebAuthTest (t * testing.T , hostPort , uiPassword , grpcWebURI string ) {
437
+ basicAuth := base64 .StdEncoding .EncodeToString (
438
+ []byte (fmt .Sprintf ("%s:%s" , uiPassword , uiPassword )),
439
+ )
440
+
441
+ header := http.Header {
442
+ "content-type" : []string {"application/grpc-web+proto" },
443
+ "x-grpc-web" : []string {"1" },
444
+ }
445
+
446
+ url := fmt .Sprintf ("https://%s%s" , hostPort , grpcWebURI )
447
+
448
+ // First test a grpc-web call without authorization, which should fail.
449
+ _ , responseHeader , err := postURL (url , emptyGrpcWebRequest , header )
450
+ require .NoError (t , err )
451
+
452
+ require .Equal (
453
+ t , "expected 1 macaroon, got 0" ,
454
+ responseHeader .Get ("grpc-message" ),
455
+ )
456
+ require .Equal (
457
+ t , fmt .Sprintf ("%d" , codes .Unknown ),
458
+ responseHeader .Get ("grpc-status" ),
459
+ )
460
+
461
+ // Now add the basic auth and try again.
462
+ header ["authorization" ] = []string {fmt .Sprintf ("Basic %s" , basicAuth )}
463
+ body , responseHeader , err := postURL (url , emptyGrpcWebRequest , header )
464
+ require .NoError (t , err )
465
+
466
+ require .Empty (t , responseHeader .Get ("grpc-message" ))
467
+ require .Empty (t , responseHeader .Get ("grpc-status" ))
468
+
469
+ // We get the status encoded as trailer in the response.
470
+ require .Contains (t , body , "grpc-status: 0" )
471
+ }
472
+
409
473
// getURL retrieves the body of a given URL, ignoring any TLS certificate the
410
474
// server might present.
411
475
func getURL (url string ) (string , error ) {
@@ -426,6 +490,42 @@ func getURL(url string) (string, error) {
426
490
return string (body ), nil
427
491
}
428
492
493
+ // postURL retrieves the body of a given URL, ignoring any TLS certificate the
494
+ // server might present.
495
+ func postURL (url string , postBody []byte , header http.Header ) (string ,
496
+ http.Header , error ) {
497
+
498
+ req , err := http .NewRequest ("POST" , url , bytes .NewReader (postBody ))
499
+ if err != nil {
500
+ return "" , nil , err
501
+ }
502
+ for key , values := range header {
503
+ for _ , value := range values {
504
+ req .Header .Add (key , value )
505
+ }
506
+ }
507
+ resp , err := client .Do (req )
508
+ if err != nil {
509
+ return "" , nil , err
510
+ }
511
+
512
+ if resp .StatusCode != 200 {
513
+ return "" , nil , fmt .Errorf ("request failed, got status code " +
514
+ "%d (%s)" , resp .StatusCode , resp .Status )
515
+ }
516
+
517
+ defer func () {
518
+ _ = resp .Body .Close ()
519
+ }()
520
+
521
+ body , err := ioutil .ReadAll (resp .Body )
522
+ if err != nil {
523
+ return "" , nil , err
524
+ }
525
+
526
+ return string (body ), resp .Header , nil
527
+ }
528
+
429
529
// getServerCertificates returns the TLS certificates that a server presents to
430
530
// clients.
431
531
func getServerCertificates (hostPort string ) ([]* x509.Certificate , error ) {
0 commit comments