Hello everyone.
A new problem in my Jupyter Hub configuration, which relies on a swarm cluster (via SwarmSpawner) with CAS authentication.
My problem is that after authentication, the user should normally arrive at the /hub/spawn page where the list of available notebooks is located.
Then they should choose a notebook and start their server. But that’s not what happens: after authentication, the user is immediately redirected to a page where the CAS ticket is located: https:///hub/login?ticket=CAS ticket-CAS URL. This page seems to want to open a link to a container (the last one opened, it seems) that isn’t started yet. This causes a 500 error.
How can I prevent the HUB from redirecting the user to this page instead of to the list of containers to start?
PS: I don’t have anything relevant in the logs, except for an error message indicating that the user cannot be created on the system because it already exists.
Newly created users don’t have this problem because they haven’t started any containers yet.
here is my config:
import os
import shutil
import subprocess
import pwd
import grp
import logging
from dockerspawner import SwarmSpawner
import sys
c.ConfigurableHTTPProxy.should_start = False
c.ConfigurableHTTPProxy.api_url = 'http://proxy:8001'
c.LocalAuthenticator.create_system_users = True
class MySwarmSpawner(SwarmSpawner):
def start(self):
# specific cmd for speciffic images
image_name = self.user_options.get('image')
if image_name == '127.0.0.1:5000/datascience-notebook-capsule':
self.cmd = ['start-notebook.sh']
else:
self.cmd = ['jupyter-labhub']
return super().start()
def create_object(self):
username = self.user.name # get the username
jupyter_username = 'jupyter-' + username
# get UID/GID
uid = pwd.getpwnam(jupyter_username).pw_uid
gid = grp.getgrnam(jupyter_username).gr_gid
self.extra_container_spec.update({"user": f"{uid}:{gid}"})
return super().create_object()
def create_dir_hook(spawner):
"""Create directory and set permissions"""
username = spawner.user.name # get the username
jupyter_username = 'jupyter-' + username
volume_path = os.path.join('/home', jupyter_username)
uid = pwd.getpwnam(jupyter_username).pw_uid
gid = grp.getgrnam(jupyter_username).gr_gid
logging.info(f"Hook création dossier avec UID/GID : {uid}:{gid}")
if not os.path.exists(volume_path):
os.makedirs(volume_path, mode=0o755, exist_ok=True)
src_path = '/home_src/jovyan/'
if os.path.exists(src_path):
shutil.copytree(src_path, volume_path, dirs_exist_ok=True)
subprocess.run(['chown', '-R', f'{uid}:{gid}', volume_path], check=True)
subprocess.run(['chmod', '-R', 'u+rwX', volume_path], check=True)
def clean_dir_hook(spawner):
"""Delete directory"""
username = spawner.user.name # get the username
temp_path = os.path.join('/home', username, 'temp')
if os.path.exists(temp_path) and os.path.isdir(temp_path):
shutil.rmtree(temp_path)
c.Spawner.pre_spawn_hook = create_dir_hook
c.Spawner.post_stop_hook = clean_dir_hook
# Volumes
c.SwarmSpawner.volumes = {
'/data/2025/prod.CAS/home/jupyter-{username}': '/home/jovyan',
}
c.JupyterHub.spawner_class = MySwarmSpawner
c.JupyterHub.hub_ip = '0.0.0.0'
c.JupyterHub.hub_connect_ip = 'hub'
c.SwarmSpawner.network_name = 'jupyterhub-net'
c.SwarmSpawner.extra_host_config = {'network_mode': 'jupyterhub-net'}
c.JupyterHub.shutdown_on_logout = True
c.JupyterHub.cleanup_servers = True
c.JupyterHub.allow_named_servers = False
c.JupyterHub.named_server_limit_per_user = 1
c.JupyterHub.default_url = '/hub/spawn'
c.JupyterHub.default_server_name = '' # no automatic redirection
c.JupyterHub.redirect_to_server = False # block auto redirection after login
# Logging DEBUG
c.JupyterHub.log_level = logging.DEBUG
from jhub_cas_authenticator.cas_auth import CASLocalAuthenticator
class CustomCASLocalAuthenticator(CASLocalAuthenticator):
async def authenticate(self, handler, data):
result = await super().authenticate(handler, data)
return result
async def validate_cas_ticket(self, ticket):
response = await super().validate_cas_ticket(ticket)
return response
c.JupyterHub.authenticator_class = CustomCASLocalAuthenticator
# debug-logging for testing
import logging
c.JupyterHub.log_level = logging.DEBUG
# CAS configuration
c.CASLocalAuthenticator.cas_login_url = 'https://<CAS server URL>/cas/'
c.CASLocalAuthenticator.cas_service_url = 'https://<jupyterhub URL>/login'
c.CASLocalAuthenticator.cas_client_ca_certs = '/srv/jupyterhub/CAS.pem'
c.CASLocalAuthenticator.cas_service_validate_url = 'https://<CAS server URL>/cas/p3/serviceValidate'
c.DockerSpawner.image_whitelist = {
"Datasciences: python, R and Julia": "127.0.0.1:5000/datascience-notebook-capsule",
"R and Spark Stack" : "quay.io/jupyter/all-spark-notebook",
"Tensor Flow" : "quay.io/jupyter/tensorflow-notebook",
"Julia" : "quay.io/jupyter/julia-notebook",
}
c.JupyterHub.services = [
{
"name": "jupyterhub-idle-culler-service",
"command": [
sys.executable,
"-m", "jupyterhub_idle_culler",
"--timeout=3000"
],
}
]
c.JupyterHub.load_roles = [
{
"name": "jupyterhub-idle-culler-role",
"scopes": [
"list:users",
"read:users:activity",
"read:servers",
"delete:servers",
],
"services": ["jupyterhub-idle-culler-service"],
}
]
Any help would be greatly appreciated