Propagate certs to everything that needs them

This commit is contained in:
Thomas Mendoza
2018-06-07 16:00:19 -07:00
parent a69e906c6e
commit c50cd1ba7f
7 changed files with 99 additions and 4 deletions

View File

@@ -1379,6 +1379,7 @@ class JupyterHub(Application):
orm_service.admin = spec.get('admin', False) orm_service.admin = spec.get('admin', False)
self.db.commit() self.db.commit()
service = Service(parent=self, service = Service(parent=self,
app=self,
base_url=self.base_url, base_url=self.base_url,
db=self.db, orm=orm_service, db=self.db, orm=orm_service,
domain=domain, host=host, domain=domain, host=host,

View File

@@ -41,7 +41,7 @@ from jupyterhub.traitlets import Command
from traitlets.config import LoggingConfigurable from traitlets.config import LoggingConfigurable
from .objects import Server from .objects import Server
from . import utils from . import utils
from .utils import url_path_join from .utils import url_path_join, make_ssl_context
def _one_at_a_time(method): def _one_at_a_time(method):
@@ -391,6 +391,15 @@ class ConfigurableHTTPProxy(Proxy):
c.ConfigurableHTTPProxy.should_start = False c.ConfigurableHTTPProxy.should_start = False
""" """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
ssl_context = make_ssl_context(
self.app.internal_ssl_key,
self.app.internal_ssl_cert,
cafile=self.app.internal_ssl_ca,
)
AsyncHTTPClient.configure(None, defaults={"ssl_options" : ssl_context})
proxy_process = Any() proxy_process = Any()
client = Instance(AsyncHTTPClient, ()) client = Instance(AsyncHTTPClient, ())
@@ -432,9 +441,22 @@ class ConfigurableHTTPProxy(Proxy):
token = utils.new_token() token = utils.new_token()
return token return token
api_url = Unicode('http://127.0.0.1:8001', config=True, api_url = Unicode(config=True,
help="""The ip (or hostname) of the proxy's API endpoint""" help="""The ip (or hostname) of the proxy's API endpoint"""
) )
@default('api_url')
def _api_url_default(self):
url = '127.0.0.1:8001'
proto = 'http'
if self.app.internal_ssl:
proto = 'https'
return "{proto}://{url}".format(
proto=proto,
url=url,
)
command = Command('configurable-http-proxy', config=True, command = Command('configurable-http-proxy', config=True,
help="""The command to start the proxy""" help="""The command to start the proxy"""
) )
@@ -541,6 +563,13 @@ class ConfigurableHTTPProxy(Proxy):
cmd.extend(['--ssl-key', self.ssl_key]) cmd.extend(['--ssl-key', self.ssl_key])
if self.ssl_cert: if self.ssl_cert:
cmd.extend(['--ssl-cert', self.ssl_cert]) cmd.extend(['--ssl-cert', self.ssl_cert])
if self.app.internal_ssl:
cmd.extend(['--api-ssl-key', self.app.internal_ssl_key])
cmd.extend(['--api-ssl-cert', self.app.internal_ssl_cert])
cmd.extend(['--api-ssl-ca', self.app.internal_ssl_ca])
cmd.extend(['--api-ssl-request-cert'])
cmd.extend(['--api-ssl-reject-unauthorized'])
cmd.extend(['--forward-ssl'])
if self.app.statsd_host: if self.app.statsd_host:
cmd.extend([ cmd.extend([
'--statsd-host', self.app.statsd_host, '--statsd-host', self.app.statsd_host,

View File

@@ -196,6 +196,27 @@ class HubAuth(SingletonConfigurable):
def _default_login_url(self): def _default_login_url(self):
return self.hub_host + url_path_join(self.hub_prefix, 'login') return self.hub_host + url_path_join(self.hub_prefix, 'login')
keyfile = Unicode('',
help="""The ssl key to use for requests
Use with certfile
"""
).tag(config=True)
certfile = Unicode('',
help="""The ssl cert to use for requests
Use with keyfile
"""
).tag(config=True)
client_ca = Unicode('',
help="""The ssl certificate authority to use to verify requests
Use with keyfile and certfile
"""
).tag(config=True)
cookie_name = Unicode('jupyterhub-services', cookie_name = Unicode('jupyterhub-services',
help="""The name of the cookie I should be looking for""" help="""The name of the cookie I should be looking for"""
).tag(config=True) ).tag(config=True)
@@ -277,6 +298,10 @@ class HubAuth(SingletonConfigurable):
allow_404 = kwargs.pop('allow_404', False) allow_404 = kwargs.pop('allow_404', False)
headers = kwargs.setdefault('headers', {}) headers = kwargs.setdefault('headers', {})
headers.setdefault('Authorization', 'token %s' % self.api_token) headers.setdefault('Authorization', 'token %s' % self.api_token)
if "cert" not in kwargs and self.certfile and self.keyfile:
kwargs["cert"] = (self.certfile, self.keyfile)
if self.client_ca:
kwargs["verify"] = self.client_ca
try: try:
r = requests.request(method, url, **kwargs) r = requests.request(method, url, **kwargs)
except requests.ConnectionError as e: except requests.ConnectionError as e:

View File

@@ -224,6 +224,7 @@ class Service(LoggingConfigurable):
domain = Unicode() domain = Unicode()
host = Unicode() host = Unicode()
hub = Any() hub = Any()
app = Any()
proc = Any() proc = Any()
# handles on globals: # handles on globals:
@@ -331,6 +332,9 @@ class Service(LoggingConfigurable):
server=self.orm.server, server=self.orm.server,
host=self.host, host=self.host,
), ),
internal_ssl=self.app.internal_ssl,
internal_certs_location=self.app.internal_certs_location,
internal_authority_name=self.app.internal_authority_name,
) )
self.spawner.start() self.spawner.start()
self.proc = self.spawner.proc self.proc = self.spawner.proc

View File

@@ -237,6 +237,27 @@ class SingleUserNotebookApp(NotebookApp):
def _default_group(self): def _default_group(self):
return os.environ.get('JUPYTERHUB_GROUP') or '' return os.environ.get('JUPYTERHUB_GROUP') or ''
keyfile = Unicode('',
help="""The ssl key to use for requests
Use with certfile
"""
).tag(config=True)
certfile = Unicode('',
help="""The ssl cert to use for requests
Use with keyfile
"""
).tag(config=True)
client_ca = Unicode('',
help="""The ssl certificate authority to use to verify requests
Use with keyfile and certfile
"""
).tag(config=True)
@observe('user') @observe('user')
def _user_changed(self, change): def _user_changed(self, change):
self.log.name = change.new self.log.name = change.new
@@ -423,6 +444,9 @@ class SingleUserNotebookApp(NotebookApp):
api_url=self.hub_api_url, api_url=self.hub_api_url,
hub_prefix=self.hub_prefix, hub_prefix=self.hub_prefix,
base_url=self.base_url, base_url=self.base_url,
keyfile=self.keyfile,
certfile=self.certfile,
client_ca=self.client_ca,
) )
# smoke check # smoke check
if not self.hub_auth.oauth_client_id: if not self.hub_auth.oauth_client_id:

