docs: rewrite README with structured overview and quick start guide

Replaces the minimal project description with a comprehensive README
including a component overview table, quick start instructions, common
Ansible operations, and links to detailed documentation. Aligns with
Red Panda Approval™ standards.
This commit is contained in:
2026-03-03 12:49:06 +00:00
parent c7be03a743
commit b4d60f2f38
219 changed files with 34586 additions and 2 deletions

View File

@@ -0,0 +1,221 @@
---
# JupyterLab Deployment with OAuth2-Proxy Sidecar
# Deploys JupyterLab as systemd service with Casdoor SSO via oauth2-proxy
# Red Panda Approved
- name: Deploy JupyterLab
hosts: ubuntu
become: true
tasks:
- name: Check if host has jupyterlab service
ansible.builtin.set_fact:
has_jupyterlab_service: "{{'jupyterlab' in services}}"
- name: Skip hosts without jupyterlab service
ansible.builtin.meta: end_host
when: not has_jupyterlab_service
# =========================================================================
# System Dependencies
# =========================================================================
- name: Install system dependencies
ansible.builtin.apt:
name:
- python3
- python3-venv
- python3-dev
- python3-pip
- nodejs
- npm
- graphviz
- git
- curl
state: present
update_cache: true
# =========================================================================
# User Setup
# =========================================================================
- name: Ensure jupyterlab user exists
ansible.builtin.user:
name: "{{ jupyterlab_user }}"
group: "{{ jupyterlab_group }}"
shell: /bin/bash
create_home: true
state: present
- name: Create Notebooks directory
ansible.builtin.file:
path: "{{ jupyterlab_notebook_dir }}"
owner: "{{ jupyterlab_user }}"
group: "{{ jupyterlab_group }}"
state: directory
mode: '0755'
- name: Create JupyterLab config directory
ansible.builtin.file:
path: /etc/jupyterlab
owner: root
group: "{{ jupyterlab_group }}"
state: directory
mode: '0755'
- name: Create JupyterLab log directory
ansible.builtin.file:
path: /var/log/jupyterlab
owner: "{{ jupyterlab_user }}"
group: "{{ jupyterlab_group }}"
state: directory
mode: '0755'
# =========================================================================
# Python Virtual Environment
# =========================================================================
- name: Create virtual environment directory
ansible.builtin.file:
path: "{{ jupyterlab_venv_dir }}"
owner: "{{ jupyterlab_user }}"
group: "{{ jupyterlab_group }}"
state: directory
mode: '0755'
- name: Create virtual environment for JupyterLab
become_user: "{{ jupyterlab_user }}"
ansible.builtin.command:
cmd: "python3 -m venv {{ jupyterlab_venv_dir }}"
creates: "{{ jupyterlab_venv_dir }}/bin/activate"
- name: Upgrade pip in virtual environment
become_user: "{{ jupyterlab_user }}"
ansible.builtin.pip:
name:
- pip
- wheel
- setuptools
state: latest
virtualenv: "{{ jupyterlab_venv_dir }}"
- name: Install JupyterLab and core packages
become_user: "{{ jupyterlab_user }}"
ansible.builtin.pip:
name:
- jupyterlab
- jupyter-ai[all]
- langchain-ollama
- matplotlib
- plotly
- jupyter_contrib_nbextensions
- "jsonschema[format-nongpl]"
- python-mermaid
- ipywidgets
state: present
virtualenv: "{{ jupyterlab_venv_dir }}"
notify: restart jupyterlab
# =========================================================================
# Configuration Files
# =========================================================================
- name: Template JupyterLab configuration
ansible.builtin.template:
src: jupyter_lab_config.py.j2
dest: /etc/jupyterlab/jupyter_lab_config.py
owner: root
group: "{{ jupyterlab_group }}"
mode: '0644'
notify: restart jupyterlab
- name: Template JupyterLab systemd service
ansible.builtin.template:
src: jupyterlab.service.j2
dest: /etc/systemd/system/jupyterlab.service
owner: root
group: root
mode: '0644'
notify:
- reload systemd
- restart jupyterlab
# =========================================================================
# OAuth2-Proxy Sidecar
# =========================================================================
- name: Create oauth2-proxy directory
ansible.builtin.file:
path: "{{ jupyterlab_oauth2_proxy_dir }}"
owner: root
group: root
state: directory
mode: '0755'
- name: Download oauth2-proxy binary
ansible.builtin.get_url:
url: "https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v{{ jupyterlab_oauth2_proxy_version }}/oauth2-proxy-v{{ jupyterlab_oauth2_proxy_version }}.linux-amd64.tar.gz"
dest: "/tmp/oauth2-proxy-v{{ jupyterlab_oauth2_proxy_version }}.tar.gz"
mode: '0644'
- name: Extract oauth2-proxy binary
ansible.builtin.unarchive:
src: "/tmp/oauth2-proxy-v{{ jupyterlab_oauth2_proxy_version }}.tar.gz"
dest: /tmp
remote_src: true
creates: "/tmp/oauth2-proxy-v{{ jupyterlab_oauth2_proxy_version }}.linux-amd64/oauth2-proxy"
- name: Install oauth2-proxy binary
ansible.builtin.copy:
src: "/tmp/oauth2-proxy-v{{ jupyterlab_oauth2_proxy_version }}.linux-amd64/oauth2-proxy"
dest: /usr/local/bin/oauth2-proxy
owner: root
group: root
mode: '0755'
remote_src: true
- name: Template oauth2-proxy configuration
ansible.builtin.template:
src: oauth2-proxy-jupyter.cfg.j2
dest: "{{ jupyterlab_oauth2_proxy_dir }}/oauth2-proxy.cfg"
owner: root
group: root
mode: '0600'
notify: restart oauth2-proxy-jupyter
- name: Template oauth2-proxy systemd service
ansible.builtin.template:
src: oauth2-proxy-jupyter.service.j2
dest: /etc/systemd/system/oauth2-proxy-jupyter.service
owner: root
group: root
mode: '0644'
notify:
- reload systemd
- restart oauth2-proxy-jupyter
# =========================================================================
# Service Management
# =========================================================================
- name: Enable and start JupyterLab service
ansible.builtin.systemd:
name: jupyterlab
enabled: true
state: started
daemon_reload: true
- name: Enable and start OAuth2-Proxy service
ansible.builtin.systemd:
name: oauth2-proxy-jupyter
enabled: true
state: started
daemon_reload: true
handlers:
- name: reload systemd
ansible.builtin.systemd:
daemon_reload: true
- name: restart jupyterlab
ansible.builtin.systemd:
name: jupyterlab
state: restarted
- name: restart oauth2-proxy-jupyter
ansible.builtin.systemd:
name: oauth2-proxy-jupyter
state: restarted

