mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-17 06:52:59 +00:00
Merge branch 'jupyterhub:main' into server
This commit is contained in:
@@ -5,15 +5,15 @@ deployment with the following assumptions:
|
||||
|
||||
- Running JupyterHub on a single cloud server
|
||||
- Using SSL on the standard HTTPS port 443
|
||||
- Using GitHub OAuth (using oauthenticator) for login
|
||||
- Using GitHub OAuth (using [OAuthenticator](https://oauthenticator.readthedocs.io/en/latest)) for login
|
||||
- Using the default spawner (to configure other spawners, uncomment and edit
|
||||
`spawner_class` as well as follow the instructions for your desired spawner)
|
||||
- Users exist locally on the server
|
||||
- Users' notebooks to be served from `~/assignments` to allow users to browse
|
||||
for notebooks within other users' home directories
|
||||
- You want the landing page for each user to be a `Welcome.ipynb` notebook in
|
||||
their assignments directory.
|
||||
- All runtime files are put into `/srv/jupyterhub` and log files in `/var/log`.
|
||||
their assignments directory
|
||||
- All runtime files are put into `/srv/jupyterhub` and log files in `/var/log`
|
||||
|
||||
The `jupyterhub_config.py` file would have these settings:
|
||||
|
||||
@@ -69,7 +69,7 @@ c.Spawner.args = ['--NotebookApp.default_url=/notebooks/Welcome.ipynb']
|
||||
```
|
||||
|
||||
Using the GitHub Authenticator requires a few additional
|
||||
environment variable to be set prior to launching JupyterHub:
|
||||
environment variables to be set prior to launching JupyterHub:
|
||||
|
||||
```bash
|
||||
export GITHUB_CLIENT_ID=github_id
|
||||
@@ -79,3 +79,5 @@ export CONFIGPROXY_AUTH_TOKEN=super-secret
|
||||
# append log output to log file /var/log/jupyterhub.log
|
||||
jupyterhub -f /etc/jupyterhub/jupyterhub_config.py &>> /var/log/jupyterhub.log
|
||||
```
|
||||
|
||||
Visit the [Github OAuthenticator reference](https://oauthenticator.readthedocs.io/en/latest/api/gen/oauthenticator.github.html) to see the full list of options for configuring Github OAuth with JupyterHub.
|
||||
|
@@ -14,7 +14,7 @@ satisfy the following:
|
||||
- After testing, the server in question should be able to score at least an A on the
|
||||
Qualys SSL Labs [SSL Server Test](https://www.ssllabs.com/ssltest/)
|
||||
|
||||
Let's start out with needed JupyterHub configuration in `jupyterhub_config.py`:
|
||||
Let's start out with the needed JupyterHub configuration in `jupyterhub_config.py`:
|
||||
|
||||
```python
|
||||
# Force the proxy to only listen to connections to 127.0.0.1 (on port 8000)
|
||||
@@ -30,15 +30,15 @@ This can take a few minutes:
|
||||
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
|
||||
```
|
||||
|
||||
## nginx
|
||||
## Nginx
|
||||
|
||||
This **`nginx` config file** is fairly standard fare except for the two
|
||||
`location` blocks within the main section for HUB.DOMAIN.tld.
|
||||
To create a new site for jupyterhub in your nginx config, make a new file
|
||||
To create a new site for jupyterhub in your Nginx config, make a new file
|
||||
in `sites.enabled`, e.g. `/etc/nginx/sites.enabled/jupyterhub.conf`:
|
||||
|
||||
```bash
|
||||
# top-level http config for websocket headers
|
||||
# Top-level HTTP config for WebSocket headers
|
||||
# If Upgrade is defined, Connection = upgrade
|
||||
# If Upgrade is empty, Connection = close
|
||||
map $http_upgrade $connection_upgrade {
|
||||
@@ -51,7 +51,7 @@ server {
|
||||
listen 80;
|
||||
server_name HUB.DOMAIN.TLD;
|
||||
|
||||
# Tell all requests to port 80 to be 302 redirected to HTTPS
|
||||
# Redirect the request to HTTPS
|
||||
return 302 https://$host$request_uri;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ server {
|
||||
ssl_stapling_verify on;
|
||||
add_header Strict-Transport-Security max-age=15768000;
|
||||
|
||||
# Managing literal requests to the JupyterHub front end
|
||||
# Managing literal requests to the JupyterHub frontend
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -101,10 +101,10 @@ server {
|
||||
If `nginx` is not running on port 443, substitute `$http_host` for `$host` on
|
||||
the lines setting the `Host` header.
|
||||
|
||||
`nginx` will now be the front facing element of JupyterHub on `443` which means
|
||||
`nginx` will now be the front-facing element of JupyterHub on `443` which means
|
||||
it is also free to bind other servers, like `NO_HUB.DOMAIN.TLD` to the same port
|
||||
on the same machine and network interface. In fact, one can simply use the same
|
||||
server blocks as above for `NO_HUB` and simply add line for the root directory
|
||||
server blocks as above for `NO_HUB` and simply add a line for the root directory
|
||||
of the site as well as the applicable location call:
|
||||
|
||||
```bash
|
||||
@@ -112,7 +112,7 @@ server {
|
||||
listen 80;
|
||||
server_name NO_HUB.DOMAIN.TLD;
|
||||
|
||||
# Tell all requests to port 80 to be 302 redirected to HTTPS
|
||||
# Redirect the request to HTTPS
|
||||
return 302 https://$host$request_uri;
|
||||
}
|
||||
|
||||
@@ -143,12 +143,12 @@ Now restart `nginx`, restart the JupyterHub, and enjoy accessing
|
||||
`https://HUB.DOMAIN.TLD` while serving other content securely on
|
||||
`https://NO_HUB.DOMAIN.TLD`.
|
||||
|
||||
### SELinux permissions for nginx
|
||||
### SELinux permissions for Nginx
|
||||
|
||||
On distributions with SELinux enabled (e.g. Fedora), one may encounter permission errors
|
||||
when the nginx service is started.
|
||||
when the Nginx service is started.
|
||||
|
||||
We need to allow nginx to perform network relay and connect to the jupyterhub port. The
|
||||
We need to allow Nginx to perform network relay and connect to the JupyterHub port. The
|
||||
following commands do that:
|
||||
|
||||
```bash
|
||||
@@ -157,26 +157,26 @@ setsebool -P httpd_can_network_relay 1
|
||||
setsebool -P httpd_can_network_connect 1
|
||||
```
|
||||
|
||||
Replace 8000 with the port the jupyterhub server is running from.
|
||||
Replace 8000 with the port the JupyterHub server is running from.
|
||||
|
||||
## Apache
|
||||
|
||||
As with nginx above, you can use [Apache](https://httpd.apache.org) as the reverse proxy.
|
||||
First, we will need to enable the apache modules that we are going to need:
|
||||
As with Nginx above, you can use [Apache](https://httpd.apache.org) as the reverse proxy.
|
||||
First, we will need to enable the Apache modules that we are going to need:
|
||||
|
||||
```bash
|
||||
a2enmod ssl rewrite proxy headers proxy_http proxy_wstunnel
|
||||
```
|
||||
|
||||
Our Apache configuration is equivalent to the nginx configuration above:
|
||||
Our Apache configuration is equivalent to the Nginx configuration above:
|
||||
|
||||
- Redirect HTTP to HTTPS
|
||||
- Good SSL Configuration
|
||||
- Support for websockets on any proxied URL
|
||||
- Support for WebSocket on any proxied URL
|
||||
- JupyterHub is running locally at http://127.0.0.1:8000
|
||||
|
||||
```bash
|
||||
# redirect HTTP to HTTPS
|
||||
# Redirect HTTP to HTTPS
|
||||
Listen 80
|
||||
<VirtualHost HUB.DOMAIN.TLD:80>
|
||||
ServerName HUB.DOMAIN.TLD
|
||||
@@ -188,26 +188,26 @@ Listen 443
|
||||
|
||||
ServerName HUB.DOMAIN.TLD
|
||||
|
||||
# enable HTTP/2, if available
|
||||
# Enable HTTP/2, if available
|
||||
Protocols h2 http/1.1
|
||||
|
||||
# HTTP Strict Transport Security (mod_headers is required) (63072000 seconds)
|
||||
Header always set Strict-Transport-Security "max-age=63072000"
|
||||
|
||||
# configure SSL
|
||||
# Configure SSL
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/letsencrypt/live/HUB.DOMAIN.TLD/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/HUB.DOMAIN.TLD/privkey.pem
|
||||
SSLOpenSSLConfCmd DHParameters /etc/ssl/certs/dhparam.pem
|
||||
|
||||
# intermediate configuration from ssl-config.mozilla.org (2022-03-03)
|
||||
# Please note, that this configuration might be out-dated - please update it accordingly using https://ssl-config.mozilla.org/
|
||||
# Intermediate configuration from SSL-config.mozilla.org (2022-03-03)
|
||||
# Please note, that this configuration might be outdated - please update it accordingly using https://ssl-config.mozilla.org/
|
||||
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
|
||||
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
SSLHonorCipherOrder off
|
||||
SSLSessionTickets off
|
||||
|
||||
# Use RewriteEngine to handle websocket connection upgrades
|
||||
# Use RewriteEngine to handle WebSocket connection upgrades
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTP:Connection} Upgrade [NC]
|
||||
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
||||
@@ -224,7 +224,7 @@ Listen 443
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
In case of the need to run the jupyterhub under /jhub/ or other location please use the below configurations:
|
||||
In case of the need to run JupyterHub under /jhub/ or another location please use the below configurations:
|
||||
|
||||
- JupyterHub running locally at http://127.0.0.1:8000/jhub/ or other location
|
||||
|
||||
@@ -241,7 +241,7 @@ httpd.conf amendments:
|
||||
jupyterhub_config.py amendments:
|
||||
|
||||
```bash
|
||||
--The public facing URL of the whole JupyterHub application.
|
||||
--This is the address on which the proxy will bind. Sets protocol, ip, base_url
|
||||
--The public-facing URL of the whole JupyterHub application.
|
||||
--This is the address on which the proxy will bind. Sets protocol, IP, base_url
|
||||
c.JupyterHub.bind_url = 'http://127.0.0.1:8000/jhub/'
|
||||
```
|
||||
|
@@ -1,49 +1,43 @@
|
||||
# Configuring user environments
|
||||
|
||||
Deploying JupyterHub means you are providing Jupyter notebook environments for
|
||||
To deploy JupyterHub means you are providing Jupyter notebook environments for
|
||||
multiple users. Often, this includes a desire to configure the user
|
||||
environment in some way.
|
||||
environment in a custom way.
|
||||
|
||||
Since the `jupyterhub-singleuser` server extends the standard Jupyter notebook
|
||||
server, most configuration and documentation that applies to Jupyter Notebook
|
||||
applies to the single-user environments. Configuration of user environments
|
||||
typically does not occur through JupyterHub itself, but rather through system-
|
||||
wide configuration of Jupyter, which is inherited by `jupyterhub-singleuser`.
|
||||
typically does not occur through JupyterHub itself, but rather through the system-wide
|
||||
configuration of Jupyter, which is inherited by `jupyterhub-singleuser`.
|
||||
|
||||
**Tip:** When searching for configuration tips for JupyterHub user
|
||||
environments, try removing JupyterHub from your search because there are a lot
|
||||
more people out there configuring Jupyter than JupyterHub and the
|
||||
configuration is the same.
|
||||
**Tip:** When searching for configuration tips for JupyterHub user environments, you might want to remove JupyterHub from your search because there are a lot more people out there configuring Jupyter than JupyterHub and the configuration is the same.
|
||||
|
||||
This section will focus on user environments, including:
|
||||
This section will focus on user environments, which includes the following:
|
||||
|
||||
- Installing packages
|
||||
- Configuring Jupyter and IPython
|
||||
- Installing kernelspecs
|
||||
- Using containers vs. multi-user hosts
|
||||
- [Installing packages](#installing-packages)
|
||||
- [Configuring Jupyter and IPython](#configuring-jupyter-and-ipython)
|
||||
- [Installing kernelspecs](#installing-kernelspecs)
|
||||
- [Using containers vs. multi-user hosts](#multi-user-hosts-vs-containers)
|
||||
|
||||
## Installing packages
|
||||
|
||||
To make packages available to users, you generally will install packages
|
||||
system-wide or in a shared environment.
|
||||
To make packages available to users, you will typically install packages system-wide or in a shared environment.
|
||||
|
||||
This installation location should always be in the same environment that
|
||||
This installation location should always be in the same environment where
|
||||
`jupyterhub-singleuser` itself is installed in, and must be _readable and
|
||||
executable_ by your users. If you want users to be able to install additional
|
||||
packages, it must also be _writable_ by your users.
|
||||
executable_ by your users. If you want your users to be able to install additional
|
||||
packages, the installation location must also be _writable_ by your users.
|
||||
|
||||
If you are using a standard system Python install, you would use:
|
||||
If you are using a standard Python installation on your system, use the following command:
|
||||
|
||||
```bash
|
||||
sudo python3 -m pip install numpy
|
||||
```
|
||||
|
||||
to install the numpy package in the default system Python 3 environment
|
||||
to install the numpy package in the default Python 3 environment on your system
|
||||
(typically `/usr/local`).
|
||||
|
||||
You may also use conda to install packages. If you do, you should make sure
|
||||
that the conda environment has appropriate permissions for users to be able to
|
||||
run Python code in the env.
|
||||
Alternatively, You may also use conda to install packages. To do this, ensure that the conda environment has appropriate users permissions needed to run Python code in the environment.
|
||||
|
||||
## Configuring Jupyter and IPython
|
||||
|
||||
@@ -51,15 +45,9 @@ run Python code in the env.
|
||||
and [IPython](https://ipython.readthedocs.io/en/stable/development/config.html)
|
||||
have their own configuration systems.
|
||||
|
||||
As a JupyterHub administrator, you will typically want to install and configure
|
||||
environments for all JupyterHub users. For example, you wish for each student in
|
||||
a class to have the same user environment configuration.
|
||||
|
||||
Jupyter and IPython support **"system-wide"** locations for configuration, which
|
||||
is the logical place to put global configuration that you want to affect all
|
||||
users. It's generally more efficient to configure user environments "system-wide",
|
||||
and it's a good idea to avoid creating files in users' home directories.
|
||||
As a JupyterHub administrator, you will typically want to install and configure environments for all JupyterHub users. For example, let's say you wish for each student in a class to have the same user environment configuration.
|
||||
|
||||
Jupyter and IPython support **"system-wide"** locations for configuration, which is the logical place to put global configuration that you want to affect all users. It's generally more efficient to configure user environments "system-wide", and it's a good practice to avoid creating files in the users' home directories.
|
||||
The typical locations for these config files are:
|
||||
|
||||
- **system-wide** in `/etc/{jupyter|ipython}`
|
||||
@@ -67,8 +55,7 @@ The typical locations for these config files are:
|
||||
|
||||
### Example: Enable an extension system-wide
|
||||
|
||||
For example, to enable the `cython` IPython extension for all of your users,
|
||||
create the file `/etc/ipython/ipython_config.py`:
|
||||
For example, to enable the `cython` IPython extension for all of your users, create the file `/etc/ipython/ipython_config.py`:
|
||||
|
||||
```python
|
||||
c.InteractiveShellApp.extensions.append("cython")
|
||||
@@ -77,21 +64,18 @@ c.InteractiveShellApp.extensions.append("cython")
|
||||
### Example: Enable a Jupyter notebook configuration setting for all users
|
||||
|
||||
:::{note}
|
||||
These examples configure the Jupyter ServerApp,
|
||||
which is used by JupyterLab, the default in JupyterHub 2.0.
|
||||
These examples configure the Jupyter ServerApp, which is used by JupyterLab, the default in JupyterHub 2.0.
|
||||
|
||||
If you are using the classing Jupyter Notebook server,
|
||||
the same things should work,
|
||||
with the following substitutions:
|
||||
|
||||
- Where you see `jupyter_server_config`, use `jupyter_notebook_config`
|
||||
- Where you see `NotebookApp`, use `ServerApp`
|
||||
- Search for `jupyter_server_config`, and replace with `jupyter_notebook_config`
|
||||
- Search for `NotebookApp`, and replace with `ServerApp`
|
||||
|
||||
:::
|
||||
|
||||
To enable Jupyter notebook's internal idle-shutdown behavior (requires
|
||||
notebook ≥ 5.4), set the following in the `/etc/jupyter/jupyter_server_config.py`
|
||||
file:
|
||||
To enable Jupyter notebook's internal idle-shutdown behavior (requires notebook ≥ 5.4), set the following in the `/etc/jupyter/jupyter_server_config.py` file:
|
||||
|
||||
```python
|
||||
# shutdown the server after no activity for an hour
|
||||
@@ -104,16 +88,14 @@ c.MappingKernelManager.cull_interval = 2 * 60
|
||||
|
||||
## Installing kernelspecs
|
||||
|
||||
You may have multiple Jupyter kernels installed and want to make sure that
|
||||
they are available to all of your users. This means installing kernelspecs
|
||||
either system-wide (e.g. in /usr/local/) or in the `sys.prefix` of JupyterHub
|
||||
You may have multiple Jupyter kernels installed and want to make sure that they are available to all of your users. This means installing kernelspecs either system-wide (e.g. in /usr/local/) or in the `sys.prefix` of JupyterHub
|
||||
itself.
|
||||
|
||||
Jupyter kernelspec installation is system wide by default, but some kernels
|
||||
Jupyter kernelspec installation is system-wide by default, but some kernels
|
||||
may default to installing kernelspecs in your home directory. These will need
|
||||
to be moved system-wide to ensure that they are accessible.
|
||||
|
||||
You can see where your kernelspecs are with:
|
||||
To see where your kernelspecs are, you can use the following command:
|
||||
|
||||
```bash
|
||||
jupyter kernelspec list
|
||||
@@ -121,8 +103,7 @@ jupyter kernelspec list
|
||||
|
||||
### Example: Installing kernels system-wide
|
||||
|
||||
Assuming I have a Python 2 and Python 3 environment that I want to make
|
||||
sure are available, I can install their specs system-wide (in /usr/local) with:
|
||||
Let's assume that I have a Python 2 and Python 3 environment that I want to make sure are available, I can install their specs **system-wide** (in /usr/local) using the following command:
|
||||
|
||||
```bash
|
||||
/path/to/python3 -m ipykernel install --prefix=/usr/local
|
||||
@@ -141,31 +122,25 @@ How you configure user environments for each category can differ a bit
|
||||
depending on what Spawner you are using.
|
||||
|
||||
The first category is a **shared system (multi-user host)** where
|
||||
each user has a JupyterHub account and a home directory as well as being
|
||||
each user has a JupyterHub account, a home directory as well as being
|
||||
a real system user. In this example, shared configuration and installation
|
||||
must be in a 'system-wide' location, such as `/etc/` or `/usr/local`
|
||||
must be in a 'system-wide' location, such as `/etc/`, or `/usr/local`
|
||||
or a custom prefix such as `/opt/conda`.
|
||||
|
||||
When JupyterHub uses **container-based** Spawners (e.g. KubeSpawner or
|
||||
DockerSpawner), the 'system-wide' environment is really the container image
|
||||
which you are using for users.
|
||||
DockerSpawner), the 'system-wide' environment is really the container image used for users.
|
||||
|
||||
In both cases, you want to _avoid putting configuration in user home
|
||||
directories_ because users can change those configuration settings. Also,
|
||||
home directories typically persist once they are created, so they are
|
||||
difficult for admins to update later.
|
||||
directories_ because users can change those configuration settings. Also, home directories typically persist once they are created, thereby making it difficult for admins to update later.
|
||||
|
||||
## Named servers
|
||||
|
||||
By default, in a JupyterHub deployment each user has exactly one server.
|
||||
By default, in a JupyterHub deployment, each user has one server only.
|
||||
|
||||
JupyterHub can, however, have multiple servers per user.
|
||||
This is most useful in deployments where users can configure the environment
|
||||
in which their server will start (e.g. resource requests on an HPC cluster),
|
||||
so that a given user can have multiple configurations running at the same time,
|
||||
without having to stop and restart their one server.
|
||||
This is mostly useful in deployments where users can configure the environment in which their server will start (e.g. resource requests on an HPC cluster), so that a given user can have multiple configurations running at the same time, without having to stop and restart their own server.
|
||||
|
||||
To allow named servers:
|
||||
To allow named servers, include this code snippet in your config file:
|
||||
|
||||
```python
|
||||
c.JupyterHub.allow_named_servers = True
|
||||
@@ -181,15 +156,15 @@ as well as the admin page:
|
||||

|
||||
|
||||
Named servers can be accessed, created, started, stopped, and deleted
|
||||
from these pages. Activity tracking is now per-server as well.
|
||||
from these pages. Activity tracking is now per server as well.
|
||||
|
||||
The number of named servers per user can be limited by setting a constant value:
|
||||
To limit the number of **named server** per user by setting a constant value, include this code snippet in your config file:
|
||||
|
||||
```python
|
||||
c.JupyterHub.named_server_limit_per_user = 5
|
||||
```
|
||||
|
||||
or a callable/awaitable based on the handler object:
|
||||
Alternatively, to use a callable/awaitable based on the handler object, include this code snippet in your config file:
|
||||
|
||||
```python
|
||||
def named_server_limit_per_user_fn(handler):
|
||||
@@ -207,12 +182,13 @@ If `named_server_limit_per_user` is set to `0`, no limit is enforced.
|
||||
|
||||
(classic-notebook-ui)=
|
||||
|
||||
## Switching back to classic notebook
|
||||
## Switching back to the classic notebook
|
||||
|
||||
By default the single-user server launches JupyterLab,
|
||||
By default, the single-user server launches JupyterLab,
|
||||
which is based on [Jupyter Server][].
|
||||
|
||||
This is the default server when running JupyterHub ≥ 2.0.
|
||||
You can switch to using the legacy Jupyter Notebook server by setting the `JUPYTERHUB_SINGLEUSER_APP` environment variable
|
||||
To switch to using the legacy Jupyter Notebook server, you can set the `JUPYTERHUB_SINGLEUSER_APP` environment variable
|
||||
(in the single-user environment) to:
|
||||
|
||||
```bash
|
||||
@@ -223,19 +199,20 @@ export JUPYTERHUB_SINGLEUSER_APP='notebook.notebookapp.NotebookApp'
|
||||
[jupyter notebook]: https://jupyter-notebook.readthedocs.io
|
||||
|
||||
:::{versionchanged} 2.0
|
||||
JupyterLab is now the default singleuser UI, if available,
|
||||
|
||||
JupyterLab is now the default single-user UI, if available,
|
||||
which is based on the [Jupyter Server][],
|
||||
no longer the legacy [Jupyter Notebook][] server.
|
||||
JupyterHub prior to 2.0 launched the legacy notebook server (`jupyter notebook`),
|
||||
and Jupyter server could be selected by specifying
|
||||
and the Jupyter server could be selected by specifying the following:
|
||||
|
||||
```python
|
||||
# jupyterhub_config.py
|
||||
c.Spawner.cmd = ["jupyter-labhub"]
|
||||
```
|
||||
|
||||
or for an otherwise customized Jupyter Server app,
|
||||
set the environment variable:
|
||||
Alternatively, for an otherwise customized Jupyter Server app,
|
||||
set the environment variable using the following command:
|
||||
|
||||
```bash
|
||||
export JUPYTERHUB_SINGLEUSER_APP='jupyter_server.serverapp.ServerApp'
|
||||
|
@@ -30,19 +30,19 @@ Some relevant points:
|
||||
Here are some key definitions to keep in mind when we are talking about OAuth.
|
||||
You can also read more detail [here](https://www.oauth.com/oauth2-servers/definitions/).
|
||||
|
||||
- **provider** the entity responsible for managing identity and authorization,
|
||||
- **provider**: The entity responsible for managing identity and authorization,
|
||||
always a web server.
|
||||
JupyterHub is _always_ an oauth provider for JupyterHub's components.
|
||||
When OAuthenticator is used, an external service, such as GitHub or KeyCloak, is also an oauth provider.
|
||||
- **client** An entity that requests OAuth **tokens** on a user's behalf,
|
||||
- **client**: An entity that requests OAuth **tokens** on a user's behalf,
|
||||
generally a web server of some kind.
|
||||
OAuth **clients** are services that _delegate_ authentication and/or authorization
|
||||
to an OAuth **provider**.
|
||||
JupyterHub _services_ or single-user _servers_ are OAuth **clients** of the JupyterHub **provider**.
|
||||
When OAuthenticator is used, JupyterHub is itself _also_ an OAuth **client** for the external oauth **provider**, e.g. GitHub.
|
||||
- **browser** A user's web browser, which makes requests and stores things like cookies
|
||||
- **token** The secret value used to represent a user's authorization. This is the final product of the OAuth process.
|
||||
- **code** A short-lived temporary secret that the **client** exchanges
|
||||
- **browser**: A user's web browser, which makes requests and stores things like cookies.
|
||||
- **token**: The secret value used to represent a user's authorization. This is the final product of the OAuth process.
|
||||
- **code**: A short-lived temporary secret that the **client** exchanges
|
||||
for a **token** at the conclusion of oauth,
|
||||
in what's generally called the "oauth callback handler."
|
||||
|
||||
@@ -56,8 +56,8 @@ A single oauth flow generally goes like this:
|
||||
|
||||
1. A **browser** makes an HTTP request to an oauth **client**.
|
||||
2. There are no credentials, so the client _redirects_ the browser to an "authorize" page on the oauth **provider** with some extra information:
|
||||
- the oauth **client id** of the client itself
|
||||
- the **redirect uri** to be redirected back to after completion
|
||||
- the oauth **client id** of the client itself.
|
||||
- the **redirect uri** to be redirected back to after completion.
|
||||
- the **scopes** requested, which the user should be presented with to confirm.
|
||||
This is the "X would like to be able to Y on your behalf. Allow this?" page you see on all the "Login with ..." pages around the Internet.
|
||||
3. During this authorize step,
|
||||
@@ -77,8 +77,8 @@ That's the end of the requests made between the **browser** and the **provider**
|
||||
|
||||
At this point:
|
||||
|
||||
- The browser is authenticated with the _provider_
|
||||
- The user's authorized permissions are recorded in an _oauth code_
|
||||
- The browser is authenticated with the _provider_.
|
||||
- The user's authorized permissions are recorded in an _oauth code_.
|
||||
- The _provider_ knows that the given oauth client's requested permissions have been granted, but the client doesn't know this yet.
|
||||
- All requests so far have been made directly by the browser.
|
||||
No requests have originated at the client or provider.
|
||||
@@ -86,8 +86,8 @@ At this point:
|
||||
### OAuth Client Handles Callback Request
|
||||
|
||||
Now we get to finish the OAuth process.
|
||||
Let's dig into what the oauth client does when it handles
|
||||
the oauth callback request with the
|
||||
Let's dig into what the OAuth client does when it handles
|
||||
the OAuth callback request.
|
||||
|
||||
- The OAuth client receives the _code_ and makes an API request to the _provider_ to exchange the code for a real _token_.
|
||||
This is the first direct request between the OAuth _client_ and the _provider_.
|
||||
@@ -113,24 +113,24 @@ So that's _one_ OAuth process.
|
||||
|
||||
## Full sequence of OAuth in JupyterHub
|
||||
|
||||
Let's go through the above oauth process in JupyterHub,
|
||||
Let's go through the above OAuth process in JupyterHub,
|
||||
with specific examples of each HTTP request and what information is contained.
|
||||
For bonus points, we are using the double-oauth example of JupyterHub configured with GitHubOAuthenticator.
|
||||
For bonus points, we are using the double-OAuth example of JupyterHub configured with GitHubOAuthenticator.
|
||||
|
||||
To disambiguate, we will call the OAuth process where JupyterHub is the **provider** "internal oauth,"
|
||||
and the one with JupyterHub as a **client** "external oauth."
|
||||
To disambiguate, we will call the OAuth process where JupyterHub is the **provider** "internal OAuth,"
|
||||
and the one with JupyterHub as a **client** "external OAuth."
|
||||
|
||||
Our starting point:
|
||||
|
||||
- a user's single-user server is running. Let's call them `danez`
|
||||
- jupyterhub is running with GitHub as an oauth provider (this means two full instances of oauth),
|
||||
- Danez has a fresh browser session with no cookies yet
|
||||
- Jupyterhub is running with GitHub as an OAuth provider (this means two full instances of OAuth),
|
||||
- Danez has a fresh browser session with no cookies yet.
|
||||
|
||||
First request:
|
||||
|
||||
- browser->single-user server running JupyterLab or Jupyter Classic
|
||||
- `GET /user/danez/notebooks/mynotebook.ipynb`
|
||||
- no credentials, so single-user server (as an oauth **client**) starts internal oauth process with JupyterHub (the **provider**)
|
||||
- no credentials, so single-user server (as an OAuth **client**) starts internal OAuth process with JupyterHub (the **provider**)
|
||||
- response: 302 redirect -> `/hub/api/oauth2/authorize`
|
||||
with:
|
||||
- client-id=`jupyterhub-user-danez`
|
||||
@@ -138,9 +138,9 @@ First request:
|
||||
|
||||
Second request, following redirect:
|
||||
|
||||
- browser->jupyterhub
|
||||
- browser->JupyterHub
|
||||
- `GET /hub/api/oauth2/authorize`
|
||||
- no credentials, so jupyterhub starts external oauth process _with GitHub_
|
||||
- no credentials, so JupyterHub starts external OAuth process _with GitHub_
|
||||
- response: 302 redirect -> `https://github.com/login/oauth/authorize`
|
||||
with:
|
||||
- client-id=`jupyterhub-client-uuid`
|
||||
@@ -154,8 +154,8 @@ c.JupyterHub.authenticator_class = 'github'
|
||||
```
|
||||
|
||||
That means authenticating a request to the Hub itself starts
|
||||
a _second_, external oauth process with GitHub as a provider.
|
||||
This external oauth process is optional, though.
|
||||
a _second_, external OAuth process with GitHub as a provider.
|
||||
This external OAuth process is optional, though.
|
||||
If you were using the default username+password PAMAuthenticator,
|
||||
this redirect would have been to `/hub/login` instead, to present the user
|
||||
with a login form.
|
||||
@@ -171,7 +171,7 @@ Here, GitHub prompts for login and asks for confirmation of authorization
|
||||
After successful authorization
|
||||
(either by looking up a pre-existing authorization,
|
||||
or recording it via form submission)
|
||||
GitHub issues an **oauth code** and redirects to `/hub/oauth_callback?code=github-code`
|
||||
GitHub issues an **OAuth code** and redirects to `/hub/oauth_callback?code=github-code`
|
||||
|
||||
Next request:
|
||||
|
||||
@@ -194,9 +194,9 @@ The second:
|
||||
- request made with access **token** in the `Authorization` header
|
||||
- response is the user model, including username, email, etc.
|
||||
|
||||
Now the external oauth callback request completes with:
|
||||
Now the external OAuth callback request completes with:
|
||||
|
||||
- set cookie on `/hub/` path, recording jupyterhub authentication so we don't need to do external oauth with GitHub again for a while
|
||||
- set cookie on `/hub/` path, recording jupyterhub authentication so we don't need to do external OAuth with GitHub again for a while
|
||||
- redirect -> `/hub/api/oauth2/authorize`
|
||||
|
||||
🎉 At this point, we have completed our first OAuth flow! 🎉
|
||||
@@ -211,14 +211,14 @@ Now, we get our first repeated request:
|
||||
2. automatically accepts authorization (shortcut taken when a user is visiting their own server)
|
||||
- redirect -> `/user/danez/oauth_callback?code=jupyterhub-code`
|
||||
|
||||
Here, we start the same oauth callback process as before, but at Danez's single-user server for the _internal_ oauth
|
||||
Here, we start the same OAuth callback process as before, but at Danez's single-user server for the _internal_ OAuth.
|
||||
|
||||
- browser->single-user server
|
||||
- `GET /user/danez/oauth_callback`
|
||||
|
||||
(in handler)
|
||||
|
||||
Inside the internal oauth callback handler,
|
||||
Inside the internal OAuth callback handler,
|
||||
Danez's server makes two API requests to JupyterHub:
|
||||
|
||||
The first:
|
||||
|
@@ -4,9 +4,9 @@ A [Spawner][] starts each single-user notebook server.
|
||||
The Spawner represents an abstract interface to a process,
|
||||
and a custom Spawner needs to be able to take three actions:
|
||||
|
||||
- start the process
|
||||
- poll whether the process is still running
|
||||
- stop the process
|
||||
- start a process
|
||||
- poll whether a process is still running
|
||||
- stop a process
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -15,9 +15,9 @@ Some examples include:
|
||||
|
||||
- [DockerSpawner](https://github.com/jupyterhub/dockerspawner) for spawning user servers in Docker containers
|
||||
- `dockerspawner.DockerSpawner` for spawning identical Docker containers for
|
||||
each users
|
||||
each user
|
||||
- `dockerspawner.SystemUserSpawner` for spawning Docker containers with an
|
||||
environment and home directory for each users
|
||||
environment and home directory for each user
|
||||
- both `DockerSpawner` and `SystemUserSpawner` also work with Docker Swarm for
|
||||
launching containers on remote machines
|
||||
- [SudoSpawner](https://github.com/jupyterhub/sudospawner) enables JupyterHub to
|
||||
@@ -34,7 +34,7 @@ Some examples include:
|
||||
|
||||
### Spawner.start
|
||||
|
||||
`Spawner.start` should start the single-user server for a single user.
|
||||
`Spawner.start` should start a single-user server for a single user.
|
||||
Information about the user can be retrieved from `self.user`,
|
||||
an object encapsulating the user's name, authentication, and server info.
|
||||
|
||||
@@ -69,13 +69,13 @@ via relaxing the `Spawner.start_timeout` config value.
|
||||
|
||||
#### Note on IPs and ports
|
||||
|
||||
`Spawner.ip` and `Spawner.port` attributes set the _bind_ url,
|
||||
`Spawner.ip` and `Spawner.port` attributes set the _bind_ URL,
|
||||
which the single-user server should listen on
|
||||
(passed to the single-user process via the `JUPYTERHUB_SERVICE_URL` environment variable).
|
||||
The _return_ value is the ip and port (or full url) the Hub should _connect to_.
|
||||
The _return_ value is the IP and port (or full URL) the Hub should _connect to_.
|
||||
These are not necessarily the same, and usually won't be in any Spawner that works with remote resources or containers.
|
||||
|
||||
The default for Spawner.ip, and Spawner.port is `127.0.0.1:{random}`,
|
||||
The default for `Spawner.ip`, and `Spawner.port` is `127.0.0.1:{random}`,
|
||||
which is appropriate for Spawners that launch local processes,
|
||||
where everything is on localhost and each server needs its own port.
|
||||
For remote or container Spawners, it will often make sense to use a different value,
|
||||
@@ -111,7 +111,7 @@ class MySpawner(Spawner):
|
||||
|
||||
#### Exception handling
|
||||
|
||||
When `Spawner.start` raises an Exception, a message can be passed on to the user via the exception via a `.jupyterhub_html_message` or `.jupyterhub_message` attribute.
|
||||
When `Spawner.start` raises an Exception, a message can be passed on to the user via the exception using a `.jupyterhub_html_message` or `.jupyterhub_message` attribute.
|
||||
|
||||
When the Exception has a `.jupyterhub_html_message` attribute, it will be rendered as HTML to the user.
|
||||
|
||||
@@ -121,11 +121,11 @@ If both attributes are not present, the Exception will be shown to the user as u
|
||||
|
||||
### Spawner.poll
|
||||
|
||||
`Spawner.poll` should check if the spawner is still running.
|
||||
`Spawner.poll` checks if the spawner is still running.
|
||||
It should return `None` if it is still running,
|
||||
and an integer exit status, otherwise.
|
||||
|
||||
For the local process case, `Spawner.poll` uses `os.kill(PID, 0)`
|
||||
In the case of local processes, `Spawner.poll` uses `os.kill(PID, 0)`
|
||||
to check if the local process is still running. On Windows, it uses `psutil.pid_exists`.
|
||||
|
||||
### Spawner.stop
|
||||
@@ -141,7 +141,7 @@ A JSON-able dictionary of state can be used to store persisted information.
|
||||
|
||||
Unlike start, stop, and poll methods, the state methods must not be coroutines.
|
||||
|
||||
For the single-process case, the Spawner state is only the process ID of the server:
|
||||
In the case of single processes, the Spawner state is only the process ID of the server:
|
||||
|
||||
```python
|
||||
def get_state(self):
|
||||
@@ -267,8 +267,8 @@ Spawners mainly do one thing: launch a command in an environment.
|
||||
|
||||
The command-line is constructed from user configuration:
|
||||
|
||||
- Spawner.cmd (default: `['jupterhub-singleuser']`)
|
||||
- Spawner.args (cli args to pass to the cmd, default: empty)
|
||||
- Spawner.cmd (default: `['jupyterhub-singleuser']`)
|
||||
- Spawner.args (CLI args to pass to the cmd, default: empty)
|
||||
|
||||
where the configuration:
|
||||
|
||||
@@ -283,7 +283,7 @@ would result in spawning the command:
|
||||
my-singleuser-wrapper --debug --flag
|
||||
```
|
||||
|
||||
The `Spawner.get_args()` method is how Spawner.args is accessed,
|
||||
The `Spawner.get_args()` method is how `Spawner.args` is accessed,
|
||||
and can be used by Spawners to customize/extend user-provided arguments.
|
||||
|
||||
Prior to 2.0, JupyterHub unconditionally added certain options _if specified_ to the command-line,
|
||||
@@ -297,36 +297,36 @@ Additional variables can be specified via the `Spawner.environment` configuratio
|
||||
|
||||
The process environment is returned by `Spawner.get_env`, which specifies the following environment variables:
|
||||
|
||||
- JUPYTERHUB*SERVICE_URL - the \_bind* url where the server should launch its http server (`http://127.0.0.1:12345`).
|
||||
This includes Spawner.ip and Spawner.port; _new in 2.0, prior to 2.0 ip,port were on the command-line and only if specified_
|
||||
- JUPYTERHUB*SERVICE_URL - the \_bind* URL where the server should launch its HTTP server (`http://127.0.0.1:12345`).
|
||||
This includes `Spawner.ip` and `Spawner.port`; _new in 2.0, prior to 2.0 IP, port were on the command-line and only if specified_
|
||||
- JUPYTERHUB_SERVICE_PREFIX - the URL prefix the service will run on (e.g. `/user/name/`)
|
||||
- JUPYTERHUB_USER - the JupyterHub user's username
|
||||
- JUPYTERHUB_SERVER_NAME - the server's name, if using named servers (default server has an empty name)
|
||||
- JUPYTERHUB_API_URL - the full url for the JupyterHub API (http://17.0.0.1:8001/hub/api)
|
||||
- JUPYTERHUB_BASE_URL - the base url of the whole jupyterhub deployment, i.e. the bit before `hub/` or `user/`,
|
||||
as set by c.JupyterHub.base_url (default: `/`)
|
||||
- JUPYTERHUB_API_URL - the full URL for the JupyterHub API (http://17.0.0.1:8001/hub/api)
|
||||
- JUPYTERHUB_BASE_URL - the base URL of the whole jupyterhub deployment, i.e. the bit before `hub/` or `user/`,
|
||||
as set by `c.JupyterHub.base_url` (default: `/`)
|
||||
- JUPYTERHUB_API_TOKEN - the API token the server can use to make requests to the Hub.
|
||||
This is also the OAuth client secret.
|
||||
- JUPYTERHUB_CLIENT_ID - the OAuth client ID for authenticating visitors.
|
||||
- JUPYTERHUB_OAUTH_CALLBACK_URL - the callback URL to use in oauth, typically `/user/:name/oauth_callback`
|
||||
- JUPYTERHUB_OAUTH_CALLBACK_URL - the callback URL to use in OAuth, typically `/user/:name/oauth_callback`
|
||||
- JUPYTERHUB_OAUTH_ACCESS_SCOPES - the scopes required to access the server (called JUPYTERHUB_OAUTH_SCOPES prior to 3.0)
|
||||
- JUPYTERHUB_OAUTH_CLIENT_ALLOWED_SCOPES - the scopes the service is allowed to request.
|
||||
If no scopes are requested explicitly, these scopes will be requested.
|
||||
|
||||
Optional environment variables, depending on configuration:
|
||||
|
||||
- JUPYTERHUB*SSL*[KEYFILE|CERTFILE|CLIENT_CI] - SSL configuration, when internal_ssl is enabled
|
||||
- JUPYTERHUB_ROOT_DIR - the root directory of the server (notebook directory), when Spawner.notebook_dir is defined (new in 2.0)
|
||||
- JUPYTERHUB_DEFAULT_URL - the default URL for the server (for redirects from /user/:name/),
|
||||
if Spawner.default_url is defined
|
||||
(new in 2.0, previously passed via cli)
|
||||
- JUPYTERHUB_DEBUG=1 - generic debug flag, sets maximum log level when Spawner.debug is True
|
||||
(new in 2.0, previously passed via cli)
|
||||
- JUPYTERHUB*SSL*[KEYFILE|CERTFILE|CLIENT_CI] - SSL configuration, when `internal_ssl` is enabled
|
||||
- JUPYTERHUB_ROOT_DIR - the root directory of the server (notebook directory), when `Spawner.notebook_dir` is defined (new in 2.0)
|
||||
- JUPYTERHUB_DEFAULT_URL - the default URL for the server (for redirects from `/user/:name/`),
|
||||
if `Spawner.default_url` is defined
|
||||
(new in 2.0, previously passed via CLI)
|
||||
- JUPYTERHUB_DEBUG=1 - generic debug flag, sets maximum log level when `Spawner.debug` is True
|
||||
(new in 2.0, previously passed via CLI)
|
||||
- JUPYTERHUB_DISABLE_USER_CONFIG=1 - disable loading user config,
|
||||
sets maximum log level when Spawner.debug is True (new in 2.0,
|
||||
previously passed via cli)
|
||||
sets maximum log level when `Spawner.debug` is True (new in 2.0,
|
||||
previously passed via CLI)
|
||||
|
||||
- JUPYTERHUB*[MEM|CPU]*[LIMIT_GUARANTEE] - the values of cpu and memory limits and guarantees.
|
||||
- JUPYTERHUB*[MEM|CPU]*[LIMIT_GUARANTEE] - the values of CPU and memory limits and guarantees.
|
||||
These are not expected to be enforced by the process,
|
||||
but are made available as a hint,
|
||||
e.g. for resource monitoring extensions.
|
||||
@@ -338,7 +338,7 @@ guarantees on resources, such as CPU and memory. To provide a consistent
|
||||
experience for sysadmins and users, we provide a standard way to set and
|
||||
discover these resource limits and guarantees, such as for memory and CPU.
|
||||
For the limits and guarantees to be useful, **the spawner must implement
|
||||
support for them**. For example, LocalProcessSpawner, the default
|
||||
support for them**. For example, `LocalProcessSpawner`, the default
|
||||
spawner, does not support limits and guarantees. One of the spawners
|
||||
that supports limits and guarantees is the `systemdspawner`.
|
||||
|
||||
@@ -367,7 +367,7 @@ limits or guarantees are provided, and no environment values are set.
|
||||
`c.Spawner.cpu_limit`: In supported spawners, you can set
|
||||
`c.Spawner.cpu_limit` to limit the total number of cpu-cores that a
|
||||
single-user notebook server can use. These can be fractional - `0.5` means 50%
|
||||
of one CPU core, `4.0` is 4 cpu-cores, etc. This value is also set in the
|
||||
of one CPU core, `4.0` is 4 CPU-cores, etc. This value is also set in the
|
||||
single-user notebook server's environment variable `CPU_LIMIT`. The limit does
|
||||
not claim that you will be able to use all the CPU up to your limit as other
|
||||
higher priority applications might be taking up CPU.
|
||||
|
@@ -84,5 +84,5 @@ template (for example, `login.html`) with:
|
||||
```
|
||||
|
||||
Extending `page.html` puts the message on all pages, but note that
|
||||
extending `page.html` take precedence over an extension of a specific
|
||||
extending `page.html` takes precedence over an extension of a specific
|
||||
page (unlike the variable-based approach above).
|
||||
|
@@ -2,13 +2,13 @@
|
||||
|
||||
This document describes how JupyterHub routes requests.
|
||||
|
||||
This does not include the [REST API](./rest.md) urls.
|
||||
This does not include the [REST API](./rest.md) URLs.
|
||||
|
||||
In general, all URLs can be prefixed with `c.JupyterHub.base_url` to
|
||||
run the whole JupyterHub application on a prefix.
|
||||
|
||||
All authenticated handlers redirect to `/hub/login` to login users
|
||||
prior to being redirected back to the originating page.
|
||||
All authenticated handlers redirect to `/hub/login` to log-in users
|
||||
before being redirected back to the originating page.
|
||||
The returned request should preserve all query parameters.
|
||||
|
||||
## `/`
|
||||
@@ -25,12 +25,12 @@ This is an authenticated URL.
|
||||
|
||||
This handler redirects users to the default URL of the application,
|
||||
which defaults to the user's default server.
|
||||
That is, it redirects to `/hub/spawn` if the user's server is not running,
|
||||
or the server itself (`/user/:name`) if the server is running.
|
||||
That is, the handler redirects to `/hub/spawn` if the user's server is not running,
|
||||
or to the server itself (`/user/:name`) if the server is running.
|
||||
|
||||
This default url behavior can be customized in two ways:
|
||||
This default URL behavior can be customized in two ways:
|
||||
|
||||
To redirect users to the JupyterHub home page (`/hub/home`)
|
||||
First, to redirect users to the JupyterHub home page (`/hub/home`)
|
||||
instead of spawning their server,
|
||||
set `redirect_to_server` to False:
|
||||
|
||||
@@ -40,7 +40,7 @@ c.JupyterHub.redirect_to_server = False
|
||||
|
||||
This might be useful if you have a Hub where you expect
|
||||
users to be managing multiple server configurations
|
||||
and automatic spawning is not desirable.
|
||||
but automatic spawning is not desirable.
|
||||
|
||||
Second, you can customise the landing page to any page you like,
|
||||
such as a custom service you have deployed e.g. with course information:
|
||||
@@ -57,7 +57,7 @@ By default, the Hub home page has just one or two buttons
|
||||
for starting and stopping the user's server.
|
||||
|
||||
If named servers are enabled, there will be some additional
|
||||
tools for management of named servers.
|
||||
tools for management of the named servers.
|
||||
|
||||
_Version added: 1.0_ named server UI is new in 1.0.
|
||||
|
||||
@@ -65,34 +65,34 @@ _Version added: 1.0_ named server UI is new in 1.0.
|
||||
|
||||
This is the JupyterHub login page.
|
||||
If you have a form-based username+password login,
|
||||
such as the default PAMAuthenticator,
|
||||
such as the default [PAMAuthenticator](https://en.wikipedia.org/wiki/Pluggable_authentication_module),
|
||||
this page will render the login form.
|
||||
|
||||

|
||||
|
||||
If login is handled by an external service,
|
||||
e.g. with OAuth, this page will have a button,
|
||||
declaring "Login with ..." which users can click
|
||||
to login with the chosen service.
|
||||
declaring "Log in with ..." which users can click
|
||||
to log in with the chosen service.
|
||||
|
||||

|
||||
|
||||
If you want to skip the user-interaction to initiate logging in
|
||||
via the button, you can set
|
||||
If you want to skip the user interaction and initiate login
|
||||
via the button, you can set:
|
||||
|
||||
```python
|
||||
c.Authenticator.auto_login = True
|
||||
```
|
||||
|
||||
This can be useful when the user is "already logged in" via some mechanism,
|
||||
but a handshake via redirects is necessary to complete the authentication with JupyterHub.
|
||||
This can be useful when the user is "already logged in" via some mechanism.
|
||||
However, a handshake via `redirects` is necessary to complete the authentication with JupyterHub.
|
||||
|
||||
## `/hub/logout`
|
||||
|
||||
Visiting `/hub/logout` clears cookies from the current browser.
|
||||
Visiting `/hub/logout` clears [cookies](https://en.wikipedia.org/wiki/HTTP_cookie) from the current browser.
|
||||
Note that **logging out does not stop a user's server(s)** by default.
|
||||
|
||||
If you would like to shutdown user servers on logout,
|
||||
If you would like to shut down user servers on logout,
|
||||
you can enable this behavior with:
|
||||
|
||||
```python
|
||||
@@ -105,8 +105,8 @@ does not mean the user is no longer actively using their server from another mac
|
||||
## `/user/:username[/:servername]`
|
||||
|
||||
If a user's server is running, this URL is handled by the user's given server,
|
||||
not the Hub.
|
||||
The username is the first part and, if using named servers,
|
||||
not by the Hub.
|
||||
The username is the first part, and if using named servers,
|
||||
the server name is the second part.
|
||||
|
||||
If the user's server is _not_ running, this will be redirected to `/hub/user/:username/...`
|
||||
@@ -120,11 +120,11 @@ if the specified server were running).
|
||||
Handling this URL is the most complicated condition in JupyterHub,
|
||||
because there can be many states:
|
||||
|
||||
1. server is not active
|
||||
1. the server is not active
|
||||
a. user matches
|
||||
b. user doesn't match
|
||||
2. server is ready
|
||||
3. server is pending, but not ready
|
||||
2. the server is ready
|
||||
3. the server is pending, but not ready
|
||||
|
||||
If the server is pending spawn,
|
||||
the browser will be redirected to `/hub/spawn-pending/:username/:servername`
|
||||
@@ -140,39 +140,37 @@ Some checks are performed and a delay is added before redirecting back to `/user
|
||||
If something is really wrong, this can result in a redirect loop.
|
||||
|
||||
Visiting this page will never result in triggering the spawn of servers
|
||||
without additional user action (i.e. clicking the link on the page)
|
||||
without additional user action (i.e. clicking the link on the page).
|
||||
|
||||

|
||||
|
||||
_Version changed: 1.0_
|
||||
|
||||
Prior to 1.0, this URL itself was responsible for spawning servers,
|
||||
and served the progress page if it was pending,
|
||||
redirected to running servers, and
|
||||
This was useful because it made sure that requested servers were restarted after they stopped,
|
||||
but could also be harmful because unused servers would continuously be restarted if e.g.
|
||||
an idle JupyterLab frontend were open pointed at it,
|
||||
which constantly makes polling requests.
|
||||
Prior to 1.0, this URL itself was responsible for spawning servers.
|
||||
If the progress page was pending, the URL redirected it to running servers.
|
||||
This was useful because it made sure that the requested servers were restarted after they stopped.
|
||||
However, it could also be harmful because unused servers would continuously be restarted if e.g.
|
||||
an idle JupyterLab frontend that constantly makes polling requests was openly pointed at it.
|
||||
|
||||
### Special handling of API requests
|
||||
|
||||
Requests to `/user/:username[/:servername]/api/...` are assumed to be
|
||||
from applications connected to stopped servers.
|
||||
These are failed with 503 and an informative JSON error message
|
||||
indicating how to spawn the server.
|
||||
This is meant to help applications such as JupyterLab
|
||||
These requests fail with a `503` status code and an informative JSON error message
|
||||
that indicates how to spawn the server.
|
||||
This is meant to help applications such as JupyterLab,
|
||||
that are connected to a server that has stopped.
|
||||
|
||||
_Version changed: 1.0_
|
||||
|
||||
JupyterHub 0.9 failed these API requests with status 404,
|
||||
but 1.0 uses 503.
|
||||
JupyterHub version 0.9 failed these API requests with status `404`,
|
||||
but version 1.0 uses 503.
|
||||
|
||||
## `/user-redirect/...`
|
||||
|
||||
This URL is for sharing a URL that will redirect a user
|
||||
The `/user-redirect/...` URL is for sharing a URL that will redirect a user
|
||||
to a path on their own default server.
|
||||
This is useful when users have the same file at the same URL on their servers,
|
||||
This is useful when different users have the same file at the same URL on their servers,
|
||||
and you want a single link to give to any user that will open that file on their server.
|
||||
|
||||
e.g. a link to `/user-redirect/notebooks/Index.ipynb`
|
||||
@@ -194,7 +192,7 @@ that is intended to make it possible.
|
||||
### `/hub/spawn[/:username[/:servername]]`
|
||||
|
||||
Requesting `/hub/spawn` will spawn the default server for the current user.
|
||||
If `username` and optionally `servername` are specified,
|
||||
If the `username` and optionally `servername` are specified,
|
||||
then the specified server for the specified user will be spawned.
|
||||
Once spawn has been requested,
|
||||
the browser is redirected to `/hub/spawn-pending/...`.
|
||||
@@ -207,7 +205,7 @@ and a POST request will trigger the actual spawn and redirect.
|
||||
|
||||
_Version added: 1.0_
|
||||
|
||||
1.0 adds the ability to specify username and servername.
|
||||
1.0 adds the ability to specify `username` and `servername`.
|
||||
Prior to 1.0, only `/hub/spawn` was recognized for the default server.
|
||||
|
||||
_Version changed: 1.0_
|
||||
@@ -247,7 +245,7 @@ against the [JupyterHub REST API](./rest.md).
|
||||
|
||||
Administrators can take various administrative actions from this page:
|
||||
|
||||
1. add/remove users
|
||||
2. grant admin privileges
|
||||
3. start/stop user servers
|
||||
4. shutdown JupyterHub itself
|
||||
- add/remove users
|
||||
- grant admin privileges
|
||||
- start/stop user servers
|
||||
- shutdown JupyterHub itself
|
||||
|
@@ -5,7 +5,7 @@ The **Security Overview** section helps you learn about:
|
||||
- the design of JupyterHub with respect to web security
|
||||
- the semi-trusted user
|
||||
- the available mitigations to protect untrusted users from each other
|
||||
- the value of periodic security audits.
|
||||
- the value of periodic security audits
|
||||
|
||||
This overview also helps you obtain a deeper understanding of how JupyterHub
|
||||
works.
|
||||
@@ -16,9 +16,9 @@ JupyterHub is designed to be a _simple multi-user server for modestly sized
|
||||
groups_ of **semi-trusted** users. While the design reflects serving semi-trusted
|
||||
users, JupyterHub is not necessarily unsuitable for serving **untrusted** users.
|
||||
|
||||
Using JupyterHub with **untrusted** users does mean more work by the
|
||||
Using JupyterHub with **untrusted** users does mean more work for the
|
||||
administrator. Much care is required to secure a Hub, with extra caution on
|
||||
protecting users from each other as the Hub is serving untrusted users.
|
||||
protecting users from each other, since the Hub serves untrusted users.
|
||||
|
||||
One aspect of JupyterHub's _design simplicity_ for **semi-trusted** users is that
|
||||
the Hub and single-user servers are placed in a _single domain_, behind a
|
||||
@@ -32,7 +32,7 @@ servers) as a single website (i.e. single domain).
|
||||
|
||||
To protect users from each other, a user must **never** be able to write arbitrary
|
||||
HTML and serve it to another user on the Hub's domain. JupyterHub's
|
||||
authentication setup prevents a user writing arbitrary HTML and serving it to
|
||||
authentication setup prevents a user from writing arbitrary HTML and serving it to
|
||||
another user because only the owner of a given single-user notebook server is
|
||||
allowed to view user-authored pages served by the given single-user notebook
|
||||
server.
|
||||
@@ -47,7 +47,7 @@ ensure that:
|
||||
- If the `PATH` is used to resolve the single-user executable (instead of
|
||||
using an absolute path), a user **may not** create new files in any `PATH`
|
||||
directory that precedes the directory containing `jupyterhub-singleuser`.
|
||||
- A user may not modify environment variables (e.g. PATH, PYTHONPATH) for
|
||||
- A user may not modify environment variables (e.g. `PATH`, `PYTHONPATH`) for
|
||||
their single-user server.
|
||||
- A user **may not** modify the configuration of the notebook server
|
||||
(the `~/.jupyter` or `JUPYTER_CONFIG_DIR` directory).
|
||||
@@ -58,7 +58,7 @@ If any additional services are run on the same domain as the Hub, the services
|
||||
|
||||
## Mitigate security issues
|
||||
|
||||
Several approaches to mitigating these issues with configuration
|
||||
The several approaches to mitigating security issues with configuration
|
||||
options provided by JupyterHub include:
|
||||
|
||||
### Enable subdomains
|
||||
@@ -76,10 +76,10 @@ resolves the cross-site issues.
|
||||
|
||||
### Disable user config
|
||||
|
||||
If subdomains are not available or not desirable, JupyterHub provides a
|
||||
If subdomains are unavailable or undesirable, JupyterHub provides a
|
||||
configuration option `Spawner.disable_user_config`, which can be set to prevent
|
||||
the user-owned configuration files from being loaded. After implementing this
|
||||
option, PATHs and package installation and PATHs are the other things that the
|
||||
option, `PATH`s and package installation are the other things that the
|
||||
admin must enforce.
|
||||
|
||||
### Prevent spawners from evaluating shell configuration files
|
||||
@@ -101,8 +101,8 @@ pose additional risk to the web application's security.
|
||||
|
||||
### Encrypt internal connections with SSL/TLS
|
||||
|
||||
By default, all communication on the server, between the proxy, hub, and single
|
||||
-user notebooks is performed unencrypted. Setting the `internal_ssl` flag in
|
||||
By default, all communications on the server, between the proxy, hub, and single
|
||||
-user notebooks are performed unencrypted. Setting the `internal_ssl` flag in
|
||||
`jupyterhub_config.py` secures the aforementioned routes. Turning this
|
||||
feature on does require that the enabled `Spawner` can use the certificates
|
||||
generated by the `Hub` (the default `LocalProcessSpawner` can, for instance).
|
||||
@@ -119,8 +119,8 @@ extend to securing the `tcp` sockets as well.
|
||||
## Security audits
|
||||
|
||||
We recommend that you do periodic reviews of your deployment's security. It's
|
||||
good practice to keep JupyterHub, configurable-http-proxy, and nodejs
|
||||
versions up to date.
|
||||
good practice to keep [JupyterHub](https://readthedocs.org/projects/jupyterhub/), [configurable-http-proxy][], and [nodejs
|
||||
versions](https://github.com/nodejs/Release) up to date.
|
||||
|
||||
A handy website for testing your deployment is
|
||||
[Qualsys' SSL analyzer tool](https://www.ssllabs.com/ssltest/analyze.html).
|
||||
@@ -129,7 +129,7 @@ A handy website for testing your deployment is
|
||||
|
||||
## Vulnerability reporting
|
||||
|
||||
If you believe you’ve found a security vulnerability in JupyterHub, or any
|
||||
If you believe you have found a security vulnerability in JupyterHub, or any
|
||||
Jupyter project, please report it to
|
||||
[security@ipython.org](mailto:security@ipython.org). If you prefer to encrypt
|
||||
your security reports, you can use [this PGP public
|
||||
|
Reference in New Issue
Block a user