Skip to content

Commit 3870f0f

Browse files
committed
Add support for passing in macaroons/tls cert as data to basic client
1 parent e23cf6e commit 3870f0f

File tree

2 files changed

+127
-41
lines changed

2 files changed

+127
-41
lines changed

basic_client.go

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package lndclient
22

33
import (
4+
"encoding/hex"
45
"fmt"
56
"io/ioutil"
67
"path/filepath"
@@ -52,13 +53,14 @@ func (bc *basicClientOptions) applyBasicClientOptions(options ...BasicClientOpti
5253
// NewBasicClient creates a new basic gRPC client to lnd. We call this client
5354
// "basic" as it falls back to expected defaults if the arguments aren't
5455
// provided.
55-
func NewBasicClient(lndHost, tlsPath, macDir, network string,
56-
basicOptions ...BasicClientOption) (
56+
func NewBasicClient(lndHost, tlsPath, macDir, tlsData, macData,
57+
network string, basicOptions ...BasicClientOption) (
5758

5859
lnrpc.LightningClient, error) {
5960

6061
conn, err := NewBasicConn(
61-
lndHost, tlsPath, macDir, network, basicOptions...,
62+
lndHost, tlsPath, macDir, tlsData, macData, network,
63+
basicOptions...,
6264
)
6365
if err != nil {
6466
return nil, err
@@ -70,54 +72,26 @@ func NewBasicClient(lndHost, tlsPath, macDir, network string,
7072
// NewBasicConn creates a new basic gRPC connection to lnd. We call this
7173
// connection "basic" as it falls back to expected defaults if the arguments
7274
// aren't provided.
73-
func NewBasicConn(lndHost, tlsPath, macDir, network string,
74-
basicOptions ...BasicClientOption) (
75+
func NewBasicConn(lndHost string, tlsPath, macDir, tlsData, macData,
76+
network string, basicOptions ...BasicClientOption) (
7577

7678
*grpc.ClientConn, error) {
7779

78-
if tlsPath == "" {
79-
tlsPath = defaultTLSCertPath
80-
}
81-
82-
// Load the specified TLS certificate and build transport credentials
83-
creds, err := credentials.NewClientTLSFromFile(tlsPath, "")
80+
creds, mac, err := parseTLSAndMacaroon(
81+
tlsPath, macDir, tlsData, macData, network, basicOptions...,
82+
)
8483
if err != nil {
8584
return nil, err
8685
}
8786

87+
// Now we append the macaroon credentials to the dial options.
88+
cred := macaroons.NewMacaroonCredential(mac)
89+
8890
// Create a dial options array.
8991
opts := []grpc.DialOption{
9092
grpc.WithTransportCredentials(creds),
91-
}
92-
93-
if macDir == "" {
94-
macDir = filepath.Join(
95-
defaultLndDir, defaultDataDir, defaultChainSubDir,
96-
"bitcoin", network,
97-
)
98-
}
99-
100-
// Starting with the set of default options, we'll apply any specified
101-
// functional options to the basic client.
102-
bco := defaultBasicClientOptions()
103-
bco.applyBasicClientOptions(basicOptions...)
104-
105-
macPath := filepath.Join(macDir, bco.macFilename)
106-
107-
// Load the specified macaroon file.
108-
macBytes, err := ioutil.ReadFile(macPath)
109-
if err == nil {
110-
// Only if file is found
111-
mac := &macaroon.Macaroon{}
112-
if err = mac.UnmarshalBinary(macBytes); err != nil {
113-
return nil, fmt.Errorf("unable to decode macaroon: %v",
114-
err)
115-
}
116-
117-
// Now we append the macaroon credentials to the dial options.
118-
cred := macaroons.NewMacaroonCredential(mac)
119-
opts = append(opts, grpc.WithPerRPCCredentials(cred))
120-
opts = append(opts, grpc.WithDefaultCallOptions(maxMsgRecvSize))
93+
grpc.WithPerRPCCredentials(cred),
94+
grpc.WithDefaultCallOptions(maxMsgRecvSize),
12195
}
12296

12397
// We need to use a custom dialer so we can also connect to unix sockets
@@ -134,3 +108,45 @@ func NewBasicConn(lndHost, tlsPath, macDir, network string,
134108

135109
return conn, nil
136110
}
111+
112+
// parseTLSAndMacaroon looks to see if the TLS certificate and macaroon were
113+
// passed in as a path or as straight-up data, and processes it accordingly so
114+
// it can be passed into grpc to establish a connection with LND.
115+
func parseTLSAndMacaroon(tlsPath, macDir, tlsData, macData, network string,
116+
basicOptions ...BasicClientOption) (credentials.TransportCredentials,
117+
*macaroon.Macaroon, error) {
118+
119+
creds, err := GetTLSCredentials(tlsData, tlsPath)
120+
if err != nil {
121+
return nil, nil, err
122+
}
123+
124+
// Starting with the set of default options, we'll apply any specified
125+
// functional options to the basic client.
126+
bco := defaultBasicClientOptions()
127+
bco.applyBasicClientOptions(basicOptions...)
128+
129+
var macBytes []byte
130+
mac := &macaroon.Macaroon{}
131+
if macData != "" {
132+
macBytes, err = hex.DecodeString(macData)
133+
if err != nil {
134+
return nil, nil, err
135+
}
136+
} else {
137+
macPath := filepath.Join(macDir, bco.macFilename)
138+
139+
// Load the specified macaroon file.
140+
macBytes, err = ioutil.ReadFile(macPath)
141+
if err != nil {
142+
return nil, nil, err
143+
}
144+
}
145+
146+
if err = mac.UnmarshalBinary(macBytes); err != nil {
147+
return nil, nil, fmt.Errorf("unable to decode macaroon: %v",
148+
err)
149+
}
150+
151+
return creds, mac, nil
152+
}

basic_client_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package lndclient
2+
3+
import (
4+
"encoding/hex"
5+
"io/ioutil"
6+
"os"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// Tests that NewBasicConn works correctly when macaroon and TLS certificate
13+
// data are passed in directly instead of being supplied as file paths.
14+
func TestParseTLSAndMacaroon(t *testing.T) {
15+
16+
tlsData := `-----BEGIN CERTIFICATE-----
17+
MIIDhzCCAm+gAwIBAgIUEkmdMOVPL92AwgsSYFFBvz4ilmUwDQYJKoZIhvcNAQEL
18+
BQAwUzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1OMRQwEgYDVQQHDAtNaW5uZWFw
19+
b2xpczEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTIxMDQy
20+
MzA2NDkyNVoXDTIxMDUyMzA2NDkyNVowUzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
21+
Ak1OMRQwEgYDVQQHDAtNaW5uZWFwb2xpczEhMB8GA1UECgwYSW50ZXJuZXQgV2lk
22+
Z2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnK21
23+
qJmWWs4Nwz2f2ZbTsDxJAumgDJdZ9JKsJBrqjFf7+25ip+1hIB15P1UHHPhtW5Yp
24+
P9Xm50z8W2RP2pHyCFB09cwKgdqPsS8Q2tzr5DINt+eNYa5JpxnWXM5ZqmYD7Zg0
25+
wSMVW3FuAWFpjlzNWs/UHSuDShiQLoMhl2xAjiGSsHbY9plV438/kypSKS+7wjxe
26+
0TJaTv/kWlHhQkXvnLqIMhD8J+ScGVSSk0OFgWiRmcCGDsLZgEGklHklC7ZKrr+Q
27+
Am2MGbvUaGuwW+R5d2ZaQRbQ5UVhHcna2MxUn6MzSjbEhpIsMKZoYVXCb0GFObcq
28+
UsLUOrIqpIyngd4G9wIDAQABo1MwUTAdBgNVHQ4EFgQU0lZJ2gp/RM79oAegXr/H
29+
sU+GU3YwHwYDVR0jBBgwFoAU0lZJ2gp/RM79oAegXr/HsU+GU3YwDwYDVR0TAQH/
30+
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAly744gq/LPuL0EnEbfxXrVqmvWh6
31+
t9kNljXybVjQNTZ00e4zGknOA3VM29JWOEYyQ7ut/tP+kquWLdfOq/Lehe7vnBSn
32+
lPR6IYbba9ck5AvPZgGG9fEncKxeUoI0ltI/luycmWL7Eb9j3128diIwljf9JXNT
33+
I/LThs8Nl5RSiMOuGer0e934vLlZlrEEI4rWs3DKK56WjrMeVf5dhvYK44usNwUh
34+
vKgMVFsUeyLLTN0EuZjGoFdi3lfLQo3vRwLD6h/EDa5uWK14pZXDQ30+fT2RjuVD
35+
XhkpT5dliEGFLNe6OOgeWTU1JpEXfCud/GImtNMHQi4EDWQfvWuCNGhOoQ==
36+
-----END CERTIFICATE-----`
37+
38+
macData := "0201047465737402067788991234560000062052d26ed139ea5af8" +
39+
"3e675500c4ccb2471f62191b745bab820f129e5588a255d2"
40+
41+
// Make sure it works when data is passed in.
42+
_, _, err := parseTLSAndMacaroon(
43+
"", "", tlsData, macData, "mainnet", MacFilename(""),
44+
)
45+
require.NoError(t, err)
46+
47+
// Now let's write the data to a file to make sure parseTLSAndMacaroon
48+
// parses that properly as well.
49+
tempDirPath, err := ioutil.TempDir("", ".testCreds")
50+
require.NoError(t, err)
51+
defer os.RemoveAll(tempDirPath)
52+
53+
certPath := tempDirPath + "/tls.cert"
54+
tlsPEMBytes := []byte(tlsData)
55+
56+
err = ioutil.WriteFile(certPath, tlsPEMBytes, 0644)
57+
require.NoError(t, err)
58+
59+
macPath := tempDirPath + "/test.macaroon"
60+
macBytes, err := hex.DecodeString(macData)
61+
require.NoError(t, err)
62+
63+
err = ioutil.WriteFile(macPath, macBytes, 0644)
64+
require.NoError(t, err)
65+
66+
_, _, err = parseTLSAndMacaroon(
67+
certPath, macPath, "", "", "mainnet", MacFilename(""),
68+
)
69+
require.NoError(t, err)
70+
}

0 commit comments

Comments
 (0)