Skip to content

Commit 6104d9e

Browse files
committed
terminal: register interceptors and RPC server to LNC
When a connection is tunneled through LNC the requests that go to the daemons running in-process with LiT need to be registered correctly. To make sure they are also authenticated, the RPC proxy's interceptors also need to be registered on the LNC gRPC server instance. But we don't want to allow calls to the LiT session server through LNC until we have proper macaroon permissions set up for that server.
1 parent b99c5c0 commit 6104d9e

File tree

2 files changed

+106
-63
lines changed

2 files changed

+106
-63
lines changed

rpc_proxy.go

Lines changed: 76 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ import (
1919
grpcProxy "github.com/mwitkow/grpc-proxy/proxy"
2020
"google.golang.org/grpc"
2121
"google.golang.org/grpc/backoff"
22+
"google.golang.org/grpc/codes"
2223
"google.golang.org/grpc/credentials"
2324
"google.golang.org/grpc/metadata"
25+
"google.golang.org/grpc/status"
2426
"google.golang.org/grpc/test/bufconn"
2527
"gopkg.in/macaroon-bakery.v2/bakery"
2628
"gopkg.in/macaroon.v2"
@@ -96,7 +98,7 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
9698
grpc.ChainStreamInterceptor(p.StreamServerInterceptor),
9799
grpc.ChainUnaryInterceptor(p.UnaryServerInterceptor),
98100
grpc.UnknownServiceHandler(
99-
grpcProxy.TransparentHandler(p.director),
101+
grpcProxy.TransparentHandler(p.makeDirector(true)),
100102
),
101103
)
102104

@@ -292,70 +294,86 @@ func (p *rpcProxy) isHandling(resp http.ResponseWriter,
292294
return false
293295
}
294296

