mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-07 10:04:07 +00:00
255 lines
9.0 KiB
ReStructuredText
255 lines
9.0 KiB
ReStructuredText
Security settings
|
||
=================
|
||
|
||
.. 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:
|
||
|
||
1. :ref:`SSL encryption <ssl-encryption>` (to enable HTTPS)
|
||
2. :ref:`Cookie secret <cookie-secret>` (a key for encrypting browser cookies)
|
||
3. Proxy :ref:`authentication token <authentication-token>` (used for the Hub and
|
||
other services to authenticate to the Proxy)
|
||
|
||
The Hub hashes all secrets (e.g. auth tokens) before storing them in its
|
||
database. A loss of control over read-access to the database should have
|
||
minimal impact on your deployment. If your database has been compromised, it
|
||
is still a good idea to revoke existing tokens.
|
||
|
||
.. _ssl-encryption:
|
||
|
||
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 a
|
||
self-signed certificate. Once you have obtained and installed a key and
|
||
certificate, you need to specify their locations in the ``jupyterhub_config.py``
|
||
configuration file as follows:
|
||
|
||
.. code-block:: python
|
||
|
||
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. It
|
||
is important that these files be put in a secure location on your server, where
|
||
they are not readable by regular users.
|
||
|
||
If you are using a **chain certificate**, see also chained certificate for SSL
|
||
in the JupyterHub `Troubleshooting FAQ <../troubleshooting.html>`_.
|
||
|
||
Using letsencrypt
|
||
~~~~~~~~~~~~~~~~~
|
||
|
||
It is also possible to use `letsencrypt <https://letsencrypt.org/>`_ to obtain
|
||
a free, trusted SSL certificate. If you run letsencrypt using the default
|
||
options, the needed configuration is (replace ``mydomain.tld`` by your fully
|
||
qualified domain name):
|
||
|
||
.. code-block:: python
|
||
|
||
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 following
|
||
would be the needed configuration:
|
||
|
||
.. code-block:: python
|
||
|
||
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, and
|
||
`SSL termination is being provided by NGINX <https://www.nginx.com/resources/admin-guide/nginx-ssl-termination/>`_,
|
||
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 not
|
||
have the same effect, and will result in an error).
|
||
|
||
.. _authentication-token:
|
||
|
||
Proxy authentication token
|
||
--------------------------
|
||
|
||
The Hub authenticates its requests to the Proxy using a secret token that
|
||
the Hub and Proxy agree upon. Note that this applies to the default
|
||
``ConfigurableHTTPProxy`` implementation. Not all proxy implementations
|
||
use an auth token.
|
||
|
||
The value of this token should be a random string (for example, generated by
|
||
``openssl rand -hex 32``). You can store it in the configuration file or an
|
||
environment variable.
|
||
|
||
Generating and storing token in the configuration file
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
You can set the value in the configuration file, ``jupyterhub_config.py``:
|
||
|
||
.. code-block:: python
|
||
|
||
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 Proxy
|
||
using the ``CONFIGPROXY_AUTH_TOKEN`` environment variable:
|
||
|
||
.. code-block:: bash
|
||
|
||
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 random
|
||
key itself. This means that any time you restart the Hub, you **must also
|
||
restart the Proxy**. If the proxy is a subprocess of the Hub, this should happen
|
||
automatically (this is the default configuration).
|
||
|
||
.. _cookie-secret:
|
||
|
||
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 for
|
||
generating 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 typically
|
||
stored in a ``jupyterhub_cookie_secret`` file. Below, is an example command to generate the
|
||
``jupyterhub_cookie_secret`` file:
|
||
|
||
.. code-block:: bash
|
||
|
||
openssl rand -hex 32 > /srv/jupyterhub/jupyterhub_cookie_secret
|
||
|
||
In most deployments of JupyterHub, you should point this to a secure location on
|
||
the file system, such as ``/srv/jupyterhub/jupyterhub_cookie_secret``.
|
||
|
||
The location of the ``jupyterhub_cookie_secret`` file can be specified in the
|
||
``jupyterhub_config.py`` file as follows:
|
||
|
||
.. code-block:: python
|
||
|
||
c.JupyterHub.cookie_secret_file = '/srv/jupyterhub/jupyterhub_cookie_secret'
|
||
|
||
If the cookie secret file doesn't exist when the Hub starts, a new cookie
|
||
secret is generated and stored in the file. The file must not be readable by
|
||
``group`` or ``other``, otherwise the server won't start. The recommended permissions
|
||
for 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 the
|
||
Hub process from the ``JPY_COOKIE_SECRET`` environment variable, which is a
|
||
hex-encoded string. You can set it this way:
|
||
|
||
.. code-block:: bash
|
||
|
||
export JPY_COOKIE_SECRET=$(openssl rand -hex 32)
|
||
|
||
For security reasons, this environment variable should only be visible to the
|
||
Hub. If you set it dynamically as above, all users will be logged out each time
|
||
the 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:
|
||
|
||
.. code-block:: python
|
||
|
||
c.JupyterHub.cookie_secret = bytes.fromhex('64 CHAR HEX STRING')
|
||
|
||
.. _cookies:
|
||
|
||
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.
|
||
|
||
.. _post: https://discourse.jupyter.org/t/how-to-force-re-login-for-users/1998/6
|
||
|
||
jupyterhub-hub-login
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
|
||
This is the login token used when visiting Hub-served pages that are
|
||
protected 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 the
|
||
single-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`` section
|
||
below).
|
||
|
||
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 cookie
|
||
shared 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 Hub
|
||
is processing.
|
||
|
||
If you use your browser development tools, you should see this cookie
|
||
for a very brief moment before you are logged in,
|
||
with an expiration date shorter than ``jupyterhub-hub-login`` or
|
||
``jupyterhub-user-<username>``.
|
||
|
||
This cookie should not exist after you have successfully logged in.
|
||
|
||
This cookie is restricted to the path ``/user/<username>``, so that only
|
||
the user’s server receives it.
|