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:
221
ansible/jupyterlab/deploy.yml
Normal file
221
ansible/jupyterlab/deploy.yml
Normal 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
|
||||
62
ansible/jupyterlab/jupyter_lab_config.py.j2
Normal file
62
ansible/jupyterlab/jupyter_lab_config.py.j2
Normal 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'
|
||||
34
ansible/jupyterlab/jupyterlab.service.j2
Normal file
34
ansible/jupyterlab/jupyterlab.service.j2
Normal 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
|
||||
68
ansible/jupyterlab/oauth2-proxy-jupyter.cfg.j2
Normal file
68
ansible/jupyterlab/oauth2-proxy-jupyter.cfg.j2
Normal 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
|
||||
23
ansible/jupyterlab/oauth2-proxy-jupyter.service.j2
Normal file
23
ansible/jupyterlab/oauth2-proxy-jupyter.service.j2
Normal 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
|
||||
Reference in New Issue
Block a user