mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-02-21 02:01:31 +00:00
create_node.py: use tor_provider to handle --listen=tor
This adds tor-related CLI arguments to "create-node" and "create-introducer", to control exactly how we should be using Tor. * --tor-launch * --tor-executable= * --tor-control-port= I went with "--tor-launch" instead of "--launch-tor" for consistency. I don't particularly like the grammatical flow of it, and it doesn't actually put all the tor-related arguments next to each other in the --help output (the flags are put in one block, then the parameters in the next). But it seems slightly more consistent to start all the tor-related argument names with a "--tor*" prefix.
This commit is contained in:
parent
a1741ce4dc
commit
6b9218ff22
@ -1,11 +1,11 @@
|
||||
import os
|
||||
from twisted.internet import defer
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.python.usage import UsageError
|
||||
from allmydata.scripts.common import BasedirOptions, NoDefaultBasedirOptions
|
||||
from allmydata.scripts.default_nodedir import _default_nodedir
|
||||
from allmydata.util.assertutil import precondition
|
||||
from allmydata.util.encodingutil import listdir_unicode, argv_to_unicode, quote_local_unicode_path
|
||||
from allmydata.util import fileutil, iputil
|
||||
from allmydata.util import fileutil, iputil, tor_provider
|
||||
|
||||
|
||||
dummy_tac = """
|
||||
@ -33,10 +33,12 @@ WHERE_OPTS = [
|
||||
TOR_OPTS = [
|
||||
("tor-control-port", None, None,
|
||||
"Tor's control port endpoint descriptor string (e.g. tcp:127.0.0.1:9051 or unix:/var/run/tor/control)"),
|
||||
("tor-executable", None, None,
|
||||
"The 'tor' executable to run (default is to search $PATH)."),
|
||||
]
|
||||
|
||||
TOR_FLAG = [
|
||||
("launch-tor", None, "Launch a tor instead of connecting to a tor control port."),
|
||||
TOR_FLAGS = [
|
||||
("tor-launch", None, "Launch a tor instead of connecting to a tor control port."),
|
||||
]
|
||||
|
||||
def validate_where_options(o):
|
||||
@ -77,6 +79,16 @@ def validate_where_options(o):
|
||||
if 'tcp' not in listeners and o['hostname']:
|
||||
raise UsageError("--listen= must be tcp to use --hostname")
|
||||
|
||||
def validate_tor_options(o):
|
||||
use_tor = "tor" in o["listen"].split(",")
|
||||
if not use_tor:
|
||||
if o["tor-launch"]:
|
||||
raise UsageError("--tor-launch requires --listen=tor")
|
||||
if o["tor-control-port"]:
|
||||
raise UsageError("--tor-control-port= requires --listen=tor")
|
||||
if o["tor-launch"] and o["tor-control-port"]:
|
||||
raise UsageError("use either --tor-launch or --tor-control-port=, not both")
|
||||
|
||||
class _CreateBaseOptions(BasedirOptions):
|
||||
optFlags = [
|
||||
("hide-ip", None, "prohibit any configuration that would reveal the node's IP address"),
|
||||
@ -107,7 +119,7 @@ class CreateClientOptions(_CreateBaseOptions):
|
||||
class CreateNodeOptions(CreateClientOptions):
|
||||
optFlags = [
|
||||
("no-storage", None, "Do not offer storage service to other nodes."),
|
||||
] + TOR_FLAG
|
||||
] + TOR_FLAGS
|
||||
|
||||
synopsis = "[options] [NODEDIR]"
|
||||
description = "Create a full Tahoe-LAFS node (client+server)."
|
||||
@ -116,17 +128,19 @@ class CreateNodeOptions(CreateClientOptions):
|
||||
def parseArgs(self, basedir=None):
|
||||
CreateClientOptions.parseArgs(self, basedir)
|
||||
validate_where_options(self)
|
||||
validate_tor_options(self)
|
||||
|
||||
class CreateIntroducerOptions(NoDefaultBasedirOptions):
|
||||
subcommand_name = "create-introducer"
|
||||
description = "Create a Tahoe-LAFS introducer."
|
||||
optFlags = [
|
||||
("hide-ip", None, "prohibit any configuration that would reveal the node's IP address"),
|
||||
] + TOR_FLAG
|
||||
] + TOR_FLAGS
|
||||
optParameters = NoDefaultBasedirOptions.optParameters + WHERE_OPTS + TOR_OPTS
|
||||
def parseArgs(self, basedir=None):
|
||||
NoDefaultBasedirOptions.parseArgs(self, basedir)
|
||||
validate_where_options(self)
|
||||
validate_tor_options(self)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def write_node_config(c, config):
|
||||
@ -161,6 +175,8 @@ def write_node_config(c, config):
|
||||
c.write("web.static = public_html\n")
|
||||
|
||||
listeners = config['listen'].split(",")
|
||||
|
||||
tor_config = {}
|
||||
tub_ports = []
|
||||
tub_locations = []
|
||||
if listeners == ["none"]:
|
||||
@ -168,15 +184,10 @@ def write_node_config(c, config):
|
||||
c.write("tub.location = disabled\n")
|
||||
else:
|
||||
if "tor" in listeners:
|
||||
key_file = "default.onion_key"
|
||||
onion_port = 3456
|
||||
c.write("[tor]\n")
|
||||
c.write("onion.external_port = %s\n" % onion_port)
|
||||
c.write("onion.private_key_file = %s\n" % key_file)
|
||||
#tor_provider = TorProvider(tor_binary = )
|
||||
# XXX fix me
|
||||
tor_provider = TorProvider()
|
||||
yield CreateOnion(tor_provider,key_file, onion_port)
|
||||
(tor_config, tor_port, tor_location) = \
|
||||
yield tor_provider.create_onion(reactor, config)
|
||||
tub_ports.append(tor_port)
|
||||
tub_locations.append(tor_location)
|
||||
if "i2p" in listeners:
|
||||
raise NotImplementedError("--listen=i2p is under development, "
|
||||
"see ticket #2490 for details")
|
||||
@ -201,7 +212,13 @@ def write_node_config(c, config):
|
||||
c.write("#ssh.port = 8022\n")
|
||||
c.write("#ssh.authorized_keys_file = ~/.ssh/authorized_keys\n")
|
||||
c.write("\n")
|
||||
yield None
|
||||
|
||||
if tor_config:
|
||||
c.write("[tor]\n")
|
||||
for key, value in tor_config.items():
|
||||
c.write("%s = %s\n" % (key, value))
|
||||
c.write("\n")
|
||||
|
||||
|
||||
def write_client_config(c, config):
|
||||
c.write("[client]\n")
|
||||
|
@ -1,9 +1,11 @@
|
||||
import os
|
||||
import mock
|
||||
from twisted.trial import unittest
|
||||
from twisted.internet import defer
|
||||
from twisted.internet import defer, reactor
|
||||
from twisted.python import usage
|
||||
from allmydata.util import configutil
|
||||
from ..common_util import run_cli, parse_cli
|
||||
from ...scripts import create_node
|
||||
|
||||
def read_config(basedir):
|
||||
tahoe_cfg = os.path.join(basedir, "tahoe.cfg")
|
||||
@ -154,14 +156,6 @@ class Config(unittest.TestCase):
|
||||
basedir)
|
||||
self.assertEqual(str(e), "--listen= must be none, or one/some of: tcp, tor, i2p")
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_node_listen_tor(self):
|
||||
basedir = self.mktemp()
|
||||
d = run_cli("create-node", "--listen=tor", basedir)
|
||||
e = yield self.assertFailure(d, NotImplementedError)
|
||||
self.assertEqual(str(e), "--listen=tor is under development, "
|
||||
"see ticket #2490 for details")
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_node_listen_i2p(self):
|
||||
basedir = self.mktemp()
|
||||
@ -201,6 +195,19 @@ class Config(unittest.TestCase):
|
||||
self.assertIn("is not empty", err)
|
||||
self.assertIn("To avoid clobbering anything, I am going to quit now", err)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_node_slow_tor(self):
|
||||
basedir = self.mktemp()
|
||||
d = defer.Deferred()
|
||||
with mock.patch("allmydata.util.tor_provider.create_onion",
|
||||
return_value=d):
|
||||
d2 = run_cli("create-node", "--listen=tor", basedir)
|
||||
d.callback(({}, "port", "location"))
|
||||
rc, out, err = yield d2
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn("Node created", out)
|
||||
self.assertEqual(err, "")
|
||||
|
||||
def test_introducer_no_hostname(self):
|
||||
basedir = self.mktemp()
|
||||
e = self.assertRaises(usage.UsageError, parse_cli,
|
||||
@ -236,3 +243,77 @@ class Config(unittest.TestCase):
|
||||
self.assertIn(basedir, err)
|
||||
self.assertIn("is not empty", err)
|
||||
self.assertIn("To avoid clobbering anything, I am going to quit now", err)
|
||||
|
||||
class Tor(unittest.TestCase):
|
||||
def test_default(self):
|
||||
basedir = self.mktemp()
|
||||
tor_config = {"abc": "def"}
|
||||
tor_port = "ghi"
|
||||
tor_location = "jkl"
|
||||
onion_d = defer.succeed( (tor_config, tor_port, tor_location) )
|
||||
with mock.patch("allmydata.util.tor_provider.create_onion",
|
||||
return_value=onion_d) as co:
|
||||
rc, out, err = self.successResultOf(
|
||||
run_cli("create-node", "--listen=tor", basedir))
|
||||
self.assertEqual(len(co.mock_calls), 1)
|
||||
args = co.mock_calls[0][1]
|
||||
self.assertIdentical(args[0], reactor)
|
||||
self.assertIsInstance(args[1], create_node.CreateNodeOptions)
|
||||
self.assertEqual(args[1]["listen"], "tor")
|
||||
cfg = read_config(basedir)
|
||||
self.assertEqual(cfg.get("tor", "abc"), "def")
|
||||
self.assertEqual(cfg.get("node", "tub.port"), "ghi")
|
||||
self.assertEqual(cfg.get("node", "tub.location"), "jkl")
|
||||
|
||||
def test_launch(self):
|
||||
basedir = self.mktemp()
|
||||
tor_config = {"abc": "def"}
|
||||
tor_port = "ghi"
|
||||
tor_location = "jkl"
|
||||
onion_d = defer.succeed( (tor_config, tor_port, tor_location) )
|
||||
with mock.patch("allmydata.util.tor_provider.create_onion",
|
||||
return_value=onion_d) as co:
|
||||
rc, out, err = self.successResultOf(
|
||||
run_cli("create-node", "--listen=tor", "--tor-launch",
|
||||
basedir))
|
||||
args = co.mock_calls[0][1]
|
||||
self.assertEqual(args[1]["listen"], "tor")
|
||||
self.assertEqual(args[1]["tor-launch"], True)
|
||||
self.assertEqual(args[1]["tor-control-port"], None)
|
||||
|
||||
def test_control_port(self):
|
||||
basedir = self.mktemp()
|
||||
tor_config = {"abc": "def"}
|
||||
tor_port = "ghi"
|
||||
tor_location = "jkl"
|
||||
onion_d = defer.succeed( (tor_config, tor_port, tor_location) )
|
||||
with mock.patch("allmydata.util.tor_provider.create_onion",
|
||||
return_value=onion_d) as co:
|
||||
rc, out, err = self.successResultOf(
|
||||
run_cli("create-node", "--listen=tor", "--tor-control-port=mno",
|
||||
basedir))
|
||||
args = co.mock_calls[0][1]
|
||||
self.assertEqual(args[1]["listen"], "tor")
|
||||
self.assertEqual(args[1]["tor-launch"], False)
|
||||
self.assertEqual(args[1]["tor-control-port"], "mno")
|
||||
|
||||
def test_not_both(self):
|
||||
e = self.assertRaises(usage.UsageError,
|
||||
parse_cli,
|
||||
"create-node", "--listen=tor",
|
||||
"--tor-launch", "--tor-control-port=foo")
|
||||
self.assertEqual(str(e), "use either --tor-launch or"
|
||||
" --tor-control-port=, not both")
|
||||
|
||||
def test_launch_without_listen(self):
|
||||
e = self.assertRaises(usage.UsageError,
|
||||
parse_cli,
|
||||
"create-node", "--listen=none", "--tor-launch")
|
||||
self.assertEqual(str(e), "--tor-launch requires --listen=tor")
|
||||
|
||||
def test_control_port_without_listen(self):
|
||||
e = self.assertRaises(usage.UsageError,
|
||||
parse_cli,
|
||||
"create-node", "--listen=none",
|
||||
"--tor-control-port=foo")
|
||||
self.assertEqual(str(e), "--tor-control-port= requires --listen=tor")
|
||||
|
Loading…
x
Reference in New Issue
Block a user