map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; server_name localhost; location ~ ^/jupyter/(?[^/]+)/?$ { return 403 "Direct directory access is forbidden. Please specify a notebook path."; } # 精准匹配入口:仅允许访问特定 notebook 页面与配套资源 location ~ ^/jupyter/(?[^/]+)(?/.*)$ { # 触发后端鉴权 auth_request /internal-auth; auth_request_set $target_upstream $upstream_http_x_upstream_addr; auth_request_set $jupyter_token $upstream_http_x_jupyter_internal_token; # 拼接完整 URL 并输出到 Response Header 方便调试 add_header X-Debug-Full-Url "$target_upstream/jupyter/$workspace_id$rest_uri$is_args$args" always; # 代理到具体的 Jupyter 子进程 proxy_pass $target_upstream/jupyter/$workspace_id$rest_uri$is_args$args; proxy_set_header Authorization "token $jupyter_token"; # 支持 WebSocket (Jupyter Kernel 必需) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 2. 内部 Auth 子请求 location location = /internal-auth { internal; # 打到宿主机的 FastAPI 8000 端口 proxy_pass http://host.docker.internal:8000/api/v1/auth/jupyter; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_pass_header x-upstream-addr; proxy_pass_header x-jupyter-internal-token; proxy_set_header X-Original-Workspace-Id $workspace_id; proxy_set_header X-Original-URI $request_uri; proxy_set_header Cookie $http_cookie; proxy_set_header Authorization $http_authorization; } # 拒绝其余非法路径 location /jupyter/ { return 403 "Access Denied"; } }