VPS/runbooks/vpn-nas-vps-pont-api.md

257 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Runbook — VPN NAS↔VPS + pont SAV + `api.navier-instruments.com`
> Objectif : exposer **un seul** service public (`api.navier-instruments.com`) qui relaie vers
> le **pont** (interne, sur le NAS), lequel parle à la gestion (Dolibarr) + ThingsBoard.
> **La gestion n'est JAMAIS exposée.** Documenté pour être reproductible — ne rien casser.
---
## ⭐ CONNEXION VPS↔NAS ACTUELLE (depuis 2026-08-06) — TUNNEL SSH INVERSÉ
> **SOURCE DE VÉRITÉ.** L'IPsec/IKEv2 Freebox (plus bas) est **RETIRÉ** : il cassait à chaque reboot
> LWS (noyau externe `6.12` livré **sans module ESP** → tunnel mort, remontée SAV gelée). Le nouveau
> lien ne dépend **ni du noyau LWS, ni de la Freebox** — seulement « le NAS a un accès internet
> sortant » + « le VPS répond en SSH ».
**Montage :** le **NAS compose** (dial-out) un SSH sortant vers le VPS et publie 2 *remote-forwards* :
- `VPS 127.0.0.1:18080``NAS localhost:8080` = **pont SAV**
- `VPS 127.0.0.1:11981``NAS localhost:1981` = **SSH du NAS** (sauvegarde / accès distant)
**Côté NAS (Synology) :**
- Clé dédiée `/volume1/docker/sav-tunnel/id_tunnel`, autorisée sur le VPS (`~deploy/.ssh/authorized_keys`,
option `restrict,port-forwarding` = ce couple de clés ne peut RIEN faire d'autre que du forwarding).
- Persistance = **Planificateur de tâches DSM** → tâche déclenchée « **Au démarrage** », utilisateur
**root**, commande :
```sh
KEY=/volume1/docker/sav-tunnel/id_tunnel
while true; do
ssh -i "$KEY" -o BatchMode=yes -o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
-N -R 18080:localhost:8080 -R 11981:localhost:1981 deploy@78.138.45.8
sleep 5
done
```
La boucle `while` = **reconnexion automatique** (« autossh du pauvre »).
**Côté VPS :**
- Le pont lit le NAS via le tunnel : `/opt/navier-sav/.env``NAS_PONT_URL=http://127.0.0.1:18080`
(sauvegarde de l'ancien `.env` : `/opt/navier-sav/.env.bak-pretunnel`). Relais confirmé actif,
remontée SAV restaurée (plus aucun « fetch failed »).
- Atteindre le NAS **depuis le VPS** : `ssh -p 11981 SteveC@127.0.0.1`
(⚠️ **plus** `192.168.10.104` : l'ancienne route IPsec est morte).
**Vérif de bout en bout :**
```bash
ssh navier-vps 'curl -s http://127.0.0.1:18080/healthz' # 200 => pont NAS joint via le tunnel
ssh navier-vps 'ss -ltn | grep -E ":18080|:11981"' # les 2 forwards doivent être UP
```
**Si ça retombe :** vérifier que la tâche DSM tourne (Planificateur de tâches), que le NAS a internet,
puis `ss -ltn | grep 18080` sur le VPS. La tâche se relance seule ; sinon « Exécuter » dans DSM.
---
## Architecture cible
```
public ──HTTPS──▶ api.navier-instruments.com
(VPS 78.138.45.8, Nginx Proxy Manager + SSL)
▼ VPN OpenVPN (le VPS est client du serveur OpenVPN du NAS)
pont @navier/sav-pont (NAS 10.8.0.1:8080)
┌─────────┴─────────┐
▼ ▼
Dolibarr (interne) ThingsBoard (navier-cloud.com)
```
## Inventaire (état 2026-07-13)
| Élément | Détail |
|---|---|
| **NAS** | Synology `N_instruments`, LAN `192.168.10.104`, **DSM 6.2.4**. SSH : `ssh nas` (SteveC, port **1981**, clé `id_ed25519_nas`). |
| **NAS = serveur OpenVPN** | `server 10.8.0.0/24`, **proto `udp6`**, **port 1194**, `dev tun`. NAS = `10.8.0.1`. IP publique actuelle = **IPv6** `2a05:6e02:106c:fc10:...`. |
| **Pont** | `@navier/sav-pont` (Node ≥20, TS). Source **propre** = repo `Ecosystem-Navier/SAV/pont` (Dockerfile, écoute **:8080**, métriques :9100). ⚠️ Le dossier `/volume1/docker/sav/` du NAS est un **dump de build cassé** (noms mélangés) → NE PAS l'utiliser. |
| **Gestion** | Dolibarr en Docker sur le NAS (`/volume1/docker/dolibarr/`, image `tuxgasy/dolibarr` + mariadb). Interne uniquement. |
| **VPS** | `78.138.45.8`, `ssh navier-vps` (user `deploy`). NPM (conteneur) fait le reverse-proxy + SSL. |
## Choix du VPN — recommandation : WireGuard (VPS serveur / NAS client)
Contrainte : le serveur OpenVPN du NAS est en **`udp6`** + IP publique **IPv6** → dépendance IPv6 ou
portforward box. On l'évite en **inversant le sens** : le **VPS** (IPv4 publique stable) = **serveur**,
le **NAS** = **client qui compose** (dialout) → **aucun port à ouvrir sur la box**, pas d'IPv6.
**Reco : WireGuard** (moderne, léger, selfhosted/souverain, reconnexion propre). Sur DSM 6.2.4 = **conteneur Docker**.
Sousréseau VPN choisi : **`10.9.0.0/24`** (VPS = `10.9.0.1`, NAS = `10.9.0.2`).
- *Alternative simplicité :* Tailscale (WireGuard cléenmain, gère NAT/IPv6/clés) — service tiers.
- *Alternative « réutiliser l'existant » :* OpenVPN NASserveur (voir annexe) — plus contraignant.
## Étape 1 — VPN WireGuard (VPS serveur, NAS client)
1. **VPS (root)** — serveur :
```bash
sudo apt-get update && sudo apt-get install -y wireguard
umask 077; wg genkey | tee /etc/wireguard/vps.key | wg pubkey > /etc/wireguard/vps.pub
# /etc/wireguard/wg0.conf :
# [Interface] Address=10.9.0.1/24 ListenPort=51820 PrivateKey=<contenu vps.key>
# [Peer] PublicKey=<nas.pub> AllowedIPs=10.9.0.2/32
sudo systemctl enable --now wg-quick@wg0
sudo ufw allow 51820/udp # (ou règle firewall équivalente)
```
2. **NAS (Docker, root)** — client (conteneur `linuxserver/wireguard`) :
```bash
mkdir -p /volume1/docker/wireguard
# /volume1/docker/wireguard/wg_confs/wg0.conf :
# [Interface] Address=10.9.0.2/32 PrivateKey=<contenu nas.key>
# [Peer] PublicKey=<vps.pub> Endpoint=<IP_PUBLIQUE_VPS>:51820 AllowedIPs=10.9.0.1/32 PersistentKeepalive=25
docker run -d --name wireguard --cap-add NET_ADMIN --restart unless-stopped \
-v /volume1/docker/wireguard:/config linuxserver/wireguard
```
3. **Vérifier** : `ssh navier-vps 'ping -c2 10.9.0.2'` (le VPS doit joindre le NAS). Le pont sera en **`10.9.0.2:8080`**.
*(Générer les clés : `wg genkey|tee nas.key|wg pubkey>nas.pub` côté NAS ; échanger les .pub entre les deux.)*
## Étape 2 — Déployer le pont (sur le NAS, proprement)
Depuis la **source du repo** (pas le dossier cassé). Le pont a un `Dockerfile` (écoute :8080).
```bash
# sur le NAS (root — Docker DSM 6)
mkdir -p /volume1/docker/sav-pont && cd /volume1/docker/sav-pont
# récupérer Ecosystem-Navier/SAV/pont (git clone ou rsync du sous-dossier)
docker build -t navier-sav-pont .
# pont.env = VRAIE config (URL Dolibarr interne, ThingsBoard, secrets) — à recréer proprement
docker run -d --name sav-pont --restart unless-stopped \
-p 8080:8080 --env-file /volume1/docker/sav-pont/pont.env navier-sav-pont
curl -s http://localhost:8080/health # doit répondre 200
```
## Étape 3 — Exposer `api.navier-instruments.com` (VPS)
> ⚠️ **PÉRIMÉ — ne pas appliquer tel quel.** Cette étape date du plan WireGuard, **abandonné**
> (voir « ✅ RÉALISÉ » plus bas : la Freebox Pro filtre l'UDP sortant → VPN **IKEv2** retenu).
> L'adresse du pont n'est **pas** `10.8.0.1` mais **`192.168.10.104:8080`**.
> Rappel du piège réseau : un conteneur en **bridge** est SNAT hors du tunnel → il faut
> **`--network host`** (c'est ce que fait `navier-espace-web`).
> État au 2026-07-20 : `api.navier-instruments.com` **n'a aucun enregistrement DNS** — cette
> étape n'a jamais été exécutée. Le pont est joint en interne, pas via un domaine public.
1. **DNS (LWS)** : `api.navier-instruments.com A 78.138.45.8` (+ `AAAA` si IPv6 VPS).
2. **NPM (Nginx Proxy Manager)** : Proxy Host `api.navier-instruments.com`~~`10.8.0.1:8080`~~
**`192.168.10.104:8080`** + **SSL Let's Encrypt** (Force SSL, HTTP/2).
3. Vérifier : `curl -I https://api.navier-instruments.com/health`**200**.
## Étape 4 — Brancher les consommateurs
- Espace client, PWA SAV atelier, webapp → base API = **`https://api.navier-instruments.com`** (le seul point d'entrée).
## Résilience / ne rien casser
- VPN VPS : `openvpn-client@nas` en `enable` → reconnexion auto. Si NAS/box down → `api.` down → les consommateurs doivent **dégrader proprement** (cache / message « temporairement indisponible »).
- **Dolibarr jamais exposé** — accessible seulement via le pont, **scopé par client**.
- Ne pas exposer le port 9100 (métriques) publiquement.
- Sauvegardes : base mariadb Dolibarr (sur le NAS) — vérifier la tâche de sauvegarde Synology.
## Vérifs de bout en bout
```bash
ssh navier-vps 'ping -c2 10.8.0.1 && curl -s http://10.8.0.1:8080/health'
curl -I https://api.navier-instruments.com/health
```
---
## ⛔ RETIRÉ le 2026-08-06 — ancien VPN Freebox IKEv2 (remplacé par le tunnel SSH inversé, voir en tête)
> **Ne plus utiliser.** Cassé par le reboot LWS du 31/07 (noyau `6.12` sans ESP). Remplacé par le
> **tunnel SSH inversé** (section « CONNEXION VPS↔NAS ACTUELLE » en tête). Le 06/08/2026, strongSwan a
> été **arrêté + désactivé au démarrage** sur le VPS, et `/etc/ipsec.conf` **archivé** dans
> `/opt/navier-sav/ipsec-retired-2026-08-06/`. Conservé ci-dessous **pour l'historique uniquement**.
WireGuard abandonné pour NAS↔VPS : le réseau du NAS (Freebox Pro) **filtre l'UDP sortant** (seul dport 53 passe, prouvé par tcpdump) → WireGuard (UDP) impossible depuis le NAS. **Retenu : serveur VPN IKEv2 de la Freebox Pro.**
Montage : `VPS (client strongSwan) → 160189667.box.freepro.com (Freebox, VPN IKEv2) → LAN → NAS 192.168.10.104`.
- **VPS** `/etc/ipsec.conf` conn `freebox` : `keyexchange=ikev2`, `right=160189667.box.freepro.com`, `rightsubnet=192.168.10.0/24` (route LAN seule — le VPS garde son net), `leftauth=eap-mschapv2`, `leftid`/`eap_identity=VPSNavier`, `leftsourceip=%config`, `auto=start`. Secrets : `/etc/ipsec.secrets``VPSNavier : EAP "<mdp>"`.
- **Fix cert indispensable** : copier `ISRG_Root_X1.pem` (+X2) **individuellement** dans `/etc/ipsec.d/cacerts/` (strongSwan ne lit qu'1 cert/fichier ; le bundle `ca-certificates.crt` ne suffit pas).
- **Persistance** : `systemctl enable strongswan-starter`. Reconnexion : MOBIKE.
- **Vérif OK** : `ping 192.168.10.104` + `curl http://192.168.10.104:4444` → 200.
- **Mobile** : même serveur Freebox IKEv2 pour iPhone/Mac nativement (Réglages → VPN → IKEv2).
> **Accès NAS depuis n'importe quel réseau** : quand on n'est PAS sur le LAN interne, le NAS
> reste joignable via le VPS : `ssh -J navier-vps nas` (ProxyJump — l'auth SSH reste boutàbout
> avec la clé NAS, seul le TCP transite par le VPS+tunnel). Idem `scp`/`tar | ssh -J navier-vps nas`.
---
## ✅ RÉALISÉ (2026-07-13) — Pont SAV sur module INTERVENTION (fichinter), pas Ticket
**DÉCISION ACTÉE (Steve + Tiago) : le SAV s'écrit en FICHE D'INTERVENTION Dolibarr (module
`Intervention`, endpoints `/interventions`, table `llx_fichinter`), PAS en Ticket.**
Raison : le module Ticket renvoie **403** sur l'instance de prod (`GET /tickets` → 403), alors que
`GET /interventions`**200**. Validé en direct sur Dolibarr **19**. Cf. commit
`steve-dev #14 « bascule Tickets → fiches d'intervention »`, convergé dans la branche `sav-integration`.
**Source du pont = branche `sav-integration`** (repo `Ecosystem-Navier`, la plus à jour : pont
modulaire *Port + Adapters* configdriven ; `HttpDolibarrClient` écrit en `/interventions`,
`/thirdparties`, `/stockmovements`). L'ancien build (`main`) tapait `/tickets` → remplacé.
### Déploiement du pont (NAS, `/volume1/docker/sav-pont`)
- Image `navier/sav-pont:interv` buildée sur le NAS (`sudo docker build`, node 22 alpine, tsc → dist).
Dossier possédé par SteveC (`chown -R`) pour édition sans sudo ; **docker exige `sudo`** sur DSM 6.
- Conteneur : `sudo docker run -d --name sav-pont --restart unless-stopped -p 8080:8080
--env-file .env -v /volume1/docker/sav-pont-data:/data navier/sav-pont:interv`.
⚠️ **`docker restart` NE recharge PAS `--env-file`** → toujours **`rm` + `run`** après un changement `.env`.
- **Volume data** : le pont écrit un magasin de comptes/sessions dans `/data` → le volume
`/volume1/docker/sav-pont-data` doit appartenir à **uid 1000** (`node`) : `sudo chown -R 1000:1000`.
Sinon boucle de crash `EACCES … accounts.tmp`.
- `.env` (secrets jamais affichés) : adapters auto — `DOLIBARR_BASE_URL` + `DOLIBARR_API_KEY`
remplis → **HttpDolibarrClient (fichinter)** ; `TB_BASE_URL` → HttpThingsBoard. Ajoutés vs l'ancien :
`SESSION_SECRET` (HMAC sessions LAN, sinon sessions perdues à chaque redémarrage), `MEDIA_DIR=/data/media`,
`SEED_SUPERVISOR_NAME=Steve` / `SEED_SUPERVISOR_PIN=1234` (**superviseur initial — CHANGER le PIN**).
- Santé : `GET /healthz``{"status":"ok","deps":{"dolibarr":"http","thingsboard":"http","media":"local"}}`.
### Prérequis Dolibarr (fichinter) — appliqués et validés
Conforme au schéma **officiel** (wiki Extrafields / Table llx_extrafields) — pas de bricolage :
- **Module Intervention activé** (`GET /interventions` → 200). Compte technique `cpont-sav` (clé `DOLAPIKEY`).
- **2 extrafields créés** sur l'élément `fichinter` (le pont les écrit en `array_options`) :
`numero_serie` (varchar 128) + `sav_statut` (varchar 64). Créés par : ligne dans `llx_extrafields`
(`elementtype='fichinter'`) **+** colonne ajoutée dans `llx_fichinter_extrafields`. Backup préalable :
`/volume1/docker/dolibarr-extrafields.backup.sql`. Requête `sqlfilters=(ef.numero_serie:=:'…')` → 200.
- **Fix pont `createThirdparty`** : ajout de **`code_client: '-1'`** (numérotation auto). Sans ça, l'API
Dolibarr (addon `mod_codeclient_monkey`) refuse la création de tiers avec `ErrorCustomerCodeRequired`.
`-1` = *« automatic assignment »* **documenté dans la classe `Societe`** du cœur Dolibarr. Poussé sur
`sav-integration` (commit `fix(sav/pont): createThirdparty envoie code_client:'-1'`).
- **Allerretour réel validé** : tiers (code auto) + fiche d'intervention avec `numero_serie`/`sav_statut`
créés puis supprimés (POST/GET/DELETE → 200) sur Dolibarr 19.
### Chaîne complète prouvée
`Mac (ssh -J) → VPS → tunnel IKEv2 → NAS pont :8080 (/interventions fichinter) → Dolibarr 19 réel`.
Le pont **n'est pas exposé publiquement** : l'espace client (VPS) le relaie en interne via le tunnel.
---
## ✅ RÉALISÉ (2026-07-13) — Espace client déployé sur le VPS + pont `/api` (host-network)
**Conteneur `navier-espace-web`** (nginx:alpine) sur le VPS, sert le front statique de l'espace
client (`/home/deploy/navier-espace-web`, rsync depuis `~/esp/navier-website/espace`) sur **:8085**
et **relaie `/api/` → le pont** `http://192.168.10.104:8080/` (conf `/home/deploy/espace.nginx.conf`).
### ⚠️ Point réseau clé : atteindre le pont (tunnel IKEv2) depuis un conteneur
La politique IPsec ne capture QUE le trafic sourcé de l'IP VPN du VPS `192.168.2.1`
(`ip xfrm policy` : `src 192.168.2.1/32 dst 192.168.10.0/24`). Un conteneur **bridge** est SNAT
vers l'IP hôte → **hors tunnel → `curl pont` = 000**. Deux solutions :
- **RETENUE (sans firewall)** : lancer le conteneur en **`--network host`** → il source depuis
l'hôte (`192.168.2.1`) → le tunnel le transporte. C'est pourquoi `navier-espace-web` tourne en
`--network host` (écoute :8085, proxy_pass direct vers le pont). Prouvé : `/api/healthz` → 200.
- **Alternative (si un conteneur *bridge* doit joindre le pont, ex. NPM en direct)** : règle SNAT
`iptables -t nat -A POSTROUTING -s 172.16.0.0/12 -d 192.168.10.0/24 -j SNAT --to-source 192.168.2.1`
(+ persistance). **Non appliquée** (changement firmware du VPS partagé = à valider hors auto-mode).
### Commandes
```bash
# déploiement statique
rsync -az --delete ~/esp/navier-website/espace/ navier-vps:/home/deploy/navier-espace-web/
# conteneur (host-network pour joindre le pont via le tunnel)
sudo docker run -d --name navier-espace-web --restart unless-stopped --network host \
-v /home/deploy/espace.nginx.conf:/etc/nginx/conf.d/default.conf:ro \
-v /home/deploy/navier-espace-web:/usr/share/nginx/html:ro nginx:alpine
# vérif
curl http://127.0.0.1:8085/ # 200 (statique)
curl http://127.0.0.1:8085/api/healthz # 200 (pont via tunnel)
```
### RESTE (2 actions distinctes)
1. **URL publique de revue** : `:8085` est fermé au public (seul NPM 80/443 est ouvert). Pour une
URL SSL → **ajouter DNS `espace.navier-instruments.com A → 78.138.45.8` (LWS)**, puis NPM proxy
host `espace.…``78.138.45.8:8085` + Let's Encrypt (même schéma que `staging.` = `set $server 78.138.45.8`).
2. **Câblage données réelles** : le front est encore un **prototype** (login factice + `var DATA` en
dur). À implémenter : login → JWT ThingsBoard (le pont le valide), puis lecture parc/SAV/factures
via le pont (`/interventions` fichinter). ⚠️ Le pont est scopé SAV-atelier (RBAC techniciens/superviseurs) :
vérifier/ajouter les routes de lecture **client** avant de câbler (ne pas deviner l'API).