Comptes de messagerie externes
Ce profil fait tourner difuzio sans serveur de messagerie à soi. difuzio relève une boîte mail ordinaire chez un hébergeur en IMAP, et renvoie les messages aux abonnés par le submission SMTP authentifié du même hébergeur. Aucun enregistrement MX à changer, aucun Postfix à configurer : une boîte et ses identifiants suffisent.
Internet -> boîte hébergeur -> poller IMAP (boucle du worker) -> pipeline -> submission 587 AUTH -> abonnés
C'est l'un des deux profils décrits dans Architecture ; l'autre suppose un Postfix en frontal. Le coeur de difuzio est identique dans les deux cas : seules changent l'entrée et la sortie du courrier.
Cette page se lit de bout en bout : elle contient tout ce qui est nécessaire à l'installation dans ce profil. Les sections Postfix, MX et rebonds VERP des autres pages ne s'appliquent pas.
À qui ce profil s'adresse
Une petite structure - typiquement une association de quelques dizaines de membres - qui possède une adresse chez un hébergeur mutualisé ou une offre mail grand public, et aucun accès administrateur à un serveur de messagerie.
Il se marie naturellement avec le backend SQLite : SQLite supprime la dépendance
à un serveur de base de données, ce profil supprime la dépendance à un MTA. Les
deux ensemble donnent une installation autonome : un binaire, un fichier .db,
une boîte mail.
Bénéfice de délivrabilité souvent sous-estimé : le courrier part réellement des serveurs de l'hébergeur, pour une adresse de son domaine, via son submission authentifié. SPF, DKIM et DMARC sont donc alignés nativement, sans publication DNS supplémentaire. Ce profil est fréquemment meilleur qu'un relais maison mal configuré. Le facteur limitant est le débit, pas la réputation.
Limites assumées
À connaître avant de promettre quoi que ce soit :
- Petites listes seulement. Le débit est plafonné par le quota d'envoi de la boîte, souvent quelques centaines de messages par jour. Un message vers 50 abonnés, ce sont 50 envois. Au-delà de cet ordre de grandeur, il faut un vrai sortant dédié.
- Abonnements par le web uniquement. Pas de commandes par courriel
(
subscribe@,unsubscribe@,owner@). L'abonnement et le désabonnement passent par les pages web et par le lien de désabonnement en un clic présent dans chaque message. En IMAP, l'enveloppe SMTP est perdue : il n'y a plus d'adresse de destination à classer, donc plus d'adresses de commande. - Pas de traitement automatique des rebonds. Un abonné dont l'adresse cesse de fonctionner n'est pas désactivé tout seul ; le retirer à la main depuis la console.
- Mono-hôte. Un seul
workerrelève la boîte. Ne pas en lancer deux sur la même boîte.
Une boîte = une liste
C'est la règle qui rend le profil simple et sûr. Tout message présent dans la
boîte membres@lists.example.org est, par définition, un message destiné à la
liste membres. Il n'y a rien à deviner.
Conséquence directe : ne jamais abonner la boîte de la liste à sa propre liste, sinon chaque message reboucle. difuzio refuse cet abonnement, mais ne pas l'ajouter à la main non plus.
Prérequis chez l'hébergeur
- Une boîte dédiée par liste, par exemple
membres@lists.example.org. - IMAP accessible : IMAPS (993) ou IMAP + STARTTLS (143).
- Submission SMTP authentifié : 587 avec STARTTLS et AUTH.
- Un mot de passe applicatif si l'offre en exige un (fréquent).
- Le quota d'envoi de l'offre, pour régler
messages_per_minute.
1. Binaire et base
Le profil va de pair avec la variante SQLite :
go build -tags sqlite -o difuzio ./cmd/difuzio
difuzio migrate up --dsn /var/lib/difuzio/difuzio.db
La variante MariaDB fonctionne aussi ; seul le DSN change. Voir Base de données.
2. Secrets
Tous les secrets vivent dans des fichiers 0600, jamais en base, jamais en clair dans le YAML :
install -d -m 0700 /etc/difuzio/secrets
printf '%s' 'MOT_DE_PASSE_IMAP' > /etc/difuzio/secrets/imap-membres
printf '%s' 'MOT_DE_PASSE_SMTP' > /etc/difuzio/secrets/smtp-membres
chmod 0600 /etc/difuzio/secrets/imap-membres /etc/difuzio/secrets/smtp-membres
for k in session token verp credential; do
openssl rand -base64 32 > /etc/difuzio/secrets/$k.key
chmod 0600 /etc/difuzio/secrets/$k.key
done
Le démarrage échoue si un fichier de secret est lisible par le groupe ou par les
autres. Les trois premiers keyrings sont obligatoires ; credential.key n'est
nécessaire que pour saisir les comptes dans la console (section 4), ce qui est le
mode recommandé.
3. Configuration
Partir de config.example-external.yaml. Les points spécifiques à ce profil :
relay:
host: smtp.example.net
port: 587
starttls: true # obligatoire dès qu'un bloc auth existe
messages_per_minute: 30 # rester sous le quota de l'hébergeur
auth:
mechanism: plain
username: "robot@lists.example.org"
password_file: "/etc/difuzio/secrets/smtp-robot"
ingest:
enabled: true
poll_interval: 1m
max_fetch: 50
mark_processed: seen # seen | move (move exige processed_folder)
mailboxes:
- server: imap.example.net
tls: implicit # implicit (993) | starttls (143)
port: 993
username: "membres@lists.example.org"
password_file: "/etc/difuzio/secrets/imap-membres"
mailbox: INBOX
domain: lists.example.org # doit correspondre à un domains[].name
list: membres
La configuration est validée une seule fois au démarrage : une clé manquante ou une incohérence (mot de passe sans STARTTLS, domaine inconnu, fichier de secret absent) fait échouer le boot en nommant la clé fautive. Détail de chaque section dans Configuration.
Hébergeurs qui exigent AUTH = From (OVH notamment)
Certains submissions n'acceptent un message que si son expéditeur correspond au
compte authentifié : le From doit être une adresse du compte (ou un alias), et
l'enveloppe MAIL FROM doit elle aussi lui appartenir - ce qui exclut l'adresse
de retour VERP employée par défaut. Deux réglages couvrent ce cas :
relay:
auth: # identité de repli : le robot du domaine
mechanism: plain
username: "robot@lists.example.org"
password_file: "/etc/difuzio/secrets/smtp-robot"
auths: # un compte d'envoi par liste
membres@lists.example.org:
mechanism: plain
username: "membres@lists.example.org"
password_file: "/etc/difuzio/secrets/smtp-membres"
envelope_from: auth # MAIL FROM = compte authentifié
Chaque liste citée dans relay.auths s'authentifie avec son propre compte : le
From (l'adresse de la liste, garantie par le munging DMARC) et l'identité AUTH
coïncident. Tout le reste - courriers de service émis par le robot, listes sans
compte dédié - retombe sur relay.auth, qui devient obligatoire dès que
relay.auths existe.
Alternative sans multi-comptes : si l'offre le permet, déclarer les adresses de
listes comme alias d'un unique compte d'envoi et ne garder que relay.auth avec
envelope_from: auth. À tester : le contrôle exact varie d'une offre à l'autre.
Limite connue : le mode digest émet avec un From en sous-adressage
(liste+digest@domaine), que les offres sans sous-adressage peuvent refuser.
Créer l'alias si possible, sinon laisser les abonnés concernés en mode regular.
4. Saisir les comptes dans la console (recommandé)
Les blocs relay.auths et ingest.mailboxes ci-dessus imposent un accès root au
serveur et un redémarrage à chaque liste ajoutée. Les mêmes identifiants se
saisissent dans la console, liste par liste, dans la section "Comptes de
messagerie du fournisseur" de la fiche de liste. Le bouton "Tester la connexion"
ouvre une vraie session (connexion, TLS, authentification, ouverture du dossier
pour l'IMAP) sans envoyer ni relever le moindre message, et affiche le refus de
l'hébergeur mot pour mot.
Ce mode demande le keyring security.credential_keyring :
security:
credential_keyring:
current: 1
keys:
- { version: 1, file: "/etc/difuzio/secrets/credential.key" }
Points à connaître :
- Le mot de passe n'est jamais réaffiché. Il est chiffré à l'enregistrement et aucune réponse de l'API ne le renvoie. Sur une modification, laisser le champ vide conserve le mot de passe stocké : corriger un port ou un dossier ne demande pas de l'avoir sous la main.
- La clé est indispensable et non reconstructible. La perdre rend les mots de passe illisibles (la console affiche "Illisible" liste par liste) et il faut les ressaisir. La sauvegarder avec les trois autres.
- Sans ce keyring, rien ne change : l'installation démarre à l'identique et la console refuse d'enregistrer un compte en nommant la clé manquante, plutôt que d'écrire un mot de passe en clair.
- La base prime sur le fichier. Une liste ayant un compte en base et une
entrée YAML utilise celui de la base ; l'entrée ignorée est nommée dans le
journal du worker. Une fois tout saisi, retirer
relay.authsetingest.mailboxesdu fichier.relay.authreste nécessaire. - Prise en compte sans redémarrage : l'envoi suit dans la demi-minute, la relève au cycle suivant.
- Qui peut le faire : un administrateur de domaine ou un super_admin. Le propriétaire de la liste voit l'état du compte sans pouvoir le modifier : le serveur d'où part le courrier est une décision d'infrastructure.
- Anti-SSRF : un hôte saisi dans la console doit résoudre vers une adresse publique. Un serveur sur adresse privée reste possible par le YAML, que seul l'exploitant peut éditer.
Reprendre des identifiants déjà présents dans le fichier, sans les ressaisir :
difuzio credential import --config /etc/difuzio/config.yaml
difuzio credential import --config /etc/difuzio/config.yaml --apply
Sans --apply, la commande affiche ce qu'elle ferait et n'écrit rien. Voir
Référence CLI, groupe credential.
5. Interdire ce profil sur un serveur
Un exploitant qui ne veut pas de comptes de fournisseur sur son installation ferme le profil pour tout le monde :
policy:
external_mailboxes: deny
Aucun administrateur de domaine ne peut alors enregistrer de compte IMAP/SMTP :
la console n'affiche plus la section, l'API répond 403 external_mailboxes_disabled, et ingest.enabled: true comme relay.auths
deviennent des erreurs de configuration au démarrage. Les détails, notamment le
comportement quand des comptes existent déjà, sont dans
Configuration, section policy.
6. Démarrage
Deux processus seulement : pas de serve-lmtp, il n'y a pas de MTA en frontal.
difuzio serve-web --config /etc/difuzio/config.yaml
difuzio worker --config /etc/difuzio/config.yaml
Le worker héberge le poller IMAP : il relève la boîte à l'intervalle
ingest.poll_interval, ingère les messages dans le pipeline, puis les marque
traités (\Seen, ou déplacement si mark_processed: move).
7. Créer la liste et inscrire les membres
-
Créer le domaine et la liste, activer le domaine (voir Domaines et Listes ; la section "Intégration Postfix" ne concerne pas ce profil).
-
Déclarer que les adresses de service ne sont pas routées :
difuzio domain set-command-addresses --domain lists.example.org --routed=falseÉtape à ne pas sauter. Sans elle, chaque message annonce dans ses en-têtes
List-Help,List-Subscribe,List-OwneretList-Unsubscribedes adresses (liste-subscribe@,liste-owner@...) que l'hébergeur ne connaît pas : l'abonné qui les utilise reçoit un rapport de non-remise. Avec le réglage, ces en-têtes ne portent plus que les URL web, qui fonctionnent. Le même interrupteur existe dans la console, sur la fiche du domaine, section "Routage du courrier entrant". -
Inscrire les membres par le formulaire web (double opt-in) ou par un import.
-
Ne pas inscrire l'adresse de la boîte elle-même.
8. Vérification
difuzio doctor --config /etc/difuzio/config.yamlconfronte le fichier à la base avant même le premier démarrage : il échoue si une boîte vise un domaine ou une liste absent ou inactif, et affiche la commande qui corrige.- Au démarrage du worker, le poller refait le contrôle : "imap preflight ok", ou une ligne par boîte fautive avec sa raison et son remède. Le worker ne s'arrête pas : créer le domaine ou la liste manquante est pris en compte au cycle suivant.
- Envoyer un message de test à l'adresse de la liste depuis une adresse abonnée : il doit être redistribué à tous les abonnés.
- Vérifier l'alignement SPF/DKIM/DMARC dans un message reçu.
Dépannage
-
"relay.starttls must be true when relay.auth is set" : activer
relay.starttls: true. difuzio n'envoie jamais d'identifiants en clair. -
"is group/other accessible" : un fichier de secret n'est pas en 0600.
-
"is not a configured domain" :
ingest.mailboxes[].domainne correspond à aucundomains[].name. -
resolve domain "X": repo: not found: le domaine est déclaré dans le fichier mais absent de la base. Aucun message n'est perdu pendant la panne : ils restent non lus dans la boîte et sont relevés après correction.difuzio add-domain --name X --base-url https://<vhost> --config /etc/difuzio/config.yaml difuzio set-domain-status --domain X --status active --config /etc/difuzio/config.yaml difuzio create-list --domain X --name <liste> --owner <email> --config /etc/difuzio/config.yaml -
AUTH refusé à l'envoi : vérifier les identifiants (mot de passe applicatif éventuel), le port et STARTTLS.
-
"relay.auths requires relay.auth" :
relay.authsexige l'identité de replirelay.auth. -
5xx "sender not allowed" : l'hébergeur refuse une enveloppe ou un
Frométrangers au compte. Passerenvelope_from: authet donner son compte à chaque liste. -
Rien n'est relevé : vérifier IMAP (hôte, port, TLS, identifiants), que la liste cible est active, et que des messages non lus sont présents.
-
Un abonné ne reçoit plus rien : son adresse a peut-être cessé de fonctionner. Il n'y a pas de purge automatique dans ce profil : le retirer à la main.
-
"security.credential_keyring is required" : le keyring de chiffrement n'est pas déclaré, voir la section 4.
-
"the database holds N mailbox credential(s) but security.credential_keyring is not configured" au démarrage : des comptes ont été saisis puis le keyring a disparu de la configuration. Le démarrage est volontairement fatal : sans la clé, ces listes seraient muettes sans que rien ne le dise. Remettre la clé, ou supprimer les comptes.
-
"policy.external_mailboxes is deny but the database still holds ..." : la politique a été fermée alors que des comptes subsistent. Les supprimer avec
difuzio credential rm, ou rouvrir la politique. -
Un compte marqué "Illisible" : le mot de passe a été scellé avec une version de clé que l'installation n'a plus. Le ressaisir rétablit la liste.
-
Un abonné dit avoir écrit à
liste-unsubscribe@sans réponse : l'étape 2 de la section 7 n'a pas été faite. Le domaine annonce encore des adresses que rien ne dessert ; passer--routed=falseet les messages suivants n'annonceront plus que les liens web.