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.
254 lines
9.5 KiB
Markdown
254 lines
9.5 KiB
Markdown
# SearXNG Authentication Design Document
|
||
# Red Panda Approved
|
||
|
||
## Overview
|
||
|
||
This document describes the design for adding Casdoor-based authentication to SearXNG,
|
||
which doesn't natively support SSO/OIDC authentication.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
┌──────────────┐ ┌───────────────┐ ┌─────────────────────────────────────┐
|
||
│ Browser │────▶│ HAProxy │────▶│ Oberon │
|
||
│ │ │ (titania) │ │ ┌────────────────┐ ┌───────────┐ │
|
||
└──────────────┘ └───────┬───────┘ │ │ OAuth2-Proxy │─▶│ SearXNG │ │
|
||
│ │ │ (port 22073) │ │ (22083) │ │
|
||
│ │ └───────┬────────┘ └───────────┘ │
|
||
│ └──────────┼─────────────────────────┘
|
||
│ │ OIDC
|
||
│ ┌──────────────────▼────────────────┐
|
||
└────▶│ Casdoor │
|
||
│ (OIDC Provider - titania) │
|
||
└───────────────────────────────────┘
|
||
```
|
||
|
||
The OAuth2-Proxy runs as a **native binary sidecar** on Oberon alongside SearXNG,
|
||
following the same pattern used for JupyterLab on Puck. The upstream connection is
|
||
`localhost` — eliminating the cross-host hop from the previous Docker-based deployment
|
||
on Titania.
|
||
|
||
> ℹ️ Each host supports at most one OAuth2-Proxy sidecar instance. The binary is
|
||
> shared at `/usr/local/bin/oauth2-proxy`; each service gets a unique config directory
|
||
> and systemd unit name.
|
||
|
||
## Components
|
||
|
||
### 1. OAuth2-Proxy (Sidecar on Oberon)
|
||
- **Purpose**: Acts as authentication gateway for SearXNG
|
||
- **Port**: 22073 (exposed to HAProxy)
|
||
- **Binary**: Native `oauth2-proxy` v7.6.0 (systemd service `oauth2-proxy-searxng`)
|
||
- **Config**: `/etc/oauth2-proxy-searxng/oauth2-proxy.cfg`
|
||
- **Upstream**: `http://127.0.0.1:22083` (localhost sidecar to SearXNG)
|
||
- **Logging**: systemd journal (`SyslogIdentifier=oauth2-proxy-searxng`)
|
||
|
||
### 2. Casdoor (Existing on Titania)
|
||
- **Purpose**: OIDC Identity Provider
|
||
- **Port**: 22081
|
||
- **URL**: https://id.ouranos.helu.ca/ (via HAProxy)
|
||
- **Required Setup**:
|
||
- Create Application for SearXNG
|
||
- Configure redirect URI
|
||
- Generate client credentials
|
||
|
||
### 3. HAProxy Updates (Titania)
|
||
- Route `searxng.ouranos.helu.ca` to OAuth2-Proxy on Oberon (`oberon.incus:22073`)
|
||
- OAuth2-Proxy handles authentication before proxying to SearXNG on localhost
|
||
|
||
### 4. SearXNG (Existing on Oberon)
|
||
- **No changes required** - remains unaware of authentication
|
||
- Receives pre-authenticated requests from OAuth2-Proxy
|
||
|
||
## Authentication Flow
|
||
|
||
1. User navigates to `https://searxng.ouranos.helu.ca/`
|
||
2. HAProxy routes to OAuth2-Proxy on oberon:22073
|
||
3. OAuth2-Proxy checks for valid session cookie (`_oauth2_proxy_searxng`)
|
||
4. **If no valid session**:
|
||
- Redirect to Casdoor login: `https://id.ouranos.helu.ca/login/oauth/authorize`
|
||
- User authenticates with Casdoor (username/password, social login, etc.)
|
||
- Casdoor redirects back with authorization code
|
||
- OAuth2-Proxy exchanges code for tokens
|
||
- OAuth2-Proxy sets session cookie
|
||
5. **If valid session**:
|
||
- OAuth2-Proxy adds `X-Forwarded-User` header
|
||
- Request proxied to SearXNG at `127.0.0.1:22083` (localhost sidecar)
|
||
|
||
## Casdoor Configuration
|
||
|
||
### Application Setup (Manual via Casdoor UI)
|
||
|
||
1. Login to Casdoor at https://id.ouranos.helu.ca/
|
||
2. Navigate to Applications → Add
|
||
3. Configure:
|
||
- **Name**: `searxng`
|
||
- **Display Name**: `SearXNG Search`
|
||
- **Organization**: `built-in` (or your organization)
|
||
- **Redirect URLs**:
|
||
- `https://searxng.ouranos.helu.ca/oauth2/callback`
|
||
- **Grant Types**: `authorization_code`, `refresh_token`
|
||
- **Response Types**: `code`
|
||
4. Save and note the `Client ID` and `Client Secret`
|
||
|
||
### Cookie Secret Generation
|
||
|
||
Generate a 32-byte random secret for OAuth2-Proxy cookies:
|
||
|
||
```bash
|
||
openssl rand -base64 32
|
||
```
|
||
|
||
## Environment Variables
|
||
|
||
### Development (Sandbox)
|
||
```yaml
|
||
# In inventory/host_vars/oberon.incus.yml
|
||
searxng_oauth2_proxy_dir: /etc/oauth2-proxy-searxng
|
||
searxng_oauth2_proxy_version: "7.6.0"
|
||
searxng_proxy_port: 22073
|
||
searxng_domain: "ouranos.helu.ca"
|
||
searxng_oauth2_oidc_issuer_url: "https://id.ouranos.helu.ca"
|
||
searxng_oauth2_redirect_url: "https://searxng.ouranos.helu.ca/oauth2/callback"
|
||
|
||
# OAuth2 Credentials (from vault)
|
||
searxng_oauth2_client_id: "{{ vault_searxng_oauth2_client_id }}"
|
||
searxng_oauth2_client_secret: "{{ vault_searxng_oauth2_client_secret }}"
|
||
searxng_oauth2_cookie_secret: "{{ vault_searxng_oauth2_cookie_secret }}"
|
||
```
|
||
|
||
> ℹ️ Variables use the `searxng_` prefix, following the same naming pattern as
|
||
> `jupyterlab_oauth2_*` variables on Puck. The upstream URL (`http://127.0.0.1:22083`)
|
||
> is derived from `searxng_port` in the config template — no cross-host URL needed.
|
||
|
||
## Deployment Steps
|
||
|
||
### 1. Add Vault Secrets
|
||
```bash
|
||
ansible-vault edit inventory/group_vars/all/vault.yml
|
||
```
|
||
|
||
Add:
|
||
```yaml
|
||
vault_searxng_oauth2_client_id: "<from-casdoor>"
|
||
vault_searxng_oauth2_client_secret: "<from-casdoor>"
|
||
vault_searxng_oauth2_cookie_secret: "<generated-32-byte-secret>"
|
||
```
|
||
|
||
Note: The `searxng_` prefix allows service-specific credentials. The Oberon host_vars
|
||
maps these directly to `searxng_oauth2_*` variables used by the sidecar config template.
|
||
|
||
### 2. Update Host Variables
|
||
OAuth2-Proxy variables are defined in `inventory/host_vars/oberon.incus.yml` alongside
|
||
the existing SearXNG configuration. No separate service entry is needed — the OAuth2-Proxy
|
||
sidecar is deployed as part of the `searxng` service.
|
||
|
||
```yaml
|
||
# SearXNG OAuth2-Proxy Sidecar (in oberon.incus.yml)
|
||
searxng_oauth2_proxy_dir: /etc/oauth2-proxy-searxng
|
||
searxng_oauth2_proxy_version: "7.6.0"
|
||
searxng_proxy_port: 22073
|
||
searxng_domain: "ouranos.helu.ca"
|
||
searxng_oauth2_oidc_issuer_url: "https://id.ouranos.helu.ca"
|
||
searxng_oauth2_redirect_url: "https://searxng.ouranos.helu.ca/oauth2/callback"
|
||
```
|
||
|
||
### 3. Update HAProxy Backend
|
||
Route SearXNG traffic through OAuth2-Proxy on Oberon:
|
||
```yaml
|
||
# In inventory/host_vars/titania.incus.yml
|
||
haproxy_backends:
|
||
- subdomain: "searxng"
|
||
backend_host: "oberon.incus" # Same host as SearXNG
|
||
backend_port: 22073 # OAuth2-Proxy port
|
||
health_path: "/ping" # OAuth2-Proxy health endpoint
|
||
```
|
||
|
||
### 4. Deploy
|
||
```bash
|
||
cd ansible
|
||
|
||
# Deploy SearXNG + OAuth2-Proxy sidecar
|
||
ansible-playbook searxng/deploy.yml
|
||
|
||
# Update HAProxy configuration
|
||
ansible-playbook haproxy/deploy.yml
|
||
```
|
||
|
||
## Monitoring
|
||
|
||
### Logs
|
||
OAuth2-Proxy logs to systemd journal on Oberon. Alloy's default `systemd_logs`
|
||
source captures these logs automatically, filterable by `SyslogIdentifier=oauth2-proxy-searxng`.
|
||
|
||
```bash
|
||
# View logs on Oberon
|
||
ssh oberon.incus
|
||
journalctl -u oauth2-proxy-searxng -f
|
||
```
|
||
|
||
### Metrics
|
||
OAuth2-Proxy exposes Prometheus metrics at `/metrics` on port 22073:
|
||
- `oauth2_proxy_requests_total` - Total requests
|
||
- `oauth2_proxy_errors_total` - Error count
|
||
- `oauth2_proxy_upstream_latency_seconds` - Upstream latency
|
||
|
||
## Security Considerations
|
||
|
||
1. **Cookie Security**:
|
||
- `cookie_secure = true` enforces HTTPS-only cookies
|
||
- `cookie_httponly = true` prevents JavaScript access
|
||
- `cookie_samesite = "lax"` provides CSRF protection
|
||
|
||
2. **Email Domain Restriction**:
|
||
- Configure `oauth2_proxy_email_domains` to limit who can access
|
||
- Example: `["yourdomain.com"]` or `["*"]` for any
|
||
|
||
3. **Group-Based Access**:
|
||
- Optional: Configure `oauth2_proxy_allowed_groups` in Casdoor
|
||
- Only users in specified groups can access SearXNG
|
||
|
||
## Troubleshooting
|
||
|
||
### Check OAuth2-Proxy Status
|
||
```bash
|
||
ssh oberon.incus
|
||
systemctl status oauth2-proxy-searxng
|
||
journalctl -u oauth2-proxy-searxng --no-pager -n 50
|
||
```
|
||
|
||
### Test OIDC Discovery
|
||
```bash
|
||
curl https://id.ouranos.helu.ca/.well-known/openid-configuration
|
||
```
|
||
|
||
### Test Health Endpoint
|
||
```bash
|
||
curl http://oberon.incus:22073/ping
|
||
```
|
||
|
||
### Verify Cookie Domain
|
||
Ensure the cookie domain (`.ouranos.helu.ca`) matches your HAProxy domain.
|
||
Cookies won't work across different domains.
|
||
|
||
## Files
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `ansible/searxng/deploy.yml` | SearXNG + OAuth2-Proxy sidecar deployment |
|
||
| `ansible/searxng/oauth2-proxy-searxng.cfg.j2` | OAuth2-Proxy OIDC configuration |
|
||
| `ansible/searxng/oauth2-proxy-searxng.service.j2` | Systemd unit for OAuth2-Proxy |
|
||
| `ansible/inventory/host_vars/oberon.incus.yml` | Host variables (`searxng_oauth2_*`) |
|
||
| `docs/searxng-auth.md` | This design document |
|
||
|
||
### Generic OAuth2-Proxy Module (Retained)
|
||
|
||
The standalone `ansible/oauth2_proxy/` directory is retained as a generic, reusable
|
||
Docker-based OAuth2-Proxy module for future services:
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `ansible/oauth2_proxy/deploy.yml` | Generic Docker Compose deployment |
|
||
| `ansible/oauth2_proxy/docker-compose.yml.j2` | Docker Compose template |
|
||
| `ansible/oauth2_proxy/oauth2-proxy.cfg.j2` | Generic OIDC configuration template |
|
||
| `ansible/oauth2_proxy/stage.yml` | Validation / dry-run playbook |
|