{"id":27354548,"url":"https://github.com/harrykodden/jupyterhub-saml","last_synced_at":"2025-07-13T07:33:37.543Z","repository":{"id":128639827,"uuid":"110111468","full_name":"HarryKodden/JupyterHub-SAML","owner":"HarryKodden","description":"This project shows howto authenticate jupyter notebook users using SAML. Upon successful authentication, a private notebook is spawned as a docker container.","archived":false,"fork":false,"pushed_at":"2017-11-10T15:22:43.000Z","size":45,"stargazers_count":10,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-12T21:54:32.128Z","etag":null,"topics":["docker","jupyter","jupyterhub","notebook","saml"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HarryKodden.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-11-09T12:17:00.000Z","updated_at":"2020-04-23T06:36:44.000Z","dependencies_parsed_at":"2023-04-16T00:59:35.966Z","dependency_job_id":null,"html_url":"https://github.com/HarryKodden/JupyterHub-SAML","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/HarryKodden/JupyterHub-SAML","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryKodden%2FJupyterHub-SAML","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryKodden%2FJupyterHub-SAML/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryKodden%2FJupyterHub-SAML/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryKodden%2FJupyterHub-SAML/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HarryKodden","download_url":"https://codeload.github.com/HarryKodden/JupyterHub-SAML/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarryKodden%2FJupyterHub-SAML/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265105781,"owners_count":23712209,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","jupyter","jupyterhub","notebook","saml"],"created_at":"2025-04-12T21:54:29.852Z","updated_at":"2025-07-13T07:33:37.537Z","avatar_url":"https://github.com/HarryKodden.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JupyterHub-SAML\n\n![alt text](diagram.svg)\n\nWhat do we need:\n\n- docker\n- docker-compose\n- domain name\n- SAML Preparations\n- install + configure our hosts\n- prepare a notebook\n\n### docker\nPlease make sure you have docker up and running on your system. \n\n~~~\nhttps://docs.docker.com/get-started/\n~~~\n\n#### docker-compose\nPlease make sure also docker-compose is installed on your system.\n\n~~~\nhttps://docs.docker.com/compose/install/\n~~~\n\n### domain name\nFor this demonstration we like to have a domainname that we can reach on the public internet. If you have such a domainname already and you do have control over the DNS settings of that domain, then the recommendation is to reserve a subdomain and register a A-record to the IP-Address of your Docker VPS/Machine. Make sure that port 443 is open on the firewall.\n\n### SAML Preparations\nHere we need several steps.\n\nIf you are new to SAML and Federated Authentication, here is some good readings that will get you prepared with the required background information.\n\n~~~\nhttps://wiki.surfnet.nl/display/surfconextdev/Schematic+overview\n~~~\n\nOur VPS machine will act as a \"Service Provider\".\nDuring this demonstration, we make use of ***surfconext*** to connect to our \"Identity Providers\".\n\nIn preparation for that we need the following:\n\n#### Service Provider key-set\n\nThe following commands will generate a self-signed keyset that is OK for this demonstration.\n\n~~~\nopenssl genrsa -out server.key\nopenssl req -new -x509 -key server.key -out server.crt -days 365\n~~~\n\n#### Service Provide Metadata\n\nWe need to prepare Metadata that we can pass on to the Identity Provider in order to establish a bilateral \"trust-relation\" between us.\n\nThis template can be used and adjusted where appropriate.\n\nFile: ***metadata.xml***\n\n~~~\n\u003c?xml version=\"1.0\"?\u003e\n\u003c!--\n     Author: Harry Kodden\n--\u003e\n\u003cmd:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" xml:id=\"MyData\" entityID=\"https://%%% SERVICE NAME %%%/metadata\"\u003e\n   \u003cds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\u003e\n    \u003cds:SignedInfo\u003e\n      \u003cds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/\u003e\n      \u003cds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/\u003e\n      \u003cds:Reference URI=\"#MyData\"\u003e\n        \u003cds:Transforms\u003e\n          \u003cds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/\u003e\n          \u003cds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/\u003e\n        \u003c/ds:Transforms\u003e\n        \u003cds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/\u003e\n        \u003cds:DigestValue/\u003e\n      \u003c/ds:Reference\u003e\n    \u003c/ds:SignedInfo\u003e\n    \u003cds:SignatureValue/\u003e\n    \u003cds:KeyInfo\u003e\n      \u003cds:KeyName/\u003e\n    \u003c/ds:KeyInfo\u003e\n  \u003c/ds:Signature\u003e  \u003cmd:Extensions xmlns:alg=\"urn:oasis:names:tc:SAML:metadata:algsupport\"\u003e\n    \u003calg:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha512\"/\u003e\n    \u003calg:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#sha384\"/\u003e\n    \u003calg:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"/\u003e\n    \u003calg:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#sha224\"/\u003e\n    \u003calg:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha224\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2009/xmldsig11#dsa-sha256\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/\u003e\n    \u003calg:SigningMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#dsa-sha1\"/\u003e\n  \u003c/md:Extensions\u003e\n    \u003cmd:SPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"\u003e\n    \u003cmd:Extensions\u003e\n      \u003cmdui:UIInfo xmlns:mdui=\"urn:oasis:names:tc:SAML:metadata:ui\"\u003e\n        \u003cmdui:DisplayName xml:lang=\"nl\"\u003eMy Velocity Service\u003c/mdui:DisplayName\u003e\n        \u003cmdui:DisplayName xml:lang=\"en\"\u003eMy Velocity Service\u003c/mdui:DisplayName\u003e\n        \u003cmdui:Description xml:lang=\"nl\"\u003eEen mooie voorbeelddienst om te laten zien hoe Shibboleth werkt\u003c/mdui:Description\u003e\n        \u003cmdui:Description xml:lang=\"en\"\u003eA nice example Service to show how to work with Shibboleth and SURFconext\u003c/mdui:Description\u003e\n        \u003cmdui:Logo height=\"300\" width=\"500\"\u003ehttps://%%% DOMAIN %%%/static/img/logo.png\u003c/mdui:Logo\u003e\n      \u003c/mdui:UIInfo\u003e\n      \u003cinit:RequestInitiator xmlns:init=\"urn:oasis:names:tc:SAML:profiles:SSO:request-init\" Binding=\"urn:oasis:names:tc:SAML:profiles:SSO:request-init\" Location=\"https://%%% DOMAIN %%%/saml/Login\"/\u003e\n    \u003c/md:Extensions\u003e\n    \u003cmd:KeyDescriptor\u003e\n      \u003cds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\u003e\n        \u003cds:KeyName\u003e%%% SERVICE NAME %%%\u003c/ds:KeyName\u003e\n        \u003cds:X509Data\u003e\n          \u003cds:X509SubjectName\u003eCN=%%% SERVICE NAME %%%\u003c/ds:X509SubjectName\u003e\n          \u003cds:X509Certificate\u003e%%% X509 %%%\u003c/ds:X509Certificate\u003e\n        \u003c/ds:X509Data\u003e\n      \u003c/ds:KeyInfo\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2009/xmlenc11#aes128-gcm\"/\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2009/xmlenc11#aes192-gcm\"/\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2009/xmlenc11#aes256-gcm\"/\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#aes128-cbc\"/\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#aes192-cbc\"/\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#aes256-cbc\"/\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#tripledes-cbc\"/\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2009/xmlenc11#rsa-oaep\"/\u003e\n      \u003cmd:EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p\"/\u003e\n    \u003c/md:KeyDescriptor\u003e\n    \u003cmd:ArtifactResolutionService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:SOAP\" Location=\"https://%%% DOMAIN %%%/saml/Artifact/SOAP\" index=\"1\"/\u003e\n    \u003cmd:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:SOAP\" Location=\"https://%%% DOMAIN %%%/saml/SLO/SOAP\"/\u003e\n    \u003cmd:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://%%% DOMAIN %%%/saml/SLO/Redirect\"/\u003e\n    \u003cmd:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://%%% DOMAIN %%%/saml/SLO/POST\"/\u003e\n    \u003cmd:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\" Location=\"https://%%% DOMAIN %%%/saml/SLO/Artifact\"/\u003e\n    \u003cmd:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://%%% DOMAIN %%%/saml/SAML2/POST\" index=\"1\"/\u003e\n    \u003cmd:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign\" Location=\"https://%%% DOMAIN %%%/saml/SAML2/POST-SimpleSign\" index=\"2\"/\u003e\n    \u003cmd:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\" Location=\"https://%%% DOMAIN %%%/saml/SAML2/Artifact\" index=\"3\"/\u003e\n    \u003cmd:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:PAOS\" Location=\"https://%%% DOMAIN %%%/saml/SAML2/ECP\" index=\"4\"/\u003e\n  \u003c/md:SPSSODescriptor\u003e\n  \u003cmd:Organization\u003e\n    \u003cmd:OrganizationName xml:lang=\"nl\"\u003eVoorbeeld (NL)\u003c/md:OrganizationName\u003e\n    \u003cmd:OrganizationName xml:lang=\"en\"\u003eExample (NL)\u003c/md:OrganizationName\u003e\n    \u003cmd:OrganizationDisplayName xml:lang=\"nl\"\u003eVoorbeeld Service\u003c/md:OrganizationDisplayName\u003e\n    \u003cmd:OrganizationDisplayName xml:lang=\"en\"\u003eExample Service\u003c/md:OrganizationDisplayName\u003e\n    \u003cmd:OrganizationURL xml:lang=\"nl\"\u003ehttps://%%% DOMAIN %%%/\u003c/md:OrganizationURL\u003e\n    \u003cmd:OrganizationURL xml:lang=\"en\"\u003ehttps://%%% DOMAIN %%%/\u003c/md:OrganizationURL\u003e\n  \u003c/md:Organization\u003e\n  \u003cmd:ContactPerson contactType=\"support\"\u003e\n    \u003cmd:GivenName\u003eJohn\u003c/md:GivenName\u003e\n    \u003cmd:SurName\u003eDoe\u003c/md:SurName\u003e\n    \u003cmd:EmailAddress\u003eJohn.Doe@example.org\u003c/md:EmailAddress\u003e\n  \u003c/md:ContactPerson\u003e\n  \u003cmd:ContactPerson contactType=\"technical\"\u003e\n    \u003cmd:GivenName\u003eJohn\u003c/md:GivenName\u003e\n    \u003cmd:SurName\u003eDoe\u003c/md:SurName\u003e\n    \u003cmd:EmailAddress\u003eJohn.Doe@example.org\u003c/md:EmailAddress\u003e\n  \u003c/md:ContactPerson\u003e\n  \u003cmd:ContactPerson contactType=\"administrative\"\u003e\n    \u003cmd:GivenName\u003eJohn\u003c/md:GivenName\u003e\n    \u003cmd:SurName\u003eDoe\u003c/md:SurName\u003e\n    \u003cmd:EmailAddress\u003eJohn.Doe@example.org\u003c/md:EmailAddress\u003e\n  \u003c/md:ContactPerson\u003e\n\u003c/md:EntityDescriptor\u003e\n~~~\n\nPlease replace at least these placeholders with the appropriate values:\n\n| Placeholder | To be replaced by |\n| --- | --- |\n| %%% DOMAIN %%%   | the full domain name, for example: ***https://www.example.org*** |\n| %%% X509 %%% | output of command ***openssl x509 -in server.crt*** \n|\t|(please remove the lines BEGIN CERTICATE and END CERTIFICATE). |\n| %%% SERVICE NAME %%% | The Service Name describing your service |\n\n\nBefore this Metadata can be send to the Identity Provider, we need to sign the contents.\n\nThis can be achieved from command line by using '***xmlsec1***'.\n\n~~~\nxmlsec1 --sign --output signed_metadata.xml --privkey-pem server.key metadata.xml\n~~~\n\n\n## Install + configure our hosts\n\nAs presented in the diagram earlier, we have 3 host components.\n\n- proxy\n- saml\n- jupyter\n\nThe proxy is connected to the public internet, the others are hosted within our shielded internal network.\n\nThe componens are specified in our docker-compose file: ***docker-compose.yml***.\n\n~~~\nversion: '2'\nservices:\n\n  proxy:\n    hostname: ${MY_HOSTNAME}\n    image: nginx:alpine\n    networks:\n      - front-end\n      - back-end\n    ports:\n      - \"443:443\"\n      - \"80:80\"\n    volumes:\n      - $PWD/etc/letsencrypt:/etc/letsencrypt:ro\n      - $PWD/etc/nginx.template:/etc/nginx/nginx.template:ro\n      - $PWD/img:/www/data/img:ro\n    restart: always\n    command: 'sh -c \"cat /etc/nginx/nginx.template | sed \\\"s/__MY_DOMAIN_NAME__/${MY_HOSTNAME}/\\\" \u003e /etc/nginx/nginx.conf \u0026\u0026 nginx -g \\\"daemon off;\\\"\"'\n\n  saml:\n    hostname: ${MY_HOSTNAME}\n    build:\n      context: saml\n      dockerfile: Dockerfile\n    networks:\n      - back-end\n    ports:\n      - \"443\"\n    environment:\n      SERVER_NAME: ${MY_HOSTNAME}\n      SHIBBOLETH_SP_ENTITY_ID: ${MY_ENTITY_ID}\n      SHIBBOLETH_SP_CERT: /run/sp/sp-cert.pem\n      SHIBBOLETH_SP_PRIVKEY: /run/sp/sp-key.pem\n      SHIBBOLETH_SP_METADATA_PROVIDER_XML_FILE: /run/sp/sp-metadata-myvelocity.xml\n    volumes:\n      - $PWD/etc/sp:/run/sp:ro\n      - $PWD/etc/sp/myvelocity-shibboleth2.xml:/etc/shibboleth/shibboleth2.xml:ro\n      - $PWD/etc/sp/attribute-map.xml:/etc/shibboleth/attribute-map.xml:ro\n      - $PWD/etc/letsencrypt:/etc/letsencrypt:ro\n      - $PWD/etc/idp/surfconext.test/certificate.pem:/opt/shibboleth-sp/etc/shibboleth/surfconext.pem:ro\n\n  jupyter:\n    build:\n      context: $PWD/jupyterhub\n      dockerfile: Dockerfile.jupyterhub\n      args:\n        - JUPYTERHUB_VERSION=${MY_JUPYTERHUB_VERSION}\n    networks:\n      - back-end\n    ports:\n      - \"8000\"\n    volumes:\n      - \"/var/run/docker.sock:/var/run/docker.sock:rw\"\n      - $PWD/etc/jupyterhub:/srv/jupyterhub\n      - $PWD/var/jupyter:/volumes/jupyter\n    command: jupyterhub\n\nnetworks:\n  front-end:\n    driver: bridge\n  back-end:\n    driver: bridge\n~~~\n\nSpecial attenticon for the network specification at the bottom of this file. The \"back-end\" network is relevant for the juputer interaction between the notebook and the hub. Later we will see that the the network name is specified within the JupyterHub-Configuration file.\n\nThe environment variables used in the ***docker-compose.yml*** file can be provided using a ***.env***\n\nThis file should contain:\n\n~~~\nMY_HOSTNAME=www.yourdomain.com\nMY_ENTITY_ID=https://%%% SERVICE NAME %%%/metadata\nMY_JUPYTERHUB_VERSION=0.8.0\nMY_DOCKER_NOTEBOOK_IMAGE=jupyter/scipy-notebook\nMY_LOCAL_NOTEBOOK_IMAGE=jupyterhub-user\n~~~\n\nNote: The value of ***MY\\_ENTITY\\_ID*** must match the value that you have provided in your METADATA at the attribute: ***entityID***\n\n#### Configure NGINX - Reverse Proxy\n\nThe NGINX Proxy functions as our single internet connected host. The proxy takes care of SSL-offloading and passing the requests downstream to the other components.\n\nThe following proxying takes place:\n\n- static contents like images are served directly.\n- all requests to ***/jupyter*** and ***/hub*** and forwarded to be handled by the SAML host. The SAML host enforces authenticated session before additional services can be offered to the user.\n- all request to ***/user*** are passed on to the Jupyter host. This host will forward request to the appropriate runnint ***notebook*** but only if there is an active valid session for the user.\n- Jupyter User Notebooks requests are directly passed onto the JupyterHUB and may bypass the SAML host.\n- All ***/saml*** requests are forwarded ot the SAML host.\n\nHere is the most relevant part of the file ***etc/nginx.template***\n\n~~~\n    location /img {\n      root /www/data;\n    }\n\n    location /jupyter {\n      proxy_pass         https://saml/jupyter;\n    }\n\n    location /hub {\n      proxy_pass         https://saml/jupyter/hub;\n    }\n\n    location /user {\n      proxy_pass         http://jupyter:8000/user;\n    }\n\n    location /saml {\n      proxy_pass         https://saml/saml;\n    }\n~~~\n\n#### Configure SAML (Apache + Shibboleth)\n\nThis host is serving a standard Apache2 webserver as well as a Shibboleth Server. Please refer to the ***saml/Dockerfile*** for the details on how this image is build.\n\nThe relevant part in the Apache Configuration takes care of the SAML handling and a value of REMOTE_USER is set after succesful authentication.\n\n~~~\n  \u003cLocation \"/saml\"\u003e\n    SetHandler shib\n  \u003c/Location\u003e\n\n  \u003cLocation /jupyter/hub/logout\u003e\n    RedirectMatch 301 .* /saml/Logout?return=/\n  \u003c/Location\u003e\n\n  \u003cLocation /jupyter/hub\u003e\n    AuthType shibboleth\n    ShibRequestSetting requireSession 1\n    Require valid-user\n\n    RewriteEngine On\n    RewriteCond %{LA-U:REMOTE_USER} (.*)\n    RewriteRule . - [E=RU:%1]\n    RequestHeader set REMOTE_USER \"%{RU}e\" env=RU\n  \u003c/Location\u003e\n\n  ProxyPreserveHost On\n\n  ProxyPass /jupyter              http://jupyter:8000/\n  ProxyPassReverse /jupyter       http://jupyter:8000/\n\n  ProxyPass /jupyter/hub          http://jupyter:8000/hub\n  ProxyPassReverse /jupyter/hub   http://jupyter:8000/hub\n  \n~~~\n\n### Prepare Jupyter\n\nThe Jupyter host is prepared from a standard JupyterHub docker image with added support for DockerSpawner and Remote User Authentication.\n\nThe Docker build file looks like:\n\n~~~\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\nARG JUPYTERHUB_VERSION\nFROM jupyterhub/jupyterhub-onbuild:$JUPYTERHUB_VERSION\n\n# Install dockerspawner, oauth, postgres\nRUN /opt/conda/bin/conda install -yq psycopg2=2.7 \u0026\u0026 \\\n    /opt/conda/bin/conda clean -tipsy \u0026\u0026 \\\n    /opt/conda/bin/pip install --no-cache-dir \\\n        jhub_remote_user_authenticator==0.0.* \\\n        dockerspawner==0.9.*\n\n~~~\n\nThe Jupyter host acts like a hub. The configuration is specified in ***etc/jupyterhub/jupyterhub_config.py***\n\nSome important details are:\n\n| Variable | Value |\n| --- | --- |\n| DOCKER\\_NOTEBOOK\\_IMAGE | \"jupyterhub-user\" |\n| c.JupyterHub.spawner_class | 'dockerspawner.DockerSpawner' |\n| network_name | 'jupyterhubsaml_back-end' |\n\n\nIn order to allow volume names in our notebook to be created with some special characters (like '@' in email names), we need to address the proper volume naming plugin.\n\n~~~\nimport dockerspawner\nc.DockerSpawner.format_volume_name = dockerspawner.volumenamingstrategy.escaped_format_volume_name\n~~~\n\nHere we instantiate the Remote User authenticator as our JupyterHub authenticator.\n\n~~~\nc.JupyterHub.authenticator_class = 'jhub_remote_user_authenticator.remote_user_auth.RemoteUserAuthenticator'\n~~~\n\n\n## Prepare a notebook\n\nThe notebook is build by a seperate Makefile and results in a Docker Image with the tag-name ***jupyterhub-user***\n\nCommand to (re-)build your notebook\n\n~~~\ncd notebook\nmake\n~~~\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharrykodden%2Fjupyterhub-saml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharrykodden%2Fjupyterhub-saml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharrykodden%2Fjupyterhub-saml/lists"}