From 5d7311dd9dac083f1ad118683e227540be7a581b Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 6 Aug 2025 11:37:26 -0700 Subject: [PATCH 1/2] raise on invalid keys in hub.config valid sub-config keys must be class names starting with uppercase. If they are lowercase, something is wrong and config will be ignored, best not to do so silently. --- jupyterhub/files/hub/jupyterhub_config.py | 27 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index ad742ed490..8915912eb7 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -478,16 +478,33 @@ def camelCaseify(s): c.CryptKeeper.keys = get_secret_value("hub.config.CryptKeeper.keys").split(";") # load hub.config values, except potentially seeded secrets already loaded -for app, cfg in get_config("hub.config", {}).items(): - if app == "JupyterHub": +for section, cfg in get_config("hub.config", {}).items(): + if section == "JupyterHub": cfg.pop("proxy_auth_token", None) cfg.pop("cookie_secret", None) cfg.pop("services", None) - elif app == "ConfigurableHTTPProxy": + elif section == "ConfigurableHTTPProxy": cfg.pop("auth_token", None) - elif app == "CryptKeeper": + elif section == "CryptKeeper": cfg.pop("keys", None) - c[app].update(cfg) + + if not section[:1].isupper(): + # traitlets config sections are Configurable class names + # that MUST start with upper-case + # if it starts with lowercase, it must be a mistake + # (e.g. putting `hub.loadRoles` under `hub.config.loadRoles`), + # and will have no effect, so warn or raise here + print( + f"FATAL: Invalid hub.config section name: {section}." + " hub.config sections must be Configurable class names (e.g. JupyterHub)." + " Maybe misplaced or misspelled config?", + file=sys.stderr, + ) + # make this fatal: + sys.exit(1) + + print(f"Loading pass-through config section hub.config.{section}") + c[section].update(cfg) # load /usr/local/etc/jupyterhub/jupyterhub_config.d config files config_dir = "/usr/local/etc/jupyterhub/jupyterhub_config.d" From 7e6c1189d3f7a59be33a698da09ee0ecfcac3cd0 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 6 Aug 2025 20:36:15 -0700 Subject: [PATCH 2/2] further validate hub.config sections in values schema - keys must start with caps to be valid (not much more validation is possible) - values must be objects does not validate class names or trait names, but gets the very basics --- jupyterhub/values.schema.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index a739554f8f..ce2c361a2a 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -207,7 +207,7 @@ properties: for more info. config: type: object - additionalProperties: true + additionalProperties: false description: | JupyterHub and its components (authenticators, spawners, etc), are Python classes that expose its configuration through @@ -271,6 +271,18 @@ properties: the `--values` or `-f` flag. During merging, lists are replaced while dictionaries are updated. ``` + # check that the first character is Capital, + # which is required for valid section names + # can't know all possible Configurable class names in advance, though + patternProperties: + "^[A-Z].*$": + type: object + additionalProperties: true + description: | + Pass-through traitlets configuration of Configurable classes. + Keys must always be class names that start with capitals, + and values must be objects. + properties: JupyterHub: type: object