View File

@@ -0,0 +1,62 @@
# JupyterLab Configuration
# Deployed via Ansible - Do not edit manually
# Red Panda Approved
# =============================================================================
# Server Settings
# =============================================================================
# Allow connections from reverse proxy
c.ServerApp.allow_remote_access = True
c.ServerApp.local_hostnames = ['localhost', '127.0.0.1', 'jupyter.{{ jupyterlab_domain }}']
# Disable browser launch
c.ServerApp.open_browser = False
# Disable token authentication (OAuth2-Proxy handles auth)
c.ServerApp.token = ''
c.ServerApp.password = ''
# Base URL for reverse proxy
c.ServerApp.base_url = '/'
# Trust X-Forwarded headers from OAuth2-Proxy
c.ServerApp.trust_xheaders = True
# =============================================================================
# WebSocket Configuration (for reverse proxy)
# =============================================================================
# Allow WebSocket connections from any origin (handled by OAuth2-Proxy)
c.ServerApp.allow_origin = '*'
c.ServerApp.allow_credentials = True
# Disable XSRF for API (OAuth2-Proxy handles CSRF)
c.ServerApp.disable_check_xsrf = True
# =============================================================================
# Notebook Settings
# =============================================================================
# Default notebook directory
c.ServerApp.root_dir = '{{ jupyterlab_notebook_dir }}'
# Allow hidden files
c.ContentsManager.allow_hidden = True
# =============================================================================
# Terminal Settings
# =============================================================================
# Enable terminal
c.ServerApp.terminals_enabled = True
# =============================================================================
# Logging
# =============================================================================
# Log level
c.Application.log_level = 'INFO'
# Log format
c.Application.log_format = '[%(levelname)s %(asctime)s %(name)s] %(message)s'

