From 0c699f24355fce44bad561b144a0e4ce3795e058 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 9 Mar 2020 13:47:49 +0100 Subject: [PATCH] lsat+client: allow macaroons on insecure proxy connections --- lsat/credential.go | 52 ++++++++++++++++++++++++++++++++++++++++ lsat/interceptor.go | 29 +++++++++++----------- lsat/interceptor_test.go | 4 ++-- swap_server_client.go | 10 +++++++- 4 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 lsat/credential.go diff --git a/lsat/credential.go b/lsat/credential.go new file mode 100644 index 0000000..8d38c9e --- /dev/null +++ b/lsat/credential.go @@ -0,0 +1,52 @@ +package lsat + +import ( + "context" + "encoding/hex" + + "gopkg.in/macaroon.v2" +) + +// MacaroonCredential wraps a macaroon to implement the +// credentials.PerRPCCredentials interface. +type MacaroonCredential struct { + *macaroon.Macaroon + + // AllowInsecure specifies if the macaroon is allowed to be sent over + // insecure transport channels. This should only ever be set to true if + // the insecure connection is proxied through tor and the destination + // address is an onion service. + AllowInsecure bool +} + +// RequireTransportSecurity implements the PerRPCCredentials interface. +func (m MacaroonCredential) RequireTransportSecurity() bool { + return !m.AllowInsecure +} + +// GetRequestMetadata implements the PerRPCCredentials interface. This method +// is required in order to pass the wrapped macaroon into the gRPC context. +// With this, the macaroon will be available within the request handling scope +// of the ultimate gRPC server implementation. +func (m MacaroonCredential) GetRequestMetadata(_ context.Context, + _ ...string) (map[string]string, error) { + + macBytes, err := m.MarshalBinary() + if err != nil { + return nil, err + } + + md := make(map[string]string) + md["macaroon"] = hex.EncodeToString(macBytes) + return md, nil +} + +// NewMacaroonCredential returns a copy of the passed macaroon wrapped in a +// MacaroonCredential struct which implements PerRPCCredentials. +func NewMacaroonCredential(m *macaroon.Macaroon, + allowInsecure bool) MacaroonCredential { + + ms := MacaroonCredential{AllowInsecure: allowInsecure} + ms.Macaroon = m.Clone() + return ms +} diff --git a/lsat/interceptor.go b/lsat/interceptor.go index c121211..4166bfc 100644 --- a/lsat/interceptor.go +++ b/lsat/interceptor.go @@ -12,7 +12,6 @@ import ( "github.com/lightninglabs/loop/lndclient" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" - "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/zpay32" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -65,12 +64,13 @@ var ( // challenges with embedded payment requests. It uses a connection to lnd to // automatically pay for an authentication token. type Interceptor struct { - lnd *lndclient.LndServices - store Store - callTimeout time.Duration - maxCost btcutil.Amount - maxFee btcutil.Amount - lock sync.Mutex + lnd *lndclient.LndServices + store Store + callTimeout time.Duration + maxCost btcutil.Amount + maxFee btcutil.Amount + lock sync.Mutex + allowInsecure bool } // NewInterceptor creates a new gRPC client interceptor that uses the provided @@ -78,14 +78,15 @@ type Interceptor struct { // indicated store already contains a usable token. func NewInterceptor(lnd *lndclient.LndServices, store Store, rpcCallTimeout time.Duration, maxCost, - maxFee btcutil.Amount) *Interceptor { + maxFee btcutil.Amount, allowInsecure bool) *Interceptor { return &Interceptor{ - lnd: lnd, - store: store, - callTimeout: rpcCallTimeout, - maxCost: maxCost, - maxFee: maxFee, + lnd: lnd, + store: store, + callTimeout: rpcCallTimeout, + maxCost: maxCost, + maxFee: maxFee, + allowInsecure: allowInsecure, } } @@ -285,7 +286,7 @@ func (i *Interceptor) addLsatCredentials(iCtx *interceptContext) error { return err } iCtx.opts = append(iCtx.opts, grpc.PerRPCCredentials( - macaroons.NewMacaroonCredential(macaroon), + NewMacaroonCredential(macaroon, i.allowInsecure), )) return nil } diff --git a/lsat/interceptor_test.go b/lsat/interceptor_test.go index 24764d8..38aa8fe 100644 --- a/lsat/interceptor_test.go +++ b/lsat/interceptor_test.go @@ -59,7 +59,7 @@ var ( testTimeout = 5 * time.Second interceptor = NewInterceptor( &lnd.LndServices, store, testTimeout, - DefaultMaxCostSats, DefaultMaxRoutingFeeSats, + DefaultMaxCostSats, DefaultMaxRoutingFeeSats, false, ) testMac = makeMac() testMacBytes = serializeMac(testMac) @@ -173,7 +173,7 @@ var ( initialPreimage: nil, interceptor: NewInterceptor( &lnd.LndServices, store, testTimeout, - 100, DefaultMaxRoutingFeeSats, + 100, DefaultMaxRoutingFeeSats, false, ), resetCb: func() { resetBackend( diff --git a/swap_server_client.go b/swap_server_client.go index 3e7a7ef..d553145 100644 --- a/swap_server_client.go +++ b/swap_server_client.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "net" + "strings" "time" "github.com/btcsuite/btcd/btcec" @@ -56,11 +57,18 @@ var _ swapServerClient = (*grpcSwapServerClient)(nil) func newSwapServerClient(cfg *ClientConfig, lsatStore lsat.Store) ( *grpcSwapServerClient, error) { + // If a proxy address is set and the destination is an onion service, we + // know tor is being used to secure the transport of our credentials. We + // need to set the flag accordingly, otherwise gRPC won't add the LSAT + // macaroon to the request. + allowInsecure := cfg.ProxyAddress != "" && + strings.HasSuffix(cfg.ServerAddress, tor.OnionSuffix) + // Create the server connection with the interceptor that will handle // the LSAT protocol for us. clientInterceptor := lsat.NewInterceptor( cfg.Lnd, lsatStore, serverRPCTimeout, cfg.MaxLsatCost, - cfg.MaxLsatFee, + cfg.MaxLsatFee, allowInsecure, ) serverConn, err := getSwapServerConn( cfg.ServerAddress, cfg.ProxyAddress, cfg.Insecure,