Photo by Smart on Unsplash

Jupyter notebook provides an interactive web application to execute your codes in language of your preference ( at the moment it supports up to 40+ programming languages). Even though Jupyter notebooks can be shared with multiple users, they are single user application by nature (and rightly so to avoid massive confusions ). However there can be cases that you want to share your notebook to other users so they can continue working from where you left off.

By default Jupyter notebook servers can be secured via password or an access token generated by the server itself. However it cannot provide user level authentication with existing authentication services.

Jupyter team have developed Jupyterhub to resolve these issues as Jupyterhub is able to provide authenticated , centralized and containerized deployments of notebook servers. This is only a little demonstration of Jupyter notebooks’ potential use case in real life.

Student Teacher Eco-system

Wouldn’t it be great if a teacher can publish a programming assignment through Jupyter notebook? You only have to create the assignment notebook as shown below and send it to students.

Example of a programming assignment on Jupyter Notebook

Had ever your life been that much easy ??? Life is supposed to be hard. These technical stuff is no exception.

problems in the practical implementation

authenticating user ( how can i make sure only my students have access to the assignment? )

sending link of the notebook ( do i send the same link to all the students? )

answering the assignment ( can everyone work on a single notebook at the same time? )

Jupyterhub for the rescue

JupyterHub is the best way to serve Jupyter notebook for multiple users. It is able to spawn multiple instances of single user notebook server. It has an authentication class which allows users to be authenticated before using the notebook. And the best thing with this authentication class is, it is

highly customizable to match your needs. At the moment it has support for, PAM (native Linux), LDAP (open LDAP, Active Directory) and OAuthenticator which supports popular authenticating services (Auth0,GitHub,Google etc).

See the full list at Jupyterhub authenticators list.

Note : Refer to Jupyterhub official documentation for installation and server startup. Make sure you generate jupyterhub_config.py file and give the configuration file path when starting up the hub-server.

So how Jupyterhub will handle the issues mentioned in our student-teacher Eco-system ? We will look into them one by one.

Authentication with LDAP

Assume the institute which students study uses LDAP authentication in their student management system. It would be great to have the same credentials to authenticate the students into Jupyterhub without any additional registration. ldapauthenticator is a plugin which can be integrated into JupyterHub to provide LDAP authentication from an openLDAP or Active Directory(AD) server. For this particular scenario we will use openLDAP server hosted free for testing purposes.

Note : Refer to ldapauthenticator official documentation for installation.

server : ldap.forumsys.com

Port: 389

Your jupyterhub_config.py file should be configured as following.

Jupyterhub Config file with LDAP configurations

Tip : You can check whether you can connect and get the correct results from LDAP using the either ldap3 (python API) or using ldapsearch in your Linux command line.

Once you finish the configurations and restart the jupyterhub with the correct configuration file, you will be able to authenticate your user with remote LDAP server. Try login with username : riemann, password : password.

Whoops !!! As always you will not succeed at once.

It is highly likely that you will get an error message close to this on the terminal.

File “/usr/lib/python3.4/site-packages/jupyterhub/spawner.py”, line 439, in user_env

home = pwd.getpwnam(self.user.name).pw_dir

KeyError: ‘getpwnam(): name not found: riemann’

If you closely examine the logs you will see that use authentication has completed successfully but the spawning process has failed with an exception. This happens when jupyterhub default system spawner tries to find the authenticated user “riemann” in the Linux users list ( /home/riemann/). Though LDAPAuthenticator can authenticate users, it cannot create users in the system for you. Create a system user named riemann and restart the hub server and log in. You will redirected to the /home/riemann by default which is the same as your spawned notebook server root.

Jupyter notebook server has started on /home/riemann

Creating new users per entry in LDAP server ( in our case one account per student in the classroom) is a tedious task which requires more effort. What if we only have to enter the credentials and a separate server will auto-magically created for the user? If the server is isolated we can ensure that one student’s work will not affect another.

Using Dockerspawner

DockerSpawner enables JupyterHub to spawn single user notebook servers in Docker containers. Having a separate docker container for each user ensures complete isolation of each user’s work. Once a docker container is spawned we only have to put the assignment notebook file into the container.

Note : Refer to DockerSpawner official documentation for installation.

Let Jupyterhub know that you are using DockerSpawner by adding these lines to jupyterhub_config.py

c.JupyterHub.spawner_class = ‘dockerspawner.DockerSpawner’’

If you restart Jupyterhub with the new configuration file you will be able to log into the hub using any valid credentials in LDAP server. User account is not required for this approach as it is taken care by the spawning of separate docker container per each user by the dockerspawner.

Tip : You might face error if Jupyterhub version mismatches in your base jupyterhub server and docker containers. Hence it is better to build a custom docker image with correct Jupyterhub version specified.

Building a custom docker image for dockerspawner

By default dockerspawner uses jupyterhub/singleuser image. You can have any image of your liking as long as you meet with some requirements specified by Jupyter team. In the student-teacher Eco-system you need to add the assignment notebook file into each docker container. Hence you must have a custom docker image spawned for each user.

Example Dockerfile with jupyterhub version specified explicitly.

Add following configurations to the jupyterhub_config.py

As of now each user is able to login to the Jupyterhub using LDAPauth. For each user separate docker container will be created with single user notebook server. Student ‘Riemann’ can access the assignment in following URL.

http://<host>:8000/user/riemann/notebooks/programing_assignment.ipynb

Final look on notebook server home directory

Sending common sharable link to the assignment

You might have noticed that Another student cannot use the same notebook link.

riemann -> /user/riemann

tesla -> /user/tesla

if Student ‘Tesla’ uses ‘/user/riemann/’ link, he will not be authorized to access. In a group assignment common URL needs to be sent for students to easily click and visit the assignment. Fortunately in Jupyterhub 1.0.0, there is an option called ‘user-redirect’ which redirects to an appropriate URL according to logged in user. Simply use the following URL as more generic URL.

http://<you_host_name>:8000/user-redirect/notebooks/hello_world.ipynb

Whitelisting Users

Now you have a complete solution for a student-teacher assignment scenario. However any user registered in the LDAP matching the bind template will have access to the notebook. If you need to allow only a specific user group

to access notebook you can whitelist those username in Jupyterhub configuration.