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
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
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
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