Skip to content

Commit fda99a8

Browse files
authored
Merge pull request #278 from lightninglabs/stateless-integrated-mode
Stateless integrated mode
2 parents d47ecee + e84267c commit fda99a8

File tree

4 files changed

+289
-29
lines changed

4 files changed

+289
-29
lines changed

config.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ type Config struct {
177177
faradayRemote bool
178178
loopRemote bool
179179
poolRemote bool
180+
181+
// lndAdminMacaroon is the admin macaroon that is given to us by lnd
182+
// over an in-memory connection on startup. This is only set in
183+
// integrated lnd mode.
184+
lndAdminMacaroon []byte
180185
}
181186

182187
// RemoteConfig holds the configuration parameters that are needed when running
@@ -219,15 +224,16 @@ type RemoteDaemonConfig struct {
219224
// lndConnectParams returns the connection parameters to connect to the local
220225
// lnd instance.
221226
func (c *Config) lndConnectParams() (string, lndclient.Network, string,
222-
string) {
227+
string, []byte) {
223228

224229
// In remote lnd mode, we just pass along what was configured in the
225230
// remote section of the lnd config.
226231
if c.LndMode == ModeRemote {
227232
return c.Remote.Lnd.RPCServer,
228233
lndclient.Network(c.Network),
229234
lncfg.CleanAndExpandPath(c.Remote.Lnd.TLSCertPath),
230-
lncfg.CleanAndExpandPath(c.Remote.Lnd.MacaroonPath)
235+
lncfg.CleanAndExpandPath(c.Remote.Lnd.MacaroonPath),
236+
nil
231237
}
232238

233239
// When we start lnd internally, we take the listen address as
@@ -248,8 +254,8 @@ func (c *Config) lndConnectParams() (string, lndclient.Network, string,
248254
)
249255
}
250256

251-
return lndDialAddr, lndclient.Network(c.Network),
252-
c.Lnd.TLSCertPath, c.Lnd.AdminMacPath
257+
return lndDialAddr, lndclient.Network(c.Network), "", "",
258+
c.lndAdminMacaroon
253259
}
254260

255261
// defaultConfig returns a configuration struct with all default values set.

rpc_proxy.go

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package terminal
22

