# Overview

The following demonstration documents a step-by-step guide to setting up a virtual “Econometrics Lab” hosted by [Amazon Web Service (AWS)](https://aws.amazon.com/), one of many cloud computing providers. Ultimately, students will be able to connect to an environment to perform live coding on [Jupyter Notebooks](https://jupyter.org/) with [Python](https://www.python.org/psf/), [R](https://www.r-project.org/foundation/), and [Stata](https://www.stata.com/) kernels.

This demonstration corresponds to workflow 3 outlined in “*Econometric Pedagogy and Cloud Computing: Training the Next Generation of Economists and Data Scientists*,” by  [Danielle V. Handel](https://daniellehandel.org/), [Anson T. Y. Ho](http://www.atyho.info/), [Kim P. Huynh](https://www.bankofcanada.ca/profile/kim-huynh/), [David T. Jacho-Chávez](https://www.davidjachochavez.org/), and [Carson H. Rea](https://www.carsonrea.org/) published in the [***Journal of Econometric Methods***](https://www.degruyter.com/view/journals/jem/jem-overview.xml). The instructions may be modified to suit different teaching styles and classroom needs.

### Functionality

This section showcases a fully operational server and the optional GitHub integration.

**Standard Functionality**

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGYTkmxcT6uRhJio9v%2F-MDGcx80fwNi8VCYdL34%2Fbase_functionality.gif?alt=media\&token=ecb520fc-b559-45be-86f7-d00f897c8fbe)

After entering the lab with a pre-assigned username and password, students may generate new Jupyter Notebooks using Python, R, or Stata. They also may upload notebooks from their local machine or clone repositories from [GitHub](https://github.com/) and work on those. None of this requires installation of any software on the local machine.

**(Optional) GitHub Integrated Functionality**

Demonstrated below is the additional functionality granted by following the optional sections, [GitHub Extensions and Packages](/optional-github-extension-and-packages/github-extension) and [GitHub Authentication](/optional-github-authentication/add-github-authentication).

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGYTkmxcT6uRhJio9v%2F-MDGeGduLEEI2y9Mu27J%2FloginGitHubDemo.gif?alt=media\&token=46b92ce9-702d-47c8-a0a0-90dc1af244c0)

A student with their free GitHub account setup can select the "Sign in with GitHub" to be prompted to authorize the server to use GitHub when logging in. After selecting the button to authorize, the student will be directed to the JupyterHub interface and may begin working.

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGYTkmxcT6uRhJio9v%2F-MDGeRDR35oTZtV9UevB%2FdjachocGitHubDemo.gif?alt=media\&token=8ea5f917-b0b4-4db3-8c99-0babfe66a4af)

The GitHub extension allows the student to access repositories as demonstrated above. By typing in a username, the student will be granted access to all of the *public* repositories that person has. A students can gain access to assignments directly from their instructor's GitHub.


# Pre-requisites

To begin launching an instance, the following pre-requisites are required:

