With running JupyterHub there are many configuration combinations you may choose to use. Below you will learn about a few common configurations that could be valuable for your configurations.

  • AWS Fargate Spawner
  • Named Servers with API Service
  • Spawner Callbacks
  • Entra ID Authentication (Azure AD Authentication)

Prerequisite

As a matter of good practice you won’t want your secrets in your configuration file. You will want to include a helper function to get a secret from your secret vault. Below we have an example with AWS Secrets Manager that you can use with your jupyterhub_config.py file. This function is used by a couple of the following sections.

import json
import boto3
from botocore.exceptions import ClientError
def get_secret(secret, key):
    secret_name = secret
    region_name = "us-east-1"
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )
    try:
        get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )
    except ClientError as e:
        raise e
    secret = get_secret_value_response['SecretString']
    val = json.loads(secret)
    return val[key]

AWS Fargate Spawner

Your JupyterHub instance can spawn Jupyter Notebook instances as AWS Fargate Tasks. This open source Fargate Spawner provides a way to interface with the AWS APIs to create new tasks.

You can install the package to your JupyterHub Docker image as shown below.

RUN pip3 install fargatespawner

Then within your jupyterhub_config.py file you will need to specify how you would like the spawner to create the tasks. Then finally you will want to configure how the spawner will authenticate with the AWS APIs.

from fargatespawner import FargateSpawner
c.JupyterHub.spawner_class = FargateSpawner
c.FargateSpawner.aws_region = 'us-east-1'
c.FargateSpawner.aws_ecs_host = 'ecs.us-east-1.amazonaws.com'
c.FargateSpawner.notebook_port = 8888
c.FargateSpawner.notebook_scheme = 'http'
c.FargateSpawner.get_run_task_args = lambda spawner: {
    'cluster': 'jupyterLabs',
    'taskDefinition': 'JupyterLab:2',
    'overrides': {
        'containerOverrides': [{
            'command': spawner.cmd,
            'environment': [
                {
                    'name': name,
                    'value': value,
                } for name, value in spawner.get_env().items()
            ],
            'name': 'dotnet',
        }],
    },
    'count': 1,
    'launchType': 'FARGATE',
    'networkConfiguration': {
        'awsvpcConfiguration': {
            'assignPublicIp': 'ENABLED',
            'securityGroups': ['sg-012345678abcd0123'],
            'subnets':  ['subnet-abcd0123','subnet-0123abcd'],
        },
    },
}

from fargatespawner import FargateSpawnerECSRoleAuthentication
c.FargateSpawner.authentication_class = FargateSpawnerECSRoleAuthentication

Named Servers with API Service

You may want to use named instances that can be spawned independent of a user interaction. To accomplish this you can allow named servers, setup a spawner with admin permissions that uses an API secret from your secret vault. These can be defined in your jupyterhub_config.py file.

c.JupyterHub.allow_named_servers = True

c.JupyterHub.services = [
    {
        "name": "spawner-admin",
        "api_token": get_secret('JupyterHubService','api_token')
    },
]

c.JupyterHub.load_roles = [
    {
        "name": "service-role",
        "scopes": [
            "admin:users",
            "admin:servers",
        ],
        "services": [
            "spawner-admin",
        ],
    }
]

Spawner Callbacks

When you want to run JupyterHub itself as a container instance, the configuration needs a little more guidance for how the Notebook instances should communicate with it. The below jupyterhub_config.py file snippet requires you to set most notably the c.Spawner.hub_connect_url to the URL of your JupyterHub instance which the Jupyter Notebook instance will communicate with.

c.Spawner.ip = '0.0.0.0'
c.Spawner.port = 8888
c.Spawner.env_keep = ['PYTHONPATH', 'CONDA_ROOT', 'CONDA_DEFAULT_ENV', 'VIRTUAL_ENV', 'LANG', 'LC_ALL', 'JUPYTERHUB_SINGLEUSER_APP']
c.Spawner.hub_connect_url = 'https://jupyter.your.domain.com'
c.Spawner.start_timeout = 120

Entra ID (Azure AD) Authentication

Enabling single sign-on with your existing Identity Provider (IdP) simplifies the experience for the users of your JupyterHub instance and the administrator burden as well. For this setup you will use Microsoft Entra ID Authentication, formerly known as Azure AD Authentication.

You can install the prerequisite Python packages in your JupyterHub Docker image as shown below.

RUN pip3 install PyJWT
RUN pip3 install oauthenticator

With your prerequisites installed you can configure your jupyterhub_config.py file to utilize the OAuthenticator package. You will need to specify your Entra ID Tenant ID (Azure AD Tenant ID) GUID for the c.AzureAdOAuthenticator.tenant_id property. Specify your JupyterHub callback URL for the authentication flow to return your users to with the c.AzureAdOAuthenticator.oauth_callback_url property. The Client ID of your Entra ID Application with the c.AzureAdOAuthenticator.client_id property. Then finally make sure your Client Secret is within your secret vault.

from oauthenticator.azuread import AzureAdOAuthenticator
c.JupyterHub.authenticator_class = AzureAdOAuthenticator

c.AzureAdOAuthenticator.tenant_id = '0123abcd-01ab-ab01-abcd-0123abcd4567'

c.AzureAdOAuthenticator.oauth_callback_url = 'https://jupyter.your.domain.com/hub/oauth_callback'
c.AzureAdOAuthenticator.client_id = 'abcd0123-ab01-01ab-dcba-45670123abcd'
c.AzureAdOAuthenticator.client_secret = get_secret('JupyterHub', 'client_secrect')
c.AzureAdOAuthenticator.scope = ['openid']

Summary

Through this post you saw how to setup:

  • A helper function to pull secrets from AWS Secrets Manager
  • Configure JupyterHub to use an AWS Fargate Spawner
  • Configure JupyterHub to allow Named Servers with an API Service
  • Configure Spawner Callbacks to a dynamic JupyterHub instance
  • Configure JupyterHub to use Entra ID Authentication (Azure AD Authentication)

There is plenty more configuration you can do with JupyterHub and the Jupyter ecosystem in general.


<
Previous Post
AWS EBS Partition Alignment
>
Next Post
Azure Arc Managed Identities