Files
nas-runbooks/common/cin-search-tt-migration-docker.md
T

9.1 KiB

Migration cin-search-tt : hors-Docker vers Docker + SMTP OVH -> Infomaniak

Contexte

Le 10/07/2026, Nabil signale que la migration SMTP globale vers Infomaniak (faite la veille) n'a pas couvert cin-search-tt car cette app tournait en dehors de la stack Docker, dans /volume1/web/cin-search-tt (Node.js standalone, jamais containerise).

Audit etendu a tout /volume1/web/ : 4 apps Node y etaient hebergees hors-Docker.

Etat trouve

App Etat Action
cin-search-tt SMTP sur ssl0.ovh.net, Baserow sur ancien alias nd.i234.me, jamais dockerise Migre vers Docker
rla Copie obsolete — la vraie version tourne deja proprement dans /volume1/docker/rla-api (Gotenberg/pptx-tt-api deja purges le 04/07) Archivee (doublon perime)
club-ia Projet termine (jamais deploye en prod) Archivee
weblinks Page HTML statique simple, aucune dependance Conservee en l'etat dans /volume1/web

Actions effectuees — cin-search-tt

  1. Copie du code (hors node_modules/zip) vers /volume1/docker/cin-search-tt
  2. .env corrige :
  3. Dockerfile ecrit (node:20-alpine, npm install --omit=dev)
  4. Image buildee via API Docker de Portainer (POST /api/endpoints/2/docker/build avec tar de contexte) car docker CLI absent du container mcp-nas (hardening 08/07)
  5. Stack deploye via API Portainer (POST /api/stacks/create/standalone/string) — pas de env_file: dans le compose (Portainer ne voit pas les chemins hote arbitraires), variables passees en environment: inline a la place
  6. Container cin-search-tt : port 3094->3000, reseau n8n, volume /volume1/docker/cin-search-tt/data:/app/data (persistance database.json)
  7. ports-registry.md mis a jour (edite via un container alpine ephemere + bind mount, car le fichier appartient a uid 1026 et mcp-nas n'a plus CAP_DAC_OVERRIDE)
  8. Reverse-proxy DSM cin.bolbol.tn -> localhost:3094 NON fait (hors scope API/Docker, a faire manuellement dans DSM Control Panel)
  9. Anciens dossiers archives dans /volume1/web/.archive/ : cin-search-tt.archived-20260710-migrated-to-docker, rla.archived-20260710-obsolete-superseded-by-docker-rla-api, club-ia.archived-20260710

Pieges decouverts (a capitaliser)

  • mcp-nas ne peut pas lancer docker/docker compose directement (binaire absent, SSH host indisponible depuis le hardening). Solution : API REST Portainer (/api/endpoints/{id}/docker/build pour build, /api/stacks/create/standalone/string pour deploy).
  • Portainer ne resout pas env_file: avec un chemin hote dans un stack deploye par API string — le moteur compose de Portainer tourne dans son propre filesystem (/data/compose/<id>/), pas celui de l'hote. Toujours utiliser environment: inline pour les stacks deployes ainsi.
  • Volume bind mounts en chemin absolu hote fonctionnent normalement (resolus par le daemon Docker, pas par Portainer) — contrairement a env_file:.
  • Ecriture de fichiers appartenant a uid 1026 depuis mcp-nas (root) : impossible, meme en root, a cause de cap_drop: [ALL] (pas de CAP_DAC_OVERRIDE). Contournement : creer un container ephemere (alpine) avec bind mount du dossier concerne via l'API Portainer, executer la commande d'ecriture dedans en root reel (container non-hardened), puis le supprimer.
  • Token Gitea GITEA_DEPLOY_TOKEN n'a pas le scope write:organization ni read:user — peut lire/ecrire du contenu de repos existants, mais pas creer de nouveaux repos ni lister les users/orgs. Creation de nouveaux repos = a faire manuellement dans l'UI Gitea.
  • /volume1/docker/ports-registry.md n'est pas versionne dans Gitea (pas de .git a la racine de /volume1/docker) — mise a jour uniquement in-place sur le NAS.

Reste a faire (hors scope automatisable)

  • Creer la regle reverse-proxy DSM cin.bolbol.tn -> localhost:3094
  • Creer le repo Gitea bolbol/cin-search-tt manuellement (token actuel insuffisant) et y pousser le code

Correctif post-deploiement (10/07/2026, meme session)

Nabil a signale "Echec de l'envoi email" apres mise en prod. Deux bugs distincts trouves via les logs container :

  1. SMTP code en dur dans server.js — le transporter nodemailer n'utilisait PAS config.smtp (qui lit le .env) mais des valeurs litterales : host: 'ssl0.ovh.net', user: 'contact@bolbol.tn', pass: '2L2u519wbolbol'. Corriger le .env seul ne changeait donc rien. Patch : les 3 champs (host/port/secure/auth) passes en process.env.SMTP_*. Le From des 2 sendMail() (etait contact@bolbol.tn, adresse OVH desormais coupee) change pour nabil.derouiche@ik.meInfomaniak rejette un From qui ne correspond pas au compte authentifie (meme contrainte deja documentee pour Baserow/Vaultwarden).
  2. BASEROW_API_URL en https:// echouait en interne (ECONNREFUSED 172.27.x.x:443) — sur le reseau Docker n8n, l'alias baserow.bolbol.tn resout directement vers le container Baserow qui n'ecoute qu'en HTTP (port 80, cf. ports-registry.md : 3888->80). Pas de TLS en interne. Corrige en http://baserow.bolbol.tn.

Piege a capitaliser : toujours verifier qu'un service qui lit un .env via config.js utilise vraiment ce module partout — un require('dotenv').config() en tete de fichier n'empeche pas des valeurs codees en dur ailleurs dans le meme fichier. Grep systematique sur host/user/pass/api url avant toute migration de credentials.

Piege reseau : https://<alias>.bolbol.tn ne fonctionne QUE depuis l'exterieur (WAN) ou un service hors du reseau Docker n8n (TLS termine par le reverse-proxy DSM). Un container sur le meme reseau n8n qu'un alias doit taper en http:// sur le port interne reel du service cible (verifier ports-registry.md), jamais supposer que le HTTPS est disponible en interne.

Regression post-migration decouverte le 24/07/2026 : recherche toujours "RAS"

Symptome

Nabil signale que l'app renvoie systematiquement "RAS" (aucun resultat) meme pour des CIN presents dans la base Baserow. L'envoi mail fonctionnait, seul le contenu etait toujours vide.

Cause racine

Le correctif du 10/07 (point 2 ci-dessus, "BASEROW_API_URL en https:// echouait en interne") n'avait ete applique qu'a la fonction searchByCIN() du fichier baserow.jsmodule jamais importe ni appele nulle part dans server.js (code mort). La vraie fonction utilisee par la route /api/search, searchBaserow() dans server.js lui-meme, contenait sa propre paire URL/token codee en dur, distincte et jamais touchee par le grep du 10/07 :

const TOKEN = 'zJaDdkttN1gr6oPvd3cxfCXNwzvvwMMF';   // token perime, non lie a .env
...
'https://baserow.bolbol.tn/api/database/rows/table/' + TABLE_ID + '/'   // https en dur

Logs container : Erreur pagination page 1: connect ECONNREFUSED 172.27.0.15:443 a chaque recherche → allRows reste vide → 0 resultat → email "RAS" systematique, quel que soit le CIN.

Lecon : un meme fichier peut contenir plusieurs implementations paralleles de la meme fonctionnalite (ici deux clients Baserow distincts). Grep sur https:///token corrige une occurrence ne garantit pas d'avoir corrige la occurrence reellement appelee par les routes. Toujours verifier par un grep -n "require(" server.js + trace des appels effectifs avant de declarer un correctif complet, pas seulement un grep sur le pattern fautif.

Correctif applique

Dans searchBaserow() (server.js), remplacement des valeurs en dur par lecture .env, coherent avec le reste du fichier (qui lit deja process.env.* partout ailleurs) :

const TABLE_ID = process.env.BASEROW_TABLE_ID || process.env.BASEROW_TABLE_SUD || '860';
const BASEROW_URL = process.env.BASEROW_API_URL || 'http://baserow.bolbol.tn';
const TOKEN = process.env.BASEROW_TOKEN;

Rebuild image (docker build -t cin-search-tt:latest .) + docker compose up -d (recreate). Verifie par recherche reelle via l'API (/api/login puis /api/search) : CIN existant → trouve avec le bon nombre d'occurrences, CIN absent → correctement liste dans notFoundCins, mail envoye. Backup de l'ancien server.js conserve sur le NAS (server.js.bak_<date>), fichier baserow.js (code mort) laisse en l'etat — a supprimer ou reutiliser lors d'un futur nettoyage.

Note infra corrigee : SSH host de nouveau disponible depuis mcp-nas

Contrairement a ce que disait ce runbook ("SSH host indisponible depuis le hardening"), l'alias nas-host (cle mcp-nas-bestof, cree le 11/07, posterieur a cette migration) permet desormais ssh nas-host puis /usr/local/bin/docker ... directement depuis mcp-nas — plus besoin du contournement API Portainer pour un simple build/recreate. Le detour Portainer reste utile uniquement pour des cas d'ecriture de fichiers appartenant a un uid hors de portee de Best0f.

Reste a faire (toujours hors scope automatisable)

  • Repo Gitea bolbol/cin-search-tt toujours pas cree (token GITEA_DEPLOY_TOKEN insuffisant pour creer un repo) — code non versionne a ce jour, uniquement sur disque NAS.