[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