1. Hardware: Personal computer with internet connection.
2. Software:
   1. SSH Client.
   2. Stata license (contact your department's IT service).
3. Cloud Service:
   1. AWS account.
   2. GitHub account (optional).


# Disclaimer

This instructional guide is part of a demonstration used for “*Econometric Pedagogy and Cloud Computing: Training the Next Generation of Economists and Data Scientists*” by  [Danielle V. Handel](https://www.daniellehandel.org), [Anson T. Y. Ho](http://www.atyho.info/), [Kim P. Huynh](https://www.bankofcanada.ca/profile/kim-huynh/), [David T. Jacho-Chávez](https://www.davidjachochavez.org/), and [Carson H. Rea](https://www.carsonrea.org). There is no guarantee this methodology works for others or using a different cloud service vendor. For specific needs or troubleshooting, independent research may be necessary.

[**AWS**](https://aws.amazon.com/) and corresponding services are trademark of [**Amazon Web Services**](https://aws.amazon.com/)**.**

[**Stata**](https://www.stata.com/) is trademark of [**Stata Corporation**](https://www.stata.com/company)**.**

[**Google Domains**](https://domains.google/) is trademark of [**Google**](https://www.google.com/)**.**

[**Python**](https://www.python.org/) is developed on an open source license distributed by the [**Python Software Foundation**](https://www.python.org/psf/)**.**

[**R**](https://www.r-project.org/) is offered on an open source license distributed by the [**R Foundation**](https://www.r-project.org/foundation/)**.**

[**JupyterHub**](https://jupyterhub.readthedocs.io/en/stable/) is an open source platform developed and maintained by [**Project Jupyter**](https://jupyter.org/)**.**


# Overview

This section details a method of launching an instance on AWS EC2 (Elastic Compute Cloud). Written instruction can be found below the corresponding demonstrational gif.

💡 What is an *instance*?\
\
An instance in AWS is a type of virtual environment. There are several types that AWS provides depending on the intended purpose; this demonstration walks through the launching of a free-tier EC2 instance. More information can be found in the [EC2 documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Instances.html) on the AWS website.


# Customizing an Instance

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGealiI9XbqYHiy83u%2F-MDGfbB2biJ1gLPMO2ak%2F2_start_instance.gif?alt=media\&token=e3ea63bc-41be-4963-9f22-31001bc03a51)

Log into your AWS account. To launch the proper instance, select EC2 from the dropdown menu labeled "All services".\
\
Select a Linux server as the desired Amazon Machine Image (AMI). This demonstration selects Ubuntu 20.04 LTS, though other Linux servers will also be suitable.\
\
Select an instance type. *This should be chosen based on class size and computational needs*. For AWS's free tier, choose "General purpose" family and "t2.micro" type. Click "Next: Configure Image" to continue.\
\
Immediately continue to "Next: Add Storage."

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGealiI9XbqYHiy83u%2F-MDGfoMS2XZv-kLVGLnx%2F3_security_group.gif?alt=media\&token=6d52b057-be5d-4ea6-b2e2-6c081075759b)

Change the storage from the default to 30 GB. *This should be chosen based on class size and computational needs*. Continue to “Next: Add Tags”.\
\
Tags are optional metadata that describe the instance for categorization and organization purposes. To add a tag, the "Add a tag" button is located on the bottom left. Otherwise, continue to "Next: Configure Security Groups".\
\
The default SSH rule will use the standard port 22. Change the source to “Anywhere” to allow the Ubuntu system adminstrator with any IP address to access the instance. For improved security, one can change the source to only allow specific IP.\
\
Since JupyterHub uses port 8000 as default for connecting to the internet, it has to be included in the security group. Add a second custom TCP security rule by clicking the “Add Rule” button. Modify the rule to include port 8000. Add an optional description of “JupyterHub.” Continue to the final stage by clicking the blue “Review and Launch” button.

💡 What is port range?\
\
A port is a designated number that specifies a network service for operating systems. E.g. Port 80 is assigned to HTTP (Hypertext Transfer Protocol). These are tied to the IP address and communicate the purpose of the network. The TCP (Transmission Control Protocol) port 22 is the default for SSH (Secure Shell). TCP Port 8000 is the default for JupyterHub.

Now, “Launch”.

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGealiI9XbqYHiy83u%2F-MDGfxFQdblYIY4Fzh5D%2F5_launch_and_key.gif?alt=media\&token=441f679a-eda0-4fe7-8e36-505ac8045075)

Selecting “Launch” will prompt the user to select an existing SSH key pair or create a new one. Name and download your key pair.

{% hint style="info" %}
Important to keep the key private and safe. Losing the key will render your instance inaccessible. Sharing the key could leave your instance vulnerable to unauthorized access.
{% endhint %}

💡 What is an SSH key?\
\
An SSH key pair serves as the "password" which connects a SSH client to the server. This demonstration suggests the use of Bitvise, although other clients, such as PuTTY for Windows/Unix users or the Terminus App for Mac users, can serve as substitutions.


# Assigning an Elastic IP Address

In case your instance is stopped or failed, restarting or relaunching your instance will end up with a different public IP address. This becomes inconvenient if users have to change the address for accessing JupyterHub. This section will demonstrate the proper set-up for allocating a static IP address to your AWS instance. For this purpose, an [Elastic IP address](https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/elastic-ip-addresses-eip.html) can be allocated to your account and asociated with an instance, so that users can always access your JupyterHub with the same IP.

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGealiI9XbqYHiy83u%2F-MDGgA1Ae_DESnj7nnBc%2Fallocate_elastic.gif?alt=media\&token=d9c51bbe-2766-4f51-adb4-8fd447822b96)

Navigate to the Elastic IP configuration on the AWS EC2 Console. Opt to allocate an elastic IP address from Amazon's pool of IPv4 addresses. Then, choose to associate this new address and select your instance. The Elastic IP is now your instance's public IP.


# Connecting to Your Cloud

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGealiI9XbqYHiy83u%2F-MDGhHR9TYwqpG0J8b1L%2F4_IP_into_bitvise.gif?alt=media\&token=6524bf14-2aa3-4e12-99c7-f76f923840d6)

The instance will now be visible in the EC2 homepage. The description of the instance and the IP address can be found at the bottom of the instance page. Open [Bitvise](https://www.bitvise.com/ssh-client-download) as an SSH client. Copy the IPv4 Public IP address from the instance page in EC2 and paste it into “Host” on Bitvise. Insert 22 as the port.

💡 What is an SSH Client?\
\
&#x20;An SSH client allows establishing a secure and authenticated SSH connections to SSH server. Download [Bitvise SSH Client](https://www.bitvise.com/ssh-client-download) or [PuTTY](https://www.putty.org/) for Windows or [Terminus App](https://termius.com/) for MacOS.

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGealiI9XbqYHiy83u%2F-MDGgL0gQ84UdOBoQyCS%2F6_put%20in%20key.gif?alt=media\&token=5257a6bf-8e30-4bf6-b0f3-bc3361f6ed3f)

Enter "ubuntu" as the default username. Change the following line “Initial method” to publickey. For first-time log in, select "Client key manager" and import the SSH key downloaded earlier (named as Global 1 in this example). An optional comment can be left for organization purposes if desired. Select the key just imported in the “client key” line. Click "Log In" to connect.


# Ubuntu: Server Update and Add Users

💡 The following directions are for use on Ubuntu servers \[Ubuntu 20.04 LTS].

{% hint style="info" %}
Obtain administrative rights by requesting root access:
{% endhint %}

```
sudo -i
```

Update the Ubuntu repository and upgrade packages with:

```
apt update
apt upgrade
```

Create new Ubuntu system users, say, designated JupyterHub administrator "admin1" and JupyterHub user "student", write:

```
adduser admin1
adduser student
```

You will be prompted to enter a password and information for each new user. Note: when typing the password, the cursor will not appear to move.

💡 Why do I need to add new users? Is there other ways?\
\
&#x20;By default, JupyterHub uses PAM (Pluggable Authentication Module) to authenticate system users with their username and password, i.e. any user with an account and password on the Ubuntu system will be allowed to login. Alternatively, GitHub Authentication provides a guide to giving students the ability to login to the server with their GitHub credentials. View the section [GitHub Authentication](/optional-github-authentication/add-github-authentication) to determine whether it is right for your server.

{% hint style="info" %}
JupyterHub administrator (we will assign  [below](/anaconda/jupyterhub-installation-and-configuration)) does not need to have Ubuntu system administrative rights.
{% endhint %}

&#x20;Continue reading the  “[Anaconda](/anaconda/anaconda-installation)” section to set up Anaconda on the instance.


# Overview

&#x20;[Anaconda](https://www.anaconda.com/) is a popular python distribution which comes with many packages needed for data science along with the open-source package management system, conda.

💡 Why is conda important?\
\
&#x20;The installation of Conda will allow the downloading and management of packages through use of the command `conda`. This command will appear throughout the guide and facilitate the remainder of the set-up.


# Anaconda Installation

{% hint style="info" %}
Obtain administrative rights by requesting root access:
{% endhint %}

```
sudo -i
```

To download and install [Anaconda](https://www.anaconda.com/):

```
wget https://repo.anaconda.com/archive/Anaconda3-2023.09-0-Linux-x86_64.sh
bash Anaconda3-2023.09-0-Linux-x86_64.sh
```

Write `yes` till prompted with `[/root/anaconda3] >>>`, then enter path:

```
/usr/anaconda3
```

Anaconda is now installed. Edit the Ubuntu system paths to include Anaconda for all users. Open the profile in [nano](https://www.nano-editor.org/):

```
nano /etc/profile
```

💡 What is nano?\
\
Nano is the default text editor for Ubuntu. This program will be used throughout the tutorial to edit the configuration files of the needed software, enabling full integration into the lab interface.\
\
Although there are many shortcuts included in this text editor, the most important for use in this project are the commands CTRL+O then enter which will overwrite a document and CTRL+X which will exit the editor.\
\
If you are using MacOS or Linux, nano may already be installed on your machine. To check, type the following line of code in the command line:

```
nano --version
```

If the output shows a version number, nano is already installed.

Enter the following into the bottom of the document as shown below.

```
export PATH="/usr/anaconda3/bin:$PATH"
```

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGiXtezLpgILCcsnw1%2F-MDGjZiYGIkb-weu47YN%2Fenter%20anaconda%20path.png?alt=media\&token=c1038dd5-d314-4d96-b6bc-a782c233c7e2)

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

Refresh the paths to include Anaconda by typing:

```
source /etc/profile
```

Make sure conda is up to date and update all packages to the latest version::

```
conda update -n root conda
conda update --all
```


# JupyterHub Installation and Configuration

Obtain administrative rights by requesting root access:

```
sudo -i
```

Install the package for [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/quickstart.html)

```
conda install -c conda-forge jupyterhub
```

Generate a JupyterHub configuration file in `/etc` by:

```
mkdir /etc/jupyterhub/
cd /etc/jupyterhub/
jupyterhub --generate-config
```

Use nano to edit the newly created configuration file:

```
nano /etc/jupyterhub/jupyterhub_config.py
```

Copy and paste the following into the configuration file:

```
# Allow Jupyter Lab as the default interface
c.Spawner.default_url = '/lab'

# Allow admin to access other users' accounts
c.JupyterHub.admin_access = True

# Specify JupyterHub administrators & user login
c.Authenticator.admin_users = {'admin1'}
c.Authenticator.allow_all = True

# Shutdown user servers on logout
c.JupyterHub.shutdown_on_logout = True

# Prevent the user-owned configuration files from being loaded
c.Spawner.disable_user_config = True
```

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

💡 For having access to the JupyterHub admin interface, at least *one* administrator has to be specified in the configuration file, e.g. `admin1`. Additional administrators can be assigned/removed from the JupyterHub admin interface by existing administrator.

Make JupyterHub a system service, so that JupyterHub will run at system startup and continue to run after the system administrator logs out. To do so, create a service file:

```
nano /etc/jupyterhub/jupyterhub.service
```

Copy and paste the following into the document as depicted below:

```
[Unit]
Description=JupyterHub
After=syslog.target network.target

[Service]
User=root
Environment="PATH=/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/anaconda3/bin"
ExecStart=/usr/anaconda3/bin/jupyterhub --no-ssl -f /etc/jupyterhub/jupyterhub_config.py

[Install]
WantedBy=multi-user.target
```

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGiXtezLpgILCcsnw1%2F-MDGjje9NfyrFjW8ergk%2Fjupytergnu.PNG?alt=media\&token=3bbfc83d-d92e-4ee7-a26e-fa810b03aca5)

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

Link the newly created service file to the `/etc/systemd/system` directory:

```
ln -s /etc/jupyterhub/jupyterhub.service /etc/systemd/system/jupyterhub.service
```

Reload the system daemon and run JupyterHub as a system service:

```
systemctl daemon-reload
systemctl enable jupyterhub.service
systemctl start jupyterhub.service
```

Check JupyterHub status (optional):

```
systemctl status jupyterhub.service
```

💡 **Your JupyterHub server should be up and running at `http://<your instance IP address>:8000`.** Make sure that `:8000` is included in your address. See [Add a Custom Domain](/optional-add-a-custom-domain/adding-a-subdomain) to attach your JupyterHub to a registered domain name instead.

{% hint style="info" %}
You are running an unsecured instance of JupyterHub. For network security, see [below](/optional-github-authentication/add-github-authentication).
{% endhint %}


# R Installation

{% hint style="info" %}
Obtain administrative rights by requesting root access:
{% endhint %}

```
sudo -i
```

To install R version 4.0 or later on Ubuntu, add the CRAN (Comprehensive R Archive Network) repository to Ubuntu and install the R packages:

```
wget -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc | sudo tee -a /etc/apt/trusted.gpg.d/cran_ubuntu_key.asc
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/'
apt update
apt install r-base r-base-dev
```

Start R:

```
R
```


# Update and Install IR Kernel

Update all packages in the current environment to the latest version, without prompts for permission:

```
update.packages(ask = FALSE)
```

Install IRkernel for all users:

```
install.packages('IRkernel', lib = '/usr/local/lib/R/site-library')
```

Make the IRkernel available to Jupyter. For system-wide installation, set user to False in the installspec command:

```
IRkernel::installspec(user = FALSE)
```

When ready, quit:

```
q()
```

💡 Install tidyverse and other packages<br>

Additional Ubuntu linux packages are needed for 'tidyverse' in R. In Ubuntu terminal:

```
apt install libssl-dev libcurl4-openssl-dev unixodbc-dev libxml2-dev libmariadb-dev libfontconfig1-dev libharfbuzz-dev libfribidi-dev libfreetype6-dev libpng-dev libtiff5-dev libjpeg-dev
```

In R, install tidyverse for all users:

```
install.packages("tidyverse", dependencies = TRUE, INSTALL_opts = '--no-lock')
```

There are other useful R packages that one can install, for example:

```
install.packages("openxlsx", lib = '/usr/local/lib/R/site-library', dependencies = TRUE, INSTALL_opts = '--no-lock')
install.packages("knitr", lib = '/usr/local/lib/R/site-library', dependencies = TRUE, INSTALL_opts = '--no-lock')
```


# Overview

{% hint style="info" %}
Stata license is required.
{% endhint %}

💡 Instructors should contact Stata to discuss licensing options.


# Stata Installation

Create a folder for your Stata installation file:

```
mkdir /home/ubuntu/stata_source
```

Download the [Stata](https://www.stata.com/support/faqs/unix/install-download-on-linux/) tar file for Linux.

Upload the tar file to the `/home/ubuntu/stata_source` folder just created (using BitVise's SFTP interface for example). You may need to give writing priviledges to the folder you just created by

```
sudo chmod -R a+rwx /home/ubuntu/stata_source
```

{% hint style="info" %}
Obtain administrative rights by requesting root access:
{% endhint %}

```
sudo -i
```

Unzip the tar file in the `/tmp/statafiles` folder:

```
cd /tmp/
mkdir statafiles
cd statafiles
tar -zxf /home/ubuntu/stata_source/Stata16Linux64.tar.gz
```

Create `/usr/local/stata16` directory for installing Stata for all users:

```
cd /usr/local
mkdir stata16
```

Install Stata. Enter licensing information when prompted:

```
cd /usr/local/stata16
/tmp/statafiles/install
```

After installation, you will be prompted to initialize Stata:

```
./stinit
```

Ubuntu users will need an additional package for Stata to work:

```
apt-get update
echo "deb http://security.ubuntu.com/ubuntu focal-security main universe" > /etc/apt/sources.list.d/ubuntu-focal-sources.list
apt-get install libncurses5
```

Edit the Ubuntu system paths to include Stata for all users. Open the profile in [nano](https://www.nano-editor.org/):

```
nano /etc/profile
```

Type the following into the bottom of the document as depicted below.

```
export PATH="/usr/local/stata16:$PATH"
```

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGiXtezLpgILCcsnw1%2F-MDGjt35Hg3Ii_0EUV-A%2Fenter%20stata%20path.png?alt=media\&token=34dfcbfd-a5da-4f70-8074-7caad902fa49)

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

Refresh the paths to include Stata by typing:

```
source /etc/profile
```


# Stata Kernel for Jupyter

Install Stata kernel to allow Jupyter users to create and run Stata files in Jupyter Notebooks:

```
pip install stata_kernel
python -m stata_kernel.install
```

Ensure that the stata\_kernel is installed correctly:

```
nano .stata_kernel.conf
```

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

{% hint style="info" %}
Make sure `stata_path` is pointing to the correct executable. *It can be different depending on your Stata version*.
{% endhint %}

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGiXtezLpgILCcsnw1%2F-MDGk37HUbE9HsqsgMCz%2Fstata_kernel.PNG?alt=media\&token=01be346e-538f-4f94-be85-b667a08c66c4)

Copy the configuration file to system configuration for all users:

```
cp .stata_kernel.conf /etc/stata_kernel.conf 
```


# GitHub Extension

This [GitHub Extension](https://github.com/jupyterlab/jupyterlab-github) allows a GitHub icon to appear on the side bar of students' interface. When clicked, students can gain access to public repositories, such as notebook-based assignments.

{% hint style="info" %}
Obtain administrative rights by requesting root access:
{% endhint %}

```
sudo -i
```

Install the extension for JupyterLab:

```
jupyter labextension install @jupyterlab/github
```

Restart to ensure recognition of the new extension:

```
systemctl restart jupyterhub.service
```


# nbgrader

The package [nbgrader](https://nbgrader.readthedocs.io/en/stable/) helps streamline the grading process for instructors using Jupyter notebooks.

{% hint style="info" %}
Obtain administrative rights by requesting root access:
{% endhint %}

```
sudo -i
```

Install the extension for JupyterLab:

```
conda install -c conda-forge nbgrader
```

Restart to ensure recognition of the new extension:

```
systemctl restart jupyterhub.service
```


# Overview

Instructors may use Amazon Web Services, a university domain, or any other domain service to add a custom domain.

{% hint style="info" %}
Adding your JupyterHub to a registered domain is necessary for running it in HTTPS.
{% endhint %}

This demonstration uses [Google Domains](https://domains.google/). If you have an existing custom domain, you may add the lab as a sub-domain as done here. This tutorial assumes an existing domain has been registered with Google Domains. Visit the [Google Domains Learning Center](https://domains.google/learning-center/) for more information on this.


# Adding a Subdomain

💡 What is a sub-domain?\
\
A sub-domain is an additional section of a domain that can help organize, specify, or navigate to different websites under a single primary domain.\
\
For example, while navigating from a website `www.example.com` to its store, you may see the domain change to `store.example.com`. This shortens the domain and makes it easier for you and your students to remember. The following method requires a domain to acquire the proper safety features.

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGealiI9XbqYHiy83u%2F-MDGhXfUlvwT-4w7w9wS%2Fdomain_assign.gif?alt=media\&token=b71868b8-bbb3-4957-a2da-38accec687ba)

Navigate to the "Domain Name Servers," then to "Custom resource records." Enter the desired name (jupyterlab, econlab, etc.). Then enter the IP4 address of your instance.

💡 This will take up to 48 hours to update and begin serving as a functioning URL.

💡 It is still necessary to add `:8000` to the address of your jupyter Hub. It is also feasible to use a reverse proxy, e.g. nginx, to reroute network traffics to your URL to the JupyterHub, see [below](/optional-github-authentication/secure-your-lab).


# Overview

This section provides a guide to allowing the server to use GitHub authentication (OAuth) in the log-in process. By entering in their GitHub log-in information, students can access and use the server without the instructor having to manually enter each user and admin. As a reminder, a student can set up a GitHub account for free. An advanced understanding of the command line is recommended before attempting.

{% hint style="info" %}
Setting up the site to run on HTTPS with SSL security is strongly recommended. In HTTPS, the communication protocol is encrypted using (TLS) Transport Layer Security or, formerly, SSL (Secure Sockets Layer).
{% endhint %}


# Generate Cookie Secret

Encrypt your lab's [cookie](https://en.wikipedia.org/wiki/HTTP_cookie) for security purposes:

{% hint style="info" %}
Obtain administrative rights by requesting root access:
{% endhint %}

```
sudo -i
```

Create a new directory for the cookie secret:

```
mkdir /srv/jupyterhub
```

Generate a random number and save it as the cookie secret:

```
openssl rand -hex 32 > /srv/jupyterhub/jupyterhub_cookie_secret
```

Edit JupyterHub's configuration file:

```
nano /etc/jupyterhub/jupyterhub_config.py
```

Copy the following and add it to the file:

```
c.JupyterHub.cookie_secret_file = '/srv/jupyterhub/jupyterhub_cookie_secret'
```

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

Ensure that only the system administrator can read and write the cookie secret:

```
chmod 600 /srv/jupyterhub/jupyterhub_cookie_secret
```

Restart to ensure recognition of the new extension:

```
systemctl restart jupyterhub.service
```


# Secure Your Lab

The following instructions will allow the use fo HTTPS in your lab address.

Navigate to the AWS Console. Go to the Security Groups settings and select your instance. Edit the inbound rules to include `HTTP` and `HTTPS`, then save.

![](https://github.com/daniellehandel/Econometric-Pedagogy/raw/master/img/http_security.gif)

{% hint style="info" %}
Obtain administrative rights on your Ubuntu server by requesting root access:
{% endhint %}

```
sudo -i
```

Create a DH parameter for OpenSSL:

```
openssl dhparam -out /etc/jupyterhub/dhparam.pem 2048
```

Change access to the system administrator only:

```
chmod 600 /etc/jupyterhub/dhparam.pem
```

Link this file to the system folder:

```
ln -s /etc/jupyterhub/dhparam.pem /etc/ssl/certs/dhparam.pem
```

Install [Certbot](https://certbot.eff.org/lets-encrypt/ubuntufocal-nginx):

```
apt update
apt install software-properties-common
add-apt-repository universe
apt update
apt install certbot python3-certbot-nginx
```

Generate an SSL Certificate using Certbot:

```
certbot certonly --nginx
```

You will be prompted to enter an email and a domain (the newly added custom domain, for example: "YOUR-DOMAIN")

You should see:

* Congratulations! Your certificate and chain have been sav /etc/letsencrypt/live/YOUR-DOMAIN/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/YOUR-DOMAIN/privkey.pem

Install nginx to set up a reverse proxy, so you do not have to type ":8000" at the end of the URL:

```
apt install nginx
```

Create a configuration file for nginx:

```
nano /etc/nginx/sites-available/jupyterhub.conf
```

Copy and paste the following code, based on [Mozilla Configuration Generator](https://ssl-config.mozilla.org/), into this file. Be careful to modify the text to include your domain wherever "YOUR-DOMAIN" is present.

```
# top-level http config for websocket headers
# If Upgrade is defined, Connection = upgrade
# If Upgrade is empty, Connection = close
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# HTTP server to redirect all 80 traffic to SSL/HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name YOUR-DOMAIN;

    # Tell all requests to port 80 to be 302 redirected to HTTPS
    return 302 https://$host$request_uri;
}

# HTTPS server to handle JupyterHub
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name YOUR-DOMAIN;
    client_max_body_size 0;

    ssl_certificate /etc/letsencrypt/live/YOUR-DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/YOUR-DOMAIN/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 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;
    ssl_prefer_server_ciphers on;

    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;
    #add_header Strict-Transport-Security max-age=1576800;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # Managing literal requests to the JupyterHub front end
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # websocket headers
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

    # Managing requests to verify letsencrypt host
    location ~ /.well-known {
        allow all;
    }
}
```

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

Unlink the existing default file:

```
unlink /etc/nginx/sites-enabled/default
```

Link the new nginx file for JupyterHub:

```
ln -s /etc/nginx/sites-available/jupyterhub.conf /etc/nginx/sites-enabled/jupyterhub.conf
```

Start nginx:

```
systemctl start nginx.service
```

Edit JupyterHub configuration file:

```
nano /etc/jupyterhub/jupyterhub_config.py
```

Add the following to force JupyterHub to only listen to local connections (127.0.0.1):

```
c.JupyterHub.bind_url = 'http://127.0.0.1:8000'
```

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

Restart nginx and JupyterHub:

```
systemctl restart jupyterhub.service
systemctl restart nginx.service
```


# Add GitHub Authentication

Execute the following to set up a method for students to sign in using GitHub.

From your GitHub account, navigate to the Developer Settings. Choose OAth Apps and create a New OAth app. Enter the corresponding information. Enter **`https://YOUR-URL/hub/oauth_callback`**&#x61;s the Authorization callback URL, being careful to replace YOUR-URL with the link to your lab.

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGealiI9XbqYHiy83u%2F-MDGhfZWyfpmEsLZop4S%2Fgithub_oath.gif?alt=media\&token=f2f94a2b-5cc5-454a-bcc2-26c71adbebfc)

On your Ubuntu server, install GitHub OAuth:

```
conda install -c conda-forge oauthenticator
```

Open the JupyterHub configuration file

```
nano /etc/jupyterhub/jupyterhub_config.py
```

Copy and paste the following into the file, being careful to replace the URL, Client ID, and Client Secret with your own. The Client ID and Client Secret can be found on the page of the GitHub "app" created above.

```
from oauthenticator.github import LocalGitHubOAuthenticator
c.JupyterHub.authenticator_class = LocalGitHubOAuthenticator
c.LocalGitHubOAuthenticator.oauth_callback_url = 'YOUR-URL/hub/oauth_callback'
c.LocalGitHubOAuthenticator.client_id = 'YOUR CLIENT ID'
c.LocalGitHubOAuthenticator.client_secret = 'YOUR CLIENT SECRET'

# This line means that it will no longer be necessary to manually add new users.
c.LocalGitHubOAuthenticator.create_system_users = True
c.LocalGitHubOAuthenticator.allow_all = True
```

It should look like this:

![](https://2920288544-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MDG8rOchFn0FENiplhZ%2F-MDGiXtezLpgILCcsnw1%2F-MDGkGsaY4QAJJDgzUvX%2Foath_editor.png?alt=media\&token=0e67a013-283f-4986-9c31-6ecc1ee0ceb4)

{% hint style="info" %}
Notice that you may also change the admin username to your GitHub username to allow administrative access.
{% endhint %}

Use CTRL+O then enter to overwrite the document and CTRL+X to exit.

Reboot the server:

```
systemctl restart jupyterhub.service
```


