# Conception — Service de courriel souverain (Chezlepro) > **Statut : CONCEPTION (cadrage).** Aucun rôle n'est encore écrit. Ce document fixe > les décisions, les prérequis et la topologie avant toute implémentation. ## 1. Décisions arrêtées | Sujet | Décision | |---|---| | Périmètre | **Vrai service de courriel** (boîtes utilisateurs, réception publique, IMAP) — pas un simple relais sortant. | | Modèle | **Full self-host** : MX entrant **et** sortant chez Chezlepro, réputation d'IP propre. | | Stack | **Postfix (MTA) + Dovecot (IMAP/LMTP) + rspamd (antispam/DKIM)** — la stack libre **mature**, éprouvée depuis 20+ ans, **entièrement configurable par fichiers**. Trois rôles Set-OPS (`serveur_postfix`, `serveur_dovecot`, `serveur_rspamd`), comme on configure nginx/powerdns. | | Identité | Boîtes/utilisateurs dans l'**OpenLDAP** existant (Postfix + Dovecot → LDAP). Pas de comptes en double. | | DNS | Enregistrements publics dans la zone `chezlepro.ca` ; noms internes dans **PowerDNS**. | | PKI | **Deux CA distinctes** (voir §6) : Let's Encrypt pour les faces publiques, **step_ca** pour l'interne. | | Démarche | **Deux étapes** (voir §11) : **(A) fonctionnement INTERNE** d'abord (step_ca, LDAP, SMTP/IMAP internes, zéro dépendance publique) ; **(B) fonctionnement EXTERNE** ensuite (Namespro, Let's Encrypt, reprise du MX `.53`). | Le rôle existant `serveur_sendmail` (relais sortant Postfix, sans boîtes) **reste** pour le courrier de notification système (via `client_smtp`/msmtp). Il ne fait pas partie de ce service et ne le remplace pas. > **Historique — pivot depuis Stalwart (2026-07-02).** On avait d'abord choisi Stalwart > (binaire unique, moderne). Le prototypage a révélé un projet **jeune et volatil** : > modèle de config **cassé entre 0.15 et 0.16**, outil IaC `stalwart config apply` > **annoncé mais non livré** dans le binaire, API REST supprimée au profit de JMAP, > backlog d'issues/PR important. Pour un **pilier critique souverain censé durer**, c'est > trop précaire. On pivote vers la stack **mature Postfix/Dovecot/rspamd**, qui a en prime > l'avantage d'être **100 % déclarable par fichiers** — donc parfaitement alignée avec le > modèle Set-OPS (déclaratif, idempotent, exploitable sans IA). La Phase 1 Stalwart > (rôle `serveur_stalwart`) est **retirée** ; git en garde la trace. Stalwart à réévaluer > dans ~2 ans, une fois mûri et son `config apply` livré. ## 2. Prérequis IP publique — VÉRIFIÉ (recon DNS, 2026-07-02) Le full self-host sortant exige un **PTR propre, forward-confirmed, hors blocklist**. C'est **déjà en place** pour le MX primaire de Chezlepro : - **`mx.chezlepro.ca = 69.70.26.53`** ; PTR `→ mx.chezlepro.ca` ; **FCrDNS aller-retour OK**. - Bloc **`69.70.26.x` (Videotron statique)** contrôlé par Chezlepro ; PTR custom **obtenable** (déjà fait pour `.53`). → **La condition dure de délivrabilité est levée pour le primaire.** Le full self-host n'est pas un pari : c'est **reprendre `.53` en préservant sa réputation**. Réserves : - **`laposte-01 = 69.70.26.55`** (MX secours) a encore le **PTR Videotron par défaut** (`modemcable055…`), pas `laposte-01.chezlepro.ca` — à corriger s'il doit **émettre**. - **DKIM** : le sélecteur `default._domainkey` est **vide** publiquement — à **(re)poser**. ### État de la prod Chezlepro (recon) | Élément | Réalité | |---|---| | DNS public | **Namespro** (`htns1/2/3.namespro.ca`) — **pas** PowerDNS. PowerDNS = interne uniquement. | | Bloc IP | `69.70.26.x` (Videotron statique) : `.51` apex, `.53` mx, `.55` laposte-01, `.59` git. | | Mail existant | MX `mx → .53` déjà en service ; SPF `-all` **correct** ; DMARC `quarantine` **correct**. | | Vue interne | split-horizon (NS `delaviorne.net`, IP `10.10.x`/`192.168.14.x`) — ne pas confondre avec le public. | > **Conséquence** : le service mail n'est pas greenfield. Il **reprend/modernise** l'existant > (MX `.53`), il ne le recrée pas. ## 3. Topologie (2-3 nœuds) ``` INTERNET │ DNS public chezlepro.ca : MX 10 → mail1 , MX 20 → mail2 │ ┌───────────────┴───────────────┐ ▼ ▼ ┌────────────────────┐ ┌───────────────────┐ │ mail-01 (PRIMAIRE) │ │ mail-02 (MX secours)│ │ Postfix (MTA) │◄──relais──│ Postfix (relais) │ │ SMTP 25 / 587 / 465│ quand │ SMTP 25 seulement │ │ Dovecot (IMAP 993 │ mail-01 │ file d'attente puis │ │ + LMTP + Sieve) │ est down │ transfert à mail-01 │ │ rspamd (spam + DKIM)│ └───────────────────┘ │ BOÎTES stockées │ └─────────┬──────────┘ │ (réseau interne, mTLS step_ca) ┌───────┼─────────────┬──────────────┐ ▼ ▼ ▼ ▼ OpenLDAP step_ca PowerDNS PostgreSQL (identité)(certs int.)(DNS interne)(métadonnées, option) ``` > **Topologie révisée (2026-07-02) — MTA dédié en périphérie, boîtes à l'intérieur.** Le > schéma ci-dessus (tout-en-un) est **remplacé** par le modèle ci-dessous. ``` Internet ─▶ edge-mta : Postfix + rspamd (EXPOSÉ, aucune boîte) │ LMTP (remise) + SASL (auth) — réseau, mTLS step_ca ▼ mail-store : Dovecot IMAP + boîtes (INTERNE, non exposé) │ OpenLDAP · step_ca · PowerDNS ``` - **edge-mta (primaire)** — **Postfix** (`:25`, soumission `:587`/`:465`) + **rspamd** (anti-spam, **DKIM**, greylisting). **Exposé à Internet ; ne stocke aucune boîte.** Auth des soumissions déléguée à Dovecot (SASL réseau) ; remise via **LMTP réseau** vers `mail-store`. - **mail-store (interne)** — **Dovecot** : IMAP `:993`, boîtes (Maildir), Sieve, **auth LDAP**. **Non exposé au public** ; ne parle qu'à `edge-mta` (LMTP + SASL) et aux clients IMAP internes. Expose LMTP + auth **sur le réseau, chiffré par step_ca**. - **mta2 (MX de secours)** — **Postfix** relais, `:25` seulement ; file puis transfert quand `edge-mta` est down ; idéalement sur un **lien/IP indépendant**. **Séparation clé** : le MTA (surface la plus exposée) est isolé des **boîtes** (données sensibles) ; le lien Postfix→Dovecot passe en **réseau chiffré par step_ca** — réutilise le mTLS. **Décisions ouvertes** : emplacement du MX secours ; edge-mta co-localisé avec l'edge web ou non. ## 4. Stockage - **v1** : stockage **embarqué (RocksDB) sur mail-01** + **sauvegardes rigoureuses**. Simple et souverain. Le MX secours protège l'**entrée**, pas les **boîtes** → la résilience des données repose sur les sauvegardes. - **HA ultérieure** : métadonnées dans **PostgreSQL** (pilier existant) + blobs sur stockage partagé, pour un primaire plus stateless. ## 5. Flux - **Entrant** : expéditeur → MX DNS → mail-01 `:25` (ou mail-02 si down) → contrôles **SPF/DKIM/DMARC** + anti-spam → livraison à la boîte (utilisateur résolu via **LDAP**). - **Sortant** : client authentifié (LDAP) → soumission `:587` → **signature DKIM** → remise directe MX vers le serveur destinataire `:25` *(← PTR + réputation décisifs)*. - **Accès client** : client mail → IMAP `:993` / JMAP `:443` → auth **LDAP** → boîte. ## 6. TLS — deux CA distinctes (à ne pas confondre) | Face | CA | Pourquoi | |---|---|---| | **Publique** (`25/465/587/993/443` vers le monde) | **Let's Encrypt** (ACME intégré à Stalwart) | Les serveurs et clients distants valident contre les **racines publiques** ; ils ne connaissent pas l'AC interne. | | **Interne** (mail ↔ LDAP, ↔ PostgreSQL, ↔ MX secours) | **step_ca** | Le mTLS souverain déjà en place pour les liaisons internes. | ## 7. Pare-feu (nftables) Le socle installe nftables **désactivé**. Les nœuds mail sont un des endroits où on l'**active** avec des règles adaptées au rôle : - **Ouvert au monde** : `25`, `465`, `587`, `993`, `443`, `80` (ACME). - **Interne seulement** : LDAP, PostgreSQL, interface d'admin Stalwart. ## 8. Enregistrements DNS publics (`chezlepro.ca`) > **Gérés chez Namespro** (registrar/DNS public), **PAS dans PowerDNS** (qui ne sert que > l'interne). Un **MX** (`mx → .53`), un **SPF** (`-all`, correct) et un **DMARC** > (`quarantine`) existent **déjà** — à **reprendre/ajuster**, pas à recréer. Le **DKIM** > (`default._domainkey` vide) est à **(re)poser**. ``` mail1 A mail2 A @ MX 10 mail1.chezlepro.ca. @ MX 20 mail2.chezlepro.ca. @ TXT "v=spf1 mx -all" default._domainkey TXT "v=DKIM1; k=rsa; p=" _dmarc TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@chezlepro.ca" ``` + **PTR** (reverse) : `` → `mail1.chezlepro.ca` (chez le propriétaire de l'IP). + **MTA-STS / TLS-RPT** : optionnels, recommandés. ## 9. Traduction Set-OPS - **Nomenclature** : 2 fonctions, p. ex. `comm-mail` (primaire) + `comm-mx2` (secours) ; VMID / IP / VLAN dérivés comme le reste. - **Rôles** (config 100 % par fichiers, comme nginx/powerdns) : - `serveur_postfix` — MTA : `main.cf`/`master.cf`, TLS step_ca, cartes LDAP (domaines, boîtes, alias), remise LMTP vers Dovecot, soumission `:587`/`:465`, milter rspamd. - `serveur_dovecot` — IMAP `:993` + LMTP + Sieve, stockage des boîtes, **auth SASL** (pour la soumission Postfix), backend **LDAP**, TLS step_ca. - `serveur_rspamd` — anti-spam, **signature DKIM**, greylisting ; branché à Postfix par milter. - `serveur_postfix` en **mode relais** (paramètre) pour le MX de secours. - **Intégrations** des nœuds mail : `client_ldap` (auth/annuaire), `client_pki` (certs internes), `client_dns`. - **Secrets (voûte)** : bind LDAP, **clé privée DKIM**, éventuel secret ACME (Étape B) — via `vault_*`, câblés aux rôles. ## 10. Décisions - ✅ **IP publique + PTR** — TRANCHÉ : `.53` FCrDNS OK, bloc Videotron contrôlé (§2). - ⬜ **Emplacement du MX de secours** (`.55` existe ; lien/PTR à cadrer) (§3). - ⬜ **Stockage** : embarqué v1 vs PostgreSQL dès le départ (§4). - ⬜ **Domaine(s)** desservi(s) : `chezlepro.ca` seul, ou multi-domaines (tenants) ? - ⬜ **Reprise du MX existant** : bascule directe de `.53` vers la nouvelle stack, ou cohabitation transitoire ? ## 11. Démarche en deux étapes ### Étape A — Fonctionnement INTERNE (d'abord, sans risque) But : prouver **toute la pile en interne**, en **code de prod**, dans le bac à sable. Aucune dépendance au public. 1. **Pilier identité** : `serveur_openldap` (TLS **step_ca**) — ✅ **déployé et prouvé** en bac à sable. 2. **`serveur_postfix` + `serveur_dovecot` + `serveur_rspamd`** sur `mail-01` : TLS **step_ca**, annuaire/auth **LDAP**, DKIM interne, nftables mail. 3. **Prouver** : réception → boîte → accès **IMAP** → envoi **intra-écosystème**, le tout en TLS interne, auth LDAP. *(Étape actuelle : identité OK ; on démarre `serveur_postfix`.)* ### Étape B — Fonctionnement EXTERNE (transition prod, plus tard) But : brancher sur le monde **en reprenant l'existant** (voir §2). 1. Basculer le TLS des faces publiques sur **Let's Encrypt** (ACME). 2. **Reprendre `mx.chezlepro.ca` (`.53`)** — préserve PTR/réputation déjà acquis. 3. Mettre à jour les enregistrements **chez Namespro** : MX, **DKIM** (nouveau sélecteur), vérifier SPF/DMARC. 4. **MX secours `.55`** : PTR custom Videotron s'il doit émettre ; sinon réception/queue seulement. 5. Tests bout en bout : envoi vers Gmail/Outlook, **SPF/DKIM/DMARC verts**, bascule MX secours. --- *Ce document est un cadrage vivant : il évolue à mesure que les décisions ouvertes se tranchent. Il ne décrit pas encore de code livré.*