33
import (
44
"context"
5+
"crypto/tls"
56
"encoding/base64"
67
"encoding/hex"
78
"fmt"
89
"io/ioutil"
10+
"net"
911
"net/http"
1012
"strings"
1113
"time"
@@ -18,6 +20,7 @@ import (
1820
"google.golang.org/grpc/backoff"
1921
"google.golang.org/grpc/credentials"
2022
"google.golang.org/grpc/metadata"
23+
"google.golang.org/grpc/test/bufconn"
2124
"gopkg.in/macaroon-bakery.v2/bakery"
2225
"gopkg.in/macaroon.v2"
2326
)
@@ -34,7 +37,8 @@ const (
3437
// or REST request and delegate (and convert if necessary) it to the correct
3538
// component.
3639
func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
37-
permissionMap map[string][]bakery.Op) *rpcProxy {
40+
permissionMap map[string][]bakery.Op,
41+
bufListener *bufconn.Listener) *rpcProxy {
3842

3943
// The gRPC web calls are protected by HTTP basic auth which is defined
4044
// by base64(username:password). Because we only have a password, we
@@ -52,6 +56,7 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
5256
cfg: cfg,
5357
basicAuth: basicAuth,
5458
macValidator: validator,
59+
bufListener: bufListener,
5560
}
5661
p.grpcServer = grpc.NewServer(
5762
// From the grpxProxy doc: This codec is *crucial* to the
@@ -125,6 +130,9 @@ type rpcProxy struct {
125130
basicAuth string
126131

127132
macValidator macaroons.MacaroonValidator
133+
bufListener *bufconn.Listener
134+
135+
superMacaroon string
128136

129137
lndConn *grpc.ClientConn
130138
faradayConn *grpc.ClientConn
@@ -140,8 +148,14 @@ func (p *rpcProxy) Start() error {
140148
var err error
141149

142150
// Setup the connection to lnd.
143-
host, _, tlsPath, _ := p.cfg.lndConnectParams()
144-
p.lndConn, err = dialBackend("lnd", host, tlsPath)
151+
host, _, tlsPath, _, _ := p.cfg.lndConnectParams()
152+
153+
// We use a bufconn to connect to lnd in integrated mode.
154+
if p.cfg.LndMode == ModeIntegrated {
155+
p.lndConn, err = dialBufConnBackend(p.bufListener)
156+
} else {
157+
p.lndConn, err = dialBackend("lnd", host, tlsPath)
158+
}
145159
if err != nil {
146160
return fmt.Errorf("could not dial lnd: %v", err)
147161
}
@@ -390,10 +404,13 @@ func (p *rpcProxy) basicAuthToMacaroon(ctx context.Context,
390404
return ctx, nil
391405
}
392406

393-
var macPath string
407+
var (
408+
macPath string
409+
macData []byte
410+
)
394411
switch {
395412
case isLndURI(requestURI):
396-
_, _, _, macPath = p.cfg.lndConnectParams()
413+
_, _, _, macPath, macData = p.cfg.lndConnectParams()
397414

398415
case isFaradayURI(requestURI):
399416
if p.cfg.faradayRemote {
@@ -421,16 +438,64 @@ func (p *rpcProxy) basicAuthToMacaroon(ctx context.Context,
421438
requestURI)
422439
}
423440

424-
// Now that we know which macaroon to load, do it and attach it to the
425-
// request context.
426-
macBytes, err := readMacaroon(lncfg.CleanAndExpandPath(macPath))
427-
if err != nil {
428-
return ctx, fmt.Errorf("error reading macaroon: %v", err)
441+
switch {
442+
// If we have a super macaroon, we can use that one directly since it
443+
// will contain all permissions we need.
444+
case len(p.superMacaroon) > 0:
445+
md.Set(HeaderMacaroon, p.superMacaroon)
446+
447+
// If we have macaroon data directly, just encode them. This could be
448+
// for initial requests to lnd while we don't have the super macaroon
449+
// just yet (or are actually calling to bake the super macaroon).
450+
case len(macData) > 0:
451+
md.Set(HeaderMacaroon, hex.EncodeToString(macData))
452+
453+
// The fall back is to read the macaroon from a path. This is in remote
454+
// mode.
455+
case len(macPath) > 0:
456+
// Now that we know which macaroon to load, do it and attach it
457+
// to the request context.
458+
macBytes, err := readMacaroon(lncfg.CleanAndExpandPath(macPath))
459+
if err != nil {
460+
return ctx, fmt.Errorf("error reading macaroon %s: %v",
461+
lncfg.CleanAndExpandPath(macPath), err)
462+
}
463+
464+
md.Set(HeaderMacaroon, hex.EncodeToString(macBytes))
429465
}
430-
md.Set(HeaderMacaroon, hex.EncodeToString(macBytes))
466+
431467
return metadata.NewIncomingContext(ctx, md), nil
432468
}
433469

470+
// dialBufConnBackend dials an in-memory connection to an RPC listener and
471+
// ignores any TLS certificate mismatches.
472+
func dialBufConnBackend(listener *bufconn.Listener) (*grpc.ClientConn, error) {
473+
tlsConfig := credentials.NewTLS(&tls.Config{
474+
InsecureSkipVerify: true,
475+
})
476+
conn, err := grpc.Dial(
477+
"",
478+
grpc.WithContextDialer(
479+
func(context.Context, string) (net.Conn, error) {
480+
return listener.Dial()
481+
},
482+
),
483+
grpc.WithTransportCredentials(tlsConfig),
484+
485+
// From the grpcProxy doc: This codec is *crucial* to the
486+
// functioning of the proxy.
487+
grpc.WithCodec(grpcProxy.Codec()), // nolint
488+
grpc.WithTransportCredentials(tlsConfig),
489+
grpc.WithDefaultCallOptions(maxMsgRecvSize),
490+
grpc.WithConnectParams(grpc.ConnectParams{
491+
Backoff: backoff.DefaultConfig,
492+
MinConnectTimeout: defaultConnectTimeout,
493+
}),
494+
)
495+
496+
return conn, err
497+
}
498+
434499
// dialBackend connects to a gRPC backend through the given address and uses the
435500
// given TLS certificate to authenticate the connection.
436501
func dialBackend(name, dialAddr, tlsCertPath string) (*grpc.ClientConn, error) {
@@ -470,12 +535,12 @@ func readMacaroon(macPath string) ([]byte, error) {
470535
// Load the specified macaroon file.
471536
macBytes, err := ioutil.ReadFile(macPath)
472537
if err != nil {
473-
return nil, fmt.Errorf("unable to read macaroon path : %v", err)
538+
return nil, fmt.Errorf("unable to read macaroon path: %v", err)
474539
}
475540

476541
// Make sure it actually is a macaroon by parsing it.
477542
mac := &macaroon.Macaroon{}
478-
if err = mac.UnmarshalBinary(macBytes); err != nil {
543+
if err := mac.UnmarshalBinary(macBytes); err != nil {
479544
return nil, fmt.Errorf("unable to decode macaroon: %v", err)
480545
}
481546

subserver_permissions.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ func getSubserverPermissions() map[string][]bakery.Op {
2626
return result
2727
}
2828

29-
// getAllPermissions returns a merged map of lnd's and all subservers' macaroon
30-
// permissions.
31-
func getAllPermissions() map[string][]bakery.Op {
29+
// getAllMethodPermissions returns a merged map of lnd's and all subservers'
30+
// method macaroon permissions.
31+
func getAllMethodPermissions() map[string][]bakery.Op {
3232
subserverPermissions := getSubserverPermissions()
3333
lndPermissions := lnd.MainRPCServerPermissions()
3434
mapSize := len(subserverPermissions) + len(lndPermissions)
@@ -42,6 +42,35 @@ func getAllPermissions() map[string][]bakery.Op {
4242
return result
4343
}
4444

45+
// getAllPermissions retrieves all the permissions needed to bake a super
46+
// macaroon.
47+
func getAllPermissions() []bakery.Op {
48+
dedupMap := make(map[string]map[string]bool)
49+
50+
for _, methodPerms := range getAllMethodPermissions() {
51+
for _, methodPerm := range methodPerms {
52+
if dedupMap[methodPerm.Entity] == nil {
53+
dedupMap[methodPerm.Entity] = make(
54+
map[string]bool,
55+
)
56+
}
57+
dedupMap[methodPerm.Entity][methodPerm.Action] = true
58+
}
59+
}
60+
61+
result := make([]bakery.Op, 0, len(dedupMap))
62+
for entity, actions := range dedupMap {
63+
for action := range actions {
64+
result = append(result, bakery.Op{
65+
Entity: entity,
66+
Action: action,
67+
})
68+
}
69+
}
70+
71+
return result
72+
}
73+
4574
// isLndURI returns true if the given URI belongs to an RPC of lnd.
4675
func isLndURI(uri string) bool {
4776
_, ok := lnd.MainRPCServerPermissions()[uri]

0 commit comments

Comments
 (0)