Skip to content

Stateless remote mode: Adds passing in macaroon and tls data, and a CheckMacaroonPermissions RPC #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 65 additions & 40 deletions basic_client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lndclient

import (
"encoding/hex"
"fmt"
"io/ioutil"
"path/filepath"
Expand Down Expand Up @@ -52,13 +53,14 @@ func (bc *basicClientOptions) applyBasicClientOptions(options ...BasicClientOpti
// NewBasicClient creates a new basic gRPC client to lnd. We call this client
// "basic" as it falls back to expected defaults if the arguments aren't
// provided.
func NewBasicClient(lndHost, tlsPath, macDir, network string,
basicOptions ...BasicClientOption) (
func NewBasicClient(lndHost, tlsPath, macDir, tlsData, macData, network string,
insecure, systemCert bool, basicOptions ...BasicClientOption) (

lnrpc.LightningClient, error) {

conn, err := NewBasicConn(
lndHost, tlsPath, macDir, network, basicOptions...,
lndHost, tlsPath, macDir, tlsData, macData, network, insecure,
systemCert, basicOptions...,
)
if err != nil {
return nil, err
Expand All @@ -70,54 +72,28 @@ func NewBasicClient(lndHost, tlsPath, macDir, network string,
// NewBasicConn creates a new basic gRPC connection to lnd. We call this
// connection "basic" as it falls back to expected defaults if the arguments
// aren't provided.
func NewBasicConn(lndHost, tlsPath, macDir, network string,
func NewBasicConn(lndHost string, tlsPath, macDir, tlsData, macData,
network string, insecure, systemCert bool,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive by comment, but rather than breaking the interface here, this should've just used a BasicClientOption as that's how the API is intended to evolve.

IMO we should likely follow this path, as otherwise updating will break every user of NewBasicConn as is.

basicOptions ...BasicClientOption) (

*grpc.ClientConn, error) {

if tlsPath == "" {
tlsPath = defaultTLSCertPath
}

// Load the specified TLS certificate and build transport credentials
creds, err := credentials.NewClientTLSFromFile(tlsPath, "")
creds, mac, err := parseTLSAndMacaroon(
tlsPath, macDir, tlsData, macData, network, insecure,
systemCert, basicOptions...,
)
if err != nil {
return nil, err
}

// Now we append the macaroon credentials to the dial options.
cred := macaroons.NewMacaroonCredential(mac)

// Create a dial options array.
opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds),
}

if macDir == "" {
macDir = filepath.Join(
defaultLndDir, defaultDataDir, defaultChainSubDir,
"bitcoin", network,
)
}

// Starting with the set of default options, we'll apply any specified
// functional options to the basic client.
bco := defaultBasicClientOptions()
bco.applyBasicClientOptions(basicOptions...)

macPath := filepath.Join(macDir, bco.macFilename)

// Load the specified macaroon file.
macBytes, err := ioutil.ReadFile(macPath)
if err == nil {
// Only if file is found
mac := &macaroon.Macaroon{}
if err = mac.UnmarshalBinary(macBytes); err != nil {
return nil, fmt.Errorf("unable to decode macaroon: %v",
err)
}

// Now we append the macaroon credentials to the dial options.
cred := macaroons.NewMacaroonCredential(mac)
opts = append(opts, grpc.WithPerRPCCredentials(cred))
opts = append(opts, grpc.WithDefaultCallOptions(maxMsgRecvSize))
grpc.WithPerRPCCredentials(cred),
grpc.WithDefaultCallOptions(maxMsgRecvSize),
}

// We need to use a custom dialer so we can also connect to unix sockets
Expand All @@ -134,3 +110,52 @@ func NewBasicConn(lndHost, tlsPath, macDir, network string,

return conn, nil
}

// parseTLSAndMacaroon looks to see if the TLS certificate and macaroon were
// passed in as a path or as straight-up data, and processes it accordingly so
// it can be passed into grpc to establish a connection with LND.
func parseTLSAndMacaroon(tlsPath, macDir, tlsData, macData, network string,
insecure, systemCert bool, basicOptions ...BasicClientOption) (
credentials.TransportCredentials, *macaroon.Macaroon, error) {

creds, err := GetTLSCredentials(tlsData, tlsPath, insecure, systemCert)
if err != nil {
return nil, nil, err
}

// Starting with the set of default options, we'll apply any specified
// functional options to the basic client.
bco := defaultBasicClientOptions()
bco.applyBasicClientOptions(basicOptions...)

var macBytes []byte
mac := &macaroon.Macaroon{}
if macData != "" {
macBytes, err = hex.DecodeString(macData)
if err != nil {
return nil, nil, err
}
} else {
if macDir == "" {
macDir = filepath.Join(
defaultLndDir, defaultDataDir, defaultChainSubDir,
"bitcoin", network,
)
}

macPath := filepath.Join(macDir, bco.macFilename)

// Load the specified macaroon file.
macBytes, err = ioutil.ReadFile(macPath)
if err != nil {
return nil, nil, err
}
}

if err = mac.UnmarshalBinary(macBytes); err != nil {
return nil, nil, fmt.Errorf("unable to decode macaroon: %v",
err)
}

return creds, mac, nil
}
72 changes: 72 additions & 0 deletions basic_client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package lndclient

import (
"encoding/hex"
"io/ioutil"
"os"
"testing"

"github.com/stretchr/testify/require"
)

// Tests that NewBasicConn works correctly when macaroon and TLS certificate
// data are passed in directly instead of being supplied as file paths.
func TestParseTLSAndMacaroon(t *testing.T) {

tlsData := `-----BEGIN CERTIFICATE-----
MIIDhzCCAm+gAwIBAgIUEkmdMOVPL92AwgsSYFFBvz4ilmUwDQYJKoZIhvcNAQEL
BQAwUzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1OMRQwEgYDVQQHDAtNaW5uZWFw
b2xpczEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTIxMDQy
MzA2NDkyNVoXDTIxMDUyMzA2NDkyNVowUzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
Ak1OMRQwEgYDVQQHDAtNaW5uZWFwb2xpczEhMB8GA1UECgwYSW50ZXJuZXQgV2lk
Z2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnK21
qJmWWs4Nwz2f2ZbTsDxJAumgDJdZ9JKsJBrqjFf7+25ip+1hIB15P1UHHPhtW5Yp
P9Xm50z8W2RP2pHyCFB09cwKgdqPsS8Q2tzr5DINt+eNYa5JpxnWXM5ZqmYD7Zg0
wSMVW3FuAWFpjlzNWs/UHSuDShiQLoMhl2xAjiGSsHbY9plV438/kypSKS+7wjxe
0TJaTv/kWlHhQkXvnLqIMhD8J+ScGVSSk0OFgWiRmcCGDsLZgEGklHklC7ZKrr+Q
Am2MGbvUaGuwW+R5d2ZaQRbQ5UVhHcna2MxUn6MzSjbEhpIsMKZoYVXCb0GFObcq
UsLUOrIqpIyngd4G9wIDAQABo1MwUTAdBgNVHQ4EFgQU0lZJ2gp/RM79oAegXr/H
sU+GU3YwHwYDVR0jBBgwFoAU0lZJ2gp/RM79oAegXr/HsU+GU3YwDwYDVR0TAQH/
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAly744gq/LPuL0EnEbfxXrVqmvWh6
t9kNljXybVjQNTZ00e4zGknOA3VM29JWOEYyQ7ut/tP+kquWLdfOq/Lehe7vnBSn
lPR6IYbba9ck5AvPZgGG9fEncKxeUoI0ltI/luycmWL7Eb9j3128diIwljf9JXNT
I/LThs8Nl5RSiMOuGer0e934vLlZlrEEI4rWs3DKK56WjrMeVf5dhvYK44usNwUh
vKgMVFsUeyLLTN0EuZjGoFdi3lfLQo3vRwLD6h/EDa5uWK14pZXDQ30+fT2RjuVD
XhkpT5dliEGFLNe6OOgeWTU1JpEXfCud/GImtNMHQi4EDWQfvWuCNGhOoQ==
-----END CERTIFICATE-----`

macData := "0201047465737402067788991234560000062052d26ed139ea5af8" +
"3e675500c4ccb2471f62191b745bab820f129e5588a255d2"

// Make sure it works when data is passed in.
_, _, err := parseTLSAndMacaroon(
"", "", tlsData, macData, "mainnet", false, false,
MacFilename(""),
)
require.NoError(t, err)

// Now let's write the data to a file to make sure parseTLSAndMacaroon
// parses that properly as well.
tempDirPath, err := ioutil.TempDir("", ".testCreds")
require.NoError(t, err)
defer os.RemoveAll(tempDirPath)

certPath := tempDirPath + "/tls.cert"
tlsPEMBytes := []byte(tlsData)

err = ioutil.WriteFile(certPath, tlsPEMBytes, 0644)
require.NoError(t, err)

macPath := tempDirPath + "/test.macaroon"
macBytes, err := hex.DecodeString(macData)
require.NoError(t, err)

err = ioutil.WriteFile(macPath, macBytes, 0644)
require.NoError(t, err)

_, _, err = parseTLSAndMacaroon(
certPath, macPath, "", "", "mainnet", false, false,
MacFilename(""),
)
require.NoError(t, err)
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
module github.com/lightninglabs/lndclient

require (
github.com/btcsuite/btcd v0.21.0-beta.0.20210513141527-ee5896bad5be
github.com/btcsuite/btcd v0.22.0-beta.0.20210803133449-f5a1fb9965e4
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/btcutil v1.0.3-0.20210527170813-e2ba6805a890
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210706234807-aaf03fee735a
github.com/lightningnetwork/lnd v0.13.0-beta.rc5.0.20210802115842-44971f0c46c9
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210822222949-9b5a201c344c
github.com/lightningnetwork/lnd v0.13.0-beta.rc5.0.20210920062527-d9f0f07142ea
github.com/stretchr/testify v1.7.0
google.golang.org/grpc v1.38.0
gopkg.in/macaroon.v2 v2.1.0
Expand Down
Loading