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
- Copie du code (hors node_modules/zip) vers /volume1/docker/cin-search-tt
.envcorrige :- SMTP_HOST ssl0.ovh.net -> mail.infomaniak.com (port 587, STARTTLS, meme compte nabil.derouiche@ik.me que Baserow/Vaultwarden)
- BASEROW_API_URL https://baserow.nd.i234.me -> https://baserow.bolbol.tn (alias reseau Docker, regle absolue)
- Dockerfile ecrit (node:20-alpine, npm install --omit=dev)
- Image buildee via API Docker de Portainer (
POST /api/endpoints/2/docker/buildavec tar de contexte) cardockerCLI absent du container mcp-nas (hardening 08/07) - Stack deploye via API Portainer (
POST /api/stacks/create/standalone/string) — pas deenv_file:dans le compose (Portainer ne voit pas les chemins hote arbitraires), variables passees enenvironment:inline a la place - Container
cin-search-tt: port 3094->3000, reseaun8n, volume/volume1/docker/cin-search-tt/data:/app/data(persistance database.json) ports-registry.mdmis 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)- Reverse-proxy DSM
cin.bolbol.tn->localhost:3094NON fait (hors scope API/Docker, a faire manuellement dans DSM Control Panel) - 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 composedirectement (binaire absent, SSH host indisponible depuis le hardening). Solution : API REST Portainer (/api/endpoints/{id}/docker/buildpour build,/api/stacks/create/standalone/stringpour 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 utiliserenvironment: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_TOKENn'a pas le scopewrite:organizationniread: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.mdn'est pas versionne dans Gitea (pas de.gita 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-ttmanuellement (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 :
- 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.envseul ne changeait donc rien. Patch : les 3 champs (host/port/secure/auth) passes enprocess.env.SMTP_*. LeFromdes 2sendMail()(etaitcontact@bolbol.tn, adresse OVH desormais coupee) change pournabil.derouiche@ik.me— Infomaniak rejette un From qui ne correspond pas au compte authentifie (meme contrainte deja documentee pour Baserow/Vaultwarden). - BASEROW_API_URL en https:// echouait en interne (
ECONNREFUSED 172.27.x.x:443) — sur le reseau Dockern8n, l'aliasbaserow.bolbol.tnresout 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 enhttp://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.js — module 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-tttoujours pas cree (tokenGITEA_DEPLOY_TOKENinsuffisant pour creer un repo) — code non versionne a ce jour, uniquement sur disque NAS.