@@ -33,6 +33,24 @@ const (
33
33
HeaderMacaroon = "Macaroon"
34
34
)
35
35
36
+ // proxyErr is an error type that adds more context to an error occurring in the
37
+ // proxy.
38
+ type proxyErr struct {
39
+ wrapped error
40
+ proxyContext string
41
+ }
42
+
43
+ // Error returns the error message as a string, including the proxy's context.
44
+ func (e * proxyErr ) Error () string {
45
+ return fmt .Sprintf ("proxy error with context %s: %v" , e .proxyContext ,
46
+ e .wrapped )
47
+ }
48
+
49
+ // Unwrap returns the wrapped error.
50
+ func (e * proxyErr ) Unwrap () error {
51
+ return e .wrapped
52
+ }
53
+
36
54
// newRpcProxy creates a new RPC proxy that can take any native gRPC, grpc-web
37
55
// or REST request and delegate (and convert if necessary) it to the correct
38
56
// component.
@@ -282,7 +300,7 @@ func (p *rpcProxy) director(ctx context.Context,
282
300
authHeaders := md .Get ("authorization" )
283
301
if len (authHeaders ) == 1 {
284
302
macBytes , err := p .basicAuthToMacaroon (
285
- authHeaders [0 ], requestURI ,
303
+ authHeaders [0 ], requestURI , nil ,
286
304
)
287
305
if err != nil {
288
306
return outCtx , nil , err
@@ -332,10 +350,17 @@ func (p *rpcProxy) UnaryServerInterceptor(
332
350
// have proper macaroon support implemented in the UI. We allow
333
351
// gRPC web requests to have it and "convert" the auth into a
334
352
// proper macaroon now.
335
- newCtx , err := p .convertBasicAuth (ctx , info .FullMethod )
353
+ newCtx , err := p .convertBasicAuth (ctx , info .FullMethod , nil )
336
354
if err != nil {
337
- return nil , fmt .Errorf ("error upgrading basic auth: %v" ,
338
- err )
355
+ // Make sure we handle the case where the super macaroon
356
+ // is still empty on startup.
357
+ if pErr , ok := err .(* proxyErr ); ok &&
358
+ pErr .proxyContext == "supermacaroon" {
359
+
360
+ return nil , fmt .Errorf ("super macaroon error: " +
361
+ "%v" , pErr )
362
+ }
363
+ return nil , err
339
364
}
340
365
341
366
// With the basic auth converted to a macaroon if necessary,
@@ -369,9 +394,19 @@ func (p *rpcProxy) StreamServerInterceptor(
369
394
// have proper macaroon support implemented in the UI. We allow
370
395
// gRPC web requests to have it and "convert" the auth into a
371
396
// proper macaroon now.
372
- ctx , err := p .convertBasicAuth (ss .Context (), info .FullMethod )
397
+ ctx , err := p .convertBasicAuth (
398
+ ss .Context (), info .FullMethod , nil ,
399
+ )
373
400
if err != nil {
374
- return fmt .Errorf ("error upgrading basic auth: %v" , err )
401
+ // Make sure we handle the case where the super macaroon
402
+ // is still empty on startup.
403
+ if pErr , ok := err .(* proxyErr ); ok &&
404
+ pErr .proxyContext == "supermacaroon" {
405
+
406
+ return fmt .Errorf ("super macaroon error: " +
407
+ "%v" , pErr )
408
+ }
409
+ return err
375
410
}
376
411
377
412
// With the basic auth converted to a macaroon if necessary,
@@ -390,21 +425,23 @@ func (p *rpcProxy) StreamServerInterceptor(
390
425
// convertBasicAuth tries to convert the HTTP authorization header into a
391
426
// macaroon based authentication header.
392
427
func (p * rpcProxy ) convertBasicAuth (ctx context.Context ,
393
- requestURI string ) (context.Context , error ) {
428
+ requestURI string , ctxErr error ) (context.Context , error ) {
394
429
395
430
md , ok := metadata .FromIncomingContext (ctx )
396
431
if ! ok {
397
- return ctx , nil
432
+ return ctx , ctxErr
398
433
}
399
434
400
435
authHeaders := md .Get ("authorization" )
401
436
if len (authHeaders ) == 0 {
402
437
// No basic auth provided, we don't add a macaroon and let the
403
438
// gRPC security interceptor reject the request.
404
- return ctx , nil
439
+ return ctx , ctxErr
405
440
}
406
441
407
- macBytes , err := p .basicAuthToMacaroon (authHeaders [0 ], requestURI )
442
+ macBytes , err := p .basicAuthToMacaroon (
443
+ authHeaders [0 ], requestURI , ctxErr ,
444
+ )
408
445
if err != nil || len (macBytes ) == 0 {
409
446
return ctx , err
410
447
}
@@ -416,8 +453,8 @@ func (p *rpcProxy) convertBasicAuth(ctx context.Context,
416
453
// basicAuthToMacaroon checks that the incoming request context has the expected
417
454
// and valid basic authentication header then attaches the correct macaroon to
418
455
// the context so it can be forwarded to the actual gRPC server.
419
- func (p * rpcProxy ) basicAuthToMacaroon (basicAuth , requestURI string ) ([] byte ,
420
- error ) {
456
+ func (p * rpcProxy ) basicAuthToMacaroon (basicAuth , requestURI string ,
457
+ ctxErr error ) ([] byte , error ) {
421
458
422
459
// The user specified an authorization header so this is very likely a
423
460
// gRPC Web call from the UI. But we only attach the macaroon if the
@@ -426,10 +463,10 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string) ([]byte,
426
463
// from the lnd backend.
427
464
authHeaderParts := strings .Split (basicAuth , " " )
428
465
if len (authHeaderParts ) != 2 {
429
- return nil , nil
466
+ return nil , ctxErr
430
467
}
431
468
if authHeaderParts [1 ] != p .basicAuth {
432
- return nil , nil
469
+ return nil , ctxErr
433
470
}
434
471
435
472
var (
@@ -473,7 +510,20 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string) ([]byte,
473
510
// If we have a super macaroon, we can use that one directly since it
474
511
// will contain all permissions we need.
475
512
case len (p .superMacaroon ) > 0 :
476
- return hex .DecodeString (p .superMacaroon )
513
+ superMacData , err := hex .DecodeString (p .superMacaroon )
514
+
515
+ // Make sure we can avoid running into an empty macaroon here if
516
+ // something went wrong with the decoding process (if we're
517
+ // still starting up).
518
+ if err != nil {
519
+ return nil , & proxyErr {
520
+ proxyContext : "supermacaroon" ,
521
+ wrapped : fmt .Errorf ("couldn't decode " +
522
+ "super macaroon: %v" , err ),
523
+ }
524
+ }
525
+
526
+ return superMacData , nil
477
527
478
528
// If we have macaroon data directly, just encode them. This could be
479
529
// for initial requests to lnd while we don't have the super macaroon
@@ -489,7 +539,10 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string) ([]byte,
489
539
return readMacaroon (lncfg .CleanAndExpandPath (macPath ))
490
540
}
491
541
492
- return nil , fmt .Errorf ("unknown macaroon to use" )
542
+ return nil , & proxyErr {
543
+ proxyContext : "auth" ,
544
+ wrapped : fmt .Errorf ("unknown macaroon to use" ),
545
+ }
493
546
}
494
547
495
548
// dialBufConnBackend dials an in-memory connection to an RPC listener and
0 commit comments