feat(jupyterhub): admin vault token renewal

This commit is contained in:
Masaki Yatsu
2025-09-08 14:06:35 +09:00
parent 5d781ff208
commit 2bf82c7f38
7 changed files with 360 additions and 40 deletions

View File

@@ -150,7 +150,7 @@ export JUPYTERHUB_VAULT_INTEGRATION_ENABLED=true
just jupyterhub::install just jupyterhub::install
``` ```
**Note**: The `just jupyterhub::setup-vault-jwt-auth` command is called automatically during installation if Vault integration is enabled. This command currently serves as a placeholder for future JWT-based authentication enhancements. **Note**: The `just jupyterhub::setup-vault-integration` command is called automatically during installation if Vault integration is enabled. This configures Vault Agent for automatic token renewal and user-specific token management.
### Usage in Notebooks ### Usage in Notebooks
@@ -183,7 +183,8 @@ secrets.delete('api-keys', field='github') # Delete only github field
### Security Features ### Security Features
- **User isolation**: Each user receives a unique Vault token with access only to their own secrets - **User isolation**: Each user receives a unique Vault token with access only to their own secrets
- **Automatic token renewal**: Tokens can be renewed to extend session lifetime - **Automatic token renewal**: Both admin and user tokens are automatically renewed by Vault Agent
- **Vault Agent integration**: JupyterHub admin token is automatically renewed using Kubernetes authentication
- **Audit trail**: All secret access is logged in Vault - **Audit trail**: All secret access is logged in Vault
- **Individual policies**: Each user has their own Vault policy restricting access to their namespace - **Individual policies**: Each user has their own Vault policy restricting access to their namespace
@@ -236,12 +237,15 @@ JUPYTER_PYTHON_KERNEL_TAG=python-3.12-28
IMAGE_REGISTRY=localhost:30500 IMAGE_REGISTRY=localhost:30500
# Vault token TTL settings # Vault token TTL settings
JUPYTERHUB_VAULT_TOKEN_TTL=720h # Admin token: 30 days (effective limit) JUPYTERHUB_VAULT_TOKEN_TTL=24h # Admin token: 1 day (auto-renewed by Vault Agent)
JUPYTERHUB_VAULT_TOKEN_MAX_TTL=8760h # Admin token: 1 year (currently unused - no auto-renewal) JUPYTERHUB_VAULT_TOKEN_MAX_TTL=720h # Admin token: 30 days (max renewal limit)
NOTEBOOK_VAULT_TOKEN_TTL=24h # User token: 1 day (auto-renewed) NOTEBOOK_VAULT_TOKEN_TTL=24h # User token: 1 day (auto-renewed)
NOTEBOOK_VAULT_TOKEN_MAX_TTL=168h # User token: 7 days (max renewal limit) NOTEBOOK_VAULT_TOKEN_MAX_TTL=168h # User token: 7 days (max renewal limit)
# Logging # Vault Agent logging
VAULT_AGENT_LOG_LEVEL=info # Options: trace, debug, info, warn, error
# Application logging
JUPYTER_BUUNSTACK_LOG_LEVEL=warning # Options: debug, info, warning, error JUPYTER_BUUNSTACK_LOG_LEVEL=warning # Options: debug, info, warning, error
``` ```
@@ -351,9 +355,10 @@ The `buunstack` SecretStore uses pre-created user-specific Vault tokens that are
**Key Components**: **Key Components**:
- **JupyterHub Admin Token**: Created with admin policy, stored at `jupyterhub/vault-token`, available as `JUPYTERHUB_VAULT_TOKEN` environment variable - **JupyterHub Admin Token**: Automatically renewed by Vault Agent, read from file at `/vault/secrets/vault-token`
- **User-Specific Tokens**: Created dynamically during notebook spawn, available as `NOTEBOOK_VAULT_TOKEN` environment variable - **User-Specific Tokens**: Created dynamically during notebook spawn, available as `NOTEBOOK_VAULT_TOKEN` environment variable
- **User Policies**: Restrict access to `secret/data/jupyter/users/{username}/*` - **User Policies**: Restrict access to `secret/data/jupyter/users/{username}/*`
- **Vault Agent**: Sidecar container that handles automatic token renewal using Kubernetes authentication
#### Token Lifecycle #### Token Lifecycle
@@ -511,10 +516,85 @@ For production deployments, consider:
- Setting up monitoring and alerts - Setting up monitoring and alerts
- Monitoring Vault token expiration and renewal patterns - Monitoring Vault token expiration and renewal patterns
## Vault Agent Integration
### Overview
JupyterHub now uses Vault Agent for automatic token renewal, eliminating the need for manual token management. Vault Agent runs as a sidecar container in the JupyterHub hub pod and automatically renews the admin token using Kubernetes authentication.
### Architecture
```plain
┌─────────────────────────────────────────────────────────────┐
│ JupyterHub Hub Pod │
│ │
│ ┌─────────────────┐ ┌─────────────────────┐ │
│ │ Hub Container │ │ Vault Agent Sidecar │ │
│ │ │ │ │ │
│ │ Reads token │◄─────────────┤ Writes token │ │
│ │ from file │ │ to shared volume │ │
│ │ │ │ │ │
│ └─────────────────┘ └─────────────────────┘ │
│ │ │ │
│ │ │ │
│ │ │ │
│ ┌─────────▼─────────┐ ┌─────────▼─────────┐ │
│ │ /vault/secrets/ │ │ Kubernetes Auth │ │
│ │ vault-token │ │ with Vault │ │
│ └───────────────────┘ └───────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### Key Features
- **Automatic Renewal**: Admin token is automatically renewed every TTL/2 interval
- **Kubernetes Authentication**: Uses Kubernetes ServiceAccount for secure token acquisition
- **File-based Token Sharing**: Vault Agent writes tokens to shared volume, Hub reads from file
- **Zero Downtime**: Token renewal happens in background without service interruption
- **Configurable Logging**: Vault Agent log level can be configured via `VAULT_AGENT_LOG_LEVEL`
### Monitoring Token Renewal
Check Vault Agent status and token renewal:
```bash
# Monitor Vault Agent logs
kubectl logs -n jupyter -l app.kubernetes.io/component=hub -c vault-agent -f
# Use monitoring script
cd jupyterhub
./monitor-vault-token.sh
# Check token details
kubectl exec -n jupyter <hub-pod> -c hub -- curl -s -H "X-Vault-Token: $(cat /vault/secrets/vault-token)" $VAULT_ADDR/v1/auth/token/lookup-self
```
### Testing Token Renewal
For testing purposes, you can use shorter TTL values to observe rapid token renewal:
```bash
# Test with 1-minute TTL (renews every 30 seconds)
JUPYTERHUB_VAULT_TOKEN_TTL=1m VAULT_AGENT_LOG_LEVEL=debug just jupyterhub::install
# Monitor renewal activity
kubectl logs -n jupyter -l app.kubernetes.io/component=hub -c vault-agent -f | grep "renewed auth token"
```
### Configuration Files
The Vault Agent integration uses several configuration files:
- `vault-agent-config.gomplate.hcl`: Vault Agent configuration template
- `token-monitor.tpl`: Template for logging token information
- `monitor-vault-token.sh`: Monitoring script for token status
## Known Limitations ## Known Limitations
1. **Admin Token Refresh**: JupyterHub's admin Vault token (`JUPYTERHUB_VAULT_TOKEN`) does not auto-refresh. You must redeploy JupyterHub before the token expires (default TTL: 720h/30 days). The `JUPYTERHUB_VAULT_TOKEN_MAX_TTL` setting is currently not utilized since automatic renewal is not implemented. Monitor the token expiration and schedule redeployments accordingly. 1. **Token Max TTL**: Even with Vault Agent auto-renewal, tokens cannot be renewed beyond `JUPYTERHUB_VAULT_TOKEN_MAX_TTL` (default: 720h/30 days). After this period, JupyterHub must be redeployed to acquire a new token from Vault. With the default 30-day limit, this requires monthly maintenance.
2. **Cull Settings**: Server idle timeout is set to 2 hours by default. Adjust `cull.timeout` and `cull.every` in the Helm values for different requirements. 2. **Cull Settings**: Server idle timeout is set to 2 hours by default. Adjust `cull.timeout` and `cull.every` in the Helm values for different requirements.
3. **NFS Storage**: When using NFS storage, ensure proper permissions are set on the NFS server. The default `JUPYTER_FSGID` is 100. 3. **NFS Storage**: When using NFS storage, ensure proper permissions are set on the NFS server. The default `JUPYTER_FSGID` is 100.
4. **Vault Agent Resource Usage**: The Vault Agent sidecar uses minimal resources (50m CPU, 64Mi memory) but adds slight overhead to the hub pod.

View File

@@ -1,2 +1,3 @@
jupyterhub-values.yaml jupyterhub-values.yaml
vault-agent-config.hcl
/notebooks/ /notebooks/

View File

@@ -1,10 +1,16 @@
hub: hub:
extraEnv: extraEnv:
JUPYTERHUB_CRYPT_KEY: {{ .Env.JUPYTERHUB_CRYPT_KEY | quote }} JUPYTERHUB_CRYPT_KEY: {{ .Env.JUPYTERHUB_CRYPT_KEY | quote }}
JUPYTERHUB_VAULT_TOKEN: {{ .Env.JUPYTERHUB_VAULT_TOKEN | quote }}
VAULT_ADDR: {{ .Env.VAULT_ADDR | quote }} VAULT_ADDR: {{ .Env.VAULT_ADDR | quote }}
NOTEBOOK_VAULT_TOKEN_TTL: {{ .Env.NOTEBOOK_VAULT_TOKEN_TTL | quote }} NOTEBOOK_VAULT_TOKEN_TTL: {{ .Env.NOTEBOOK_VAULT_TOKEN_TTL | quote }}
NOTEBOOK_VAULT_TOKEN_MAX_TTL: {{ .Env.NOTEBOOK_VAULT_TOKEN_MAX_TTL | quote }} NOTEBOOK_VAULT_TOKEN_MAX_TTL: {{ .Env.NOTEBOOK_VAULT_TOKEN_MAX_TTL | quote }}
{{- if eq .Env.JUPYTERHUB_VAULT_INTEGRATION_ENABLED "true" }}
# Vault Agent will provide token via file
VAULT_TOKEN_FILE: "/vault/secrets/vault-token"
{{- else }}
# Traditional token via environment variable
JUPYTERHUB_VAULT_TOKEN: {{ .Env.JUPYTERHUB_VAULT_TOKEN | quote }}
{{- end }}
# Install packages at container startup # Install packages at container startup
extraFiles: extraFiles:
@@ -57,6 +63,25 @@ hub:
# Set environment variables for spawned containers # Set environment variables for spawned containers
import hvac import hvac
def get_vault_token():
"""Read Vault token from file written by Vault Agent"""
import os
token_file = os.environ.get('VAULT_TOKEN_FILE', '/vault/secrets/vault-token')
try:
with open(token_file, 'r') as f:
token = f.read().strip()
if token:
return token
else:
raise Exception(f"Empty token file: {token_file}")
except FileNotFoundError:
# Fallback to environment variable for backward compatibility
return os.environ.get("JUPYTERHUB_VAULT_TOKEN")
except Exception as e:
# Log error but attempt fallback
print(f"Error reading token file {token_file}: {e}")
return os.environ.get("JUPYTERHUB_VAULT_TOKEN")
async def pre_spawn_hook(spawner): async def pre_spawn_hook(spawner):
"""Set essential environment variables for spawned containers""" """Set essential environment variables for spawned containers"""
# PostgreSQL configuration # PostgreSQL configuration
@@ -73,15 +98,19 @@ hub:
try: try:
username = spawner.user.name username = spawner.user.name
# Step 1: Initialize admin Vault client # Step 1: Initialize admin Vault client with file-based token
import os import os
vault_addr = os.environ.get("VAULT_ADDR", "{{ .Env.VAULT_ADDR }}") vault_addr = os.environ.get("VAULT_ADDR", "{{ .Env.VAULT_ADDR }}")
vault_token = os.environ.get("JUPYTERHUB_VAULT_TOKEN", "{{ .Env.JUPYTERHUB_VAULT_TOKEN }}") vault_token = get_vault_token()
spawner.log.info(f"pre_spawn_hook starting for {username}") spawner.log.info(f"pre_spawn_hook starting for {username}")
spawner.log.info(f"Vault address: {vault_addr}") spawner.log.info(f"Vault address: {vault_addr}")
spawner.log.info(f"Vault token source: {'file' if os.path.exists(os.environ.get('VAULT_TOKEN_FILE', '/vault/secrets/vault-token')) else 'env'}")
spawner.log.info(f"Vault token present: {bool(vault_token)}, length: {len(vault_token) if vault_token else 0}") spawner.log.info(f"Vault token present: {bool(vault_token)}, length: {len(vault_token) if vault_token else 0}")
if not vault_token:
raise Exception("No Vault token available from file or environment")
vault_client = hvac.Client(url=vault_addr, verify=False) vault_client = hvac.Client(url=vault_addr, verify=False)
vault_client.token = vault_token vault_client.token = vault_token
@@ -135,6 +164,55 @@ hub:
c.KubeSpawner.pre_spawn_hook = pre_spawn_hook c.KubeSpawner.pre_spawn_hook = pre_spawn_hook
{{- if eq .Env.JUPYTERHUB_VAULT_INTEGRATION_ENABLED "true" }}
# Vault Agent sidecar configuration
extraVolumes:
- name: vault-secrets
emptyDir: {}
- name: vault-config
configMap:
name: vault-agent-config
extraVolumeMounts:
- name: vault-secrets
mountPath: /vault/secrets
- name: vault-config
mountPath: /vault/config
extraContainers:
- name: vault-agent
image: hashicorp/vault:1.15.2
securityContext:
runAsUser: 100
runAsGroup: 101
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop:
- ALL
command:
- /bin/sh
- -c
- |
# Start Vault Agent
vault agent -config=/vault/config/agent.hcl
env:
- name: VAULT_ADDR
value: {{ .Env.VAULT_ADDR | quote }}
volumeMounts:
- name: vault-secrets
mountPath: /vault/secrets
- name: vault-config
mountPath: /vault/config
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
{{- end }}
podSecurityContext: podSecurityContext:
fsGroup: {{ .Env.JUPYTER_FSGID }} fsGroup: {{ .Env.JUPYTER_FSGID }}

View File

@@ -19,15 +19,17 @@ export JUPYTER_PROFILE_PYTORCH_ENABLED := env("JUPYTER_PROFILE_PYTORCH_ENABLED",
export JUPYTER_PROFILE_TENSORFLOW_ENABLED := env("JUPYTER_PROFILE_TENSORFLOW_ENABLED", "false") export JUPYTER_PROFILE_TENSORFLOW_ENABLED := env("JUPYTER_PROFILE_TENSORFLOW_ENABLED", "false")
export JUPYTER_PROFILE_BUUN_STACK_ENABLED := env("JUPYTER_PROFILE_BUUN_STACK_ENABLED", "false") 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 JUPYTER_PROFILE_BUUN_STACK_CUDA_ENABLED := env("JUPYTER_PROFILE_BUUN_STACK_CUDA_ENABLED", "false")
export IMAGE_REGISTRY := env("IMAGE_REGISTRY", "localhost:30500") export JUPYTERHUB_VAULT_TOKEN_TTL := env("JUPYTERHUB_VAULT_TOKEN_TTL", "24h")
export JUPYTERHUB_VAULT_TOKEN_TTL := env("JUPYTERHUB_VAULT_TOKEN_TTL", "720h") # 30 days export JUPYTERHUB_VAULT_TOKEN_MAX_TTL := env("JUPYTERHUB_VAULT_TOKEN_MAX_TTL", "720h")
export JUPYTERHUB_VAULT_TOKEN_MAX_TTL := env("JUPYTERHUB_VAULT_TOKEN_MAX_TTL", "8760h") # 1 year export NOTEBOOK_VAULT_TOKEN_TTL := env("NOTEBOOK_VAULT_TOKEN_TTL", "24h")
export NOTEBOOK_VAULT_TOKEN_TTL := env("NOTEBOOK_VAULT_TOKEN_TTL", "24h") # 1 day export NOTEBOOK_VAULT_TOKEN_MAX_TTL := env("NOTEBOOK_VAULT_TOKEN_MAX_TTL", "168h")
export NOTEBOOK_VAULT_TOKEN_MAX_TTL := env("NOTEBOOK_VAULT_TOKEN_MAX_TTL", "168h") # 7 days export VAULT_AGENT_LOG_LEVEL := env("VAULT_AGENT_LOG_LEVEL", "info")
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", "warning") export JUPYTER_BUUNSTACK_LOG_LEVEL := env("JUPYTER_BUUNSTACK_LOG_LEVEL", "warning")
export IMAGE_REGISTRY := env("IMAGE_REGISTRY", "localhost:30500")
export LONGHORN_NAMESPACE := env("LONGHORN_NAMESPACE", "longhorn")
export KEYCLOAK_REALM := env("KEYCLOAK_REALM", "buunstack")
export VAULT_HOST := env("VAULT_HOST", "")
export VAULT_ADDR := "https://" + VAULT_HOST
[private] [private]
default: default:
@@ -116,13 +118,26 @@ install:
kubectl apply -n ${JUPYTERHUB_NAMESPACE} -f nfs-pvc.yaml kubectl apply -n ${JUPYTERHUB_NAMESPACE} -f nfs-pvc.yaml
fi fi
# Always create new JupyterHub Vault token on deployment # Setup Vault Agent for automatic token management
echo "Creating new JupyterHub Vault token for this deployment..." if [ -z "${JUPYTERHUB_VAULT_INTEGRATION_ENABLED}" ]; then
just create-jupyterhub-vault-token if gum confirm "Are you going to enable Vault integration?"; then
export JUPYTERHUB_VAULT_TOKEN=$(just vault::get jupyterhub/vault-token token) JUPYTERHUB_VAULT_INTEGRATION_ENABLED=true
else
JUPYTERHUB_VAULT_INTEGRATION_ENABLED=false
fi
fi
if [ "${JUPYTERHUB_VAULT_INTEGRATION_ENABLED}" = "true" ]; then
echo "Setting up Vault Agent for automatic token management..."
echo " Token TTL: ${JUPYTERHUB_VAULT_TOKEN_TTL}"
echo " Token Max TTL: ${JUPYTERHUB_VAULT_TOKEN_MAX_TTL}"
just setup-vault-integration
# Read user policy template for Vault # Read user policy template for Vault
export USER_POLICY_HCL=$(cat user_policy.hcl) export USER_POLICY_HCL=$(cat user_policy.hcl)
else
echo "Vault integration disabled - deploying without Vault support"
export USER_POLICY_HCL=""
fi
# https://z2jh.jupyter.org/en/stable/ # https://z2jh.jupyter.org/en/stable/
gomplate -f jupyterhub-values.gomplate.yaml -o jupyterhub-values.yaml gomplate -f jupyterhub-values.gomplate.yaml -o jupyterhub-values.yaml
@@ -133,17 +148,6 @@ install:
# wait deployments manually because `helm upgrade --wait` does not work for JupyterHub # wait deployments manually because `helm upgrade --wait` does not work for JupyterHub
just k8s::wait-deployments-ready ${JUPYTERHUB_NAMESPACE} hub proxy just k8s::wait-deployments-ready ${JUPYTERHUB_NAMESPACE} hub proxy
if [ -z "${JUPYTERHUB_VAULT_INTEGRATION_ENABLED}" ]; then
if gum confirm "Are you going to enable Vault integration?"; then
JUPYTERHUB_VAULT_INTEGRATION_ENABLED=true
else
JUPYTERHUB_VAULT_INTEGRATION_ENABLED=false
fi
fi
if [ "${JUPYTERHUB_VAULT_INTEGRATION_ENABLED}" = "true" ]; then
just setup-vault-jwt-auth
fi
# Uninstall JupyterHub # Uninstall JupyterHub
uninstall: uninstall:
#!/bin/bash #!/bin/bash
@@ -205,18 +209,53 @@ push-kernel-images:
docker push ${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG} docker push ${IMAGE_REGISTRY}/${KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY}:${JUPYTER_PYTHON_KERNEL_TAG}
fi fi
# Setup Vault integration for JupyterHub (user-specific tokens) # Setup Vault integration for JupyterHub (user-specific tokens + auto-renewal)
setup-vault-jwt-auth: setup-vault-integration root_token='':
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
echo "Setting up Vault integration for JupyterHub..." echo "Setting up Vault integration for JupyterHub..."
echo "✓ Vault integration configured (user-specific tokens)" # Create Kubernetes role for JupyterHub in Vault
echo "Creating Kubernetes authentication role for JupyterHub..."
echo " Service Account: hub"
echo " Namespace: jupyter"
echo " Policies: admin"
echo " TTL: ${JUPYTERHUB_VAULT_TOKEN_TTL}"
echo " Max TTL: ${JUPYTERHUB_VAULT_TOKEN_MAX_TTL}"
export VAULT_TOKEN="{{ root_token }}"
while [ -z "${VAULT_TOKEN}" ]; do
VAULT_TOKEN=$(gum input --prompt="Vault root token: " --password --width=100)
done
vault write auth/kubernetes/role/jupyterhub \
bound_service_account_names=hub \
bound_service_account_namespaces=jupyter \
policies=admin \
ttl=${JUPYTERHUB_VAULT_TOKEN_TTL} \
max_ttl=${JUPYTERHUB_VAULT_TOKEN_MAX_TTL}
# Create Vault Agent configuration with gomplate
echo "Creating Vault Agent configuration..."
gomplate -f vault-agent-config.gomplate.hcl -o vault-agent-config.hcl
kubectl create configmap vault-agent-config -n ${JUPYTERHUB_NAMESPACE} \
--from-file=agent.hcl=vault-agent-config.hcl \
--from-file=token-monitor.tpl=token-monitor.tpl \
--dry-run=client -o yaml | kubectl apply -f -
echo "✓ Vault integration configured (user-specific tokens + auto-renewal)"
echo ""
echo "Configuration Summary:"
echo " JupyterHub Token TTL: ${JUPYTERHUB_VAULT_TOKEN_TTL}"
echo " JupyterHub Token Max TTL: ${JUPYTERHUB_VAULT_TOKEN_MAX_TTL}"
echo " User Token TTL: ${NOTEBOOK_VAULT_TOKEN_TTL}"
echo " User Token Max TTL: ${NOTEBOOK_VAULT_TOKEN_MAX_TTL}"
echo " Vault Agent Log Level: ${VAULT_AGENT_LOG_LEVEL}"
echo " Auto-renewal: Every $(( $(echo ${JUPYTERHUB_VAULT_TOKEN_TTL} | sed 's/m/*60/g; s/h/*3600/g; s/s//g' | bc) / 2 ))s (TTL/2)"
echo "" echo ""
echo "Users can now access Vault from notebooks using:" echo "Users can now access Vault from notebooks using:"
echo " from buunstack import SecretStore" echo " from buunstack import SecretStore"
echo " secrets = SecretStore()" echo " secrets = SecretStore()"
echo " # Each user gets their own isolated Vault token and policy" echo " # Each user gets their own isolated Vault token and policy"
echo " # Admin token is automatically renewed by Vault Agent"
# Create JupyterHub Vault token (uses admin policy for JWT operations) # Create JupyterHub Vault token (uses admin policy for JWT operations)
create-jupyterhub-vault-token: create-jupyterhub-vault-token:

View File

@@ -0,0 +1,73 @@
#!/bin/bash
# JupyterHub Vault Token Monitor Script
# Usage: ./monitor-vault-token.sh [pod-name]
set -euo pipefail
NAMESPACE="jupyter"
POD_NAME=${1:-$(kubectl get pods -n ${NAMESPACE} -l app.kubernetes.io/component=hub -o jsonpath='{.items[0].metadata.name}')}
echo "🔍 Monitoring Vault Agent for JupyterHub Pod: ${POD_NAME}"
echo "=================================================="
# Check if pod exists and is running
if ! kubectl get pod ${POD_NAME} -n ${NAMESPACE} >/dev/null 2>&1; then
echo "❌ Pod ${POD_NAME} not found in namespace ${NAMESPACE}"
exit 1
fi
echo "📊 Pod Status:"
kubectl get pod ${POD_NAME} -n ${NAMESPACE}
echo ""
echo "📄 Vault Secrets Directory:"
kubectl exec -n ${NAMESPACE} ${POD_NAME} -c hub -- ls -la /vault/secrets/ 2>/dev/null || echo "❌ Cannot access /vault/secrets/"
echo ""
echo "🔐 Current Token Info:"
kubectl exec -n ${NAMESPACE} ${POD_NAME} -c hub -- sh -c '
if [ -f /vault/secrets/vault-token ]; then
echo "Token file exists ($(wc -c < /vault/secrets/vault-token) bytes)"
echo "Last modified: $(stat -c %y /vault/secrets/vault-token 2>/dev/null || stat -f %Sm /vault/secrets/vault-token)"
# Test token validity
if command -v curl >/dev/null 2>&1; then
echo ""
echo "Token validation:"
RESPONSE=$(curl -s -w "%{http_code}" -H "X-Vault-Token: $(cat /vault/secrets/vault-token)" $VAULT_ADDR/v1/auth/token/lookup-self)
HTTP_CODE="${RESPONSE: -3}"
if [ "$HTTP_CODE" = "200" ]; then
echo "✅ Token is valid"
echo "$RESPONSE" | head -c -3 | grep -E "(ttl|expire_time|renewable)" | head -3
else
echo "❌ Token validation failed (HTTP $HTTP_CODE)"
fi
fi
else
echo "❌ Token file not found"
fi
' 2>/dev/null || echo "❌ Cannot check token info"
echo ""
echo "📋 Recent Vault Agent Logs:"
kubectl logs -n ${NAMESPACE} ${POD_NAME} -c vault-agent --tail=10 2>/dev/null || echo "❌ Cannot access vault-agent logs"
echo ""
echo "📋 Token Renewal Log (if exists):"
kubectl exec -n ${NAMESPACE} ${POD_NAME} -c hub -- sh -c '
if [ -f /vault/secrets/renewal.log ]; then
echo "Recent renewal events:"
tail -10 /vault/secrets/renewal.log
else
echo "No renewal log file found yet"
fi
' 2>/dev/null || echo "❌ Cannot check renewal logs"
echo ""
echo "🔄 To monitor token renewals in real-time, run:"
echo " kubectl logs -n ${NAMESPACE} ${POD_NAME} -c vault-agent -f | grep 'renewed auth token'"
echo ""
echo "🔍 To check token info periodically, run:"
echo " watch -n 30 \"kubectl exec -n ${NAMESPACE} ${POD_NAME} -c hub -- sh -c 'curl -s -H \\\"X-Vault-Token: \\\$(cat /vault/secrets/vault-token)\\\" \\\$VAULT_ADDR/v1/auth/token/lookup-self | grep -E \\\"(ttl|expire_time)\\\"'\""

View File

@@ -0,0 +1,11 @@
{{- with secret "auth/token/lookup-self" -}}
=== Vault Token Status ===
TTL: {{ .Data.ttl }} seconds
Renewable: {{ .Data.renewable }}
Expire Time: {{ .Data.expire_time }}
Policies: {{ range .Data.policies }}{{ . }} {{ end }}
Display Name: {{ .Data.display_name }}
Entity ID: {{ .Data.entity_id }}
Token Type: {{ .Data.type }}
===========================
{{- end -}}

View File

@@ -0,0 +1,38 @@
vault {
address = "{{ .Env.VAULT_ADDR }}"
}
# Enable detailed logging
log_level = "{{ .Env.VAULT_AGENT_LOG_LEVEL }}"
log_format = "standard"
auto_auth {
method "kubernetes" {
mount_path = "auth/kubernetes"
config = {
role = "jupyterhub"
}
}
sink "file" {
config = {
path = "/vault/secrets/vault-token"
}
}
}
cache {
use_auto_auth_token = true
}
listener "tcp" {
address = "127.0.0.1:8100"
tls_disable = true
}
# Add template for token monitoring
template {
source = "/vault/config/token-monitor.tpl"
destination = "/vault/secrets/token-info.log"
perms = 0644
}