config: add reveal-IP-address=False

This adds a safety flag named `[node] reveal-IP-address`, for which the
default value is True. When this is set to False, any configuration that
might reveal the node's IP address (to servers, or the external network)
will cause a PrivacyError to be raised at startup, terminating the node
before it gets a chance to betray the user's privacy. It also adds docs
and tests.

refs ticket:1010
This commit is contained in:
Brian Warner 2016-08-31 02:44:27 -07:00
parent 325028c967
commit d47fc0fd27
3 changed files with 96 additions and 1 deletions

View File

@ -324,6 +324,23 @@ set the ``tub.location`` option described below.
used for files that usually (on a Unix system) go into ``/tmp``. The
string will be interpreted relative to the node's base directory.
``reveal-IP-address = (boolean, optional, defaults to True)``
This is a safety flag. If False, any of the following configuration
problems will cause ``tahoe start`` to throw a PrivacyError instead of
starting the node:
* ``[node] tub.location`` contains any ``tcp:`` hints
* ``[node] tub.location`` uses ``AUTO``, or is missing/empty (because
that defaults to AUTO)
* ``[connections] tcp =`` is set to ``tcp`` (or left as the default),
rather than being set to ``tor``
These configuration problems would reveal the node's IP address to
servers and external networks.
Connection Management
=====================
@ -334,6 +351,10 @@ also controls when Tor and I2P are used: for all TCP connections (to hide
your IP address), or only when necessary (just for servers which declare that
they need Tor, because they use ``.onion`` addresses).
Note that if you want to protect your node's IP address, you should set
``[node] reveal-IP-address = False``, which will refuse to launch the node if
any of the other configuration settings might violate this privacy property.
``[connections]``
-----------------

View File

@ -76,6 +76,9 @@ class UnescapedHashError(Exception):
return ("The configuration entry %s contained an unescaped '#' character."
% quote_output("[%s]%s = %s" % self.args))
class PrivacyError(Exception):
"""reveal-IP-address = false, but the node is configured in such a way
that the IP address could be revealed"""
class Node(service.MultiService):
# this implements common functionality of both Client nodes and Introducer
@ -99,6 +102,7 @@ class Node(service.MultiService):
assert type(self.nickname) is unicode
self.init_tempdir()
self.check_privacy()
self.init_connections()
self.set_tub_options()
self.create_main_tub()
@ -181,6 +185,10 @@ class Node(service.MultiService):
twlog.msg(e)
raise e
def check_privacy(self):
self._reveal_ip = self.get_config("node", "reveal-IP-address", True,
boolean=True)
def _make_tcp_handler(self):
# this is always available
from foolscap.connections.tcp import default
@ -279,6 +287,10 @@ class Node(service.MultiService):
% (tcp_handler_name, tcp_handler_name))
self._default_connection_handlers["tcp"] = tcp_handler_name
if not self._reveal_ip:
if self._default_connection_handlers["tcp"] == "tcp":
raise PrivacyError("tcp = tcp, must be set to 'tor'")
def set_tub_options(self):
self.tub_options = {
"logLocalFailures": True,
@ -339,6 +351,8 @@ class Node(service.MultiService):
# addresses. Don't probe for local addresses unless necessary.
split_location = location.split(",")
if "AUTO" in split_location:
if not self._reveal_ip:
raise PrivacyError("tub.location uses AUTO")
local_addresses = iputil.get_local_addresses_sync()
# tubport must be like "tcp:12345" or "tcp:12345:morestuff"
local_portnum = int(tubport.split(":")[1])
@ -348,6 +362,10 @@ class Node(service.MultiService):
new_locations.extend(["tcp:%s:%d" % (ip, local_portnum)
for ip in local_addresses])
else:
if not self._reveal_ip:
hint_type = loc.split(":")[0]
if hint_type == "tcp":
raise PrivacyError("tub.location includes tcp: hint")
new_locations.append(loc)
return ",".join(new_locations)

View File

@ -5,12 +5,13 @@ from twisted.trial import unittest
from twisted.internet import reactor, endpoints
from ConfigParser import SafeConfigParser
from foolscap.connections import tcp
from ..node import Node
from ..node import Node, PrivacyError
class FakeNode(Node):
def __init__(self, config_str):
self.config = SafeConfigParser()
self.config.readfp(BytesIO(config_str))
self._reveal_ip = True
BASECONFIG = ("[client]\n"
"introducer.furl = \n"
@ -230,3 +231,58 @@ class Connections(unittest.TestCase):
e = self.assertRaises(ValueError, n.init_connections)
self.assertIn("'tahoe.cfg [connections] tcp='", str(e))
self.assertIn("uses unknown handler type 'unknown'", str(e))
class Privacy(unittest.TestCase):
def test_flag(self):
n = FakeNode(BASECONFIG)
n.check_privacy()
self.assertTrue(n._reveal_ip)
n = FakeNode(BASECONFIG+"[node]\nreveal-IP-address = true\n")
n.check_privacy()
self.assertTrue(n._reveal_ip)
n = FakeNode(BASECONFIG+"[node]\nreveal-IP-address = false\n")
n.check_privacy()
self.assertFalse(n._reveal_ip)
n = FakeNode(BASECONFIG+"[node]\nreveal-ip-address = false\n")
n.check_privacy()
self.assertFalse(n._reveal_ip)
def test_connections(self):
n = FakeNode(BASECONFIG+"[node]\nreveal-IP-address = false\n")
n.check_privacy()
e = self.assertRaises(PrivacyError, n.init_connections)
self.assertEqual(str(e), "tcp = tcp, must be set to 'tor'")
def test_tub_location_auto(self):
n = FakeNode(BASECONFIG+"[node]\nreveal-IP-address = false\n")
n._portnumfile = "missing"
n.check_privacy()
e = self.assertRaises(PrivacyError, n.get_tub_location, None)
self.assertEqual(str(e), "tub.location uses AUTO")
n = FakeNode(BASECONFIG+"[node]\nreveal-IP-address = false\n" +
"tub.location = AUTO\n")
n._portnumfile = "missing"
n.check_privacy()
e = self.assertRaises(PrivacyError, n.get_tub_location, None)
self.assertEqual(str(e), "tub.location uses AUTO")
n = FakeNode(BASECONFIG+"[node]\nreveal-IP-address = false\n" +
"tub.location = AUTO,tcp:hostname:1234\n")
n._portnumfile = "missing"
n.check_privacy()
e = self.assertRaises(PrivacyError, n.get_tub_location, None)
self.assertEqual(str(e), "tub.location uses AUTO")
def test_tub_location_tcp(self):
n = FakeNode(BASECONFIG+"[node]\nreveal-IP-address = false\n" +
"tub.location = tcp:hostname:1234\n")
n._portnumfile = "missing"
n.check_privacy()
e = self.assertRaises(PrivacyError, n.get_tub_location, None)
self.assertEqual(str(e), "tub.location includes tcp: hint")