Playbook CAPBE — Infrastructure Incus

📄 Source : playbook/README.md 🕒 Généré le 06/08/2026 09:56
Sommaire

Déploiement complet et professionnel de l'infrastructure CAPBE sur Incus : un réseau privé avec accès internet, 2 stacks backend partagées (PostgreSQL+GIS / Oracle AI Database Free+GIS), Odoo 19 multi-tenant, des conteneurs projets/frontends, et un conteneur Caddy comme unique point d'entrée public (forward public créé manuellement dans la web UI Incus — aucune IP failover requise).

⚙️ Prérequis

  • Serveur dédié OVH avec Incus installé et initialisé (incus admin init)
  • Domaines déjà configurés chez OVH (pointent vers l'adresse d'écoute du forward) :
  • ci.capbe.ovh, *.ci.capbe.ovh, *.app.ci.capbe.ovh
  • Aucune IP failover requise : le forward public (80/443 → caddy) est créé manuellement dans la web UI Incus juste après l'étape 03 (le playbook vérifie qu'il est en place avant de poursuivre — voir DEPLOY.md)
  • Oracle : télécharger le RPM oracle-database-free-*.rpm depuis oracle.com/database/free (compte gratuit) et le placer dans playbook/downloads/
  • Messagerie : jeton API OVH OVH_AK/OVH_AS/OVH_CK créé sur eu.api.ovh.com/createToken (GET/PUT/POST/DELETE sur /domain/zone/*) et renseigné dans config/secrets.env ; port 25 sortant débloqué pour l'IP publique (espace client OVH)
  • ⚠️ Zone DNS OVH = capbe.ovh et domaine mail = capbe.ovh (racine) — les apps vivent sous ci (A ci, A *.ci…) mais les records de la messagerie sont à la RACINE (A mail, A webmail, A autoconfig, A autodiscover, MX/TXT/_dmarc/SRV sans préfixe) ; sudo ./publish-mail-dns.sh publie MX/SPF/DMARC/DKIM/SRV (idempotent)

🔒 Contraintes d'architecture (non négociables)

  • Aucun port de l'hôte n'est sollicité : l'exposition publique passe exclusivement par un network forward Incus (pur DNAT noyau nftables). Aucun listener TCP sur l'hôte, aucun proxy device (listen=tcp:0.0.0.0:...) — rien d'ouvert dans ss -tln.
  • Aucun conteneur Docker n'est créé : tout tourne dans des conteneurs système Incus (LXC) avec des services systemd natifs (messagerie incluse : Stalwart Mail en binaire natif Rust, webmail SnappyMail en PHP). Docker peut rester installé et actif sur l'hôte pour d'autres projets — le playbook ne l'utilise jamais (le préflight émet un avertissement, pas un arrêt).

🚀 Ordre d'exécution (sur l'hôte, en root)

cd playbook
chmod +x *.sh lib/common.sh

sudo ./00-preflight.sh   # vérifications + génération des secrets
sudo ./01-network.sh     # réseau capbe-br0 (10.10.10.0/24, NAT)
sudo ./02-profiles.sh    # profils ressources + réglages Oracle
sudo ./03-caddy.sh       # Conteneur caddy + VÉRIFICATION du forward MANUEL (80/443 → caddy)
sudo ./04-pgis.sh        # Stack #1 : PostgreSQL 18 + PostGIS + Prisma + SQLAlchemy + FastAPI
sudo ./05-oracle.sh      # Stack #2 : Oracle Free + Spatial + TypeORM + SQLAlchemy + FastAPI
sudo ./06-odoo.sh        # Odoo 19 multi-tenant (base = pgis)
sudo ./check-mail-prereqs.sh  # (optionnel) prérequis de la messagerie avant l'étape 07
sudo ./07-mail.sh        # Messagerie Stalwart : SMTP/IMAP/POP3/JMAP + webmail (backend pgis)
sudo ./08-frontend.sh projetA   # un conteneur par projet (répéter au besoin)
sudo ./09-caddy-config.sh # Caddy : déploiement du Caddyfile complet (config finale)
sudo ./10-backup.sh --cron  # sauvegardes quotidiennes (snapshots + dumps)
sudo ./smoke-test.sh     # (post-09) validation complète : points d'entrée + messagerie + SRV + conformité
sudo ./12-migrate-projects.sh agrimeca=meca agroworoba=agro  # (optionnel) migration des projets hérités — voir section dédiée

⚠️ Forward public = MANUEL : à l'étape 03, le playbook vérifie que le network forward (80/443 → caddy, puis les ports mail) est en place avant de poursuivre — il ne le crée jamais. Les commandes exactes sont affichées.

> 📋 **Checklist détaillée** (prérequis, durées, vérifications, pièges) : voir
> [`DEPLOY.md`](DEPLOY.md).
> 📬 **Post-déploiement** (dernier point rouge PTR, mail-tester, sort des
> conteneurs héritage) : voir [`POST-DEPLOY.md`](POST-DEPLOY.md).
> 🧪 **Smoke-test** : `sudo ./smoke-test.sh` après l'étape 09 (points d'entrée
> publics, rendu webmail, JMAP, autoconfig/autodiscover, records SRV,
> conformité hôte).
> 📮 **Prérequis mail** : `sudo ./check-mail-prereqs.sh` avant l'étape 07.
> 🔒 **Forward manuel** : `80/443 → caddy` vérifié à l'étape 03 ; ports mail
> (`25/110/143/465/587/993/995/4190 → mail`) rappelés à l'étape 07.
> 🔒 **Sécurité des commits** : un hook git refuse tout secret dans le staging
> (activer sur un clone via `git config core.hooksPath .githooks`).
> 🧹 **Rollback** : `sudo ./11-teardown.sh` (destructeur — confirmation `OUI`,
> conserve sauvegardes et secrets, `--purge-secrets` pour tout effacer).

## 🧱 Architecture
    Internet ── IP publique (adresse d'écoute du forward, manuelle)
                    │
             [ capbe-caddy 10.10.10.x ]   ← chef d'orchestre (TLS auto)
                    │
      Réseau privé capbe-br0 (10.10.10.0/24) — NAT sortant
   ┌──────────┬──────────┬──────────┬──────────────┬──────────────────┐
   ▼          ▼          ▼          ▼              ▼                  ▼

capbe-pgis capbe-oracle capbe-odoo capbe-mail capbe-projetA-front capbe-projetB-front 10.10.10.2 10.10.10.3 10.10.10.4 10.10.10.5 (par projet) Postgres18 OracleFree Odoo19 Stalwart Node.js / apps +PostGIS +Spatial multi-tenant SMTP/IMAP/JMAP ```

  • Une seule IP publique (adresse d'écoute du forward, choisie manuellement dans la web UI Incus) — HTTP(S) via caddy (80/443) et messagerie via le forward (25/110/143/465/587/993/995/4190), tout en DNAT noyau (aucun port hôte ouvert).
  • Aucune modification de la configuration réseau de l'hôte ni chez OVH : le routage est assuré dans Incus (network forward = DNAT noyau, aucun port hôte ouvert, aucun Docker).
  • Tous les conteneurs partagent les 3 backends via le réseau privé.

📋 Tableau des conteneurs

Conteneur Image Rôle RAM IP privée
capbe-pgis Ubuntu 24.04 PostgreSQL 18 + PostGIS 3.6 + Python/Node + Prisma/SQLAlchemy/FastAPI 4 Go 10.10.10.2
capbe-oracle Oracle Linux 9 Oracle AI DB Free + Spatial (SDO_GEOMETRY) + Python/Node + TypeORM/SQLAlchemy/FastAPI 6 Go 10.10.10.3
capbe-odoo Ubuntu 24.04 Odoo 19 multi-tenant (dbfilter, list_db=False) 4 Go 10.10.10.4
capbe-mail Ubuntu 24.04 Messagerie Stalwart (SMTP/IMAP/POP3/JMAP/CalDAV/CardDAV) + webmail SnappyMail 2 Go 10.10.10.5
capbe-<projet>-front Ubuntu 24.04 Frontend / application d'un projet 2 Go 10.10.10.x
capbe-caddy Ubuntu 24.04 Reverse proxy + TLS auto (HTTP-01) 1 Go 10.10.10.x (DHCP)

🔌 Routage Caddy (Caddyfile)

  • odoo.ci.capbe.ovhcapbe-odoo:8069 (multi-tenant : un tenant = une base)
  • api.ci.capbe.ovhcapbe-pgis:8000 (FastAPI Stack PostgreSQL)
  • oracle-api.ci.capbe.ovhcapbe-oracle:8000 (FastAPI Stack Oracle)
  • webmail.capbe.ovhcapbe-mail:80 (webmail SnappyMail) ; mail.capbe.ovh → protocoles mail directs (SMTP/IMAP/POP3/JMAP sur IP dédiée) et /jmap*capbe-mail:8080 (JMAP Stalwart, trafic transitoire)
  • autoconfig.capbe.ovh / autodiscover.capbe.ovhcapbe-mail:80 (configuration auto des clients)
  • <projet>.app.ci.capbe.ovhcapbe-<projet>-front:80 (dynamique)

🔀 Migration de projets existants (étape 12)

./12-migrate-projects.sh <legacy>[=<id>] [<legacy>[=<id>]...] transforme un conteneur « héritage » (pile complète autonome : Caddy local + FastAPI + Postgres + Odoo) en architecture CAPBE :

  • Frontend statique (build Flutter/JS) → capbe-<id> (profil front, capbe-br0, nginx :80) → https://<id>.app.ci.capbe.ovh. Racine web détectée : /var/www/html (nginx) ou /usr/share/caddy (racine Caddy par défaut — cas agroworoba)
  • API FastAPI + sa basecapbe-pgis (base <id>_api, rôle app, propriété réattribuée via fix_owner + sweep_owner — DO blocks PG15+, REASSIGN OWNED échoue sur postgres). Code détecté dans /opt/<legacy>/api ou /app (layout supervisor/venv — cas agroworoba). Les API se répartissent sur les ports de pgis : 8000 pour la 1re, 8001, 8002… pour les suivantes (un port = une API). La 1re est servie sur https://api.ci.capbe.ovh (rewrite /api/<res>/api/v2/<res>) ; chaque API migrée est aussi servie en même-origine via https://<id>.app.ci.capbe.ovh/api/*pgis:<port> (voir les blocs meca.app.ci.capbe.ovh / agro.app.ci.capbe.ovh du Caddyfile). Redis + worker Celery déployés automatiquement si l'app déclare un celery_app
  • Odoo → tenant de capbe-odoo (base <id> sur pgis, rôle odoo, dbfilter ^%d$https://<id>.ci.capbe.ovh) + copie du filestore. Si la base Odoo source n'a jamais été initialisée (pas de ir_module_module), le script crée un tenant vierge (odoo -i base) pour une URL fonctionnelle — aucune donnée ERP à attendre dans ce cas

Exemple : sudo ./12-migrate-projects.sh agrimeca=meca agroworoba=agro (conteneurs cibles capbe-meca / capbe-agro).

Le conteneur héritage n'est jamais touché (ni arrêté ni supprimé) : son sort est décidé manuellement après validation des tests (en faisant tourner les secrets). Limite : un seul service uvicorn par port sur capbe-pgis — les API se répartissent sur 8000, 8001… (routage Caddy même-origine à adapter dans config/Caddyfile).

🔐 Sécurité

  • Firewall hôte OVH : n'ouvrir que 22, 80, 443, 25, 110, 143, 465, 587, 993, 995, 4190 (le trafic atteint l'IP failover puis est routé par le forward — aucun port hôte n'écoute pour autant)
  • Bases jamais exposées publiquement : accès restreint au réseau 10.10.10.0/24 (pg_hba.conf, listener Oracle interne)
  • Secrets générés dans playbook/config/secrets.env (chmod 600) — à protéger
  • Sauvegardes quotidiennes : snapshots Incus + pg_dumpall + export Oracle

⚠️ Points d'attention Oracle

  • Plafonds de l'édition Free : 2 vCPU, 2 Go SGA/PGA, 12 Go de données max
  • Oracle Spatial & Graph (SDO_GEOMETRY) inclus par défaut
  • RPM à télécharger manuellement (compte Oracle gratuit) → playbook/downloads/
  • Si l'installation LXC bloque sur /dev/shm ou les sysctls, bascule possible en VM Incus (voir 05-oracle.sh — mêmes commandes sur image VM)