diff --git a/lnd.go b/lnd.go index 3e6ed885..875f0e73 100644 --- a/lnd.go +++ b/lnd.go @@ -45,6 +45,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/signal" + "github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower/wtdb" @@ -473,6 +474,28 @@ func Main(lisCfg ListenerCfg) error { defer towerClientDB.Close() } + // If tor is active and either v2 or v3 onion services have been specified, + // make a tor controller and pass it into both the watchtower server and + // the regular lnd server. + var torController *tor.Controller + if cfg.Tor.Active && (cfg.Tor.V2 || cfg.Tor.V3) { + torController = tor.NewController( + cfg.Tor.Control, cfg.Tor.TargetIPAddress, cfg.Tor.Password, + ) + + // Start the tor controller before giving it to any other subsystems. + if err := torController.Start(); err != nil { + err := fmt.Errorf("unable to initialize tor controller: %v", err) + ltndLog.Error(err) + return err + } + defer func() { + if err := torController.Stop(); err != nil { + ltndLog.Errorf("error stopping tor controller: %v", err) + } + }() + } + var tower *watchtower.Standalone if cfg.Watchtower.Active { // Segment the watchtower directory by chain and network. @@ -506,7 +529,7 @@ func Main(lisCfg ListenerCfg) error { return err } - wtConfig, err := cfg.Watchtower.Apply(&watchtower.Config{ + wtCfg := &watchtower.Config{ BlockFetcher: activeChainControl.chainIO, DB: towerDB, EpochRegistrar: activeChainControl.chainNotifier, @@ -519,7 +542,23 @@ func Main(lisCfg ListenerCfg) error { NodePrivKey: towerPrivKey, PublishTx: activeChainControl.wallet.PublishTransaction, ChainHash: *activeNetParams.GenesisHash, - }, lncfg.NormalizeAddresses) + } + + // If there is a tor controller (user wants auto hidden services), then + // store a pointer in the watchtower config. + if torController != nil { + wtCfg.TorController = torController + wtCfg.WatchtowerKeyPath = cfg.Tor.WatchtowerKeyPath + + switch { + case cfg.Tor.V2: + wtCfg.Type = tor.V2 + case cfg.Tor.V3: + wtCfg.Type = tor.V3 + } + } + + wtConfig, err := cfg.Watchtower.Apply(wtCfg, lncfg.NormalizeAddresses) if err != nil { err := fmt.Errorf("Unable to configure watchtower: %v", err) @@ -543,6 +582,7 @@ func Main(lisCfg ListenerCfg) error { server, err := newServer( cfg.Listeners, chanDB, towerClientDB, activeChainControl, idPrivKey, walletInitParams.ChansToRestore, chainedAcceptor, + torController, ) if err != nil { err := fmt.Errorf("Unable to create server: %v", err) diff --git a/server.go b/server.go index fe935f3c..a1dce6ec 100644 --- a/server.go +++ b/server.go @@ -322,7 +322,8 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, towerClientDB *wtdb.ClientDB, cc *chainControl, privKey *btcec.PrivateKey, chansToRestore walletunlocker.ChannelsToRecover, - chanPredicate chanacceptor.ChannelAcceptor) (*server, error) { + chanPredicate chanacceptor.ChannelAcceptor, + torController *tor.Controller) (*server, error) { var err error @@ -428,6 +429,8 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, // schedule sphinx: hop.NewOnionProcessor(sphinxRouter), + torController: torController, + persistentPeers: make(map[string]bool), persistentPeersBackoff: make(map[string]time.Duration), persistentConnReqs: make(map[string][]*connmgr.ConnReq), @@ -598,16 +601,6 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, selfAddrs = append(selfAddrs, ip) } - // If we were requested to route connections through Tor and to - // automatically create an onion service, we'll initiate our Tor - // controller and establish a connection to the Tor server. - if cfg.Tor.Active && (cfg.Tor.V2 || cfg.Tor.V3) { - s.torController = tor.NewController( - cfg.Tor.Control, cfg.Tor.TargetIPAddress, - cfg.Tor.Password, - ) - } - chanGraph := chanDB.ChannelGraph() // We'll now reconstruct a node announcement based on our current @@ -1251,7 +1244,7 @@ func (s *server) Start() error { var startErr error s.start.Do(func() { if s.torController != nil { - if err := s.initTorController(); err != nil { + if err := s.createNewHiddenService(); err != nil { startErr = err return } @@ -1442,10 +1435,6 @@ func (s *server) Stop() error { close(s.quit) - if s.torController != nil { - s.torController.Stop() - } - // Shutdown the wallet, funding manager, and the rpc server. s.chanStatusMgr.Stop() s.cc.chainNotifier.Stop() @@ -1965,14 +1954,9 @@ func (s *server) initialPeerBootstrap(ignore map[autopilot.NodeID]struct{}, } } -// initTorController initiliazes the Tor controller backed by lnd and -// automatically sets up a v2 onion service in order to listen for inbound -// connections over Tor. -func (s *server) initTorController() error { - if err := s.torController.Start(); err != nil { - return err - } - +// createNewHiddenService automatically sets up a v2 or v3 onion service in +// order to listen for inbound connections over Tor. +func (s *server) createNewHiddenService() error { // Determine the different ports the server is listening on. The onion // service's virtual port will map to these ports and one will be picked // at random when the onion service is being accessed. diff --git a/watchtower/config.go b/watchtower/config.go index 26dfac75..a71bd043 100644 --- a/watchtower/config.go +++ b/watchtower/config.go @@ -34,8 +34,8 @@ var ( ) // Config defines the resources and parameters used to configure a Watchtower. -// All nil-able elements with the Config must be set in order for the Watchtower -// to function properly. +// All nil-able elements besides tor-related ones must be set in order for the +// Watchtower to function properly. type Config struct { // ChainHash identifies the chain that the watchtower will be monitoring // for breaches and that will be advertised in the server's Init message @@ -89,4 +89,16 @@ type Config struct { // message from the other end, if the connection has stopped buffering // the server's replies. WriteTimeout time.Duration + + // TorController allows the watchtower to optionally setup an onion hidden + // service. + TorController *tor.Controller + + // WatchtowerKeyPath allows the watchtower to specify where the private key + // for a watchtower hidden service should be stored. + WatchtowerKeyPath string + + // Type specifies the hidden service type (V2 or V3) that the watchtower + // will create. + Type tor.OnionType } diff --git a/watchtower/standalone.go b/watchtower/standalone.go index 8f50dc65..b1a36bb5 100644 --- a/watchtower/standalone.go +++ b/watchtower/standalone.go @@ -6,6 +6,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/lightningnetwork/lnd/brontide" + "github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/watchtower/lookout" "github.com/lightningnetwork/lnd/watchtower/wtserver" ) @@ -112,6 +113,15 @@ func (w *Standalone) Start() error { log.Infof("Starting watchtower") + // If a tor controller exists in the config, then automatically create a + // hidden service for the watchtower to accept inbound connections from. + if w.cfg.TorController != nil { + log.Infof("Creating watchtower hidden service") + if err := w.createNewHiddenService(); err != nil { + return err + } + } + if err := w.lookout.Start(); err != nil { return err } @@ -142,6 +152,39 @@ func (w *Standalone) Stop() error { return nil } +// createNewHiddenService automatically sets up a v2 or v3 onion service in +// order to listen for inbound connections over Tor. +func (w *Standalone) createNewHiddenService() error { + // Get all the ports the watchtower is listening on. These will be used to + // map the hidden service's virtual port. + listenPorts := make([]int, 0, len(w.listeners)) + for _, listener := range w.listeners { + port := listener.Addr().(*net.TCPAddr).Port + listenPorts = append(listenPorts, port) + } + + // Once we've created the port mapping, we can automatically create the + // hidden service. The service's private key will be saved on disk in order + // to persistently have access to this hidden service across restarts. + onionCfg := tor.AddOnionConfig{ + VirtualPort: DefaultPeerPort, + TargetPorts: listenPorts, + Store: tor.NewOnionFile(w.cfg.WatchtowerKeyPath, 0600), + Type: w.cfg.Type, + } + + addr, err := w.cfg.TorController.AddOnion(onionCfg) + if err != nil { + return err + } + + // Append this address to ExternalIPs so that it will be exposed in + // tower info calls. + w.cfg.ExternalIPs = append(w.cfg.ExternalIPs, addr) + + return nil +} + // PubKey returns the public key for the watchtower used to authentication and // encrypt traffic with clients. //