[tor-commits] [bridgedb/develop] Add HTTPS_ROTATION_PERIOD and EMAIL_ROTATION_PERIOD config options.
isis at torproject.org
isis at torproject.org
Tue Mar 31 11:55:45 UTC 2015
commit ceac2ff7b2c966cfea495850d6905d93e675ec03
Author: Isis Lovecruft <isis at torproject.org>
Date: Tue Mar 31 02:19:27 2015 +0000
Add HTTPS_ROTATION_PERIOD and EMAIL_ROTATION_PERIOD config options.
This implements configuration of the hashring rotation periods for
BridgeDB's distributors via the configuration file.
The behaviour for hashring rotation for the EmailBasedDistributor is
such that, when `bridgedb.email.autoresponder.createResponseBody()`
calls `EmailBasedDistributor.getBridgesForEmail(EMAIL_ADDRESS, EPOCH)`
with the `EPOCH` set to the start of the current EMAIL_ROTATION_PERIOD,
the client's position in the hashring is determined by the HMAC of the
string `"<EPOCH>EMAIL_ADDRESS"`. Therefore, the EMAIL_ROTATION_PERIOD
directly effects where the client is placed in the hashring, resulting
in different bridges for the user, depending on whether the period has
elapsed. With the default setting of `"1 day"`, and taking into account
also that the EmailBasedDistributor only responds to a particular user
once per three hours, this results in the client being able to ask for
(and receive) vanilla bridges (starting from hashring position A) at
9:00, obfs3 bridges (also from position A) at 12:00, obfs4 bridges (from
position A again) at 15:00, and so on, and finally different vanilla
bridges (from hashring position B) the next morning at 9:00.
For the IPBasedDistributor, the behaviour for hashring rotation is that
the client's hashring position is determined by the HMAC of the string
`"<EPOCH>AREA"` where `EPOCH` is again the start of the current
HTTPS_ROTATION_PERIOD, and the `AREA` is the `/16` subnet which the
client's IP address resides within. If the client is using Tor or some
other open proxy, then the client's hashring position is determined by
`"known-proxy<EPOCH>GROUP"` where `EPOCH` is the same as before, and
`GROUP` is a number (currently 1 through 4, inclusive) deterministically
derived from the IP address of the Tor Exit relay or open proxy that the
client is using. Using `GROUP` causes there to only be 4 sets of
bridges available to any and all Tor/proxy users at a given time. Hence
additionally using `EPOCH` rotates the set of 4 bridges available. The
default setting is `"3 hours"`, causing all Tor/proxy users to have 4
different sets of bridges every three hours, while non-Tor users have a
new set of bridges (probably) unique to their IP address available every
three hours.
These defaults for HTTPS_ROTATION_PERIOD and EMAIL_ROTATION_PERIOD will
likely need to be changed and fiddled with to make the behaviour an
optimum balance between user-friendly and resilient to enumeration.
* FIXES #1839: https://bugs.torproject.org/1839.
This implement Roger's suggestion to add the rotation periods as
configuration file options.
---
bridgedb.conf | 32 ++++++++++++++++++++++++++++++++
lib/bridgedb/HTTPServer.py | 15 ++++++++++-----
lib/bridgedb/Main.py | 8 ++------
lib/bridgedb/configure.py | 4 ++++
lib/bridgedb/email/server.py | 12 +++++++++---
lib/bridgedb/test/email_helpers.py | 3 +++
lib/bridgedb/test/test_email_server.py | 2 +-
7 files changed, 61 insertions(+), 15 deletions(-)
diff --git a/bridgedb.conf b/bridgedb.conf
index c91cc73..2a1c367 100644
--- a/bridgedb.conf
+++ b/bridgedb.conf
@@ -20,6 +20,11 @@
#
# CHANGELOG:
# ~~~~~~~~~~
+# Changed in version 0.3.2 - 2015-03-30
+# * CHANGE to using BridgeDB release versions for bridgedb.conf file versions.
+# * ADD support for specifying bridge rotation periods via the
+# EMAIL_ROTATION_PERIOD and HTTPS_ROTATION_PERIOD settings.
+#
# Changed in version 0.0.15 - 2015-03-26
# * ADD new SUPPORTED_TRANSPORTS and DEFAULT_TRANSPORT settings.
#
@@ -295,6 +300,20 @@ HTTPS_INCLUDE_FINGERPRINTS = True
# the *last* entry from its X-Forwarded-For header as the client's IP.
HTTPS_USE_IP_FROM_FORWARDED_HEADER = False
+# (string or None) The period at which the available bridges rotates to a
+# separate set of bridges. This setting can be used in the form
+#
+# "COUNT PERIOD" where
+# COUNT is an integer
+# PERIOD is one of "second", "minute", "hour", "day",
+# "week", or "month" (or any plural form).
+#
+# For example, setting HTTPS_ROTATION_PERIOD = "3 days" will result in the set
+# of bridges which are available through the web interface (either HTTP or
+# HTTPS) getting rotated once every three days. Setting this to None disables
+# rotation entirely.
+HTTPS_ROTATION_PERIOD = "3 hours"
+
# (string or None) The IP address to listen on for unencrypted HTTP
# connections. Set to ``None`` to disable unencrypted connections to the web
# interface.
@@ -342,6 +361,19 @@ GIMP_CAPTCHA_RSA_KEYFILE = 'captcha_rsa_key'
# True if we are enabling distribution via Email; false otherwise.
EMAIL_DIST = True
+# (string or None) The period at which the available bridges rotates to a
+# separate set of bridges. This setting can be used in the form
+#
+# "COUNT PERIOD" where
+# COUNT is an integer
+# PERIOD is one of "second", "minute", "hour", "day",
+# "week", or "month" (or any plural form).
+#
+# For example, setting EMAIL_ROTATION_PERIOD = "3 days" will result in the set
+# of bridges which are available through the email interface getting rotated
+# once every three days. Setting this to None disables rotation entirely.
+EMAIL_ROTATION_PERIOD = "1 day"
+
# What email addresses do we use for outgoing email?
# EMAIL_FROM_ADDR goes in the 'From:' header on outgoing emails:
diff --git a/lib/bridgedb/HTTPServer.py b/lib/bridgedb/HTTPServer.py
index ae86957..2e0398c 100644
--- a/lib/bridgedb/HTTPServer.py
+++ b/lib/bridgedb/HTTPServer.py
@@ -54,6 +54,8 @@ from bridgedb.parse import headers
from bridgedb.parse.addr import isIPAddress
from bridgedb.qrcodes import generateQR
from bridgedb.safelog import logSafely
+from bridgedb.schedule import Unscheduled
+from bridgedb.schedule import ScheduledInterval
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), 'templates')
@@ -819,7 +821,7 @@ class WebRoot(resource.Resource):
return rendered
-def addWebServer(cfg, dist, sched):
+def addWebServer(cfg, dist):
"""Set up a web server for HTTP(S)-based bridge distribution.
:type cfg: :class:`bridgedb.persistent.Conf`
@@ -835,6 +837,7 @@ def addWebServer(cfg, dist, sched):
HTTPS_PORT
HTTPS_BIND_IP
HTTPS_USE_IP_FROM_FORWARDED_HEADER
+ HTTPS_ROTATION_PERIOD
RECAPTCHA_ENABLED
RECAPTCHA_PUB_KEY
RECAPTCHA_SEC_KEY
@@ -845,10 +848,6 @@ def addWebServer(cfg, dist, sched):
GIMP_CAPTCHA_RSA_KEYFILE
:type dist: :class:`bridgedb.Dist.IPBasedDistributor`
:param dist: A bridge distributor.
- :type sched: :class:`bridgedb.schedule.ScheduledInterval`
- :param sched: The scheduled interval at which bridge selection, which
- are ultimately displayed on the :class:`WebResourceBridges` page, will
- be shifted.
:raises SystemExit: if the servers cannot be started.
:rtype: :api:`twisted.web.server.Site`
:returns: A webserver.
@@ -887,6 +886,12 @@ def addWebServer(cfg, dist, sched):
hmacKey=hmacKey,
captchaDir=cfg.GIMP_CAPTCHA_DIR)
+ if cfg.HTTPS_ROTATION_PERIOD:
+ count, period = cfg.HTTPS_ROTATION_PERIOD.split()
+ sched = ScheduledInterval(count, period)
+ else:
+ sched = Unscheduled()
+
bridges = WebResourceBridges(dist, sched, numBridges,
fwdHeaders, includeFingerprints=fprInclude)
if captcha:
diff --git a/lib/bridgedb/Main.py b/lib/bridgedb/Main.py
index 31a9c66..3b018f9 100644
--- a/lib/bridgedb/Main.py
+++ b/lib/bridgedb/Main.py
@@ -512,13 +512,9 @@ def run(options, reactor=reactor):
# Configure all servers:
if config.HTTPS_DIST and config.HTTPS_SHARE:
- #webSchedule = schedule.ScheduledInterval("day", 2)
- webSchedule = schedule.Unscheduled()
- HTTPServer.addWebServer(config, ipDistributor, webSchedule)
+ HTTPServer.addWebServer(config, ipDistributor)
if config.EMAIL_DIST and config.EMAIL_SHARE:
- #emailSchedule = schedule.ScheduledInterval("day", 1)
- emailSchedule = schedule.Unscheduled()
- addSMTPServer(config, emailDistributor, emailSchedule)
+ addSMTPServer(config, emailDistributor)
tasks = {}
diff --git a/lib/bridgedb/configure.py b/lib/bridgedb/configure.py
index 54dcdff..8a24d35 100644
--- a/lib/bridgedb/configure.py
+++ b/lib/bridgedb/configure.py
@@ -115,6 +115,10 @@ def loadConfig(configFile=None, configCls=None):
else:
setattr(config, attr, os.path.abspath(os.path.expanduser(setting)))
+ for attr in ["HTTPS_ROTATION_PERIOD", "EMAIL_ROTATION_PERIOD"]:
+ setting = getattr(config, attr, None) # Default to None
+ setattr(config, attr, setting)
+
for attr in ["FORCE_PORTS", "FORCE_FLAGS", "NO_DISTRIBUTION_COUNTRIES"]:
setting = getattr(config, attr, []) # Default to empty lists
setattr(config, attr, setting)
diff --git a/lib/bridgedb/email/server.py b/lib/bridgedb/email/server.py
index cde4637..8034d35 100644
--- a/lib/bridgedb/email/server.py
+++ b/lib/bridgedb/email/server.py
@@ -68,6 +68,8 @@ from bridgedb.email import request
from bridgedb.parse import addr
from bridgedb.parse.addr import UnsupportedDomain
from bridgedb.parse.addr import canonicalizeEmailDomain
+from bridgedb.schedule import ScheduledInterval
+from bridgedb.schedule import Unscheduled
class MailServerContext(object):
@@ -458,7 +460,7 @@ class SMTPIncomingServerFactory(smtp.SMTPFactory):
return p
-def addServer(config, distributor, schedule):
+def addServer(config, distributor):
"""Set up a SMTP server which listens on the configured ``EMAIL_PORT`` for
incoming connections, and responds as necessary to requests for bridges.
@@ -467,9 +469,13 @@ def addServer(config, distributor, schedule):
:type distributor: :class:`bridgedb.Dist.EmailBasedDistributor`
:param dist: A distributor which will handle database interactions, and
will decide which bridges to give to who and when.
- :type schedule: :class:`bridgedb.schedule.ScheduledInterval`
- :param schedule: The schedule. XXX: Is this even used?
"""
+ if config.EMAIL_ROTATION_PERIOD:
+ count, period = config.EMAIL_ROTATION_PERIOD.split()
+ schedule = ScheduledInterval(count, period)
+ else:
+ schedule = Unscheduled()
+
context = MailServerContext(config, distributor, schedule)
factory = SMTPIncomingServerFactory()
factory.setContext(context)
diff --git a/lib/bridgedb/test/email_helpers.py b/lib/bridgedb/test/email_helpers.py
index 5e4262d..c80e2ea 100644
--- a/lib/bridgedb/test/email_helpers.py
+++ b/lib/bridgedb/test/email_helpers.py
@@ -24,6 +24,7 @@ from bridgedb.test.test_HTTPServer import DummyBridge
EMAIL_DIST = True
+EMAIL_ROTATION_PERIOD = "1 day"
EMAIL_INCLUDE_FINGERPRINTS = True
EMAIL_GPG_SIGNING_ENABLED = True
EMAIL_GPG_HOMEDIR = '.gnupg'
@@ -54,6 +55,7 @@ EMAIL_PORT = 5225
TEST_CONFIG_FILE = io.StringIO(unicode("""\
EMAIL_DIST = %s
+EMAIL_ROTATION_PERIOD = %s
EMAIL_INCLUDE_FINGERPRINTS = %s
EMAIL_GPG_SIGNING_ENABLED = %s
EMAIL_GPG_HOMEDIR = %s
@@ -75,6 +77,7 @@ EMAIL_FROM_ADDR = %s
EMAIL_BIND_IP = %s
EMAIL_PORT = %s
""" % (repr(EMAIL_DIST),
+ repr(EMAIL_ROTATION_PERIOD),
repr(EMAIL_INCLUDE_FINGERPRINTS),
repr(EMAIL_GPG_SIGNING_ENABLED),
repr(EMAIL_GPG_HOMEDIR),
diff --git a/lib/bridgedb/test/test_email_server.py b/lib/bridgedb/test/test_email_server.py
index f79f603..e8c9112 100644
--- a/lib/bridgedb/test/test_email_server.py
+++ b/lib/bridgedb/test/test_email_server.py
@@ -524,7 +524,7 @@ class EmailServerServiceTests(SMTPTestCaseMixin, unittest.TestCase):
def test_addServer(self):
"""Call :func:`bridgedb.email.server.addServer` to test startup."""
- factory = server.addServer(self.config, self.dist, self.sched)
+ factory = server.addServer(self.config, self.dist)
factory.timeout = None
factory.protocol.timeout = None # Or else the reactor gets dirty
More information about the tor-commits
mailing list