def main(): pprint.pprint(dict(os.environ), stream=sys.stderr) if os.getenv('JUPYTERHUB_SERVICE_URL'): url = urlparse(os.environ['JUPYTERHUB_SERVICE_URL']) app = web.Application( [ (r'.*/env', EnvHandler), (r'.*/api/(.*)', APIHandler), (r'.*/whoami/?', WhoAmIHandler), (r'.*/owhoami/?', OWhoAmIHandler), (r'.*/oauth_callback', HubOAuthCallbackHandler), (r'.*', EchoHandler), ], cookie_secret=os.urandom(32), ) ssl_context = None key = os.environ.get('JUPYTERHUB_SSL_KEYFILE') or '' cert = os.environ.get('JUPYTERHUB_SSL_CERTFILE') or '' ca = os.environ.get('JUPYTERHUB_SSL_CLIENT_CA') or '' if key and cert and ca: ssl_context = make_ssl_context(key, cert, cafile=ca, check_hostname=False) server = httpserver.HTTPServer(app, ssl_options=ssl_context) server.listen(url.port, url.hostname) try: ioloop.IOLoop.instance().start() except KeyboardInterrupt: print('\nInterrupted')
def ssl_context(self): if self.keyfile and self.certfile and self.cafile: return make_ssl_context(self.keyfile, self.certfile, cafile=self.cafile, check_hostname=False) else: return None
def configure_http_client(): keyfile = os.environ.get('JUPYTERHUB_SSL_KEYFILE', '') certfile = os.environ.get('JUPYTERHUB_SSL_CERTFILE', '') client_ca = os.environ.get('JUPYTERHUB_SSL_CLIENT_CA', '') if keyfile == '' and certfile == '' and client_ca == '': return ssl_context = make_ssl_context(keyfile, certfile, cafile=client_ca) httpclient.AsyncHTTPClient.configure(None, defaults={"ssl_options": ssl_context})
async def poll(self): """Poll the spawned process to see if it is still running and reachable If the process is still running, and we can connect to the remote singleuser server over the tunnel, we return None. If it is not running, or unreachable we return the exit code of the process if we have access to it, or 0 otherwise. """ status = await super().poll() if status is not None: return status elif not os.path.exists(self.ssh_socket): # tunnel is closed or non-existent return 0 else: protocol = "http" if not self.user.settings["internal_ssl"] \ else "https" url = "{protocol}://{ip}:{port}".format( protocol=protocol, ip=(self.ip or '127.0.0.1'), port=self.port ) key = self.user.settings.get('internal_ssl_key') cert = self.user.settings.get('internal_ssl_cert') ca = self.user.settings.get('internal_ssl_ca') ctx = make_ssl_context(key, cert, cafile=ca) try: reachable = await wait_for_http_server(url, ssl_context=ctx) except Exception as e: if isinstance(e, TimeoutError): e.reason = 'timeout' self.log.warning( "Unable to reach {user}'s server for 10 seconds. " "Giving up: {err}".format( user=self.user.name, err=e ), ) return 1 else: e.reason = 'error' self.log.warning( "Error reaching {user}'s server: {err}".format( user=self.user.name, err=e ) ) return 2 else: return None if reachable else 0
async def poll(self): """Poll the spawned process to see if it is still running and reachable If the process is still running, and we can connect to the remote singleuser server over the tunnel, we return None. If it is not running, or unreachable we return the exit code of the process if we have access to it, or 0 otherwise. """ status = await super().poll() if status is not None: if status == 255 and not self._stopping: self._stopping = True await self.stop() return status elif not os.path.exists(self.ssh_socket) and not self._started: # tunnel is closed or non-existent return 0 else: if self.user.settings["internal_ssl"]: protocol = "https" key = self.user.settings.get("internal_ssl_key") cert = self.user.settings.get("internal_ssl_cert") ca = self.user.settings.get("internal_ssl_ca") ctx = make_ssl_context(key, cert, cafile=ca) else: protocol = "http" ctx = None ip = self.ip or "127.0.0.1" url = f"{protocol}://{ip}:{self.port}" try: reachable = await wait_for_http_server(url, ssl_context=ctx) except Exception as e: if isinstance(e, TimeoutError): e.reason = "timeout" self.log.warning( f"Unable to reach {self.user.name}'s server for 10 seconds. " f"Giving up: {e}", ) return 1 else: e.reason = "error" self.log.warning( f"Error reaching {self.user.name}'s server: {e}") return 2 else: return None if reachable else 0
async def start(self): """Start the whole thing. Overridden to make service.start() asynchronous.""" self.io_loop = loop = IOLoop.current() if self.subapp: self.subapp.start() loop.stop() return if self.generate_config: self.write_config_file() loop.stop() return if self.generate_certs: self.load_config_file(self.config_file) if not self.internal_ssl: self.log.warning( "You'll need to enable `internal_ssl` " "in the `jupyterhub_config` file to use " "these certs." ) self.internal_ssl = True self.init_internal_ssl() self.log.info( "Certificates written to directory `{}`".format( self.internal_certs_location ) ) loop.stop() return # start the proxy if self.proxy.should_start: try: await self.proxy.start() except Exception: self.log.critical("Failed to start proxy", exc_info=True) self.exit(1) else: self.log.info("Not starting proxy") # verify that we can talk to the proxy before listening. # avoids delayed failure if we can't talk to the proxy await self.proxy.get_all_routes() ssl_context = make_ssl_context( self.internal_ssl_key, self.internal_ssl_cert, cafile=self.internal_ssl_ca, check_hostname=False, ) # start the webserver self.http_server = tornado.httpserver.HTTPServer( self.tornado_application, ssl_options=ssl_context, xheaders=True, trusted_downstream=self.trusted_downstream_ips, ) bind_url = urlparse(self.hub.bind_url) try: if bind_url.scheme.startswith('unix+'): from tornado.netutil import bind_unix_socket socket = bind_unix_socket(unquote(bind_url.netloc)) self.http_server.add_socket(socket) else: ip = bind_url.hostname port = bind_url.port if not port: if bind_url.scheme == 'https': port = 443 else: port = 80 self.http_server.listen(port, address=ip) self.log.info("Hub API listening on %s", self.hub.bind_url) if self.hub.url != self.hub.bind_url: self.log.info("Private Hub API connect url %s", self.hub.url) except Exception: self.log.error("Failed to bind hub to %s", self.hub.bind_url) raise # start the service(s) for service_name, service in self._service_map.items(): msg = ( '%s at %s' % (service_name, service.url) if service.url else service_name ) if service.managed: self.log.info("Starting managed service %s", msg) try: await service.start() except Exception: self.log.critical( "Failed to start service %s", service_name, exc_info=True ) self.exit(1) else: self.log.info("Adding external service %s", msg) if service.url: tries = 10 if service.managed else 1 for i in range(tries): try: ssl_context = make_ssl_context( self.internal_ssl_key, self.internal_ssl_cert, cafile=self.internal_ssl_ca, ) await Server.from_orm(service.orm.server).wait_up( http=True, timeout=1, ssl_context=ssl_context ) except TimeoutError: if service.managed: status = await service.spawner.poll() if status is not None: self.log.error( "Service %s exited with status %s", service_name, status, ) break else: break else: self.log.error( "Cannot connect to %s service %s at %s. Is it running?", service.kind, service_name, service.url, ) await self.proxy.check_routes(self.users, self._service_map) if self.service_check_interval and any( s.url for s in self._service_map.values() ): pc = PeriodicCallback( self.check_services_health, 1e3 * self.service_check_interval ) pc.start() if self.last_activity_interval: pc = PeriodicCallback( self.update_last_activity, 1e3 * self.last_activity_interval ) self.last_activity_callback = pc pc.start() self.log.info("JupyterHub is now running at %s", self.proxy.public_url) # Use atexit for Windows, it doesn't have signal handling support if os.name == "nt": atexit.register(self.atexit) # register cleanup on both TERM and INT self.init_signal() self._start_future.set_result(None)