Document ia.enso nginx proxy (operator guide, cross-links)

**Motivations:**
- Single operational reference for deploy script vs manual steps and troubleshooting.

**Root causes:**
- README mixed http-maps manual path with script using conf.d without full operator context.

**Correctifs:**
- Align documentation with deploy script paths and prerequisites.

**Evolutions:**
- Expanded README-ia-enso.md (tables, SSRF context, env vars, rotation, troubleshooting).
- Feature doc table and deployment pointers; links from docs/README, infrastructure, services.

**Pages affectées:**
- deploy/nginx/README-ia-enso.md
- docs/features/ia-enso-nginx-proxy-ollama-anythingllm.md
- docs/README.md
- docs/infrastructure.md
- docs/services.md
This commit is contained in:
Nicolas Cantu 2026-03-23 01:04:04 +01:00
parent f39de69e55
commit a6bd0ea14c
5 changed files with 138 additions and 53 deletions

View File

@ -1,57 +1,93 @@
# ia.enso.4nkweb.com — Nginx on the proxy (192.168.1.100) # ia.enso.4nkweb.com — Nginx sur le proxy (192.168.1.100)
## Automated deploy (from a machine with SSH access) Reverse TLS vers lhôte LAN **`192.168.1.164`** :
From the `smart_ide` repository root: | Chemin public | Backend | Port | Protection |
|---------------|---------|------|------------|
| `/ollama/` | Ollama API | `11434` | **Bearer** vérifié par nginx ; en-tête `Authorization` **retiré** avant Ollama |
| `/anythingllm/` | AnythingLLM | `3001` | Auth **application** AnythingLLM (pas le Bearer Ollama) |
```bash **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 nest pas une IP RFC1918.
export IA_ENSO_OLLAMA_BEARER_TOKEN='your-long-secret' # optional: omit to auto-generate (printed once)
export DEPLOY_SSH_PROXY_HOST='4nk.myftp.biz' # optional: empty for direct LAN SSH to proxy
./deploy/nginx/deploy-ia-enso-to-proxy.sh
```
Uses `ia_dev/deploy/_lib/ssh.sh` (BatchMode, ProxyJump). Requires passwordless `sudo` for `nginx` on the proxy. **Fichiers dans le dépôt :** `sites/ia.enso.4nkweb.com.conf`, `http-maps/*.example`, `deploy-ia-enso-to-proxy.sh`. Détails darchitecture : [docs/features/ia-enso-nginx-proxy-ollama-anythingllm.md](../../docs/features/ia-enso-nginx-proxy-ollama-anythingllm.md).
--- ---
Reverse proxy to `192.168.1.164`: ## Déploiement recommandé : script SSH
- `https://ia.enso.4nkweb.com/ollama/` → Ollama `11434` (Bearer gate, then `Authorization` cleared upstream). Depuis la racine du dépôt **`smart_ide`**, sur une machine avec accès SSH au bastion puis au proxy :
- `https://ia.enso.4nkweb.com/anythingllm/` → AnythingLLM `3001`.
## 1. DNS and TLS ```bash
export IA_ENSO_OLLAMA_BEARER_TOKEN='secret-long-ascii-sans-guillemets-ni-backslash'
# optionnel si accès LAN direct au proxy, sans bastion :
# export DEPLOY_SSH_PROXY_HOST=
./deploy/nginx/deploy-ia-enso-to-proxy.sh
```
DNS must resolve `ia.enso.4nkweb.com` to the public entry that reaches this proxy. Issue a certificate, for example: 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).
- **Certificats** Lets Encrypt pour `ia.enso.4nkweb.com` aux chemins du fichier site (voir section TLS).
- **`sudo` non interactif** pour `nginx` et `systemctl reload nginx`.
### Fichiers installés par le script
| Chemin sur le proxy | Rôle |
|---------------------|------|
| `/etc/nginx/conf.d/ia-enso-http-maps.conf` | `map` Bearer (`$ia_enso_ollama_authorized`) et, si besoin, `map` WebSocket (`$connection_upgrade`) |
| `/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 dun **doublon** `map $http_upgrade $connection_upgrade` déjà présent ailleurs, le script retente avec **Bearer seul** dans `ia-enso-http-maps.conf`.
### Variables denvironnement du script
| Variable | Défaut | Rôle |
|----------|--------|------|
| `IA_ENSO_OLLAMA_BEARER_TOKEN` | généré | 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) |
| `DEPLOY_SSH_PROXY_HOST` | `4nk.myftp.biz` | Bastion ProxyJump ; vide = SSH direct |
| `DEPLOY_SSH_PROXY_USER` | idem proxy | Utilisateur sur le bastion |
Bibliothèque utilisée : `ia_dev/deploy/_lib/ssh.sh` (`BatchMode=yes`).
---
## Déploiement manuel (sans script)
### 1. DNS et TLS
Le DNS doit résoudre `ia.enso.4nkweb.com` vers lentrée publique qui atteint ce proxy.
```bash ```bash
sudo certbot certonly --webroot -w /var/www/certbot -d ia.enso.4nkweb.com sudo certbot certonly --webroot -w /var/www/certbot -d ia.enso.4nkweb.com
``` ```
Adjust `ssl_certificate` paths in `sites/ia.enso.4nkweb.com.conf` if the live directory name differs. Adapter dans `sites/ia.enso.4nkweb.com.conf` les directives `ssl_certificate` / `ssl_certificate_key` si le répertoire `live/` diffère.
## 2. HTTP-level maps (required) ### 2. Maps HTTP (`$ia_enso_ollama_authorized`, WebSocket)
Copy the examples on the proxy and include them **inside** `http { }` **before** `server` blocks that use the variables: **Option A — un seul fichier sous `conf.d` (équivalent au script)**
Créer `/etc/nginx/conf.d/ia-enso-http-maps.conf` en reprenant le contenu généré par le script ou en combinant :
From a checkout of this repository on the admin machine (paths relative to `deploy/nginx/http-maps/`): - `http-maps/websocket-connection.map.conf.example` (uniquement si `$connection_upgrade` nexiste pas déjà dans linstance),
- et un `map $http_authorization $ia_enso_ollama_authorized { ... "Bearer <secret>" 1; }`.
```bash **Option B — fichiers séparés sous `/etc/nginx/http-maps/`**
sudo mkdir -p /etc/nginx/http-maps Copier les `.example` sans suffixe, éditer le secret Bearer, puis dans `http { }` :
sudo cp deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example /etc/nginx/http-maps/ia-enso-ollama-bearer.map.conf
sudo cp deploy/nginx/http-maps/websocket-connection.map.conf.example /etc/nginx/http-maps/websocket-connection.map.conf
sudo nano /etc/nginx/http-maps/ia-enso-ollama-bearer.map.conf # set the Bearer secret (single line value)
```
In `/etc/nginx/nginx.conf` (or a file already included from `http { }`). Include the websocket map **only if** `$connection_upgrade` is not already defined elsewhere (duplicate `map` names will fail `nginx -t`):
```nginx ```nginx
include /etc/nginx/http-maps/websocket-connection.map.conf; include /etc/nginx/http-maps/websocket-connection.map.conf;
include /etc/nginx/http-maps/ia-enso-ollama-bearer.map.conf; include /etc/nginx/http-maps/ia-enso-ollama-bearer.map.conf;
``` ```
Do not commit the non-example `ia-enso-ollama-bearer.map.conf` with a real secret. Ne pas commiter un fichier contenant le secret réel.
## 3. Site file ### 3. Fichier `server`
```bash ```bash
sudo cp deploy/nginx/sites/ia.enso.4nkweb.com.conf /etc/nginx/sites-available/ia.enso.4nkweb.com.conf sudo cp deploy/nginx/sites/ia.enso.4nkweb.com.conf /etc/nginx/sites-available/ia.enso.4nkweb.com.conf
@ -59,22 +95,52 @@ sudo ln -sf /etc/nginx/sites-available/ia.enso.4nkweb.com.conf /etc/nginx/sites-
sudo nginx -t && sudo systemctl reload nginx sudo nginx -t && sudo systemctl reload nginx
``` ```
## 4. Checks ---
## Vérifications
### API Ollama via le proxy
```bash ```bash
curl -sS -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer CHANGE_ME_TO_LONG_RANDOM_SECRET" \ curl -sS -o /dev/null -w "%{http_code}\n" \
-H "Authorization: Bearer <secret>" \
https://ia.enso.4nkweb.com/ollama/v1/models https://ia.enso.4nkweb.com/ollama/v1/models
``` ```
Expect `200`. Without the header or with a wrong token, expect `401`. Attendu : **200** avec le bon secret ; **401** sans en-tête ou secret incorrect.
AnythingLLM: open `https://ia.enso.4nkweb.com/anythingllm/` and use the **application** login. If static assets fail to load, verify upstream base-path settings for AnythingLLM or adjust proxy headers per upstream docs. ### AnythingLLM
## 5. Cursor (OpenAI-compatible) Navigateur : `https://ia.enso.4nkweb.com/anythingllm/` (redirection vers `/anythingllm/`). Connexion avec les identifiants **AnythingLLM**.
Si les assets statiques échouent, vérifier la doc upstream (sous-chemin, en-têtes `X-Forwarded-*`).
- Override base URL: `https://ia.enso.4nkweb.com/ollama/v1` ### Cursor
- API key: **exactly** the same string as in the map after `Bearer ` (Cursor sends `Authorization: Bearer <key>`; nginx compares the full `Authorization` value to `Bearer <secret>`).
## 6. Backend firewall - URL de base OpenAI : `https://ia.enso.4nkweb.com/ollama/v1`
- Clé API : **identique** au secret Bearer (sans préfixe `Bearer ` dans le champ ; Cursor envoie `Authorization: Bearer <clé>`).
Allow from the proxy host only: TCP `11434` and `3001` on `192.168.1.164` if a host firewall is enabled. ---
## Pare-feu backend
Sur `192.168.1.164`, nautoriser **11434** et **3001** TCP que depuis **192.168.1.100** (proxy) si un pare-feu hôte est actif.
---
## Rotation du secret Bearer
1. Mettre à jour la ligne `"Bearer …"` dans `/etc/nginx/conf.d/ia-enso-http-maps.conf` (ou le fichier `map` manuel équivalent).
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 lun des blocs ou ninstaller que le `map` Bearer. |
| `401` sur `/ollama/` | Secret différent entre client et `map` ; en-tête `Authorization` absent ou mal formé (`Bearer ` + secret exact). |
| `502` / timeout | Ollama ou AnythingLLM arrêtés sur `.164` ; pare-feu ; mauvais IP/upstream dans le fichier site. |
| Erreur SSL | Certificat absent ou chemins `ssl_certificate` incorrects pour `ia.enso.4nkweb.com`. |
| Cursor `ssrf_blocked` | Lhôte utilisé résout encore vers une IP privée côté infrastructure Cursor ; vérifier DNS public / NAT. |

