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:
parent
f39de69e55
commit
a6bd0ea14c
@ -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 l’hô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
|
||||
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
|
||||
```
|
||||
**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.
|
||||
|
||||
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 d’architecture : [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).
|
||||
- `https://ia.enso.4nkweb.com/anythingllm/` → AnythingLLM `3001`.
|
||||
Depuis la racine du dépôt **`smart_ide`**, sur une machine avec accès SSH au bastion puis au proxy :
|
||||
|
||||
## 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** Let’s 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 d’un **doublon** `map $http_upgrade $connection_upgrade` déjà présent ailleurs, le script retente avec **Bearer seul** dans `ia-enso-http-maps.conf`.
|
||||
|
||||
### Variables d’environnement 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 l’entrée publique qui atteint ce proxy.
|
||||
|
||||
```bash
|
||||
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` n’existe pas déjà dans l’instance),
|
||||
- et un `map $http_authorization $ia_enso_ollama_authorized { ... "Bearer <secret>" 1; }`.
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/nginx/http-maps
|
||||
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`):
|
||||
**Option B — fichiers séparés sous `/etc/nginx/http-maps/`**
|
||||
Copier les `.example` sans suffixe, éditer le secret Bearer, puis dans `http { }` :
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/http-maps/websocket-connection.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
|
||||
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
|
||||
```
|
||||
|
||||
## 4. Checks
|
||||
---
|
||||
|
||||
## Vérifications
|
||||
|
||||
### API Ollama via le proxy
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
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`
|
||||
- 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>`).
|
||||
### Cursor
|
||||
|
||||
## 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`, n’autoriser **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 l’un des blocs ou n’installer 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` | L’hôte utilisé résout encore vers une IP privée côté infrastructure Cursor ; vérifier DNS public / NAT. |
|
||||
|
||||
@ -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 |
|
||||
| [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 |
|
||||
| [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 |
|
||||
| [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 |
|
||||
|
||||
@ -8,33 +8,36 @@ Expose Ollama and AnythingLLM on the public proxy hostname with HTTPS, path pref
|
||||
|
||||
## 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).
|
||||
- **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`.
|
||||
- `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/deploy-ia-enso-to-proxy.sh` — push maps + site over SSH, `nginx -t`, reload (Bearer-only retry if websocket `map` already exists).
|
||||
- `deploy/nginx/README-ia-enso.md` — installation and verification on the proxy.
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `deploy/nginx/sites/ia.enso.4nkweb.com.conf` | `server` blocks, upstreams to `192.168.1.164` |
|
||||
| `deploy/nginx/http-maps/ia-enso-ollama-bearer.map.conf.example` | Example Bearer `map` (manual install) |
|
||||
| `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
|
||||
|
||||
1. DNS for `ia.enso.4nkweb.com` points to the proxy entry used for HTTPS.
|
||||
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 { }`.
|
||||
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.
|
||||
**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).
|
||||
|
||||
**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`.
|
||||
|
||||
Restrict backend ports on `192.168.1.164` to the proxy source where a host firewall is used.
|
||||
|
||||
## Analysis modalities
|
||||
|
||||
- `curl` to `/ollama/v1/models` with and without `Authorization: Bearer <secret>` (expect 200 / 401).
|
||||
- Browser access to `/anythingllm/` and application login.
|
||||
- Cursor connectivity after configuration change (no `ssrf_blocked` if hostname resolves to a public IP from Cursor’s perspective).
|
||||
- Cursor connectivity after configuration (no `ssrf_blocked` if the hostname does not resolve to a blocked private IP from Cursor’s perspective).
|
||||
|
||||
## 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.
|
||||
- A public URL for `/ollama` exposes the inference endpoint to anyone who knows the secret; combine with network controls if required.
|
||||
|
||||
@ -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.
|
||||
|
||||
## 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`). 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)
|
||||
|
||||
### `add-ssh-key.sh`
|
||||
|
||||
@ -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)
|
||||
|
||||
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).
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user