feat(jupyterhub): vault token w/o keycloak auth

This commit is contained in:
Masaki Yatsu
2025-09-03 10:11:06 +09:00
parent 02ec5eb1e2
commit d233373219
15 changed files with 583 additions and 612 deletions

View File

@@ -5,7 +5,7 @@ export JUPYTERHUB_CHART_VERSION := env("JUPYTERHUB_CHART_VERSION", "4.2.0")
export JUPYTERHUB_OIDC_CLIENT_ID := env("JUPYTERHUB_OIDC_CLIENT_ID", "jupyterhub")
export JUPYTERHUB_NFS_PV_ENABLED := env("JUPYTERHUB_NFS_PV_ENABLED", "")
export JUPYTERHUB_VAULT_INTEGRATION_ENABLED := env("JUPYTERHUB_VAULT_INTEGRATION_ENABLED", "")
export JUPYTER_PYTHON_KERNEL_TAG := env("JUPYTER_PYTHON_KERNEL_TAG", "python-3.12-8")
export JUPYTER_PYTHON_KERNEL_TAG := env("JUPYTER_PYTHON_KERNEL_TAG", "python-3.12-24")
export KERNEL_IMAGE_BUUN_STACK_REPOSITORY := env("KERNEL_IMAGE_BUUN_STACK_REPOSITORY", "buun-stack-notebook")
export KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY := env("KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY", "buun-stack-cuda-notebook")
export JUPYTER_PROFILE_MINIMAL_ENABLED := env("JUPYTER_PROFILE_MINIMAL_ENABLED", "false")
@@ -20,6 +20,7 @@ export IMAGE_REGISTRY := env("IMAGE_REGISTRY", "localhost:30500")
export KEYCLOAK_REALM := env("KEYCLOAK_REALM", "buunstack")
export LONGHORN_NAMESPACE := env("LONGHORN_NAMESPACE", "longhorn")
export VAULT_ADDR := env("VAULT_ADDR", "http://vault.vault.svc:8200")
export JUPYTER_BUUNSTACK_LOG_LEVEL := env("JUPYTER_BUUNSTACK_LOG_LEVEL", "info")
[private]
default:
@@ -54,6 +55,15 @@ install:
--placeholder="e.g., jupyter.example.com"
)
done
# Generate JUPYTERHUB_CRYPT_KEY if not exists
if [ -z "${JUPYTERHUB_CRYPT_KEY:-}" ]; then
echo "Generating JUPYTERHUB_CRYPT_KEY..."
export JUPYTERHUB_CRYPT_KEY=$(just utils::random-password)
echo "JUPYTERHUB_CRYPT_KEY=${JUPYTERHUB_CRYPT_KEY}" >> ../../.env.local
echo "✓ JUPYTERHUB_CRYPT_KEY generated and saved to .env.local"
fi
just create-namespace
# just k8s::copy-regcred ${JUPYTERHUB_NAMESPACE}
just keycloak::create-client ${KEYCLOAK_REALM} ${JUPYTERHUB_OIDC_CLIENT_ID} \
@@ -96,8 +106,17 @@ install:
fi
kubectl apply -n ${JUPYTERHUB_NAMESPACE} -f nfs-pvc.yaml
fi
# Create or get JupyterHub Vault token before gomplate
if ! just vault::exist jupyterhub/vault-token &>/dev/null; then
echo "Creating JupyterHub Vault token..."
just create-jupyterhub-vault-token
fi
export JUPYTERHUB_VAULT_TOKEN=$(just vault::get jupyterhub/vault-token token)
# https://z2jh.jupyter.org/en/stable/
gomplate -f jupyterhub-values.gomplate.yaml -o jupyterhub-values.yaml
helm upgrade --cleanup-on-fail --install jupyterhub jupyterhub/jupyterhub \
--version ${JUPYTERHUB_CHART_VERSION} -n ${JUPYTERHUB_NAMESPACE} \
--timeout=20m -f jupyterhub-values.yaml
@@ -138,62 +157,68 @@ delete-pv:
# Build Jupyter notebook kernel images
build-kernel-images:
#!/bin/bash
set -euo pipefail
# Build python package wheel
cd ../python-package
rm -rf dist/ build/ *.egg-info/
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_BUUNSTACK=0.1.0 python -m build --wheel
cd ../jupyterhub
# Copy built wheel to image directories
cp ../python-package/dist/*.whl ./images/datastack-notebook/
cp ../python-package/dist/*.whl ./images/datastack-cuda-notebook/
set -euxo pipefail
(
cd ../python-package
rm -rf dist/ build/ *.egg-info/
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_BUUNSTACK=0.1.0 python -m build --wheel
)
(
cd ./images/datastack-notebook
cp ../../../python-package/dist/*.whl ./
docker build -t \
${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG} \
--build-arg spark_version="3.5.4" \
--build-arg spark_download_url="https://archive.apache.org/dist/spark/" \
.
)
(
cd ./images/datastack-cuda-notebook
docker build -t \
${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG} \
--build-arg spark_version="3.5.4" \
--build-arg spark_download_url="https://archive.apache.org/dist/spark/" \
.
)
# Clean up copied wheel files
rm -f ./images/datastack-notebook/*.whl
rm -f ./images/datastack-cuda-notebook/*.whl
if [ "${JUPYTER_PROFILE_BUUN_STACK_CUDA_ENABLED}" = "true" ]; then
(
cd ./images/datastack-cuda-notebook
cp ../../../python-package/dist/*.whl ./
docker build -t \
${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG} \
--build-arg spark_version="3.5.4" \
--build-arg spark_download_url="https://archive.apache.org/dist/spark/" \
.
)
rm -f ./images/datastack-cuda-notebook/*.whl
fi
# Push Jupyter notebook kernel images
push-kernel-images: build-kernel-images
docker push ${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG}
docker push ${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG}
# Configure Vault for JupyterHub integration
setup-vault-integration:
#!/bin/bash
set -euo pipefail
echo "Creating JupyterHub Vault policy..."
just vault::write-policy jupyter-user $(pwd)/vault-policy.hcl
echo "✓ JupyterHub policy created"
docker push ${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG}
if [ "${JUPYTER_PROFILE_BUUN_STACK_CUDA_ENABLED}" = "true" ]; then
docker push ${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG}
fi
# Setup JWT auth for JupyterHub tokens (no re-authentication needed)
# Setup Vault integration for JupyterHub (user-specific tokens)
setup-vault-jwt-auth:
#!/bin/bash
set -euo pipefail
echo "Setting up Vault integration for JupyterHub..."
just setup-vault-integration
just vault::setup-jwt-auth "jupyterhub" "jupyter-token" "jupyter-user"
echo "✓ Vault integration configured"
echo "✓ Vault integration configured (user-specific tokens)"
echo ""
echo "Users can now access Vault from notebooks using:"
echo " import os, hvac"
echo " client = hvac.Client(url=os.getenv('VAULT_ADDR'), verify=False)"
echo " client.auth.jwt.jwt_login("
echo " role='jupyter-token',"
echo " jwt=os.getenv('JUPYTERHUB_OIDC_ACCESS_TOKEN'),"
echo " path='jwt'"
echo " )"
echo " from buunstack import SecretStore"
echo " secrets = SecretStore()"
echo " # Each user gets their own isolated Vault token and policy"
# Create JupyterHub Vault token (uses admin policy for JWT operations)
create-jupyterhub-vault-token ttl="720h":
#!/bin/bash
set -euo pipefail
echo "Creating JupyterHub Vault token with admin policy..."
# JupyterHub needs admin privileges to read Keycloak credentials from Vault
# Create token and store in Vault
just vault::create-token-and-store admin jupyterhub/vault-token {{ ttl }}
echo "✓ JupyterHub Vault token created and stored"
echo ""
echo "To use in JupyterHub deployment:"
echo " JUPYTERHUB_VAULT_TOKEN=\$(just vault::get jupyterhub/vault-token token)"