feat(spawner): trust enterprise CA bundle in singleuser/app pods#102
Conversation
Wire _setup_trust_bundle into the _pre_spawn_hook orchestrator (step 1b, after Nebi auto-auth) gated on _trust_bundle_enabled. Add TDD tests that verify the flag reflects config and that the orchestrator skips or applies the CA merge accordingly.
The nebi-pull init container runs `nebi pull` + `pixi install` over HTTPS, which behind a TLS-inspecting proxy needs the merged org CA too. Inject the five CA env vars and the ca-merged mount into nebi-pull when the trust bundle is enabled, and run _setup_trust_bundle before Nebi auto-auth so merge-ca-bundle executes first and the ca-merged volume exists when nebi-pull mounts it. Also pins merge-ca-bundle's imagePullPolicy to IfNotPresent.
Manual verification on a cloud (Hetzner) cluster — real hub-spawned podVerified end-to-end on a nebari-infrastructure-core cluster on Hetzner (amd64), deployed with an org CA in Setup
What I verified
All three pass: trust succeeds only when the merged bundle (org CA) is in play. Note on the toolchainThe data-science-pack jupyterlab image is pixi-based — it ships neither Reproduction steps (cloud / Hetzner)Replace 0. Prerequisites
1. Create the org CA (fed to NIC's
|
…ndle-85 # Conflicts: # config/jupyterhub/01-spawner.py
dcmcand
left a comment
There was a problem hiding this comment.
Thanks for this @tylerpotts
Summary
Closes #85.
Makes JupyterHub singleuser and jhub-apps pods trust the enterprise CA so
pip install,conda install,git clone, and arbitrary user-driven HTTPS work through a TLS-inspecting proxy with no--trusted-host/ssl_verify: falseworkarounds.nebari-trust-bundleConfigMap (keyca-certificates.crt) — no cross-repo config plumbing; the Bundle'snamespaceSelector: {}already covers JupyterHub's namespace.merge-ca-bundle) concatenates the singleuser image's system CA bundle with the org CA into anemptyDir; every CA env var (REQUESTS_CA_BUNDLE,SSL_CERT_FILE,NODE_EXTRA_CA_CERTS,CURL_CA_BUNDLE,GIT_SSL_CAINFO) points at the merged file. This verifies both proxy-inspected (org-signed) and genuine public-root endpoints.nebi-pullinit container (nebi pull+pixi installover HTTPS) gets the same CA env vars + merged mount, and_setup_trust_bundleruns before Nebi auto-auth somerge-ca-bundleexecutes first and theca-mergedvolume exists whennebi-pullmounts it.custom.trust-bundle-enabled(defaultfalse) — existing behavior is unchanged byte-for-byte. ConfigMap name/key are overridable viacustom.trust-bundle-configmap/custom.trust-bundle-key. The ConfigMap mount staysoptional: trueso a cluster without trust-manager (or a spawn racing the projection) degrades to just the system bundle.Design + plan:
docs/superpowers/specs/2026-06-03-singleuser-ca-bundle-design.md,docs/superpowers/plans/2026-06-03-singleuser-ca-bundle.md.Test plan
pytest tests/unit/test_spawner_ca_bundle.py— 9 passing (setup, custom configmap/key, append-without-clobber, toggle reflection, orchestrator on/off, nebi-pull CA on/off, init-container ordering).pytest tests/unit/test_spawner_storage.py tests/unit/test_nss_wrapper_shared_dir.py— no regression from the orchestrator reorder.pip install requests,conda install,git clonean HTTPS repo, and a Nebi-workspace spawn (pixi install) — all with no flags.