@@ -33,6 +33,24 @@ const (
3333 HeaderMacaroon = "Macaroon"
3434)
3535
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+
3654// newRpcProxy creates a new RPC proxy that can take any native gRPC, grpc-web
3755// or REST request and delegate (and convert if necessary) it to the correct
3856// component.
@@ -282,7 +300,7 @@ func (p *rpcProxy) director(ctx context.Context,
282300 authHeaders := md .Get ("authorization" )
283301 if len (authHeaders ) == 1 {
284302 macBytes , err := p .basicAuthToMacaroon (
285- authHeaders [0 ], requestURI ,
303+ authHeaders [0 ], requestURI , nil ,
286304 )
287305 if err != nil {
288306 return outCtx , nil , err
@@ -332,10 +350,17 @@ func (p *rpcProxy) UnaryServerInterceptor(
332350 // have proper macaroon support implemented in the UI. We allow
333351 // gRPC web requests to have it and "convert" the auth into a
334352 // proper macaroon now.
335- newCtx , err := p .convertBasicAuth (ctx , info .FullMethod )
353+ newCtx , err := p .convertBasicAuth (ctx , info .FullMethod , nil )
336354 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
339364 }
340365
341366 // With the basic auth converted to a macaroon if necessary,
@@ -369,9 +394,19 @@ func (p *rpcProxy) StreamServerInterceptor(
369394 // have proper macaroon support implemented in the UI. We allow
370395 // gRPC web requests to have it and "convert" the auth into a
371396 // proper macaroon now.
372- ctx , err := p .convertBasicAuth (ss .Context (), info .FullMethod )
397+ ctx , err := p .convertBasicAuth (
398+ ss .Context (), info .FullMethod , nil ,
399+ )
373400 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
375410 }
376411
377412 // With the basic auth converted to a macaroon if necessary,
@@ -390,21 +425,23 @@ func (p *rpcProxy) StreamServerInterceptor(
390425// convertBasicAuth tries to convert the HTTP authorization header into a
391426// macaroon based authentication header.
392427func (p * rpcProxy ) convertBasicAuth (ctx context.Context ,
393- requestURI string ) (context.Context , error ) {
428+ requestURI string , ctxErr error ) (context.Context , error ) {
394429
395430 md , ok := metadata .FromIncomingContext (ctx )
396431 if ! ok {
397- return ctx , nil
432+ return ctx , ctxErr
398433 }
399434
400435 authHeaders := md .Get ("authorization" )
401436 if len (authHeaders ) == 0 {
402437 // No basic auth provided, we don't add a macaroon and let the
403438 // gRPC security interceptor reject the request.
404- return ctx , nil
439+ return ctx , ctxErr
405440 }
406441
407- macBytes , err := p .basicAuthToMacaroon (authHeaders [0 ], requestURI )
442+ macBytes , err := p .basicAuthToMacaroon (
443+ authHeaders [0 ], requestURI , ctxErr ,
444+ )
408445 if err != nil || len (macBytes ) == 0 {
409446 return ctx , err
410447 }
@@ -416,8 +453,8 @@ func (p *rpcProxy) convertBasicAuth(ctx context.Context,
416453// basicAuthToMacaroon checks that the incoming request context has the expected
417454// and valid basic authentication header then attaches the correct macaroon to
418455// 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 ) {
421458
422459 // The user specified an authorization header so this is very likely a
423460 // 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,
426463 // from the lnd backend.
427464 authHeaderParts := strings .Split (basicAuth , " " )
428465 if len (authHeaderParts ) != 2 {
429- return nil , nil
466+ return nil , ctxErr
430467 }
431468 if authHeaderParts [1 ] != p .basicAuth {
432- return nil , nil
469+ return nil , ctxErr
433470 }
434471
435472 var (
@@ -473,7 +510,20 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string) ([]byte,
473510 // If we have a super macaroon, we can use that one directly since it
474511 // will contain all permissions we need.
475512 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
477527
478528 // If we have macaroon data directly, just encode them. This could be
479529 // 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,
489539 return readMacaroon (lncfg .CleanAndExpandPath (macPath ))
490540 }
491541
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+ }
493546}
494547
495548// dialBufConnBackend dials an in-memory connection to an RPC listener and
0 commit comments