295-
// director is a function that directs an incoming request to the correct
296-
// backend, depending on what kind of authentication information is attached to
297-
// the request.
298-
func (p *rpcProxy) director(ctx context.Context,
297+
// makeDirector is a function that returns a director that directs an incoming
298+
// request to the correct backend, depending on what kind of authentication
299+
// information is attached to the request.
300+
func (p *rpcProxy) makeDirector(allowLitRPC bool) func(ctx context.Context,
299301
requestURI string) (context.Context, *grpc.ClientConn, error) {
300302

301-
// If this header is present in the request from the web client,
302-
// the actual connection to the backend will not be established.
303-
// https://github.com/improbable-eng/grpc-web/issues/568
304-
md, _ := metadata.FromIncomingContext(ctx)
305-
mdCopy := md.Copy()
306-
delete(mdCopy, "connection")
307-
308-
outCtx := metadata.NewOutgoingContext(ctx, mdCopy)
303+
return func(ctx context.Context, requestURI string) (context.Context,
304+
*grpc.ClientConn, error) {
305+
306+
// If this header is present in the request from the web client,
307+
// the actual connection to the backend will not be established.
308+
// https://github.com/improbable-eng/grpc-web/issues/568
309+
md, _ := metadata.FromIncomingContext(ctx)
310+
mdCopy := md.Copy()
311+
delete(mdCopy, "connection")
312+
313+
outCtx := metadata.NewOutgoingContext(ctx, mdCopy)
314+
315+
// Is there a basic auth or super macaroon set?
316+
authHeaders := md.Get("authorization")
317+
macHeader := md.Get(HeaderMacaroon)
318+
switch {
319+
case len(authHeaders) == 1:
320+
macBytes, err := p.basicAuthToMacaroon(
321+
authHeaders[0], requestURI, nil,
322+
)
323+
if err != nil {
324+
return outCtx, nil, err
325+
}
326+
if len(macBytes) > 0 {
327+
mdCopy.Set(HeaderMacaroon, hex.EncodeToString(
328+
macBytes,
329+
))
330+
}
309331

310-
// Is there a basic auth or super macaroon set?
311-
authHeaders := md.Get("authorization")
312-
macHeader := md.Get(HeaderMacaroon)
313-
switch {
314-
case len(authHeaders) == 1:
315-
macBytes, err := p.basicAuthToMacaroon(
316-
authHeaders[0], requestURI, nil,
317-
)
318-
if err != nil {
319-
return outCtx, nil, err
320-
}
321-
if len(macBytes) > 0 {
322-
mdCopy.Set(HeaderMacaroon, hex.EncodeToString(macBytes))
332+
case len(macHeader) == 1 && session.IsSuperMacaroon(macHeader[0]):
333+
// If we have a macaroon, and it's a super macaroon,
334+
// then we need to convert it into the actual daemon
335+
// macaroon if they're running in remote mode.
336+
macBytes, err := p.convertSuperMacaroon(
337+
ctx, macHeader[0], requestURI,
338+
)
339+
if err != nil {
340+
return outCtx, nil, err
341+
}
342+
if len(macBytes) > 0 {
343+
mdCopy.Set(HeaderMacaroon, hex.EncodeToString(
344+
macBytes,
345+
))
346+
}
323347
}
324348

325-
case len(macHeader) == 1 && session.IsSuperMacaroon(macHeader[0]):
326-
// If we have a macaroon, and it's a super macaroon, then we
327-
// need to convert it into the actual daemon macaroon if they're
328-
// running in remote mode.
329-
macBytes, err := p.convertSuperMacaroon(
330-
ctx, macHeader[0], requestURI,
331-
)
332-
if err != nil {
333-
return outCtx, nil, err
334-
}
335-
if len(macBytes) > 0 {
336-
mdCopy.Set(HeaderMacaroon, hex.EncodeToString(macBytes))
349+
// Direct the call to the correct backend. All gRPC calls end up
350+
// here since our gRPC server instance doesn't have any handlers
351+
// registered itself. So all daemon calls that are remote are
352+
// forwarded to them directly. Everything else will go to lnd
353+
// since it must either be an lnd call or something that'll be
354+
// handled by the integrated daemons that are hooking into lnd's
355+
// gRPC server.
356+
switch {
357+
case isFaradayURI(requestURI) && p.cfg.faradayRemote:
358+
return outCtx, p.faradayConn, nil
359+
360+
case isLoopURI(requestURI) && p.cfg.loopRemote:
361+
return outCtx, p.loopConn, nil
362+
363+
case isPoolURI(requestURI) && p.cfg.poolRemote:
364+
return outCtx, p.poolConn, nil
365+
366+
// Calls to LiT session RPC aren't allowed in some cases.
367+
case isLitURI(requestURI) && !allowLitRPC:
368+
return outCtx, nil, status.Errorf(
369+
codes.Unimplemented, "unknown service %s",
370+
requestURI,
371+
)
372+
373+
default:
374+
return outCtx, p.lndConn, nil
337375
}
338376
}
339-
340-
// Direct the call to the correct backend. All gRPC calls end up here
341-
// since our gRPC server instance doesn't have any handlers registered
342-
// itself. So all daemon calls that are remote are forwarded to them
343-
// directly. Everything else will go to lnd since it must either be an
344-
// lnd call or something that'll be handled by the integrated daemons
345-
// that are hooking into lnd's gRPC server.
346-
switch {
347-
case isFaradayURI(requestURI) && p.cfg.faradayRemote:
348-
return outCtx, p.faradayConn, nil
349-
350-
case isLoopURI(requestURI) && p.cfg.loopRemote:
351-
return outCtx, p.loopConn, nil
352-
353-
case isPoolURI(requestURI) && p.cfg.poolRemote:
354-
return outCtx, p.poolConn, nil
355-
356-
default:
357-
return outCtx, p.lndConn, nil
358-
}
359377
}
360378

361379
// UnaryServerInterceptor is a gRPC interceptor that checks whether the

terminal.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,24 @@ func (g *LightningTerminal) Run() error {
212212
func(opts ...grpc.ServerOption) *grpc.Server {
213213
allOpts := []grpc.ServerOption{
214214
grpc.CustomCodec(grpcProxy.Codec()), // nolint: staticcheck,
215+
grpc.ChainStreamInterceptor(
216+
g.rpcProxy.StreamServerInterceptor,
217+
),
218+
grpc.ChainUnaryInterceptor(
219+
g.rpcProxy.UnaryServerInterceptor,
220+
),
215221
grpc.UnknownServiceHandler(
216222
grpcProxy.TransparentHandler(
217-
g.rpcProxy.director,
223+
// Don't allow calls to litrpc.
224+
g.rpcProxy.makeDirector(false),
218225
),
219226
),
220227
}
221228
allOpts = append(allOpts, opts...)
222-
return grpc.NewServer(allOpts...)
229+
grpcServer := grpc.NewServer(allOpts...)
230+
g.registerSubDaemonGrpcServers(grpcServer, false)
231+
232+
return grpcServer
223233
},
224234
)
225235
g.sessionRpcServer = &sessionRpcServer{
@@ -579,6 +589,21 @@ func (g *LightningTerminal) RegisterGrpcSubserver(server *grpc.Server) error {
579589
return err
580590
}
581591

592+
// Register all other daemon RPC servers that are running in-process.
593+
// The LiT session server should be enabled on the main interface.
594+
g.registerSubDaemonGrpcServers(server, true)
595+
596+
return nil
597+
}
598+
599+
// registerSubDaemonGrpcServers registers the sub daemon (Faraday, Loop, Pool
600+
// and LiT session) servers to a given gRPC server, given they are running in
601+
// the local process. The lit session server is gated by its own boolean because
602+
// we don't necessarily want to expose it on all listeners, given its security
603+
// implications.
604+
func (g *LightningTerminal) registerSubDaemonGrpcServers(server *grpc.Server,
605+
withLitRPC bool) {
606+
582607
// In remote mode the "director" of the RPC proxy will act as a catch-
583608
// all for any gRPC request that isn't known because we didn't register
584609
// any server for it. The director will then forward the request to the
@@ -595,9 +620,9 @@ func (g *LightningTerminal) RegisterGrpcSubserver(server *grpc.Server) error {
595620
poolrpc.RegisterTraderServer(server, g.poolServer)
596621
}
597622

598-
litrpc.RegisterSessionsServer(server, g.sessionRpcServer)
599-
600-
return nil
623+
if withLitRPC {
624+
litrpc.RegisterSessionsServer(server, g.sessionRpcServer)
625+
}
601626
}
602627

603628
// RegisterRestSubserver is a callback on the lnd.SubserverConfig struct that is

0 commit comments

Comments
 (0)