diff --git a/basic_client.go b/basic_client.go index 0e40253..de4e723 100644 --- a/basic_client.go +++ b/basic_client.go @@ -14,20 +14,24 @@ import ( macaroon "gopkg.in/macaroon.v2" ) -// BasicClientOption is a functional option argument that allows adding arbitrary -// lnd basic client configuration overrides, without forcing existing users of -// NewBasicClient to update their invocation. These are always processed in -// order, with later options overriding earlier ones. +// BasicClientOption is a functional option argument that allows adding +// arbitrary lnd basic client configuration overrides, without forcing existing +// users of NewBasicClient to update their invocation. These are always +// processed in order, with later options overriding earlier ones. type BasicClientOption func(*basicClientOptions) // basicClientOptions is a set of options that can configure the lnd client // returned by NewBasicClient. type basicClientOptions struct { macFilename string + tlsData string + macData string + insecure bool + systemCerts bool } -// defaultBasicClientOptions returns a basicClientOptions set to lnd basic client -// defaults. +// defaultBasicClientOptions returns a basicClientOptions set to lnd basic +// client defaults. func defaultBasicClientOptions() *basicClientOptions { return &basicClientOptions{ macFilename: adminMacFilename, @@ -42,9 +46,43 @@ func MacFilename(macFilename string) BasicClientOption { } } +// TLSData is a basic client option that sets TLS data (encoded in PEM format) +// directly instead of loading them from a file. +func TLSData(tlsData string) BasicClientOption { + return func(bc *basicClientOptions) { + bc.tlsData = tlsData + } +} + +// MacaroonData is a basic client option that sets macaroon data (encoded as hex +// string) directly instead of loading it from a file. +func MacaroonData(macData string) BasicClientOption { + return func(bc *basicClientOptions) { + bc.macData = macData + } +} + +// Insecure allows the basic client to establish an insecure (non-TLS) +// connection to the RPC server. +func Insecure() BasicClientOption { + return func(bc *basicClientOptions) { + bc.insecure = true + } +} + +// SystemCerts instructs the basic client to use the system's certificate trust +// store for verifying the TLS certificate of the RPC server. +func SystemCerts() BasicClientOption { + return func(bc *basicClientOptions) { + bc.systemCerts = true + } +} + // applyBasicClientOptions updates a basicClientOptions set with functional // options. -func (bc *basicClientOptions) applyBasicClientOptions(options ...BasicClientOption) { +func (bc *basicClientOptions) applyBasicClientOptions( + options ...BasicClientOption) { + for _, option := range options { option(bc) } @@ -53,14 +91,11 @@ 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, tlsData, macData, network string, - insecure, systemCert bool, basicOptions ...BasicClientOption) ( - - lnrpc.LightningClient, error) { +func NewBasicClient(lndHost, tlsPath, macDir, network string, + basicOptions ...BasicClientOption) (lnrpc.LightningClient, error) { conn, err := NewBasicConn( - lndHost, tlsPath, macDir, tlsData, macData, network, insecure, - systemCert, basicOptions..., + lndHost, tlsPath, macDir, network, basicOptions..., ) if err != nil { return nil, err @@ -72,15 +107,13 @@ func NewBasicClient(lndHost, tlsPath, macDir, tlsData, macData, 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 string, tlsPath, macDir, tlsData, macData, - network string, insecure, systemCert bool, +func NewBasicConn(lndHost string, tlsPath, macDir, network string, basicOptions ...BasicClientOption) ( *grpc.ClientConn, error) { creds, mac, err := parseTLSAndMacaroon( - tlsPath, macDir, tlsData, macData, network, insecure, - systemCert, basicOptions..., + tlsPath, macDir, network, basicOptions..., ) if err != nil { return nil, err @@ -105,33 +138,36 @@ func NewBasicConn(lndHost string, tlsPath, macDir, tlsData, macData, ) conn, err := grpc.Dial(lndHost, opts...) if err != nil { - return nil, fmt.Errorf("unable to connect to RPC server: %v", err) + return nil, fmt.Errorf("unable to connect to RPC server: %v", + err) } 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 +// 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 - } +func parseTLSAndMacaroon(tlsPath, macDir, network string, + basicOptions ...BasicClientOption) (credentials.TransportCredentials, + *macaroon.Macaroon, error) { // Starting with the set of default options, we'll apply any specified // functional options to the basic client. bco := defaultBasicClientOptions() bco.applyBasicClientOptions(basicOptions...) + creds, err := GetTLSCredentials( + bco.tlsData, tlsPath, bco.insecure, bco.systemCerts, + ) + if err != nil { + return nil, nil, err + } + var macBytes []byte mac := &macaroon.Macaroon{} - if macData != "" { - macBytes, err = hex.DecodeString(macData) + if bco.macData != "" { + macBytes, err = hex.DecodeString(bco.macData) if err != nil { return nil, nil, err } diff --git a/basic_client_test.go b/basic_client_test.go index cf3b78e..3fa6f2a 100644 --- a/basic_client_test.go +++ b/basic_client_test.go @@ -40,8 +40,8 @@ XhkpT5dliEGFLNe6OOgeWTU1JpEXfCud/GImtNMHQi4EDWQfvWuCNGhOoQ== // Make sure it works when data is passed in. _, _, err := parseTLSAndMacaroon( - "", "", tlsData, macData, "mainnet", false, false, - MacFilename(""), + "", "", "mainnet", MacFilename(""), TLSData(tlsData), + MacaroonData(macData), ) require.NoError(t, err) @@ -65,8 +65,7 @@ XhkpT5dliEGFLNe6OOgeWTU1JpEXfCud/GImtNMHQi4EDWQfvWuCNGhOoQ== require.NoError(t, err) _, _, err = parseTLSAndMacaroon( - certPath, macPath, "", "", "mainnet", false, false, - MacFilename(""), + certPath, macPath, "mainnet", MacFilename(""), ) require.NoError(t, err) } diff --git a/lnd_services.go b/lnd_services.go index ca10a7c..9d9d494 100644 --- a/lnd_services.go +++ b/lnd_services.go @@ -784,20 +784,21 @@ func getClientConn(cfg *LndServicesConfig) (*grpc.ClientConn, error) { // GetTLSCredentials gets the tls credentials, whether provided as straight-up // data or a path to a certificate file. -func GetTLSCredentials(tlsData, tlsPath string, insecure, systemCert bool) ( - credentials.TransportCredentials, error) { - - if tlsPath != "" && tlsData != "" { - return nil, fmt.Errorf("must set only one: TLSPath or TLSData") - } - - var creds credentials.TransportCredentials - var err error +func GetTLSCredentials(tlsData, tlsPath string, insecure, + systemCert bool) (credentials.TransportCredentials, error) { // We'll determine if the tls certificate is passed in directly as // data, by a path, or try the system's certificate chain, and then // load it. + var creds credentials.TransportCredentials switch { + case tlsPath != "" && tlsData != "": + return nil, fmt.Errorf("must set only one: TLSPath or TLSData") + + case insecure && systemCert: + return nil, fmt.Errorf("cannot set insecure and system cert " + + "at the same time") + case insecure: // If we don't need to use tls, such as if we're connecting to // lnd via a bufconn, then we'll skip verification. @@ -833,26 +834,29 @@ func GetTLSCredentials(tlsData, tlsPath string, insecure, systemCert bool) ( creds = credentials.NewClientTLSFromCert(pool, "") case tlsPath != "": + var err error creds, err = credentials.NewClientTLSFromFile(tlsPath, "") if err != nil { return nil, err } default: - // If neither tlsData nor tlsPath were set, we'll try the default - // lnd tls cert path. - if _, err := os.Stat(defaultTLSCertPath); err == nil { - creds, err = credentials.NewClientTLSFromFile( - defaultTLSCertPath, "", - ) - if err != nil { - return nil, err - } - - } else { - return nil, err + // If neither tlsData nor tlsPath were set, we'll try the + // default lnd tls cert path. + _, err := os.Stat(defaultTLSCertPath) + if err != nil { + return nil, fmt.Errorf("couldn't find out if default "+ + "lnd TLS cert at %s exists: %v", + defaultTLSCertPath, err) + } + creds, err = credentials.NewClientTLSFromFile( + defaultTLSCertPath, "", + ) + if err != nil { + return nil, fmt.Errorf("couldn't load default lnd "+ + "TLS cert at %s: %v", defaultTLSCertPath, err) } } - return creds, err + return creds, nil }