Merge pull request #737 from Roasbeef/tor-docs

tor: add stream isolation, additional documentation, neutrino support
This commit is contained in:
Olaoluwa Osuntokun 2018-02-18 15:28:53 -08:00 committed by GitHub
commit 9709e5f31f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 192 additions and 14 deletions

View File

@ -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

View File

@ -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

120
docs/configuring_tor.md Normal file
View File

@ -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
<snip>
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
```

6
glide.lock generated
View File

@ -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

View File

@ -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

7
lnd.go
View File

@ -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,

View File

@ -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(),

View File

@ -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

View File

@ -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.

View File

@ -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)
}