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.

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:
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, 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
Last updated