mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-18 02:40:07 +00:00
implement --listen=none, use it for create-client
Improve docs on server configuration to explain --listen options.
This commit is contained in:
parent
d69757e069
commit
02ba2a05c3
@ -26,6 +26,54 @@ The first step when setting up a server is to figure out how clients will
|
||||
reach it. Then you need to configure the server to listen on some ports, and
|
||||
then configure the location properly.
|
||||
|
||||
Manual Configuration
|
||||
====================
|
||||
|
||||
Each server has two settings in their ``tahoe.cfg`` file: ``tub.port``, and
|
||||
``tub.location``. The "port" controls what the server node listens to: this
|
||||
is generally a TCP port.
|
||||
|
||||
The "location" controls what is advertised to the outside world. This is a
|
||||
"foolscap connection hint", and it includes both the type of the connection
|
||||
(tcp, tor, or i2p) and the connection details (hostname/address, port
|
||||
number). Various proxies, port-forwardings, and privacy networks might be
|
||||
involved, so it's not uncommon for ``tub.port`` and ``tub.location`` to look
|
||||
different.
|
||||
|
||||
You can directly control the ``tub.port`` and ``tub.location`` configuration
|
||||
settings by providing ``--port=`` and ``--location=`` when running ``tahoe
|
||||
create-node``.
|
||||
|
||||
Automatic Configuration
|
||||
=======================
|
||||
|
||||
Instead of providing ``--port=/--location=``, you can use ``--listen=``.
|
||||
Servers can listen on TCP, Tor, I2P, a combination of those, or none at all.
|
||||
The ``--listen=`` argument controls which kinds of listeners the new server
|
||||
will use.
|
||||
|
||||
``--listen=none`` means the server should not listen at all. This doesn't
|
||||
make sense for a server, but is appropriate for a client-only node. The
|
||||
``tahoe create-client`` command automatically includes ``--listen=none``.
|
||||
|
||||
``--listen=tcp`` is the default, and turns on a standard TCP listening port.
|
||||
Using ``--listen=tcp`` requires a ``--hostname=`` argument too, which will be
|
||||
incorporated into the node's advertised location. We've found that computers
|
||||
cannot reliably determine their externally-reachable hostname, so rather than
|
||||
having the server make a guess (or scanning its interfaces for IP addresses
|
||||
that might or might not be appropriate), node creation requires the user to
|
||||
provide the hostname.
|
||||
|
||||
``--listen=tor`` will talk to a local Tor daemon and create a new "onion
|
||||
server" address (which look like ``alzrgrdvxct6c63z.onion``). Likewise
|
||||
``--listen=i2p`` will talk to a local I2P daemon and create a new server
|
||||
address. See :doc:`anonymity-configuration` for details.
|
||||
|
||||
You could listen on all three by using ``--listen=tcp,tor,i2p``.
|
||||
|
||||
Deployment Scenarios
|
||||
====================
|
||||
|
||||
The following are some suggested scenarios for configuring servers using
|
||||
various network transports. These examples do not include specifying an
|
||||
introducer FURL which normally you would want when provisioning storage
|
||||
@ -39,7 +87,7 @@ nodes. For these and other configuration details please refer to
|
||||
|
||||
|
||||
Server has a public DNS name
|
||||
============================
|
||||
----------------------------
|
||||
|
||||
The simplest case is where your server host is directly connected to the
|
||||
internet, without a firewall or NAT box in the way. Most VPS (Virtual Private
|
||||
@ -72,7 +120,7 @@ support for IPv6 is new, and may still have problems. Please see ticket
|
||||
|
||||
|
||||
Server has a public IPv4/IPv6 address
|
||||
=====================================
|
||||
-------------------------------------
|
||||
|
||||
If the host has a routeable (public) IPv4 address (e.g. ``203.0.113.1``), but
|
||||
no DNS name, you will need to choose a TCP port (e.g. ``3457``), and use the
|
||||
@ -113,7 +161,7 @@ IPv6-enabled port with this::
|
||||
|
||||
|
||||
Server is behind a firewall with port forwarding
|
||||
================================================
|
||||
------------------------------------------------
|
||||
|
||||
To configure a storage node behind a firewall with port forwarding you will
|
||||
need to know:
|
||||
@ -140,7 +188,7 @@ port 3457, then do this::
|
||||
|
||||
|
||||
Using I2P/Tor to Avoid Port-Forwarding
|
||||
======================================
|
||||
--------------------------------------
|
||||
|
||||
I2P and Tor onion services, among other great properties, also provide NAT
|
||||
penetration without port-forwarding, hostnames, or IP addresses. So setting
|
||||
|
@ -30,6 +30,12 @@ WHERE_OPTS = [
|
||||
]
|
||||
|
||||
def validate_where_options(o):
|
||||
if o['listen'] == "none":
|
||||
# no other arguments are accepted
|
||||
if o['hostname']:
|
||||
raise UsageError("--hostname cannot be used when --listen=none")
|
||||
if o['port'] or o['location']:
|
||||
raise UsageError("--port/--location cannot be used when --listen=none")
|
||||
# --location and --port: overrides all others, rejects all others
|
||||
if o['location'] and not o['port']:
|
||||
raise UsageError("--location must be used with --port")
|
||||
@ -50,15 +56,16 @@ def validate_where_options(o):
|
||||
# then change parseArgs() to transform the None into "tcp"
|
||||
else:
|
||||
# no --location and --port? expect --listen= (maybe the default), and
|
||||
# --listen=tcp requires --hostname
|
||||
listeners = o['listen'].split(",")
|
||||
if 'tcp' in listeners and not o['hostname']:
|
||||
raise UsageError("--listen=tcp requires --hostname=")
|
||||
if 'tcp' not in listeners and o['hostname']:
|
||||
raise UsageError("--listen= must be tcp to use --hostname")
|
||||
for l in listeners:
|
||||
if l not in ["tcp", "tor", "i2p"]:
|
||||
raise UsageError("--listen= must be: tcp, tor, i2p")
|
||||
# --listen=tcp requires --hostname. But --listen=none is special.
|
||||
if o['listen'] != "none":
|
||||
listeners = o['listen'].split(",")
|
||||
for l in listeners:
|
||||
if l not in ["tcp", "tor", "i2p"]:
|
||||
raise UsageError("--listen= must be none, or one/some of: tcp, tor, i2p")
|
||||
if 'tcp' in listeners and not o['hostname']:
|
||||
raise UsageError("--listen=tcp requires --hostname=")
|
||||
if 'tcp' not in listeners and o['hostname']:
|
||||
raise UsageError("--listen= must be tcp to use --hostname")
|
||||
|
||||
class _CreateBaseOptions(BasedirOptions):
|
||||
optFlags = [
|
||||
@ -139,27 +146,27 @@ def write_node_config(c, config):
|
||||
c.write("web.port = %s\n" % (webport.encode('utf-8'),))
|
||||
c.write("web.static = public_html\n")
|
||||
|
||||
if 'hostname' in config and config['hostname'] is not None:
|
||||
new_port = iputil.allocate_tcp_port()
|
||||
c.write("tub.port = tcp:%s\n" % new_port)
|
||||
c.write("tub.location = tcp:%s:%s\n" % (config.get('hostname').encode('utf-8'), new_port))
|
||||
elif 'listen' in config and config['listen'] == "tor":
|
||||
raise NotImplementedError("This feature addition is being tracked by this ticket:" +
|
||||
"https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2490")
|
||||
elif 'listen' in config and config['listen'] == "i2p":
|
||||
raise NotImplementedError("This feature addition is being tracked by this ticket:" +
|
||||
"https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2490")
|
||||
elif config.get('port') is not None:
|
||||
c.write("tub.port = %s\n" % config.get('port').encode('utf-8'))
|
||||
c.write("tub.location = %s\n" % config.get('location').encode('utf-8'))
|
||||
else:
|
||||
listeners = config['listen'].split(",")
|
||||
if listeners == ["none"]:
|
||||
c.write("tub.port = disabled\n")
|
||||
c.write("tub.location = disabled\n")
|
||||
|
||||
if config.get('hostname', None) or config.get('listen', None):
|
||||
c.write("# to prevent the Tub from listening at all, use this:\n")
|
||||
c.write("# tub.port = disabled\n")
|
||||
c.write("# tub.location = disabled\n")
|
||||
else:
|
||||
if "tor" in listeners:
|
||||
raise NotImplementedError("--listen=tor is under development, "
|
||||
"see ticket #2490 for details")
|
||||
if "i2p" in listeners:
|
||||
raise NotImplementedError("--listen=i2p is under development, "
|
||||
"see ticket #2490 for details")
|
||||
if "tcp" in listeners:
|
||||
if config["port"]: # --port/--location are a pair
|
||||
c.write("tub.port = %s\n" % config["port"].encode('utf-8'))
|
||||
c.write("tub.location = %s\n" % config["location"].encode('utf-8'))
|
||||
else:
|
||||
assert "hostname" in config
|
||||
hostname = config["hostname"]
|
||||
new_port = iputil.allocate_tcp_port()
|
||||
c.write("tub.port = tcp:%s\n" % new_port)
|
||||
c.write("tub.location = tcp:%s:%s\n" % (hostname.encode('utf-8'), new_port))
|
||||
|
||||
c.write("#log_gatherer.furl =\n")
|
||||
c.write("#timeout.keepalive =\n")
|
||||
@ -234,6 +241,7 @@ def create_node(config):
|
||||
|
||||
def create_client(config):
|
||||
config['no-storage'] = True
|
||||
config['listen'] = "none"
|
||||
return create_node(config)
|
||||
|
||||
|
||||
|
@ -94,21 +94,59 @@ class Config(unittest.TestCase):
|
||||
"create-node", "--listen=tcp", basedir)
|
||||
self.assertIn("--listen=tcp requires --hostname=", str(e))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_node_listen_none(self):
|
||||
basedir = self.mktemp()
|
||||
rc, out, err = yield run_cli("create-node", "--listen=none", basedir)
|
||||
cfg = self.read_config(basedir)
|
||||
self.assertEqual(cfg.get("node", "tub.port"), "disabled")
|
||||
self.assertEqual(cfg.get("node", "tub.location"), "disabled")
|
||||
|
||||
def test_node_listen_none_errors(self):
|
||||
basedir = self.mktemp()
|
||||
e = self.assertRaises(usage.UsageError,
|
||||
parse_cli,
|
||||
"create-node", "--listen=none",
|
||||
"--hostname=foo",
|
||||
basedir)
|
||||
self.assertEqual(str(e), "--hostname cannot be used when --listen=none")
|
||||
|
||||
e = self.assertRaises(usage.UsageError,
|
||||
parse_cli,
|
||||
"create-node", "--listen=none",
|
||||
"--port=foo", "--location=foo",
|
||||
basedir)
|
||||
self.assertEqual(str(e), "--port/--location cannot be used when --listen=none")
|
||||
|
||||
e = self.assertRaises(usage.UsageError,
|
||||
parse_cli,
|
||||
"create-node", "--listen=tcp,none",
|
||||
basedir)
|
||||
self.assertEqual(str(e), "--listen= must be none, or one/some of: tcp, tor, i2p")
|
||||
|
||||
def test_node_listen_bad(self):
|
||||
basedir = self.mktemp()
|
||||
e = self.assertRaises(usage.UsageError,
|
||||
parse_cli,
|
||||
"create-node", "--listen=XYZZY,tcp",
|
||||
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), "This feature addition is being tracked by this ticket:" +
|
||||
"https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2490")
|
||||
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()
|
||||
d = run_cli("create-node", "--listen=i2p", basedir)
|
||||
e = yield self.assertFailure(d, NotImplementedError)
|
||||
self.failUnlessEqual(str(e), "This feature addition is being tracked by this ticket:" +
|
||||
"https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2490")
|
||||
self.assertEqual(str(e), "--listen=i2p is under development, "
|
||||
"see ticket #2490 for details")
|
||||
|
||||
def test_node_port_only(self):
|
||||
e = self.assertRaises(usage.UsageError,
|
||||
|
@ -87,6 +87,7 @@ class ConfigUtilTests(GridTestMixin, unittest.TestCase):
|
||||
opts = {"nickname": "nick",
|
||||
"webport": "tcp:3456",
|
||||
"hide-ip": False,
|
||||
"listen": "none",
|
||||
}
|
||||
create_node.write_node_config(f, opts)
|
||||
create_node.write_client_config(f, opts)
|
||||
|
@ -20,7 +20,8 @@ class MultiIntroTests(unittest.TestCase):
|
||||
# create a custom tahoe.cfg
|
||||
self.basedir = os.path.dirname(self.mktemp())
|
||||
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
||||
config = {'hide-ip':False}
|
||||
config = {'hide-ip':False, 'listen': 'tcp',
|
||||
'port': None, 'location': None, 'hostname': 'example.net'}
|
||||
write_node_config(c, config)
|
||||
fake_furl = "furl1"
|
||||
c.write("[client]\n")
|
||||
@ -66,7 +67,8 @@ class MultiIntroTests(unittest.TestCase):
|
||||
the tahoe.cfg file. """
|
||||
# create a custom tahoe.cfg
|
||||
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
||||
config = {'hide-ip':False}
|
||||
config = {'hide-ip':False, 'listen': 'tcp',
|
||||
'port': None, 'location': None, 'hostname': 'example.net'}
|
||||
write_node_config(c, config)
|
||||
fake_furl = "furl1"
|
||||
c.write("[client]\n")
|
||||
@ -96,7 +98,8 @@ class NoDefault(unittest.TestCase):
|
||||
# create a custom tahoe.cfg
|
||||
self.basedir = os.path.dirname(self.mktemp())
|
||||
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
||||
config = {'hide-ip':False}
|
||||
config = {'hide-ip':False, 'listen': 'tcp',
|
||||
'port': None, 'location': None, 'hostname': 'example.net'}
|
||||
write_node_config(c, config)
|
||||
c.write("[client]\n")
|
||||
c.write("# introducer.furl =\n") # omit default
|
||||
|
Loading…
Reference in New Issue
Block a user