Exemple #1
0
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.

    :type config: :class:`bridgedb.configure.Conf`
    :param config: A configuration object.
    :type distributor: :class:`bridgedb.distributors.email.distributor.EmailDistributor`
    :param dist: A distributor which will handle database interactions, and
        will decide which bridges to give to who and when.
    """
    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)

    addr = config.EMAIL_BIND_IP or ""
    port = config.EMAIL_PORT or 6725

    try:
        reactor.listenTCP(port, factory, interface=addr)
    except CannotListenError as error:  # pragma: no cover
        logging.fatal(error)
        raise SystemExit(str(error))

    # Set up a LoopingCall to run every 30 minutes and forget old email times.
    lc = LoopingCall(distributor.cleanDatabase)
    lc.start(1800, now=False)

    return factory
 def setUp(self):
     """Create a MailServerContext and EmailDistributor."""
     self.config = _createConfig()
     self.context = _createMailServerContext(self.config)
     self.smtpFromAddr = self.context.smtpFromAddr  # 'bridges@localhost'
     self.sched = Unscheduled()
     self.dist = self.context.distributor
def _createMailServerContext(config=None, distributor=None):
    if not config:
        config = _createConfig()

    if not distributor:
        distributor = DummyEmailDistributor(
            domainmap=config.EMAIL_DOMAIN_MAP,
            domainrules=config.EMAIL_DOMAIN_RULES)

    context = MailServerContext(config, distributor, Unscheduled())
    return context
Exemple #4
0
def addWebServer(config, distributor):
    """Set up a web server for HTTP(S)-based bridge distribution.

    :type config: :class:`bridgedb.persistent.Conf`
    :param config: A configuration object from
         :mod:`bridgedb.Main`. Currently, we use these options::
             HTTP_UNENCRYPTED_PORT
             HTTP_UNENCRYPTED_BIND_IP
             HTTP_USE_IP_FROM_FORWARDED_HEADER
             HTTPS_N_BRIDGES_PER_ANSWER
             HTTPS_INCLUDE_FINGERPRINTS
             HTTPS_KEY_FILE
             HTTPS_CERT_FILE
             HTTPS_PORT
             HTTPS_BIND_IP
             HTTPS_USE_IP_FROM_FORWARDED_HEADER
             HTTPS_ROTATION_PERIOD
             RECAPTCHA_ENABLED
             RECAPTCHA_PUB_KEY
             RECAPTCHA_SEC_KEY
             RECAPTCHA_REMOTEIP
             GIMP_CAPTCHA_ENABLED
             GIMP_CAPTCHA_DIR
             GIMP_CAPTCHA_HMAC_KEYFILE
             GIMP_CAPTCHA_RSA_KEYFILE
             SERVER_PUBLIC_FQDN
             CSP_ENABLED
             CSP_REPORT_ONLY
             CSP_INCLUDE_SELF
    :type distributor: :class:`bridgedb.https.distributor.HTTPSDistributor`
    :param distributor: A bridge distributor.
    :raises SystemExit: if the servers cannot be started.
    :rtype: :api:`twisted.web.server.Site`
    :returns: A webserver.
    """
    captcha = None
    fwdHeaders = config.HTTP_USE_IP_FROM_FORWARDED_HEADER
    numBridges = config.HTTPS_N_BRIDGES_PER_ANSWER
    fprInclude = config.HTTPS_INCLUDE_FINGERPRINTS

    logging.info("Starting web servers...")

    setFQDN(config.SERVER_PUBLIC_FQDN)

    index   = IndexResource()
    options = OptionsResource()
    howto   = HowtoResource()
    robots  = static.File(os.path.join(TEMPLATE_DIR, 'robots.txt'))
    assets  = static.File(os.path.join(TEMPLATE_DIR, 'assets/'))
    keys    = static.Data(bytes(strings.BRIDGEDB_OPENPGP_KEY), 'text/plain')
    csp     = CSPResource(enabled=config.CSP_ENABLED,
                          includeSelf=config.CSP_INCLUDE_SELF,
                          reportViolations=config.CSP_REPORT_ONLY,
                          useForwardedHeader=fwdHeaders)

    root = CustomErrorHandlingResource()
    root.putChild('', index)
    root.putChild('robots.txt', robots)
    root.putChild('keys', keys)
    root.putChild('assets', assets)
    root.putChild('options', options)
    root.putChild('howto', howto)
    root.putChild('maintenance', maintenance)
    root.putChild('error', resource500)
    root.putChild(CSPResource.reportURI, csp)

    if config.RECAPTCHA_ENABLED:
        publicKey = config.RECAPTCHA_PUB_KEY
        secretKey = config.RECAPTCHA_SEC_KEY
        captcha = partial(ReCaptchaProtectedResource,
                          remoteIP=config.RECAPTCHA_REMOTEIP)
    elif config.GIMP_CAPTCHA_ENABLED:
        # Get the master HMAC secret key for CAPTCHA challenges, and then
        # create a new HMAC key from it for use on the server.
        captchaKey = crypto.getKey(config.GIMP_CAPTCHA_HMAC_KEYFILE)
        hmacKey = crypto.getHMAC(captchaKey, "Captcha-Key")
        # Load or create our encryption keys:
        secretKey, publicKey = crypto.getRSAKey(config.GIMP_CAPTCHA_RSA_KEYFILE)
        captcha = partial(GimpCaptchaProtectedResource,
                          hmacKey=hmacKey,
                          captchaDir=config.GIMP_CAPTCHA_DIR)

    if config.HTTPS_ROTATION_PERIOD:
        count, period = config.HTTPS_ROTATION_PERIOD.split()
        sched = ScheduledInterval(count, period)
    else:
        sched = Unscheduled()

    bridges = BridgesResource(distributor, sched, numBridges, fwdHeaders,
                              includeFingerprints=fprInclude)
    if captcha:
        # Protect the 'bridges' page with a CAPTCHA, if configured to do so:
        protected = captcha(publicKey=publicKey,
                            secretKey=secretKey,
                            useForwardedHeader=fwdHeaders,
                            protectedResource=bridges)
        root.putChild('bridges', protected)
        logging.info("Protecting resources with %s." % captcha.func.__name__)
    else:
        root.putChild('bridges', bridges)

    site = Site(root)
    site.displayTracebacks = False

    if config.HTTP_UNENCRYPTED_PORT:  # pragma: no cover
        ip = config.HTTP_UNENCRYPTED_BIND_IP or ""
        port = config.HTTP_UNENCRYPTED_PORT or 80
        try:
            reactor.listenTCP(port, site, interface=ip)
        except CannotListenError as error:
            raise SystemExit(error)
        logging.info("Started HTTP server on %s:%d" % (str(ip), int(port)))

    if config.HTTPS_PORT:  # pragma: no cover
        ip = config.HTTPS_BIND_IP or ""
        port = config.HTTPS_PORT or 443
        try:
            from twisted.internet.ssl import DefaultOpenSSLContextFactory
            factory = DefaultOpenSSLContextFactory(config.HTTPS_KEY_FILE,
                                                   config.HTTPS_CERT_FILE)
            reactor.listenSSL(port, site, factory, interface=ip)
        except CannotListenError as error:
            raise SystemExit(error)
        logging.info("Started HTTPS server on %s:%d" % (str(ip), int(port)))

    return site
Exemple #5
0
def addWebServer(cfg, dist):
    """Set up a web server for HTTP(S)-based bridge distribution.

    :type cfg: :class:`bridgedb.persistent.Conf`
    :param cfg: A configuration object from
         :mod:`bridgedb.Main`. Currently, we use these options::
             HTTP_UNENCRYPTED_PORT
             HTTP_UNENCRYPTED_BIND_IP
             HTTP_USE_IP_FROM_FORWARDED_HEADER
             HTTPS_N_BRIDGES_PER_ANSWER
             HTTPS_INCLUDE_FINGERPRINTS
             HTTPS_KEY_FILE
             HTTPS_CERT_FILE
             HTTPS_PORT
             HTTPS_BIND_IP
             HTTPS_USE_IP_FROM_FORWARDED_HEADER
             HTTPS_ROTATION_PERIOD
             RECAPTCHA_ENABLED
             RECAPTCHA_PUB_KEY
             RECAPTCHA_SEC_KEY
             RECAPTCHA_REMOTEIP
             GIMP_CAPTCHA_ENABLED
             GIMP_CAPTCHA_DIR
             GIMP_CAPTCHA_HMAC_KEYFILE
             GIMP_CAPTCHA_RSA_KEYFILE
    :type dist: :class:`bridgedb.Dist.IPBasedDistributor`
    :param dist: A bridge distributor.
    :raises SystemExit: if the servers cannot be started.
    :rtype: :api:`twisted.web.server.Site`
    :returns: A webserver.
    """
    captcha = None
    fwdHeaders = cfg.HTTP_USE_IP_FROM_FORWARDED_HEADER
    numBridges = cfg.HTTPS_N_BRIDGES_PER_ANSWER
    fprInclude = cfg.HTTPS_INCLUDE_FINGERPRINTS

    logging.info("Starting web servers...")

    httpdist = resource.Resource()
    httpdist.putChild('', WebRoot())
    httpdist.putChild('robots.txt',
                      static.File(os.path.join(TEMPLATE_DIR, 'robots.txt')))
    httpdist.putChild('keys',
                      static.File(os.path.join(TEMPLATE_DIR, 'bridgedb.asc')))
    httpdist.putChild('assets',
                      static.File(os.path.join(TEMPLATE_DIR, 'assets/')))
    httpdist.putChild('options', WebResourceOptions())
    httpdist.putChild('howto', WebResourceHowto())

    if cfg.RECAPTCHA_ENABLED:
        publicKey = cfg.RECAPTCHA_PUB_KEY
        secretKey = cfg.RECAPTCHA_SEC_KEY
        captcha = partial(ReCaptchaProtectedResource,
                          remoteIP=cfg.RECAPTCHA_REMOTEIP)
    elif cfg.GIMP_CAPTCHA_ENABLED:
        # Get the master HMAC secret key for CAPTCHA challenges, and then
        # create a new HMAC key from it for use on the server.
        captchaKey = crypto.getKey(cfg.GIMP_CAPTCHA_HMAC_KEYFILE)
        hmacKey = crypto.getHMAC(captchaKey, "Captcha-Key")
        # Load or create our encryption keys:
        secretKey, publicKey = crypto.getRSAKey(cfg.GIMP_CAPTCHA_RSA_KEYFILE)
        captcha = partial(GimpCaptchaProtectedResource,
                          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:
        # Protect the 'bridges' page with a CAPTCHA, if configured to do so:
        protected = captcha(publicKey=publicKey,
                            secretKey=secretKey,
                            useForwardedHeader=fwdHeaders,
                            protectedResource=bridges)
        httpdist.putChild('bridges', protected)
        logging.info("Protecting resources with %s." % captcha.func.__name__)
    else:
        httpdist.putChild('bridges', bridges)

    site = server.Site(httpdist)
    site.displayTracebacks = False

    if cfg.HTTP_UNENCRYPTED_PORT:
        ip = cfg.HTTP_UNENCRYPTED_BIND_IP or ""
        port = cfg.HTTP_UNENCRYPTED_PORT or 80
        try:
            reactor.listenTCP(port, site, interface=ip)
        except CannotListenError as error:
            raise SystemExit(error)
        logging.info("Started HTTP server on %s:%d" % (str(ip), int(port)))

    if cfg.HTTPS_PORT:
        ip = cfg.HTTPS_BIND_IP or ""
        port = cfg.HTTPS_PORT or 443
        try:
            from twisted.internet.ssl import DefaultOpenSSLContextFactory
            factory = DefaultOpenSSLContextFactory(cfg.HTTPS_KEY_FILE,
                                                   cfg.HTTPS_CERT_FILE)
            reactor.listenSSL(port, site, factory, interface=ip)
        except CannotListenError as error:
            raise SystemExit(error)
        logging.info("Started HTTPS server on %s:%d" % (str(ip), int(port)))

    return site
Exemple #6
0
def addMoatServer(config, distributor):
    """Set up a web server for moat bridge distribution.

    :type config: :class:`bridgedb.persistent.Conf`
    :param config: A configuration object from
         :mod:`bridgedb.main`. Currently, we use these options::
             GIMP_CAPTCHA_DIR
             SERVER_PUBLIC_FQDN
             SUPPORTED_TRANSPORTS
             MOAT_DIST
             MOAT_DIST_VIA_MEEK_ONLY
             MOAT_TLS_CERT_FILE
             MOAT_TLS_KEY_FILE
             MOAT_SERVER_PUBLIC_ROOT
             MOAT_HTTPS_IP
             MOAT_HTTPS_PORT
             MOAT_HTTP_IP
             MOAT_HTTP_PORT
             MOAT_BRIDGES_PER_ANSWER
             MOAT_TRANSPORT_PREFERENCE_LIST
             MOAT_USE_IP_FROM_FORWARDED_HEADER
             MOAT_SKIP_LOOPBACK_ADDRESSES
             MOAT_ROTATION_PERIOD
             MOAT_GIMP_CAPTCHA_HMAC_KEYFILE
             MOAT_GIMP_CAPTCHA_RSA_KEYFILE
    :type distributor: :class:`bridgedb.distributors.moat.distributor.MoatDistributor`
    :param distributor: A bridge distributor.
    :raises SystemExit: if the servers cannot be started.
    :rtype: :api:`twisted.web.server.Site`
    :returns: A webserver.
    """
    captcha = None
    fwdHeaders = config.MOAT_USE_IP_FROM_FORWARDED_HEADER
    numBridges = config.MOAT_BRIDGES_PER_ANSWER
    skipLoopback = config.MOAT_SKIP_LOOPBACK_ADDRESSES

    logging.info("Starting moat servers...")

    setFQDN(config.SERVER_PUBLIC_FQDN)
    setRoot(config.MOAT_SERVER_PUBLIC_ROOT)
    setSupportedTransports(config.SUPPORTED_TRANSPORTS)
    setPreferredTransports(config.MOAT_TRANSPORT_PREFERENCE_LIST)

    # Get the master HMAC secret key for CAPTCHA challenges, and then
    # create a new HMAC key from it for use on the server.
    captchaKey = crypto.getKey(config.MOAT_GIMP_CAPTCHA_HMAC_KEYFILE)
    hmacKey = crypto.getHMAC(captchaKey, "Moat-Captcha-Key")
    # Load or create our encryption keys:
    secretKey, publicKey = crypto.getRSAKey(
        config.MOAT_GIMP_CAPTCHA_RSA_KEYFILE)
    sched = Unscheduled()

    if config.MOAT_ROTATION_PERIOD:
        count, period = config.MOAT_ROTATION_PERIOD.split()
        sched = ScheduledInterval(count, period)

    sitePublicDir = getRoot()

    meek = CustomErrorHandlingResource()
    moat = CustomErrorHandlingResource()
    fetch = CaptchaFetchResource(hmacKey, publicKey, secretKey,
                                 config.GIMP_CAPTCHA_DIR, fwdHeaders,
                                 skipLoopback)
    check = CaptchaCheckResource(distributor, sched, numBridges, hmacKey,
                                 publicKey, secretKey, fwdHeaders,
                                 skipLoopback)

    moat.putChild(b"fetch", fetch)
    moat.putChild(b"check", check)
    meek.putChild(b"moat", moat)

    root = CustomErrorHandlingResource()
    root.putChild(b"meek", meek)
    root.putChild(b"moat", moat)

    site = Site(root)
    site.displayTracebacks = False

    if config.MOAT_HTTP_PORT:  # pragma: no cover
        ip = config.MOAT_HTTP_IP or ""
        port = config.MOAT_HTTP_PORT or 80
        try:
            reactor.listenTCP(port, site, interface=ip)
        except CannotListenError as error:
            raise SystemExit(error)
        logging.info("Started Moat HTTP server on %s:%d" %
                     (str(ip), int(port)))

    if config.MOAT_HTTPS_PORT:  # pragma: no cover
        ip = config.MOAT_HTTPS_IP or ""
        port = config.MOAT_HTTPS_PORT or 443
        try:
            from twisted.internet.ssl import DefaultOpenSSLContextFactory
            factory = DefaultOpenSSLContextFactory(config.MOAT_TLS_KEY_FILE,
                                                   config.MOAT_TLS_CERT_FILE)
            reactor.listenSSL(port, site, factory, interface=ip)
        except CannotListenError as error:
            raise SystemExit(error)
        logging.info("Started Moat TLS server on %s:%d" % (str(ip), int(port)))

    return site