The Littlest JupyterHub

Notes on setting up TLJH

  1. Install TLJH
  2. Configure TLJH
  3. Configure TRAEFIK
  4. Setup NGINX
    1. Getting SSL
    2. If we have external certs:
    3. If we get certs from letsencrypt
    4. If port 80 is closed we do DNS challenge
    5. Reload the proxy
  5. Configure nginx
    1. If we use letsencrypt certs
    2. If we have our own certs
  6. Restart nginx
  7. Uninstall

Install TLJH

https://tljh.jupyter.org/en/latest/install/custom-server.html

sudo apt install python3 python3-dev git curl    
curl -L https://tljh.jupyter.org/bootstrap.py | sudo -E python3 - --admin <admin-user-name>

Configure TLJH

https://tljh.jupyter.org/en/latest/topic/tljh-config.html

sudo tljh-config set http.port 8888
sudo tljh-config reload proxy

Configure TRAEFIK

Make traefik only listen to localhost

sudo nano /opt/tljh/state/traefik.toml
[entryPoints.http]
address = "127.0.0.1:8000" 

https://github.com/jupyterhub/the-littlest-jupyterhub/issues/446

Setup NGINX

Getting SSL

tljh

sudo apt install certbot
sudo mkdir /etc/mycerts
sudo tljh-config set https.enabled true

We set https to port 8888 since we want nginx to take port 443

sudo tljh-config set http.port 8080
sudo tljh-config set https.port 8888

If we have external certs:

sudo cp postgis_bren_ucsb_edu.key /etc/mycerts/
sudo cp postgis_bren_ucsb_edu_cert.cer /etc/mycerts/
sudo tljh-config set https.tls.key /etc/mycerts/postgis_bren_ucsb_edu.key
sudo tljh-config set https.tls.cert /etc/mycerts/postgis_bren_ucsb_edu_cert.cer

If we get certs from letsencrypt

sudo apt install python3-certbot-nginx
sudo tljh-config set https.letsencrypt.email griessbaum@gmail.com
sudo tljh-config add-item https.letsencrypt.domains poststare.duckdns.org
sudo tljh-config show

If port 80 is closed we do DNS challenge

Redit post

sudo certbot certonly -a manual -d holyromanpv.duckdns.org --email griessbaum@gmail.com --preferred-challenges dns 

Examples:

TOKEN='13826710-5e84-4d9c-a3d0-023557bc5bd0'
CHALLENGE='w5Xk_72skfyli-R3NqOh0akMKvXXkKk-IjCnUiSPc1E'
DOMAIN='holyromanpv.duckdns.org'
curl "https://www.duckdns.org/update?domains=$DOMAIN&token=$TOKEN&txt=$CHALLENGE"
echo "https://www.duckdns.org/update?domains=$DOMAIN&token=$TOKEN&txt=$CHALLENGE"

Reload the proxy

sudo tljh-config reload proxy

Configure nginx

sudo apt install nginx
DOMAIN=
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/$DOMAIN
sudo ln -s /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled

If we use letsencrypt certs

sudo nano /etc/nginx/sites-available/$DOMAIN

First, just configure http

server {
        listen 80;
        server_name $DOMAIN www.$DOMAIN;
        return 301 https://$DOMAIN$request_uri;
}
sudo certbot --nginx certonly
sudo nano /etc/nginx/sites-available/$DOMAIN

Then, add the httpS config

server {
        listen 443 ssl;
        listen [::]:443 ssl;
        
        server_name $DOMAIN;

        ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;
        
        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;
        }
}
sudo nginx -t
sudo systemctl restart nginx

If we have our own certs

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# HTTPS server to handle JupyterHub
server {
    listen 443 ssl;

    server_name $HOST;

    ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_stapling on;
    ssl_stapling_verify on;
    add_header Strict-Transport-Security max-age=15768000;

    # 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_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_buffering off;
    }

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

Restart nginx

sudo nginx -t
sudo systemctl restart nginx

Uninstall

At least instructions as to which files are installed where would be welcome. As far as I can see, the following locations would have to be removed during an uninstall:

/opt/tljh (directory)
/usr/bin/tljh-config (symlink)

Then there is the three services located at:

/etc/systemd/system/traefik.service
/etc/systemd/system/jupyterhub.service

Edit: you would remove those services by first running

sudo systemctl disable <service>

and then remove the files themselves, I guess.

Two other things to do are

Remove the jupyterhub-admins and jupyterhub-users groups with sudo delgroup <group> Remove the extra sudoers file located at /etc/sudoers.d/jupyterhub-admins. Of course, as @yuvipanda said, if you allowed your users to run arbitrary command, then there is no point at all in uninstalling. However, in some cases it might make sense to include the information above in the documentation.

/home/jupyter-[users added]