From 2eb9059cf78a294a0ef772f1f25babf4a660f3cd Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 5 Feb 2018 18:32:52 -0800 Subject: [PATCH 1/7] torsvc: add support for stream isolation In this commit, we extend the TorDial function and add a new attribute to the TorProxyNet struct to allow the caller to opt for stream isolation or not. Using stream isolation, we ensure that each new connection uses a distinct circuit. --- torsvc/net.go | 8 +++++++- torsvc/torsvc.go | 13 ++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/torsvc/net.go b/torsvc/net.go index 0a5576fd..859189ac 100644 --- a/torsvc/net.go +++ b/torsvc/net.go @@ -39,6 +39,12 @@ type TorProxyNet struct { // This is used for an outbound-only mode, so the node will not listen for // incoming connections TorSocks string + + // StreamIsolation is a bool that determines if we should force the + // creation of a new circuit for this connection. If true, then this + // means that our traffic may be harder to correlate as each connection + // will now use a distinct circuit. + StreamIsolation bool } // Dial on the Tor network uses the torsvc TorDial() function, and requires @@ -47,7 +53,7 @@ func (t *TorProxyNet) Dial(network, address string) (net.Conn, error) { if network != "tcp" { return nil, fmt.Errorf("Cannot dial non-tcp network via Tor") } - return TorDial(address, t.TorSocks) + return TorDial(address, t.TorSocks, t.StreamIsolation) } // LookupHost on Tor network uses the torsvc TorLookupHost function. diff --git a/torsvc/torsvc.go b/torsvc/torsvc.go index 7325000c..29369373 100644 --- a/torsvc/torsvc.go +++ b/torsvc/torsvc.go @@ -44,9 +44,16 @@ var ( ) // TorDial returns a connection to a remote peer via Tor's socks proxy. Only -// TCP is supported over Tor. -func TorDial(address, socksPort string) (net.Conn, error) { - p := &socks.Proxy{Addr: localhost + ":" + socksPort} +// TCP is supported over Tor. The final argument determines if we should force +// stream isolation for this new connection. If we do, then this means this new +// connection will use a fresh circuit, rather than possibly re-using an +// existing circuit. +func TorDial(address, socksPort string, streamIsolation bool) (net.Conn, error) { + p := &socks.Proxy{ + Addr: localhost + ":" + socksPort, + TorIsolation: streamIsolation, + } + return p.Dial("tcp", address) } From abcf822bf90568083767a838d780d7a7d0a5893a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 5 Feb 2018 18:33:43 -0800 Subject: [PATCH 2/7] config: add new option to the tor config for stream isolation --- config.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/config.go b/config.go index 27baa58e..c890c551 100644 --- a/config.go +++ b/config.go @@ -134,8 +134,9 @@ type autoPilotConfig struct { } type torConfig struct { - Socks string `long:"socks" description:"The port that Tor's exposed SOCKS5 proxy is listening on. Using Tor allows outbound-only connections (listening will be disabled) -- NOTE port must be between 1024 and 65535"` - DNS string `long:"dns" description:"The DNS server as IP:PORT that Tor will use for SRV queries - NOTE must have TCP resolution enabled"` + Socks string `long:"socks" description:"The port that Tor's exposed SOCKS5 proxy is listening on. Using Tor allows outbound-only connections (listening will be disabled) -- NOTE port must be between 1024 and 65535"` + DNS string `long:"dns" description:"The DNS server as IP:PORT that Tor will use for SRV queries - NOTE must have TCP resolution enabled"` + StreamIsolation bool `long:"streamisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."` } // config defines the configuration options for lnd. @@ -325,8 +326,9 @@ func loadConfig() (*config, error) { } cfg.net = &torsvc.TorProxyNet{ - TorDNS: cfg.Tor.DNS, - TorSocks: cfg.Tor.Socks, + TorDNS: cfg.Tor.DNS, + TorSocks: cfg.Tor.Socks, + StreamIsolation: cfg.Tor.StreamIsolation, } // If we are using Tor, since we only want connections routed From 2aac20ec121c200e80a899f20daf059093aee116 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 5 Feb 2018 18:34:09 -0800 Subject: [PATCH 3/7] docs: update sample-lnd.conf with Tor info --- sample-lnd.conf | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sample-lnd.conf b/sample-lnd.conf index c21e5495..582b9760 100644 --- a/sample-lnd.conf +++ b/sample-lnd.conf @@ -261,3 +261,20 @@ litecoin.node=btcd ; within the wallet should be used to automatically establish channels. The total ; amount of attempted channels will still respect the maxchannels param. ; autopilot.allocation=0.6 + +[tor] +; The port that Tor's exposed SOCKS5 proxy is listening on. Using Tor allows +; outbound-only connections (listening will be disabled) -- NOTE port must be +; between 1024 and 65535 +; tor.socks=9050 + +; The DNS server as IP:PORT that Tor will use for SRV queries - NOTE must have +; TCP resolution enabled. The current active DNS sever for Testnet listening is +; nodes.lightning.directory +; tor.dns=nodes.lightning.directory + +; Enable Tor stream isolation by randomizing user credentials for each +; connection. With this mode active, each connection will use a new circuit. +; This means that multiple applications (other than lnd) using Tor won't be mixed +; in with lnd's traffic. +; tor.streamisolation=1 From 4cba9dc8d3bae93f4e3478c46dca8b41cfb1b619 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 5 Feb 2018 18:34:38 -0800 Subject: [PATCH 4/7] docs: add new documentation for configuring lnd+tor --- docs/configuring_tor.md | 120 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 docs/configuring_tor.md diff --git a/docs/configuring_tor.md b/docs/configuring_tor.md new file mode 100644 index 00000000..d4395fb1 --- /dev/null +++ b/docs/configuring_tor.md @@ -0,0 +1,120 @@ +# Table of Contents +1. [Overview](#Overview) +2. [Outbound Connections Only](#outbound-connections-only) +3. [Tor Stream Isolation](#tor-stream-isolation) + +## 1. Overview + +`lnd` currently has _partial_ support for using Lightning over +[Tor](https://www.torproject.org/). Usage of Lightning over Tor is valuable as +routing nodes no longer need to potentially expose their location via their +advertised IP address. Additionally, leaf nodes can also protect their location +by using Tor for anonymous networking to establish connections. + +At the time of the writing of this documentation, `lnd` only supports usage of +Tor for establishing _outbound_ connections. In the near future, support for +full [Onion Service](https://www.torproject.org/docs/onion-services.html.en) +usage will be added as well. Support for both `v2` and `v3` onion services are +planned. With widespread usage of Onion Services within the network, concerns +about the difficulty of proper NAT traversal are alleviated, as usage of Onion +Services allows nodes to accept inbound connections even if they're behind a +NAT. + +Before following the remainder of this documentation, you should ensure that +you already have Tor installed locally. Official instructions to install the +latest release of Tor can be found +[here](https://www.torproject.org/docs/tor-doc-unix.html.en). + +**NOTE**: This documentation covers how to ensure that `lnd`'s _Lightning +protocol traffic_ is tunnled over Tor. Users will need to take care that if +they're running using a Bitcoin full-node, then that is also configured to +proxy all trafic over Tor. If using the `neutrino` backend for `lnd`, then it +will automatically also default to Tor usage if active within `lnd`. + + +## 2. Outbound Connections Only + +Currenty, `lnd` only supports purely _outbound_ Tor usage. In this mode, `lnd` +_won't_ listen at all, and will only be able to establish outbound connections. +_All_ protocol traffic will be tunneled over Tor. Additionally, we'll also +force any DNS requests over Tor such that we don't leak our IP address to the +clear net. + +The remainder of this tutorial assumes one already has the `tor` daemon +installed locally. + +First, you'll want to run `tor` locally before starting up `lnd`. Depending on +how you installed Tor, you'll find the configuration file at +`/usr/local/etc/tor/torrc`. Here's an example configuration file that we'll be +using for the remainder of the tutorial: +``` +SOCKSPort 9050 # Default: Bind to localhost:9050 for local connections. +Log notice stdout +ControlPort 9051 +CookieAuthentication 1 +``` + +With the configuration file created, you'll then want to start the Tor daemon: +``` +⛰ tor +Feb 05 17:02:06.501 [notice] Tor 0.3.1.8 (git-ad5027f7dc790624) running on Darwin with Libevent 2.1.8-stable, OpenSSL 1.0.2l, Zlib 1.2.8, Liblzma N/A, and Libzstd N/A. +Feb 05 17:02:06.502 [notice] Tor can't help you if you use it wrong! Learn how to be safe at https://www.torproject.org/download/download#warning +Feb 05 17:02:06.502 [notice] Read configuration file "/usr/local/etc/tor/torrc". +Feb 05 17:02:06.506 [notice] Opening Socks listener on 127.0.0.1:9050 +Feb 05 17:02:06.506 [notice] Opening Control listener on 127.0.0.1:9051 +``` + +Once the `tor` daemon has started and it has finished bootstrapping, you'll see this in the logs: +``` +Feb 05 17:02:06.000 [notice] Bootstrapped 0%: Starting +Feb 05 17:02:07.000 [notice] Starting with guard context "default" +Feb 05 17:02:07.000 [notice] Bootstrapped 80%: Connecting to the Tor network +Feb 05 17:02:07.000 [notice] Bootstrapped 85%: Finishing handshake with first hop +Feb 05 17:02:08.000 [notice] Bootstrapped 90%: Establishing a Tor circuit +Feb 05 17:02:11.000 [notice] Tor has successfully opened a circuit. Looks like client functionality is working. +Feb 05 17:02:11.000 [notice] Bootstrapped 100%: Done +``` + +This indicates the daemon is fully bootstrapped and ready to proxy connections. +At this point, we can now start `lnd` with the relevant arguments: + +``` +⛰ ./lnd -h + + + +Tor: + --tor.socks= The port that Tor's exposed SOCKS5 proxy is listening on. Using Tor allows outbound-only connections (listening will be disabled) -- NOTE port must be between 1024 and 65535 + --tor.dns= The DNS server as IP:PORT that Tor will use for SRV queries - NOTE must have TCP resolution enabled +``` + +The `--tor.socks` argument should point to the interface that the `Tor` daemon +is listening on to proxy connections. The `--tor.dns` flag is required in order +to be able to properly automatically bootstrap a set of peer connections. The +`tor` daemon doesn't currently support proxying `SRV` queries over Tor. So +instead, we need to connect directly to the authoritative DNS server over TCP, +in order query for `SRV` records that we can use to bootstrap our connections. +As of the time this documentation was written, for Bitcoin's Testnet, clients +should point to `nodes.lightning.directory`. + +Finally, we'll start `lnd` with the proper arguments: +``` +⛰ ./lnd --tor.socks=9050 --tor.dns=nodes.lightning.directory +``` + +With the above arguments, `lnd` will proxy _all_ network traffic over Tor! + + +## 3. Tor Stream Isolation + +Our support for Tor also has an additional privacy enhancing modified: stream +isolation. Usage of this mode means that Tor will always use _new circuit_ for +each connection. This added features means that it's harder to correlate +connections. As otherwise, several applications using Tor might share the same +circuit. + +Activating stream isolation is very straightforward, we only require the +specification of an additional argument: +``` +⛰ ./lnd --tor.socks=9050 --tor.dns=nodes.lightning.directory --tor.streamisolation +``` From 076b6761959bb90c4a9739e84842ee85e4fbbdd4 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 5 Feb 2018 18:34:59 -0800 Subject: [PATCH 5/7] build: update to neutrino version w/ custom dialer+DNS support --- glide.lock | 6 +++--- glide.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/glide.lock b/glide.lock index 9a091067..9fc1c19b 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: a69cfd6352bed3cc0d24a01f42609b849f9f6d5664be7470b281bf6b09c58d84 -updated: 2018-02-06T17:14:31.208312805-08:00 +hash: 63c0706e71ebdd4664e6a452bf58035571a8799b24e517ea0d8aa2fb8dd94155 +updated: 2018-02-08T15:26:31.423935935-08:00 imports: - name: github.com/aead/chacha20 version: d31a916ded42d1640b9d89a26f8abd53cc96790c @@ -72,7 +72,7 @@ imports: - name: github.com/lightninglabs/gozmq version: 0d266ba6d55ea65c18c7a02d8992c89355149e80 - name: github.com/lightninglabs/neutrino - version: 6c8f30a130bb170348ceb754ce4204156efe2741 + version: c933ad49936ac3e5bc1b42e855f2da69745e34cc subpackages: - filterdb - headerfs diff --git a/glide.yaml b/glide.yaml index c647ba3e..9367b6de 100644 --- a/glide.yaml +++ b/glide.yaml @@ -73,7 +73,7 @@ import: subpackages: - chaincfg - package: github.com/lightninglabs/neutrino - version: 6c8f30a130bb170348ceb754ce4204156efe2741 + version: c933ad49936ac3e5bc1b42e855f2da69745e34cc - package: gopkg.in/macaroon.v2 - package: gopkg.in/macaroon-bakery.v2 - package: github.com/juju/loggo From 3fcce9959b38b22b477cb5e270bf45e19761e984 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 5 Feb 2018 18:36:11 -0800 Subject: [PATCH 6/7] lnd: pass through the custom dialer+DNS to neutrino In this commit, we modify our initialization of neutrino to also pass in the custom dialer and name resolver function. With this change, if lnd is configured to use Tor, then neutrino will as well. This means that *both* the Bitcoin P2P as well as the Lightning P2P traffic will be proxied over Tor. --- chainregistry.go | 21 +++++++++++++++++++++ lnd.go | 7 +++++++ 2 files changed, 28 insertions(+) diff --git a/chainregistry.go b/chainregistry.go index 9bd09b55..7cbed86e 100644 --- a/chainregistry.go +++ b/chainregistry.go @@ -174,6 +174,27 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB, ChainParams: *activeNetParams.Params, AddPeers: cfg.NeutrinoMode.AddPeers, ConnectPeers: cfg.NeutrinoMode.ConnectPeers, + Dialer: func(addr net.Addr) (net.Conn, error) { + return cfg.net.Dial(addr.Network(), addr.String()) + }, + NameResolver: func(host string) ([]net.IP, error) { + addrs, err := cfg.net.LookupHost(host) + if err != nil { + return nil, err + } + + ips := make([]net.IP, 0, len(addrs)) + for _, strIP := range addrs { + ip := net.ParseIP(strIP) + if ip == nil { + continue + } + + ips = append(ips, ip) + } + + return ips, nil + }, } neutrino.WaitForMoreCFHeaders = time.Second * 1 neutrino.MaxPeers = 8 diff --git a/lnd.go b/lnd.go index 09532fb6..9f4e10f8 100644 --- a/lnd.go +++ b/lnd.go @@ -251,6 +251,13 @@ func lndMain() error { } idPrivKey.Curve = btcec.S256() + if cfg.Tor.Socks != "" && cfg.Tor.DNS != "" { + srvrLog.Infof("Proxying all network traffic via Tor "+ + "(stream_isolation=%v)! NOTE: If running with a full-node "+ + "backend, ensure that is proxying over Tor as well", + cfg.Tor.StreamIsolation) + } + // Set up the core server which will listen for incoming peer // connections. server, err := newServer(cfg.Listeners, chanDB, activeChainControl, From 7f61d8cf3d14d11f686b372ba9312f158b775246 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 9 Feb 2018 12:05:24 -0800 Subject: [PATCH 7/7] lnwallet: update tests to use latest neutrino API --- lnwallet/interface_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 612be9c7..08b83497 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -1700,7 +1700,6 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver, neutrino.Config{ DataDir: tempTestDirAlice, Database: aliceDB, - Namespace: []byte("alice"), ChainParams: *netParams, ConnectPeers: []string{ miningNode.P2PAddress(), @@ -1726,7 +1725,6 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver, neutrino.Config{ DataDir: tempTestDirBob, Database: bobDB, - Namespace: []byte("bob"), ChainParams: *netParams, ConnectPeers: []string{ miningNode.P2PAddress(),