View File

@@ -0,0 +1,34 @@
[Unit]
Description=JupyterLab Server
After=network.target
Wants=oauth2-proxy-jupyter.service
[Service]
Type=simple
User={{ jupyterlab_user }}
Group={{ jupyterlab_group }}
WorkingDirectory={{ jupyterlab_notebook_dir }}
ExecStart={{ jupyterlab_venv_dir }}/bin/jupyter-lab \
--config=/etc/jupyterlab/jupyter_lab_config.py \
--ip=127.0.0.1 \
--port={{ jupyterlab_port }} \
--no-browser \
--notebook-dir={{ jupyterlab_notebook_dir }}
Environment="PATH={{ jupyterlab_venv_dir }}/bin:/usr/local/bin:/usr/bin:/bin"
Restart=on-failure
RestartSec=10
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=jupyterlab
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,68 @@
# OAuth2-Proxy Configuration for JupyterLab
# Authenticates users via Casdoor OIDC before proxying to JupyterLab
# Red Panda Approved
# Provider Configuration (Casdoor OIDC)
provider = "oidc"
provider_display_name = "Casdoor"
oidc_issuer_url = "{{ jupyterlab_oauth2_oidc_issuer_url }}"
client_id = "{{ jupyterlab_oauth_client_id }}"
client_secret = "{{ jupyterlab_oauth_client_secret }}"
# Redirect URL after authentication
redirect_url = "{{ jupyterlab_oauth2_redirect_url }}"
# Upstream service (JupyterLab on localhost)
upstreams = [
"http://127.0.0.1:{{ jupyterlab_port }}"
]
# Session/Cookie Configuration
cookie_secret = "{{ jupyterlab_oauth2_cookie_secret }}"
cookie_name = "_oauth2_proxy_jupyter"
cookie_secure = true
cookie_httponly = true
cookie_samesite = "lax"
cookie_domains = [
".{{ jupyterlab_domain }}"
]
# Authentication settings
email_domains = ["*"]
oidc_email_claim = "email"
oidc_groups_claim = "groups"
# Session settings
session_store_type = "cookie"
cookie_expire = "168h"
cookie_refresh = "1h"
# Request settings - pass user info to JupyterLab
pass_access_token = false
pass_authorization_header = false
set_authorization_header = false
set_xauthrequest = true
# Logging
request_logging = true
auth_logging = true
standard_logging = true
# Network settings
http_address = "0.0.0.0:{{ jupyterlab_proxy_port }}"
reverse_proxy = true
real_client_ip_header = "X-Forwarded-For"
# Skip authentication for health check endpoints
skip_auth_routes = [
"^/api/status$",
"^/healthz$"
]
# OIDC specific settings
skip_provider_button = true
oidc_extra_audiences = []
insecure_oidc_allow_unverified_email = true
# SSL verification (internal Casdoor uses valid certs)
ssl_insecure_skip_verify = false

View File

@@ -0,0 +1,23 @@
[Unit]
Description=OAuth2-Proxy for JupyterLab
After=network.target jupyterlab.service
Requires=jupyterlab.service
[Service]
Type=simple
ExecStart=/usr/local/bin/oauth2-proxy --config={{ jupyterlab_oauth2_proxy_dir }}/oauth2-proxy.cfg
Restart=on-failure
RestartSec=5
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=oauth2-proxy-jupyter
[Install]
WantedBy=multi-user.target