Important
You should not run JupyterHub without SSL encryption on a public network.
Security is the most important aspect of configuring Jupyter.Three (3) configuration settings are the main aspects of security configuration:
SSL encryption (to enable HTTPS)
Cookie secret (a key for encrypting browser cookies)
Proxy authentication token (used for the Hub andother services to authenticate to the Proxy)
The Hub hashes all secrets (e.g. auth tokens) before storing them in itsdatabase. A loss of control over read-access to the database should haveminimal impact on your deployment. If your database has been compromised, itis still a good idea to revoke existing tokens.
Enabling SSL encryption#
Since JupyterHub includes authentication and allows arbitrary code execution,you should not run it without SSL (HTTPS).
Using an SSL certificate#
This will require you to obtain an official, trusted SSL certificate or create aself-signed certificate. Once you have obtained and installed a key andcertificate, you need to specify their locations in the jupyterhub_config.py
configuration file as follows:
c.JupyterHub.ssl_key = '/path/to/my.key'c.JupyterHub.ssl_cert = '/path/to/my.cert'
Some cert files also contain the key, in which case only the cert is needed. Itis important that these files be put in a secure location on your server, wherethey are not readable by regular users.
If you are using a chain certificate, see also chained certificate for SSLin the JupyterHub Troubleshooting FAQ.
Using letsencrypt#
It is also possible to use letsencrypt to obtaina free, trusted SSL certificate. If you run letsencrypt using the defaultoptions, the needed configuration is (replace mydomain.tld
by your fullyqualified domain name):
c.JupyterHub.ssl_key = '/etc/letsencrypt/live/{mydomain.tld}/privkey.pem'c.JupyterHub.ssl_cert = '/etc/letsencrypt/live/{mydomain.tld}/fullchain.pem'
If the fully qualified domain name (FQDN) is example.com
, the followingwould be the needed configuration:
c.JupyterHub.ssl_key = '/etc/letsencrypt/live/example.com/privkey.pem'c.JupyterHub.ssl_cert = '/etc/letsencrypt/live/example.com/fullchain.pem'
If SSL termination happens outside of the Hub#
In certain cases, for example, if the hub is running behind a reverse proxy, andSSL termination is being provided by NGINX,it is reasonable to run the hub without SSL.
To achieve this, remove c.JupyterHub.ssl_key
and c.JupyterHub.ssl_cert
from your configuration (setting them to None
or an empty string does nothave the same effect, and will result in an error).
Proxy authentication token#
The Hub authenticates its requests to the Proxy using a secret token thatthe Hub and Proxy agree upon. Note that this applies to the defaultConfigurableHTTPProxy
implementation. Not all proxy implementationsuse an auth token.
The value of this token should be a random string (for example, generated byopenssl rand -hex 32
). You can store it in the configuration file or anenvironment variable.
Generating and storing token in the configuration file#
You can set the value in the configuration file, jupyterhub_config.py
:
c.ConfigurableHTTPProxy.api_token = 'abc123...' # any random string
Generating and storing as an environment variable#
You can pass this value of the proxy authentication token to the Hub and Proxyusing the CONFIGPROXY_AUTH_TOKEN
environment variable:
export CONFIGPROXY_AUTH_TOKEN=$(openssl rand -hex 32)
This environment variable needs to be visible to the Hub and Proxy.
Default if token is not set#
If you do not set the Proxy authentication token, the Hub will generate a randomkey itself. This means that any time you restart the Hub, you must alsorestart the Proxy. If the proxy is a subprocess of the Hub, this should happenautomatically (this is the default configuration).
Cookie secret#
The cookie secret is an encryption key, used to encrypt the browser cookies,which are used for authentication. Three common methods are described forgenerating and configuring the cookie secret.
Generating and storing as a cookie secret file#
The cookie secret should be 32 random bytes, encoded as hex, and is typicallystored in a jupyterhub_cookie_secret
file. Below, is an example command to generate thejupyterhub_cookie_secret
file:
openssl rand -hex 32 > /srv/jupyterhub/jupyterhub_cookie_secret
In most deployments of JupyterHub, you should point this to a secure location onthe file system, such as /srv/jupyterhub/jupyterhub_cookie_secret
.
The location of the jupyterhub_cookie_secret
file can be specified in thejupyterhub_config.py
file as follows:
c.JupyterHub.cookie_secret_file = '/srv/jupyterhub/jupyterhub_cookie_secret'
If the cookie secret file doesn’t exist when the Hub starts, a new cookiesecret is generated and stored in the file. The file must not be readable bygroup
or other
, otherwise the server won’t start. The recommended permissionsfor the cookie secret file are 600
(owner-only rw).
Generating and storing as an environment variable#
If you would like to avoid the need for files, the value can be loaded in theHub process from the JPY_COOKIE_SECRET
environment variable, which is ahex-encoded string. You can set it this way:
export JPY_COOKIE_SECRET=$(openssl rand -hex 32)
For security reasons, this environment variable should only be visible to theHub. If you set it dynamically as above, all users will be logged out each timethe Hub starts.
Generating and storing as a binary string#
You can also set the cookie secret, as a binary string,in the configuration file (jupyterhub_config.py
) itself:
c.JupyterHub.cookie_secret = bytes.fromhex('64 CHAR HEX STRING')
Cookies used by JupyterHub authentication#
The following cookies are used by the Hub for handling user authentication.
This section was created based on this post from Discourse.
jupyterhub-hub-login#
This is the login token used when visiting Hub-served pages that areprotected by authentication, such as the main home, the spawn form, etc.If this cookie is set, then the user is logged in.
Resetting the Hub cookie secret effectively revokes this cookie.
This cookie is restricted to the path /hub/
.
jupyterhub-user-<username>#
This is the cookie used for authenticating with a single-user server.It is set by the single-user server, after OAuth with the Hub.
Effectively the same as jupyterhub-hub-login
, but for thesingle-user server instead of the Hub. It contains an OAuth access token,which is checked with the Hub to authenticate the browser.
Each OAuth access token is associated with a session id (see jupyterhub-session-id
sectionbelow).
To avoid hitting the Hub on every request, the authentication response is cached.The cache key is comprised of both the token and session id, to avoid a stale cache.
Resetting the Hub cookie secret effectively revokes this cookie.
This cookie is restricted to the path /user/<username>
,to ensure that only the user’s server receives it.
jupyterhub-session-id#
This is a random string, meaningless in itself, and the only cookieshared by the Hub and single-user servers.
Its sole purpose is to coordinate the logout of the multiple OAuth cookies.
This cookie is set to /
so all endpoints can receive it, clear it, etc.
jupyterhub-user-<username>-oauth-state#
A short-lived cookie, used solely to store and validate OAuth state.It is only set while OAuth between the single-user server and the Hubis processing.
If you use your browser development tools, you should see this cookiefor a very brief moment before you are logged in,with an expiration date shorter than jupyterhub-hub-login
orjupyterhub-user-<username>
.
This cookie should not exist after you have successfully logged in.
This cookie is restricted to the path /user/<username>
, so that onlythe user’s server receives it.