From c4215044f0511099dc482c4d125e87de1c0404fd Mon Sep 17 00:00:00 2001 From: Nicolas Cantu Date: Mon, 23 Mar 2026 07:49:06 +0100 Subject: [PATCH] Re-enable nginx Bearer auth on ia.enso /ollama MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivations:** - Restore gate on /ollama/; document Cursor streamFromAgentBackend note. **Root causes:** - N/A. **Correctifs:** - location /ollama/ if map + clear Authorization upstream; deploy script emits Bearer + websocket maps with retry bearer_only. **Evolutions:** - README Cursor subsection on streamFromAgentBackend (observed behavior); feature/services/infrastructure aligned. **Pages affectées:** - deploy/nginx/sites/ia.enso.4nkweb.com.conf - deploy/nginx/deploy-ia-enso-to-proxy.sh - deploy/nginx/README-ia-enso.md - deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example - docs/features/ia-enso-nginx-proxy-ollama-anythingllm.md - docs/services.md - docs/infrastructure.md --- deploy/nginx/README-ia-enso.md | 45 +++++++++------ deploy/nginx/deploy-ia-enso-to-proxy.sh | 55 +++++++++++++------ .../ia-enso-ollama-bearer.map.conf.example | 9 +-- deploy/nginx/sites/ia.enso.4nkweb.com.conf | 14 +++-- .../ia-enso-nginx-proxy-ollama-anythingllm.md | 35 +++++------- docs/infrastructure.md | 2 +- docs/services.md | 5 +- 7 files changed, 99 insertions(+), 66 deletions(-) diff --git a/deploy/nginx/README-ia-enso.md b/deploy/nginx/README-ia-enso.md index 0268993..52fc0cc 100644 --- a/deploy/nginx/README-ia-enso.md +++ b/deploy/nginx/README-ia-enso.md @@ -10,11 +10,11 @@ Reverse TLS vers l’hôte LAN **`192.168.1.164`** (Ollama + AnythingLLM ; IP su | **Ollama** API native (ex. liste des modèles) | `https://ia.enso.4nkweb.com/ollama/api/tags` | | **Ollama** API compatible OpenAI (Cursor, etc.) | base URL `https://ia.enso.4nkweb.com/ollama/v1` — ex. `https://ia.enso.4nkweb.com/ollama/v1/models` | -**Sécurité :** `/ollama/` n’a **pas** de garde Bearer nginx par défaut : toute personne qui peut joindre l’URL peut utiliser l’API Ollama. Restreindre par **pare-feu** (IP du proxy uniquement vers `.164`) ou réintroduire un `map` Bearer (voir `http-maps/ia-enso-ollama-bearer.map.conf.example`). AnythingLLM sous `/anythingllm/` reste derrière son **login applicatif**. +**Bearer nginx :** tout `/ollama/` exige `Authorization: Bearer ` (fichier `map` sur le proxy). La valeur n’est **pas** transmise à Ollama (`Authorization` effacé en amont). AnythingLLM sous `/anythingllm/` : auth **applicative** uniquement. | Chemin (relatif) | Backend | Port LAN | Protection | |------------------|---------|----------|------------| -| `/ollama/` | Ollama | `11434` | Aucune auth nginx (Ollama sans clé par défaut) | +| `/ollama/` | Ollama | `11434` | **Bearer** nginx | | `/anythingllm/` | AnythingLLM | `3001` | Login AnythingLLM | **Contexte Cursor :** une URL en IP privée (ex. `http://192.168.1.164:11434`) peut être refusée par Cursor (`ssrf_blocked`). Un **nom public** HTTPS vers le proxy évite ce blocage si le DNS résolu depuis Internet n’est pas une IP RFC1918. @@ -28,11 +28,14 @@ Reverse TLS vers l’hôte LAN **`192.168.1.164`** (Ollama + AnythingLLM ; IP su Depuis la racine du dépôt **`smart_ide`**, sur une machine avec accès SSH au bastion puis au proxy : ```bash -# accès LAN direct au proxy (.100), sans bastion (variable vide = pas de ProxyJump) : +export IA_ENSO_OLLAMA_BEARER_TOKEN='secret-ascii-sans-guillemets-ni-backslash' +# accès LAN direct au proxy (.100), sans bastion : # export DEPLOY_SSH_PROXY_HOST= ./deploy/nginx/deploy-ia-enso-to-proxy.sh ``` +Si `IA_ENSO_OLLAMA_BEARER_TOKEN` est absent, le script génère un token hex (affichage unique) à conserver pour Cursor. + ### Prérequis sur le proxy - `http { include /etc/nginx/conf.d/*.conf; ... }` dans `/etc/nginx/nginx.conf` (sinon le script échoue avec un message explicite). @@ -58,16 +61,17 @@ sudo certbot certonly --webroot -w /var/www/certbot -d ia.enso.4nkweb.com --non- | Chemin sur le proxy | Rôle | |---------------------|------| -| `/etc/nginx/conf.d/ia-enso-http-maps.conf` | `map` WebSocket `$connection_upgrade` (ou fichier stub si doublon ailleurs) | +| `/etc/nginx/conf.d/ia-enso-http-maps.conf` | `map_hash_bucket_size`, `map` Bearer `$ia_enso_ollama_authorized`, et souvent `map` WebSocket | | `/etc/nginx/sites-available/ia.enso.4nkweb.com.conf` | `server` HTTP→HTTPS + HTTPS | | Lien `sites-enabled/ia.enso.4nkweb.com.conf` | Activation du vhost | -Si `nginx -t` échoue à cause d’un **doublon** `map $http_upgrade $connection_upgrade`, le script retente avec un **stub** commenté à la place du `map`. +Si `nginx -t` échoue à cause d’un **doublon** `map $http_upgrade $connection_upgrade`, le script retente avec **Bearer seul** (sans dupliquer le `map` WebSocket). ### Variables d’environnement du script | Variable | Défaut | Rôle | |----------|--------|------| +| `IA_ENSO_OLLAMA_BEARER_TOKEN` | généré (`openssl rand -hex 32`) | Secret pour `Authorization: Bearer …` | | `IA_ENSO_SSH_KEY` | `~/.ssh/id_ed25519` | Clé privée SSH | | `IA_ENSO_PROXY_USER` | `ncantu` | Utilisateur SSH sur le proxy | | `IA_ENSO_PROXY_HOST` | `192.168.1.100` | Cible SSH (IP ou hostname LAN) | @@ -91,11 +95,9 @@ sudo certbot certonly --webroot -w /var/www/certbot -d ia.enso.4nkweb.com Adapter dans `sites/ia.enso.4nkweb.com.conf` les directives `ssl_certificate` / `ssl_certificate_key` si le répertoire `live/` diffère. -### 2. Maps HTTP (WebSocket ; Bearer optionnel) +### 2. Maps HTTP (Bearer + WebSocket) -**WebSocket (AnythingLLM)** : si `$connection_upgrade` n’existe pas déjà dans l’instance, inclure `http-maps/websocket-connection.map.conf.example` dans `http { }` ou utiliser le fichier `ia-enso-http-maps.conf` déployé par le script. - -**Bearer sur `/ollama/` (optionnel)** : pour réactiver une garde nginx, ajouter le `map` de `http-maps/ia-enso-ollama-bearer.map.conf.example` et dans `location /ollama/` un `if ($ia_enso_ollama_authorized = 0) { return 401; }` (+ `map_hash_bucket_size 256` si secret long). Ne pas commiter le secret. +Le script déploie `ia-enso-http-maps.conf` avec `map_hash_bucket_size 256`, le `map` Bearer et le `map` WebSocket (ou Bearer seul si doublon WebSocket ailleurs). Installation manuelle : combiner `http-maps/ia-enso-ollama-bearer.map.conf.example` et `websocket-connection.map.conf.example` dans `http { }` si besoin. ### 3. Fichier `server` @@ -119,11 +121,12 @@ sudo nginx -t && sudo systemctl reload nginx ### API Ollama via le proxy ```bash -curl -sS -o /dev/null -w "%{http_code}\n" https://ia.enso.4nkweb.com/ollama/v1/models -curl -sS -o /dev/null -w "%{http_code}\n" https://ia.enso.4nkweb.com/ollama/api/tags +curl -sS -o /dev/null -w "%{http_code}\n" \ + -H "Authorization: Bearer " \ + https://ia.enso.4nkweb.com/ollama/v1/models ``` -Attendu : **200** sans en-tête `Authorization` (config par défaut sans Bearer nginx). +Attendu : **200** avec le bon secret ; **401** sans `Authorization` ou secret incorrect. ### AnythingLLM @@ -133,9 +136,9 @@ Si les assets statiques échouent, vérifier la doc upstream (sous-chemin, en-t ### Cursor - URL de base OpenAI : `https://ia.enso.4nkweb.com/ollama/v1` -- Clé API : laisser vide ou valeur factice si Cursor l’exige (plus de Bearer nginx sur `/ollama/` par défaut). +- Clé API : **identique** au secret du `map` nginx (sans préfixe `Bearer ` dans le champ). -Si **`curl`** vers `/ollama/v1/models` renvoie **200** mais Cursor affiche **`ERROR_BAD_USER_API_KEY`**, l’échec vient **du client Cursor** (validation / infra Cursor), pas du proxy : [forum](https://forum.cursor.com/t/unauthorized-user-api-key-with-custom-openai-api-key-url/132572). +**`streamFromAgentBackend` (comportement observé)** : dans l’application Cursor (bundle Electron), le flux **Agent / chat** appelle une couche interne qui ouvre un stream vers **les serveurs Cursor** (`getAgentStreamResponse`, etc.), pas un `fetch` direct depuis ton poste vers ton URL OpenAI override. Cursor peut donc valider une **« User API key »** ou des droits **avant** ou **en parallèle** de l’usage de l’override. Si **`curl`** avec le Bearer vers `/ollama/v1/models` renvoie **200** mais Cursor affiche **`ERROR_BAD_USER_API_KEY`**, l’échec reste **côté client / infra Cursor** : [forum](https://forum.cursor.com/t/unauthorized-user-api-key-with-custom-openai-api-key-url/132572). Le code minifié du produit n’est pas dans ce dépôt ; seuls les noms de fonctions dans la stack trace décrivent ce chemin d’exécution. --- @@ -145,13 +148,21 @@ Sur **`192.168.1.164`**, n’autoriser **11434** et **3001** TCP que depuis **19 --- +## Rotation du secret Bearer + +1. Mettre à jour `"Bearer …"` dans `/etc/nginx/conf.d/ia-enso-http-maps.conf` (ou redéployer avec `IA_ENSO_OLLAMA_BEARER_TOKEN`). +2. `sudo nginx -t && sudo systemctl reload nginx`. +3. Mettre à jour la clé API dans Cursor (et tout autre client). + +--- + ## Dépannage | Symptôme | Piste | |----------|--------| -| `nginx -t` erreur sur `connection_upgrade` | Doublon de `map $http_upgrade $connection_upgrade` : retirer l’un des blocs ou laisser le stub du script. | -| `could not build map_hash` / `map_hash_bucket_size` | Uniquement si tu réactives un `map` Bearer avec un secret très long ; ajouter `map_hash_bucket_size 256;` dans `http { }`. | -| `401` sur `/ollama/` | Bearer nginx réactivé manuellement : aligner client et `map`, ou désactiver la garde. | +| `nginx -t` erreur sur `connection_upgrade` | Doublon de `map $http_upgrade $connection_upgrade` : retirer l’un des blocs ou laisser le déploiement « Bearer seul » du script. | +| `could not build map_hash` / `map_hash_bucket_size` | Secret Bearer long : le fichier généré par le script inclut `map_hash_bucket_size 256;`. | +| `401` sur `/ollama/` | Secret différent entre client et `map` ; en-tête `Authorization` absent ou mal formé. | | `502` / timeout | Ollama ou AnythingLLM arrêtés sur le backend ; pare-feu ; mauvaise IP dans `upstream` (vérifier `grep server /etc/nginx/sites-available/ia.enso.4nkweb.com.conf` sur le proxy ; redéployer avec `IA_ENSO_BACKEND_IP=192.168.1.164`). | | Erreur SSL / `cannot load certificate` | Certificat absent : exécuter certbot sur le proxy pour `ia.enso.4nkweb.com`, ou adapter les chemins `ssl_certificate` dans le fichier site. | | Cursor `ssrf_blocked` | L’hôte utilisé résout encore vers une IP privée côté infrastructure Cursor ; vérifier DNS public / NAT. | diff --git a/deploy/nginx/deploy-ia-enso-to-proxy.sh b/deploy/nginx/deploy-ia-enso-to-proxy.sh index 50a87be..62b0dd6 100755 --- a/deploy/nginx/deploy-ia-enso-to-proxy.sh +++ b/deploy/nginx/deploy-ia-enso-to-proxy.sh @@ -4,6 +4,7 @@ # Requires passwordless sudo for nginx on the proxy host. # # Environment: +# IA_ENSO_OLLAMA_BEARER_TOKEN Bearer secret for /ollama (if unset, openssl rand -hex 32). # IA_ENSO_SSH_KEY SSH private key (default: ~/.ssh/id_ed25519). # IA_ENSO_PROXY_USER SSH user on proxy (default: ncantu). # IA_ENSO_PROXY_HOST Proxy IP or hostname (default: 192.168.1.100). @@ -37,26 +38,47 @@ elif [[ -z "$DEPLOY_SSH_PROXY_HOST" ]]; then fi export DEPLOY_SSH_PROXY_USER +TOKEN="${IA_ENSO_OLLAMA_BEARER_TOKEN:-}" +if [[ -z "$TOKEN" ]]; then + TOKEN="$(openssl rand -hex 32)" + echo "IA_ENSO_OLLAMA_BEARER_TOKEN was unset; generated token (store for Cursor API key):" + echo "$TOKEN" + echo "---" +fi + +if [[ "$TOKEN" == *'"'* ]] || [[ "$TOKEN" == *'\'* ]]; then + echo "Token must not contain double quotes or backslashes." >&2 + exit 1 +fi + if [[ ! "$IA_ENSO_BACKEND_IP" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "IA_ENSO_BACKEND_IP must be an IPv4 address (got: ${IA_ENSO_BACKEND_IP})" >&2 exit 1 fi +# mode: full = websocket + bearer; bearer_only = bearer + map_hash (duplicate websocket elsewhere) write_maps_file() { local path="$1" - local with_websocket="$2" - if [[ "$with_websocket" == "1" ]]; then - cat <<'MAPEOF' >"$path" + local mode="$2" + { + cat <<'HASHOF' +map_hash_bucket_size 256; +HASHOF + if [[ "$mode" == "full" ]]; then + cat <<'MAPEOF' map $http_upgrade $connection_upgrade { default upgrade; '' close; } MAPEOF - else - cat <<'STUB' >"$path" -# ia-enso: $connection_upgrade is defined in another conf.d file; no duplicate map here. -STUB - fi + fi + cat <"$path" } TMP_DIR="$(mktemp -d)" @@ -66,8 +88,8 @@ cleanup() { trap cleanup EXIT try_install() { - local with_ws="$1" - write_maps_file "${TMP_DIR}/ia-enso-http-maps.conf" "$with_ws" + local mode="$1" + write_maps_file "${TMP_DIR}/ia-enso-http-maps.conf" "$mode" sed "s/__IA_ENSO_BACKEND_IP__/${IA_ENSO_BACKEND_IP}/g" "${SCRIPT_DIR}/sites/ia.enso.4nkweb.com.conf" >"${TMP_DIR}/ia.enso.4nkweb.com.conf" scp_copy "$IA_ENSO_SSH_KEY" "${TMP_DIR}/ia-enso-http-maps.conf" "$IA_ENSO_PROXY_USER" "$IA_ENSO_PROXY_HOST" "/tmp/ia-enso-http-maps.conf" scp_copy "$IA_ENSO_SSH_KEY" "${TMP_DIR}/ia.enso.4nkweb.com.conf" "$IA_ENSO_PROXY_USER" "$IA_ENSO_PROXY_HOST" "/tmp/ia.enso.4nkweb.com.conf" @@ -91,16 +113,13 @@ REMOTE echo "Deploying ia.enso upstreams to ${IA_ENSO_BACKEND_IP} (Ollama :11434, AnythingLLM :3001)." -if ! try_install 1; then - echo "Retrying with stub maps file (websocket map likely already defined on proxy)..." - if ! try_install 0; then +if ! try_install full; then + echo "Retrying with Bearer map only (websocket map likely already defined on proxy)..." + if ! try_install bearer_only; then echo "Deploy failed (SSH, sudo, nginx -t, or missing include /etc/nginx/conf.d/*.conf)." >&2 - echo "Re-run from a host with SSH access to the proxy (LAN direct: DEPLOY_SSH_PROXY_HOST=)." >&2 + echo "Re-run from a host with SSH access (LAN: DEPLOY_SSH_PROXY_HOST=); set IA_ENSO_OLLAMA_BEARER_TOKEN to reuse secret." >&2 exit 1 fi fi -echo "Done. Public URLs (no nginx Bearer on /ollama/):" -echo " AnythingLLM: https://ia.enso.4nkweb.com/anythingllm/" -echo " Ollama native: https://ia.enso.4nkweb.com/ollama/api/tags" -echo " OpenAI-compat: https://ia.enso.4nkweb.com/ollama/v1" +echo "Done. Bearer required on /ollama/. Cursor base: https://ia.enso.4nkweb.com/ollama/v1 — API key = token above (if generated) or IA_ENSO_OLLAMA_BEARER_TOKEN." diff --git a/deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example b/deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example index f440f6d..ddde18a 100644 --- a/deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example +++ b/deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example @@ -1,8 +1,9 @@ -# OPTIONAL: Bearer gate on /ollama/ (default repo site has no nginx auth on Ollama). -# Install inside `http { ... }` before server blocks that use $ia_enso_ollama_authorized, and add to -# location /ollama/ { if ($ia_enso_ollama_authorized = 0) { return 401; } ... } +# Bearer gate for /ollama/ (matches default site: if ($ia_enso_ollama_authorized = 0) { return 401; }). +# Install inside `http { ... }` before server blocks that use $ia_enso_ollama_authorized: +# include /etc/nginx/http-maps/ia-enso-ollama-bearer.map.conf; # -# Copy without the .example suffix, set secret (ASCII, no double quotes in value). +# Copy without the .example suffix; set secret (ASCII, no double quotes in value). +# Cursor: OpenAI base .../ollama/v1 and API key = same secret (no "Bearer " in field). map_hash_bucket_size 256; diff --git a/deploy/nginx/sites/ia.enso.4nkweb.com.conf b/deploy/nginx/sites/ia.enso.4nkweb.com.conf index ae904c0..b13c15b 100644 --- a/deploy/nginx/sites/ia.enso.4nkweb.com.conf +++ b/deploy/nginx/sites/ia.enso.4nkweb.com.conf @@ -4,12 +4,12 @@ # AnythingLLM UI: https://ia.enso.4nkweb.com/anythingllm/ # Ollama OpenAI API: https://ia.enso.4nkweb.com/ollama/v1/ (e.g. .../v1/models, .../v1/chat/completions) # Ollama native API: https://ia.enso.4nkweb.com/ollama/api/tags (and other /api/* paths) -# /ollama/* has NO nginx Bearer gate (public inference if DNS is reachable); restrict at firewall or re-add map. -# Cursor base URL: https://ia.enso.4nkweb.com/ollama/v1 +# /ollama/* requires Authorization: Bearer at nginx (map in conf.d); secret not forwarded to Ollama. +# Cursor base URL: https://ia.enso.4nkweb.com/ollama/v1 — API key field = same secret (no "Bearer " prefix). # # Prerequisites on the proxy host: # - TLS certificate for ia.enso.4nkweb.com (e.g. certbot). -# - Optional: include http-maps/websocket-connection.map.conf.example in http { } if not using deploy script maps file. +# - HTTP map $ia_enso_ollama_authorized (see deploy script / http-maps/ia-enso-ollama-bearer.map.conf.example). # # Upstream backend: replaced at deploy time (default 192.168.1.164). Manual install: replace __IA_ENSO_BACKEND_IP__. @@ -54,8 +54,12 @@ server { client_max_body_size 100M; - # Ollama: no nginx auth (Ollama itself does not enforce API keys by default). + # Ollama: nginx Bearer gate (map $ia_enso_ollama_authorized); Authorization cleared upstream. location /ollama/ { + if ($ia_enso_ollama_authorized = 0) { + return 401; + } + proxy_pass http://ia_enso_ollama/; proxy_http_version 1.1; proxy_set_header Host $host; @@ -64,6 +68,8 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Connection ""; + proxy_set_header Authorization ""; + proxy_buffering off; proxy_read_timeout 3600s; proxy_send_timeout 3600s; diff --git a/docs/features/ia-enso-nginx-proxy-ollama-anythingllm.md b/docs/features/ia-enso-nginx-proxy-ollama-anythingllm.md index 1917ee1..234a03b 100644 --- a/docs/features/ia-enso-nginx-proxy-ollama-anythingllm.md +++ b/docs/features/ia-enso-nginx-proxy-ollama-anythingllm.md @@ -4,46 +4,41 @@ ## Objective -Expose Ollama and AnythingLLM on the public proxy hostname with HTTPS, path prefixes `/ollama` and `/anythingllm`. **Default:** no nginx Bearer on `/ollama/` (optional `map` in `http-maps/ia-enso-ollama-bearer.map.conf.example` to re-enable). +Expose Ollama and AnythingLLM on the public proxy hostname with HTTPS, path prefixes `/ollama` and `/anythingllm`, and **gate `/ollama/`** with a **Bearer token** at nginx (compatible with OpenAI clients that send `Authorization: Bearer `). The secret is **not** forwarded to Ollama. ## Public URLs (HTTPS) - AnythingLLM UI: `https://ia.enso.4nkweb.com/anythingllm/` -- Ollama native API (example): `https://ia.enso.4nkweb.com/ollama/api/tags` +- Ollama native API (example): `https://ia.enso.4nkweb.com/ollama/api/tags` — Bearer required at nginx - OpenAI-compatible base (Cursor): `https://ia.enso.4nkweb.com/ollama/v1` ## Impacts -- **Proxy (nginx):** `server_name`, TLS, locations; `conf.d/ia-enso-http-maps.conf` holds WebSocket `map` when deployed by script (or stub if duplicate elsewhere). +- **Proxy (nginx):** `server_name`, TLS, locations; `conf.d/ia-enso-http-maps.conf` with `map_hash_bucket_size`, Bearer `map`, and WebSocket `map` (or Bearer-only if WebSocket map exists elsewhere). - **Backend (192.168.1.164):** must accept connections from the proxy on `11434` and `3001`. -- **Clients:** Cursor can use `https://ia.enso.4nkweb.com/ollama/v1` without a matching nginx secret if Bearer is disabled; hostname may avoid private-IP SSRF blocks when DNS resolves publicly. +- **Clients:** send `Authorization: Bearer ` for `/ollama/*`; Cursor API key field = same secret as in the nginx `map`. ## Repository layout | Path | Purpose | |------|---------| -| `deploy/nginx/sites/ia.enso.4nkweb.com.conf` | `server` blocks ; upstreams use `__IA_ENSO_BACKEND_IP__` (default `192.168.1.164` substituted by `deploy-ia-enso-to-proxy.sh` or manual `sed`) | -| `deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example` | **Optional** Bearer `map` + `location /ollama/` `if` to re-enable auth | -| `deploy/nginx/http-maps/websocket-connection.map.conf.example` | Example WebSocket `map` (manual install) | -| `deploy/nginx/deploy-ia-enso-to-proxy.sh` | SSH deploy: maps + site, `nginx -t`, reload; stub retry if websocket `map` already exists | -| `deploy/nginx/sites/ia.enso.4nkweb.com.http-only.conf` | Temporary HTTP-only vhost for first Let’s Encrypt `webroot` issuance when `live/ia.enso…` is missing | -| `deploy/nginx/README-ia-enso.md` | **Operator reference:** automated + manual steps, env vars, checks, troubleshooting, TLS bootstrap | +| `deploy/nginx/sites/ia.enso.4nkweb.com.conf` | `server` blocks ; upstreams use `__IA_ENSO_BACKEND_IP__` | +| `deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example` | Bearer `map` reference for manual installs | +| `deploy/nginx/http-maps/websocket-connection.map.conf.example` | WebSocket `map` reference | +| `deploy/nginx/deploy-ia-enso-to-proxy.sh` | SSH deploy; retry Bearer-only if duplicate WebSocket `map` | +| `deploy/nginx/sites/ia.enso.4nkweb.com.http-only.conf` | TLS bootstrap HTTP-only vhost | +| `deploy/nginx/README-ia-enso.md` | Operator reference (includes note on Cursor `streamFromAgentBackend`) | ## Deployment modalities -**Preferred:** run `./deploy/nginx/deploy-ia-enso-to-proxy.sh` from `smart_ide` on a host with SSH access (see `README-ia-enso.md`). - -**Manual:** DNS → TLS (certbot) → WebSocket `map` if needed → install site → `nginx -t` → reload. Details: `deploy/nginx/README-ia-enso.md`. - -Restrict backend ports on `192.168.1.164` to the proxy source where a host firewall is used. +Run `./deploy/nginx/deploy-ia-enso-to-proxy.sh` with optional `IA_ENSO_OLLAMA_BEARER_TOKEN`. See `README-ia-enso.md`. ## Analysis modalities -- `curl` to `/ollama/v1/models` and `/ollama/api/tags` without `Authorization` (expect **200** when Bearer is off). -- Browser access to `/anythingllm/` and application login. -- Cursor connectivity; `ERROR_BAD_USER_API_KEY` may still be a Cursor client issue (see README forum link). +- `curl` to `/ollama/v1/models` with and without Bearer (200 / 401). +- Browser: `/anythingllm/`. ## Security notes -- **Default `/ollama/` is unauthenticated at nginx:** anyone who can reach the URL can call Ollama unless restricted by firewall or Ollama-level controls. Re-add Bearer using the example `map` if needed. -- AnythingLLM remains protected by **its own** application authentication. +- Bearer secret is equivalent to an API key; rotate in `ia-enso-http-maps.conf` and client configs together. +- AnythingLLM uses its own application login on `/anythingllm/`. diff --git a/docs/infrastructure.md b/docs/infrastructure.md index 1598a74..a75e362 100644 --- a/docs/infrastructure.md +++ b/docs/infrastructure.md @@ -27,7 +27,7 @@ Internet access to backends uses **SSH ProxyJump** via `ncantu@4nk.myftp.biz` (s ## Reverse proxy `ia.enso.4nkweb.com` (Ollama / AnythingLLM) -Hostname TLS sur le **proxy** `192.168.1.100` : préfixes `/ollama` et `/anythingllm` vers l’hôte LAN `192.168.1.164` (ports `11434` et `3001`, voir `deploy/nginx/sites/ia.enso.4nkweb.com.conf`). **`/ollama/`** sans garde Bearer nginx par défaut (option documentée dans `deploy/nginx/http-maps/`) ; AnythingLLM reste derrière son auth applicative. +Hostname TLS sur le **proxy** `192.168.1.100` : préfixes `/ollama` et `/anythingllm` vers l’hôte LAN `192.168.1.164` (ports `11434` et `3001`, voir `deploy/nginx/sites/ia.enso.4nkweb.com.conf`). **`/ollama/`** protégé par **Bearer** nginx (`map` dans `conf.d`) ; AnythingLLM reste derrière son auth applicative. Documentation opérationnelle : [deploy/nginx/README-ia-enso.md](../deploy/nginx/README-ia-enso.md). Fiche évolution : [features/ia-enso-nginx-proxy-ollama-anythingllm.md](./features/ia-enso-nginx-proxy-ollama-anythingllm.md). diff --git a/docs/services.md b/docs/services.md index ee10696..14b55bb 100644 --- a/docs/services.md +++ b/docs/services.md @@ -99,12 +99,13 @@ The last command must succeed after `OLLAMA_HOST=0.0.0.0:11434` and `host.docker ## Public reverse proxy (ia.enso.4nkweb.com) -When Ollama runs on a LAN host (e.g. `192.168.1.164` via `IA_ENSO_BACKEND_IP` / `deploy/nginx/sites/ia.enso.4nkweb.com.conf`) and must be reached via the **proxy** with HTTPS, use `deploy/nginx/` and **[deploy/nginx/README-ia-enso.md](../deploy/nginx/README-ia-enso.md)** (script `deploy-ia-enso-to-proxy.sh`, checks, troubleshooting). **Default:** no nginx Bearer on `/ollama/` (optional `http-maps/ia-enso-ollama-bearer.map.conf.example`). +When Ollama runs on a LAN host (e.g. `192.168.1.164` via `IA_ENSO_BACKEND_IP` / `deploy/nginx/sites/ia.enso.4nkweb.com.conf`) and must be reached via the **proxy** with HTTPS and a **Bearer** gate on `/ollama/`, use `deploy/nginx/` and **[deploy/nginx/README-ia-enso.md](../deploy/nginx/README-ia-enso.md)** (script `deploy-ia-enso-to-proxy.sh`, checks, troubleshooting). **Full URLs** - AnythingLLM UI: `https://ia.enso.4nkweb.com/anythingllm/` -- Ollama native API example: `https://ia.enso.4nkweb.com/ollama/api/tags` +- Ollama native API example: `https://ia.enso.4nkweb.com/ollama/api/tags` (header `Authorization: Bearer `) - Cursor / OpenAI-compatible base URL: `https://ia.enso.4nkweb.com/ollama/v1` +- Cursor API key: same value as the Bearer secret in nginx `map` Feature note: [ia-enso-nginx-proxy-ollama-anythingllm.md](./features/ia-enso-nginx-proxy-ollama-anythingllm.md).