View File

@ -6,8 +6,13 @@ Operational, architectural, and UX-design notes for the local-AI IDE initiative
|----------|---------| |----------|---------|
| [../README.md](../README.md) | Project overview (French): vision, Lapce, AnythingLLM per project | | [../README.md](../README.md) | Project overview (French): vision, Lapce, AnythingLLM per project |
| [deployment-target.md](./deployment-target.md) | First target: Linux client + SSH remote server (AI stack + repos) | | [deployment-target.md](./deployment-target.md) | First target: Linux client + SSH remote server (AI stack + repos) |
| [ia_dev-submodule.md](./ia_dev-submodule.md) | Git submodule `ia_dev` (clone, update, SSH URL) |
| [lecoffre_ng-checkout.md](./lecoffre_ng-checkout.md) | Plain clone `lecoffre_ng` next to `smart_ide` (`/home/ncantu/code`) |
| [split-lecoffre-repos.md](./split-lecoffre-repos.md) | Split monorepo into five Gitea repos (`setup/split-lecoffre-ng-to-five-repos.sh`) |
| [infrastructure.md](./infrastructure.md) | Host inventory (LAN), SSH key workflow, host scripts | | [infrastructure.md](./infrastructure.md) | Host inventory (LAN), SSH key workflow, host scripts |
| [services.md](./services.md) | Ollama, AnythingLLM (Docker), Desktop installer, Ollama ↔ Docker | | [services.md](./services.md) | Ollama, AnythingLLM (Docker), Desktop installer, Ollama ↔ Docker |
| [../deploy/nginx/README-ia-enso.md](../deploy/nginx/README-ia-enso.md) | Proxy HTTPS `ia.enso.4nkweb.com` → Ollama / AnythingLLM (Bearer, script SSH, dépannage) |
| [features/ia-enso-nginx-proxy-ollama-anythingllm.md](./features/ia-enso-nginx-proxy-ollama-anythingllm.md) | Fiche évolution : objectifs, impacts, modalités du reverse proxy ia.enso |
| [anythingllm-workspaces.md](./anythingllm-workspaces.md) | One AnythingLLM workspace per project; sync pipeline | | [anythingllm-workspaces.md](./anythingllm-workspaces.md) | One AnythingLLM workspace per project; sync pipeline |
| [ux-navigation-model.md](./ux-navigation-model.md) | Beyond file explorer: intentions, graph, palette, risks, expert mode | | [ux-navigation-model.md](./ux-navigation-model.md) | Beyond file explorer: intentions, graph, palette, risks, expert mode |
| [system-architecture.md](./system-architecture.md) | Layers, modules, agent gateway, OpenShell, events, Lapce | | [system-architecture.md](./system-architecture.md) | Layers, modules, agent gateway, OpenShell, events, Lapce |

