diff --git a/jupyterhub/jupyterhub-values.gomplate.yaml b/jupyterhub/jupyterhub-values.gomplate.yaml index aaeb3a5..beb8b2f 100644 --- a/jupyterhub/jupyterhub-values.gomplate.yaml +++ b/jupyterhub/jupyterhub-values.gomplate.yaml @@ -11,6 +11,11 @@ hub: #!/bin/bash pip install --no-cache-dir hvac==2.3.0 exec jupyterhub --config /usr/local/etc/jupyterhub/jupyterhub_config.py --upgrade-db + user_policy.hcl: + mountPath: /srv/jupyterhub/user_policy.hcl + mode: 0644 + stringData: | +{{ .Env.USER_POLICY_HCL | strings.Indent 8 }} # Override the default command to run our startup script first command: @@ -73,36 +78,15 @@ hub: # Step 2: Create user-specific policy user_policy_name = "jupyter-user-{}".format(username) - user_path = "secret/data/jupyter/users/{}/*".format(username) - user_metadata_path = "secret/metadata/jupyter/users/{}/*".format(username) - user_base_path = "secret/metadata/jupyter/users/{}".format(username) - user_policy = ( - "# User-specific policy for {}\n".format(username) + - "path \"{}\" ".format(user_path) + "{\n" + - " capabilities = [\"create\", \"update\", \"read\", \"delete\", \"list\"]\n" + - "}\n\n" + - "path \"{}\" ".format(user_metadata_path) + "{\n" + - " capabilities = [\"list\", \"read\", \"delete\", \"update\"]\n" + - "}\n\n" + - "path \"{}\" ".format(user_base_path) + "{\n" + - " capabilities = [\"list\"]\n" + - "}\n\n" + - "# Read access to shared resources\n" + - "path \"secret/data/jupyter/shared/*\" {\n" + - " capabilities = [\"read\", \"list\"]\n" + - "}\n\n" + - "path \"secret/metadata/jupyter/shared\" {\n" + - " capabilities = [\"list\"]\n" + - "}\n\n" + - "# Token management capabilities\n" + - "path \"auth/token/lookup-self\" {\n" + - " capabilities = [\"read\"]\n" + - "}\n\n" + - "path \"auth/token/renew-self\" {\n" + - " capabilities = [\"update\"]\n" + - "}" - ) + # Read policy template from file + import os + policy_template_path = "/srv/jupyterhub/user_policy.hcl" + with open(policy_template_path, 'r') as f: + policy_template = f.read() + + # Replace {username} placeholder with actual username + user_policy = policy_template.replace("{username}", username) # Write user-specific policy try: diff --git a/jupyterhub/justfile b/jupyterhub/justfile index 0f76488..fa20059 100644 --- a/jupyterhub/justfile +++ b/jupyterhub/justfile @@ -17,6 +17,8 @@ export JUPYTER_PROFILE_TENSORFLOW_ENABLED := env("JUPYTER_PROFILE_TENSORFLOW_ENA export JUPYTER_PROFILE_BUUN_STACK_ENABLED := env("JUPYTER_PROFILE_BUUN_STACK_ENABLED", "false") export JUPYTER_PROFILE_BUUN_STACK_CUDA_ENABLED := env("JUPYTER_PROFILE_BUUN_STACK_CUDA_ENABLED", "false") export IMAGE_REGISTRY := env("IMAGE_REGISTRY", "localhost:30500") +export NOTEBOOK_VAULT_TOKEN_TTL := env("NOTEBOOK_VAULT_TOKEN_TTL", "1h") +export NOTEBOOK_VAULT_TOKEN_MAX_TTL := env("NOTEBOOK_VAULT_TOKEN_MAX_TTL", "720h") 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") @@ -112,6 +114,9 @@ install: just create-jupyterhub-vault-token export JUPYTERHUB_VAULT_TOKEN=$(just vault::get jupyterhub/vault-token token) + # Read user policy template for Vault + export USER_POLICY_HCL=$(cat user_policy.hcl) + # https://z2jh.jupyter.org/en/stable/ gomplate -f jupyterhub-values.gomplate.yaml -o jupyterhub-values.yaml @@ -207,14 +212,16 @@ setup-vault-jwt-auth: 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="8760h": +create-jupyterhub-vault-token: #!/bin/bash set -euo pipefail echo "Creating JupyterHub Vault token with admin policy..." + echo " TTL: ${NOTEBOOK_VAULT_TOKEN_TTL}" + echo " Max TTL: ${NOTEBOOK_VAULT_TOKEN_MAX_TTL}" # 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 }} + just vault::create-token-and-store admin jupyterhub/vault-token ${NOTEBOOK_VAULT_TOKEN_TTL} ${NOTEBOOK_VAULT_TOKEN_MAX_TTL} echo "✓ JupyterHub Vault token created and stored" echo "" diff --git a/jupyterhub/user_policy.hcl b/jupyterhub/user_policy.hcl new file mode 100644 index 0000000..f3f8960 --- /dev/null +++ b/jupyterhub/user_policy.hcl @@ -0,0 +1,30 @@ +# User-specific policy for {username} +path "secret/data/jupyter/users/{username}/*" { + capabilities = ["create", "update", "read", "delete", "list"] +} + +path "secret/metadata/jupyter/users/{username}/*" { + capabilities = ["list", "read", "delete", "update"] +} + +path "secret/metadata/jupyter/users/{username}" { + capabilities = ["list"] +} + +# Read access to shared resources +path "secret/data/jupyter/shared/*" { + capabilities = ["read", "list"] +} + +path "secret/metadata/jupyter/shared" { + capabilities = ["list"] +} + +# Token management capabilities +path "auth/token/lookup-self" { + capabilities = ["read"] +} + +path "auth/token/renew-self" { + capabilities = ["update"] +} \ No newline at end of file diff --git a/vault/justfile b/vault/justfile index 883fc6e..3092b2a 100644 --- a/vault/justfile +++ b/vault/justfile @@ -137,14 +137,18 @@ create-admin-token root_token='': check-env vault token create -policy=admin # Create token with specified policy and store in Vault -create-token-and-store policy path ttl="24h" root_token='': check-env +create-token-and-store policy path ttl="24h" max_ttl="" root_token='': check-env #!/bin/bash set -euo pipefail {{ _vault_root_env_setup }} echo "Creating token with policy '{{ policy }}'..." # Create token with specified policy - token_output=$(vault token create -policy={{ policy }} -ttl={{ ttl }} -format=json) + max_ttl_arg="" + if [ -n "{{ max_ttl }}" ]; then + max_ttl_arg="-explicit-max-ttl={{ max_ttl }}" + fi + token_output=$(vault token create -policy={{ policy }} -ttl={{ ttl }} ${max_ttl_arg} -format=json) service_token=$(echo "${token_output}" | jq -r '.auth.client_token') echo "Storing token in Vault at path '{{ path }}'..."