Merge pull request #737 from Roasbeef/tor-docs
tor: add stream isolation, additional documentation, neutrino support
This commit is contained in:
commit
9709e5f31f
@ -174,6 +174,27 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
|
|||||||
ChainParams: *activeNetParams.Params,
|
ChainParams: *activeNetParams.Params,
|
||||||
AddPeers: cfg.NeutrinoMode.AddPeers,
|
AddPeers: cfg.NeutrinoMode.AddPeers,
|
||||||
ConnectPeers: cfg.NeutrinoMode.ConnectPeers,
|
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.WaitForMoreCFHeaders = time.Second * 1
|
||||||
neutrino.MaxPeers = 8
|
neutrino.MaxPeers = 8
|
||||||
|
10
config.go
10
config.go
@ -134,8 +134,9 @@ type autoPilotConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type torConfig 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"`
|
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"`
|
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.
|
// config defines the configuration options for lnd.
|
||||||
@ -325,8 +326,9 @@ func loadConfig() (*config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg.net = &torsvc.TorProxyNet{
|
cfg.net = &torsvc.TorProxyNet{
|
||||||
TorDNS: cfg.Tor.DNS,
|
TorDNS: cfg.Tor.DNS,
|
||||||
TorSocks: cfg.Tor.Socks,
|
TorSocks: cfg.Tor.Socks,
|
||||||
|
StreamIsolation: cfg.Tor.StreamIsolation,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are using Tor, since we only want connections routed
|
// If we are using Tor, since we only want connections routed
|
||||||
|
120
docs/configuring_tor.md
Normal file
120
docs/configuring_tor.md
Normal 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
6
glide.lock
generated
@ -1,5 +1,5 @@
|
|||||||
hash: a69cfd6352bed3cc0d24a01f42609b849f9f6d5664be7470b281bf6b09c58d84
|
hash: 63c0706e71ebdd4664e6a452bf58035571a8799b24e517ea0d8aa2fb8dd94155
|
||||||
updated: 2018-02-06T17:14:31.208312805-08:00
|
updated: 2018-02-08T15:26:31.423935935-08:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/aead/chacha20
|
- name: github.com/aead/chacha20
|
||||||
version: d31a916ded42d1640b9d89a26f8abd53cc96790c
|
version: d31a916ded42d1640b9d89a26f8abd53cc96790c
|
||||||
@ -72,7 +72,7 @@ imports:
|
|||||||
- name: github.com/lightninglabs/gozmq
|
- name: github.com/lightninglabs/gozmq
|
||||||
version: 0d266ba6d55ea65c18c7a02d8992c89355149e80
|
version: 0d266ba6d55ea65c18c7a02d8992c89355149e80
|
||||||
- name: github.com/lightninglabs/neutrino
|
- name: github.com/lightninglabs/neutrino
|
||||||
version: 6c8f30a130bb170348ceb754ce4204156efe2741
|
version: c933ad49936ac3e5bc1b42e855f2da69745e34cc
|
||||||
subpackages:
|
subpackages:
|
||||||
- filterdb
|
- filterdb
|
||||||
- headerfs
|
- headerfs
|
||||||
|
@ -73,7 +73,7 @@ import:
|
|||||||
subpackages:
|
subpackages:
|
||||||
- chaincfg
|
- chaincfg
|
||||||
- package: github.com/lightninglabs/neutrino
|
- package: github.com/lightninglabs/neutrino
|
||||||
version: 6c8f30a130bb170348ceb754ce4204156efe2741
|
version: c933ad49936ac3e5bc1b42e855f2da69745e34cc
|
||||||
- package: gopkg.in/macaroon.v2
|
- package: gopkg.in/macaroon.v2
|
||||||
- package: gopkg.in/macaroon-bakery.v2
|
- package: gopkg.in/macaroon-bakery.v2
|
||||||
- package: github.com/juju/loggo
|
- package: github.com/juju/loggo
|
||||||
|
7
lnd.go
7
lnd.go
@ -251,6 +251,13 @@ func lndMain() error {
|
|||||||
}
|
}
|
||||||
idPrivKey.Curve = btcec.S256()
|
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
|
// Set up the core server which will listen for incoming peer
|
||||||
// connections.
|
// connections.
|
||||||
server, err := newServer(cfg.Listeners, chanDB, activeChainControl,
|
server, err := newServer(cfg.Listeners, chanDB, activeChainControl,
|
||||||
|
@ -1700,7 +1700,6 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
|||||||
neutrino.Config{
|
neutrino.Config{
|
||||||
DataDir: tempTestDirAlice,
|
DataDir: tempTestDirAlice,
|
||||||
Database: aliceDB,
|
Database: aliceDB,
|
||||||
Namespace: []byte("alice"),
|
|
||||||
ChainParams: *netParams,
|
ChainParams: *netParams,
|
||||||
ConnectPeers: []string{
|
ConnectPeers: []string{
|
||||||
miningNode.P2PAddress(),
|
miningNode.P2PAddress(),
|
||||||
@ -1726,7 +1725,6 @@ func runTests(t *testing.T, walletDriver *lnwallet.WalletDriver,
|
|||||||
neutrino.Config{
|
neutrino.Config{
|
||||||
DataDir: tempTestDirBob,
|
DataDir: tempTestDirBob,
|
||||||
Database: bobDB,
|
Database: bobDB,
|
||||||
Namespace: []byte("bob"),
|
|
||||||
ChainParams: *netParams,
|
ChainParams: *netParams,
|
||||||
ConnectPeers: []string{
|
ConnectPeers: []string{
|
||||||
miningNode.P2PAddress(),
|
miningNode.P2PAddress(),
|
||||||
|
@ -261,3 +261,20 @@ litecoin.node=btcd
|
|||||||
; within the wallet should be used to automatically establish channels. The total
|
; within the wallet should be used to automatically establish channels. The total
|
||||||
; amount of attempted channels will still respect the maxchannels param.
|
; amount of attempted channels will still respect the maxchannels param.
|
||||||
; autopilot.allocation=0.6
|
; 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
|
||||||
|
@ -39,6 +39,12 @@ type TorProxyNet struct {
|
|||||||
// This is used for an outbound-only mode, so the node will not listen for
|
// This is used for an outbound-only mode, so the node will not listen for
|
||||||
// incoming connections
|
// incoming connections
|
||||||
TorSocks string
|
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
|
// 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" {
|
if network != "tcp" {
|
||||||
return nil, fmt.Errorf("Cannot dial non-tcp network via Tor")
|
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.
|
// LookupHost on Tor network uses the torsvc TorLookupHost function.
|
||||||
|
@ -44,9 +44,16 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TorDial returns a connection to a remote peer via Tor's socks proxy. Only
|
// TorDial returns a connection to a remote peer via Tor's socks proxy. Only
|
||||||
// TCP is supported over Tor.
|
// TCP is supported over Tor. The final argument determines if we should force
|
||||||
func TorDial(address, socksPort string) (net.Conn, error) {
|
// stream isolation for this new connection. If we do, then this means this new
|
||||||
p := &socks.Proxy{Addr: localhost + ":" + socksPort}
|
// 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)
|
return p.Dial("tcp", address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user