View File

@ -8,33 +8,36 @@ Expose Ollama and AnythingLLM on the public proxy hostname with HTTPS, path pref
## Impacts ## Impacts
- **Proxy (nginx):** new `server_name`, TLS, locations, HTTP `map` for Bearer validation; optional new includes under `/etc/nginx/http-maps/`. - **Proxy (nginx):** new `server_name`, TLS, locations, HTTP `map` for Bearer validation; maps deployed under `/etc/nginx/conf.d/` when using the provided script.
- **Backend (192.168.1.164):** must accept connections from the proxy on `11434` and `3001`; Ollama must not rely on the client `Authorization` header (nginx clears it after validation). - **Backend (192.168.1.164):** must accept connections from the proxy on `11434` and `3001`; Ollama must not rely on the client `Authorization` header (nginx clears it after validation).
- **Clients:** Cursor uses `https://ia.enso.4nkweb.com/ollama/v1` and the shared secret as API key; avoids private-IP SSRF blocks in Cursor when the hostname resolves publicly. - **Clients:** Cursor uses `https://ia.enso.4nkweb.com/ollama/v1` and the shared secret as API key; avoids private-IP SSRF blocks in Cursor when the hostname resolves publicly from the client infrastructure.
## Modifications (repository) ## Repository layout
- `deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example``map` for `$ia_enso_ollama_authorized`. | Path | Purpose |
- `deploy/nginx/http-maps/websocket-connection.map.conf.example``map` for `$connection_upgrade` (AnythingLLM WebSocket). |------|---------|
- `deploy/nginx/sites/ia.enso.4nkweb.com.conf``server` blocks and upstreams. | `deploy/nginx/sites/ia.enso.4nkweb.com.conf` | `server` blocks, upstreams to `192.168.1.164` |
- `deploy/nginx/deploy-ia-enso-to-proxy.sh` — push maps + site over SSH, `nginx -t`, reload (Bearer-only retry if websocket `map` already exists). | `deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example` | Example Bearer `map` (manual install) |
- `deploy/nginx/README-ia-enso.md` — installation and verification on the proxy. | `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; Bearer-only retry if websocket `map` already exists |
| `deploy/nginx/README-ia-enso.md` | **Operator reference:** automated + manual steps, env vars, checks, troubleshooting |
## Deployment modalities ## Deployment modalities
1. DNS for `ia.enso.4nkweb.com` points to the proxy entry used for HTTPS. **Preferred:** run `./deploy/nginx/deploy-ia-enso-to-proxy.sh` from `smart_ide` on a host with SSH access (see `README-ia-enso.md` for prerequisites and environment variables).
2. Obtain TLS certificates (e.g. certbot) for that name.
3. Install map files under `/etc/nginx/http-maps/`, set the Bearer secret, include maps inside `http { }`. **Manual:** DNS → TLS (certbot) → install `map` directives inside `http { }` (via `conf.d` or `http-maps` includes) → install site under `sites-available` / `sites-enabled``nginx -t` → reload. Details: `deploy/nginx/README-ia-enso.md`.
4. Install the site file under `sites-available` / `sites-enabled`, `nginx -t`, reload nginx.
5. Restrict backend ports at the firewall to the proxy source where applicable. Restrict backend ports on `192.168.1.164` to the proxy source where a host firewall is used.
## Analysis modalities ## Analysis modalities
- `curl` to `/ollama/v1/models` with and without `Authorization: Bearer <secret>` (expect 200 / 401). - `curl` to `/ollama/v1/models` with and without `Authorization: Bearer <secret>` (expect 200 / 401).
- Browser access to `/anythingllm/` and application login. - Browser access to `/anythingllm/` and application login.
- Cursor connectivity after configuration change (no `ssrf_blocked` if hostname resolves to a public IP from Cursors perspective). - Cursor connectivity after configuration (no `ssrf_blocked` if the hostname does not resolve to a blocked private IP from Cursors perspective).
## Security notes ## Security notes
- The Bearer secret is equivalent to an API key; rotate by updating the map file and client configs together. - The Bearer secret is equivalent to an API key; rotate by updating the `map` file and client configs together.
- AnythingLLM remains protected by **its own** application authentication; the `/anythingllm` location does not add the Ollama Bearer gate. - AnythingLLM remains protected by **its own** application authentication; the `/anythingllm` location does not add the Ollama Bearer gate.
- A public URL for `/ollama` exposes the inference endpoint to anyone who knows the secret; combine with network controls if required.

View File

@ -25,6 +25,12 @@ Private segment **192.168.1.0/24** (DHCP with MAC reservations). The table match
Internet access to backends uses **SSH ProxyJump** via `ncantu@4nk.myftp.biz` (see `JUMP` in `add-ssh-key.sh`). On the same LAN, direct `ssh ncantu@192.168.1.x` is valid. Internet access to backends uses **SSH ProxyJump** via `ncantu@4nk.myftp.biz` (see `JUMP` in `add-ssh-key.sh`). On the same LAN, direct `ssh ncantu@192.168.1.x` is valid.
## Reverse proxy `ia.enso.4nkweb.com` (Ollama / AnythingLLM)
Hostname TLS sur le **proxy** `192.168.1.100` : préfixes `/ollama` et `/anythingllm` vers lhôte LAN `192.168.1.164` (ports `11434` et `3001`). Gate Ollama par **Bearer** au nginx ; 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).
## Scripts (infrastructure / access) ## Scripts (infrastructure / access)
### `add-ssh-key.sh` ### `add-ssh-key.sh`

View File

@ -99,4 +99,9 @@ The last command must succeed after `OLLAMA_HOST=0.0.0.0:11434` and `host.docker
## Public reverse proxy (ia.enso.4nkweb.com) ## Public reverse proxy (ia.enso.4nkweb.com)
When Ollama runs on a LAN host (e.g. `192.168.1.164`) and must be reached via the **proxy** with HTTPS and a **Bearer** gate (for clients such as Cursor that block private IPs), use the nginx snippets in `deploy/nginx/` and the steps in `deploy/nginx/README-ia-enso.md`. Cursor base URL: `https://ia.enso.4nkweb.com/ollama/v1`; API key must match the configured Bearer secret. Feature note: [ia-enso-nginx-proxy-ollama-anythingllm.md](./features/ia-enso-nginx-proxy-ollama-anythingllm.md). When Ollama runs on a LAN host (e.g. `192.168.1.164`) and must be reached via the **proxy** with HTTPS and a **Bearer** gate (for clients such as Cursor that block private IPs), 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).
- Cursor base URL: `https://ia.enso.4nkweb.com/ollama/v1`
- Cursor API key: same value as the Bearer secret configured on the proxy
Feature note: [ia-enso-nginx-proxy-ollama-anythingllm.md](./features/ia-enso-nginx-proxy-ollama-anythingllm.md).