def test_setFQDN_http(self):
     """Calling ``server.setFQDN([…], https=False)`` should not prepend
     anything at all to the module :data:`server.SERVER_PUBLIC_FQDN`
     variable.
     """
     server.setFQDN('example.com', https=False)
     self.assertEqual(server.SERVER_PUBLIC_FQDN, "example.com")
 def test_setFQDN_https(self):
     """Calling ``server.setFQDN([…], https=True)`` should prepend
     ``"https://"`` to the module :data:`server.SERVER_PUBLIC_FQDN`
     variable.
     """
     server.setFQDN('example.com', https=True)
     self.assertEqual(server.SERVER_PUBLIC_FQDN, "https://example.com")
示例#3
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.distributors.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
示例#4
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
示例#5
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.distributors.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
示例#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("fetch", fetch)
    moat.putChild("check", check)
    meek.putChild("moat", moat)

    root = CustomErrorHandlingResource()
    root.putChild("meek", meek)
    root.putChild("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