View File

@@ -158,6 +158,11 @@ class Spawner(LoggingConfigurable):
if self.orm_spawner: if self.orm_spawner:
return self.orm_spawner.name return self.orm_spawner.name
return '' return ''
hub = Any()
authenticator = Any()
internal_ssl = Bool(False)
internal_certs_location = Unicode('')
internal_authority_name = Unicode('')
admin_access = Bool(False) admin_access = Bool(False)
api_token = Unicode() api_token = Unicode()
oauth_client_id = Unicode() oauth_client_id = Unicode()

View File

@@ -11,7 +11,7 @@ from sqlalchemy import inspect
from tornado import gen from tornado import gen
from tornado.log import app_log from tornado.log import app_log
from .utils import maybe_future, url_path_join from .utils import maybe_future, url_path_join, make_ssl_context
from . import orm from . import orm
from ._version import _check_version, __version__ from ._version import _check_version, __version__
@@ -215,6 +215,9 @@ class User:
db=self.db, db=self.db,
oauth_client_id=client_id, oauth_client_id=client_id,
cookie_options = self.settings.get('cookie_options', {}), cookie_options = self.settings.get('cookie_options', {}),
internal_ssl=self.settings.get('internal_ssl'),
internal_certs_location=self.settings.get('internal_certs_location'),
internal_authority_name=self.settings.get('internal_authority_name'),
) )
# update with kwargs. Mainly for testing. # update with kwargs. Mainly for testing.
spawn_kwargs.update(kwargs) spawn_kwargs.update(kwargs)
@@ -493,7 +496,11 @@ class User:
db.commit() db.commit()
spawner._waiting_for_response = True spawner._waiting_for_response = True
try: try:
resp = await server.wait_up(http=True, timeout=spawner.http_timeout) key = self.settings['internal_ssl_key']
cert = self.settings['internal_ssl_cert']
ca = self.settings['internal_ssl_ca']
ssl_context = make_ssl_context(key, cert, cafile=ca)
resp = await server.wait_up(http=True, timeout=spawner.http_timeout, ssl_context=ssl_context)
except Exception as e: except Exception as e:
if isinstance(e, TimeoutError): if isinstance(e, TimeoutError):
self.log.warning( self.log.warning(