import os from dockerspawner import DockerSpawner from nativeauthenticator import NativeAuthenticator import secrets import string from typing import Optional def generate_api_token( length: int = 32, prefix: Optional[str] = None, ) -> dict: """ Generate a secure random API token with optional prefix and expiration. Args: length: Length of the token (default: 32) prefix: Optional prefix for the token (e.g., 'hub_') expiration_days: Optional expiration in days from now Returns: - token: The generated token """ # Generate cryptographically secure random string alphabet = string.ascii_letters + string.digits token = "".join(secrets.choice(alphabet) for _ in range(length)) # Add prefix if specified if prefix: token = f"{prefix}{token}" return token # Configuration file for JupyterHub c = get_config() # Base URL configuration # The new way, dont use ip/port/bind_url # attributes it throws a warning and is deprecated port = int(os.environ.get("JUPYTERHUB_PORT", "8000")) base_url = os.environ.get("JUPYTERHUB_BASE_URL", "/jupyter/") c.JupyterHub.bind_url = f"http://0.0.0.0:{port}{base_url}" # Mission Critical this should be the service name not the container name # without this option containers cant reach the api endpoint # this is due to nginx external rounting and might help # migration this project to a cluster setup c.JupyterHub.hub_connect_ip = "jupyterhub" # Database configuration p_user = os.environ["POSTGRES_USER"] p_pass = os.environ["POSTGRES_PASSWORD"] p_host = os.environ["POSTGRES_HOST"] p_db = os.environ["POSTGRES_DB"] c.JupyterHub.db_url = f"postgresql://{p_user}:{p_pass}@{p_host}:5432/{p_db}" # c.JupyterHub.db_url = 'sqlite:///jupyterhub.sqlite' # Authenticator configuration - NativeAuthenticator c.JupyterHub.authenticator_class = NativeAuthenticator # Native Authenticator settings c.NativeAuthenticator.minimum_password_length = int( os.environ.get("NATIVE_AUTH_MIN_PASSWORD_LENGTH", "8") ) c.NativeAuthenticator.check_common_password = True c.NativeAuthenticator.enable_signup = True c.NativeAuthenticator.ask_email_on_signup = False c.NativeAuthenticator.allow_2fa = False # Admin configuration admin_user = os.environ.get("JUPYTERHUB_ADMIN_USER", "admin") c.Authenticator.admin_users = {admin_user} c.Authenticator.any_allow_config = True c.Authenticator.allow_all = True # Custom logo and templates c.JupyterHub.logo_file = "/srv/jupyterhub/templates/logo.png" c.JupyterHub.template_paths = ["/etc/jupyterhub/templates"] # Hub environment for containers # Spawn Single-User-Servers as Docker Containers c.JupyterHub.spawner_class = DockerSpawner # Spawn Containers from this Image c.DockerSpawner.image = os.environ.get("NOTEBOOK_IMAGE", "jupyter/base-notebook:latest") # Spawn this "version" of the Notebook c.DockerSpawner.default_url = os.environ.get("SPAWNER_DEFAULT_URL", "/tree") # Connect Containers to this network network_name = os.environ.get("DOCKER_NETWORK_NAME", "stack_default") # Let docker manage network communications c.DockerSpawner.use_internal_ip = True c.DockerSpawner.network_name = network_name # Explicitly set notebook directory because we'll mounting a volume to it. notebook_dir = os.environ.get("DOCKER_NOTEBOOK_DIR", "/home/jovyan/work") c.DockerSpawner.notebook_dir = notebook_dir # Mount real User docker volume to the host c.DockerSpawner.volumes = {"jupyterhub-user-{username}": notebook_dir} # Remove Containers once there stopped c.DockerSpawner.remove = True # For Debbugging (passed to spawned Containers) ds_debug = True if int(os.environ.get("DOCKER_SPAWNER_DEBUG", "0")) else False c.DockerSpawner.debug = ds_debug # Dont mess with this # The Spawner sits inside a docker container which manages # everything network related c.Spawner.ip = "0.0.0.0" # Set Resource Limits c.DockerSpawner.mem_limit = os.environ.get("NOTEBOOK_MEMORY_LIMIT", "500M") c.DockerSpawner.cpu_limit = float(os.environ.get("NOTEBOOK_CPU_LIMIT", "1.0")) # Fixing CORS because Nginx # this isn't a security risk because the container is served behind nginx # this only affects the traffic within the docker network itself c.Spawner.args = [ "--ServerApp.allow_origin=*", "--ServerApp.allow_credentials=True", "--ServerApp.disable_check_xsrf=True", "--NotebookApp.allow_origin=*", "--NotebookApp.allow_credentials=True", "--NotebookApp.disable_check_xsrf=True", '--ServerApp.tornado_settings={"headers": {"Access-Control-Allow-Origin": "*"}}', # NBgrader extensions "--NotebookApp.nbserver_extensions={'nbgrader.server_extensions.formgrader':True}", "--NotebookApp.nbserver_extensions={'nbgrader.server_extensions.assignment_list':True}", "--NotebookApp.nbserver_extensions={'nbgrader.server_extensions.course_list':True}", ] # Security settings c.JupyterHub.cookie_secret_file = "/srv/jupyterhub/cookie_secret" c.ConfigurableHTTPProxy.auth_token = os.environ.get("JUPYTERHUB_AUTH_TOKEN", "") # Logging c.JupyterHub.log_level = os.environ.get("LOG_LEVEL", "INFO") # Shutdown settings c.JupyterHub.shutdown_on_logout = False c.JupyterHub.cleanup_servers = True # Timeout settings c.JupyterHub.active_server_limit = 0 c.JupyterHub.concurrent_spawn_limit = 10 # Allow named servers c.JupyterHub.allow_named_servers = True c.JupyterHub.named_server_limit_per_user = 3 # ngshare service configuration c.JupyterHub.services.append( { "name": "ngshare", "url": "http://127.0.0.1:10101", "command": [ "python3", "-m", "ngshare", "--admins", "admin", "--debug" if ds_debug else "", ], "environment": { "JUPYTERHUB_SERVICE_URL": "http://127.0.0.1:10101", "JUPYTERHUB_SERVICE_REDIRECT_URL": f"{base_url}services/ngshare/", "JUPYTERHUB_BASE_URL": base_url, }, } ) # Additional ngshare configuration c.JupyterHub.load_roles = [ { "name": "ngshare", "scopes": [ "admin:servers", "access:services", "list:users", "read:users:activity", ], "services": ["ngshare"], } ]