Configurer la réception des e-mails en livraison directe pour sites auto-hébergés avec Mail-Receiver

Discourse is all about enabling civilized discussion. While plenty of people like a web interface, e-mail is still the “hub” of many people’s online lives. That’s why sending e-mail is so important, and when you’re sending e-mail, you really want to be able to receive it, too. There are several reasons why:

  • If e-mails “bounce” (they can’t be delivered for some reason), you need to know about that. Repeatedly sending e-mails that bounce will get your e-mails flagged as spam. Receiving e-mail bounces allows you to disable sending to non-existent addresses.
  • Allowing people to reply to posts via e-mail can significantly improve engagement, as people can reply straight away from their mail client, even if they’re not able to visit the forum at that moment.
  • Letting people post new topics, or send PMs, via e-mail has similar benefits to engagement. In addition, you can use Discourse to handle e-mail for a group, such as an e-mail-based support channel (which is how Discourse’ own e-mail support is handled).

Delivering e-mail directly into your Discourse forum, rather than setting up POP3 polling, has a number of benefits:

  • No need to deal with gmail or another provider’s foibles;
  • You have more control over the e-mail addresses that people use to send posts; and
  • There are no delays in delivery – no more waiting for the next polling run to see new posts appear!

This howto is all about getting that hawtness into your forum.

Overview

This procedure creates a new container on your Discourse server, alongside the typical app container, which receives e-mail and forwards it into Discourse for processing. It supports all e-mail processes: handling bounces, replies, and new topic creation. Any self-hosted Discourse forum using our supported installation process can make use of this procedure to get easy, smooth-flowing incoming e-mail.

Container Setup

We’re going to get the mail-receiver container up and running on the server that’s already running your Discourse instance. There’s no need for a separate droplet just to handle mail – the whole container only takes about 5MB of memory!

So, start off by logging into your Discourse server, and becoming root via sudo:

ssh ubuntu@192.0.2.42
sudo -i

Now, go to your /var/discourse directory and create a new mail-receiver.yml container definition from the sample conveniently provided:

cd /var/discourse
git pull
cp samples/mail-receiver.yml containers/

Since every site is unique, open containers/mail-receiver.yml in your preferred text editor and change the MAIL_DOMAIN, DISCOURSE_MAIL_ENDPOINT, and DISCOURSE_API_KEY variables to suit your site. (If you are an advanced user and know that you are using nginx outside your container, see below for additional configuration for external nginx.)

:bulb: If you use the default mail endpoint (/admin/email/handle_mail), we suggest using the receive_email API key scope to provide an extra layer of security.

If you’re not sure what your favourite text editor is, try nano:

nano containers/mail-receiver.yml

Use Ctrl-X to exit (say “Yes” to “Do you want to save changes?”, or all your work will be for nothing).

Now, do an initial build of the container, and fire it up!

./launcher bootstrap mail-receiver
./launcher start mail-receiver

To check everything’s OK, take a peek in the logs:

./launcher logs mail-receiver

The last line printed should look rather a lot like this:

<22>Aug 31 04:14:31 postfix/master[1]: daemon started -- version 3.1.1, configuration /etc/postfix

If so, all is well, and you can go on to then next step.

DNS Setup

In order for everyone else on the Internet to know where to deliver mail, you must create an MX record for your forum. The exact details of how to do this vary by DNS provider, but in general, the procedure should be very similar to how you setup the DNS records for your forum in the first place, except that instead of creating an A (or “Address”) record, you’re creating an MX (or “Mail eXchange”) record. If your forum is at forum.example.com, and you set MAIL_DOMAIN to forum.example.com in the mail-receiver.yml, then the DNS record should look like this:

  • DNS Name: forum.example.com (this is the MAIL_DOMAIN)
  • Type: MX
  • Priority: 10
  • Value: forum.example.com (this is the domain of your forum)

To make sure the DNS is setup correctly, use a testing site such as http://mxtoolbox.com/ to look up the MAIL_DOMAIN you configured, and make sure it’s pointing to where you expect.

Note: outbound email providers like mailgun may ask you to add MX records pointing to their servers. You want to remove these so the MX records for your forum only point to your forum’s domain name. SPF and DKIM records must still point to your outbound email provider servers so you can send email.

Discourse Configuration

Now e-mail is being fed into Discourse, it’s time to explain to Discourse what to do with the e-mail it receives.

  • Log into your Discourse forum as Admin and navigate to the Admin panel’s Site Settings, then click the Email tab. (forum.example.com/admin/site_settings/category/email)
  • Change the following settings:
    • Enable the reply by email setting
    • In the reply_by_email_address field, enter replies+%{reply_key}@forum.example.com
    • Enable the manual polling setting

You can automatically, without any further setup, use any address @forum.example.com as an address for category or group e-mails.

Troubleshooting

Nothing ever goes according to plan. Here’s how to figure out what went wrong.

  1. OCI runtime create failed error running ./launcher start mail-receiver? Your hostname might be too long. Rename it using these instructions and choose a shorter name, then rebuild.
  2. Did the e-mail even make it to mail-receiver? Run ./launcher logs mail-receiver, and look for log entries that mentions the address that the e-mail was sent from and to. If there’s none of those, then the message never even made it, and the problem is upstream. Check MX records, sending mail server logs, and firewall permissions (SMTP port 25).
  3. Is the message stuck in the queue? Run ./launcher enter mail-receiver, then run mailq. It should report, “Mail queue is empty”. If there’s any messages in there, you’ll get the to/from addresses listed. Messages only sit around in the queue if there’s a problem delivering to Discourse itself, so exit out of the container and then check…
  4. Did mail-receiver error out somehow? Run ./launcher logs mail-receiver | grep receive-mail and look for anything that looks like a stack trace, or basically anything other than “Recipient: <something>@forum.example.com”. Those error messages, whilst not necessarily self-explanatory, should go an awfully long way to explaining what went wrong. Look for typos in your yml file. In particular, check that DISCOURSE_MAIL_ENDPOINT URL matches your site URL, usually starting with https://.

Integrating with External nginx

If you are an advanced user and have configured external nginx such as for Add an offline page to display when Discourse is rebuilding or starting up you will find that the combination of mail-receiver and HTTPS being handled in external nginx requires slightly different handling to enable SSL for email over TLS. Here are example containers/mail-receiver.yml snippets that work with the recommended configuration for external nginx with letsencrypt certificates:

  POSTCONF_smtpd_tls_key_file:  /letsencrypt/live/=DOMAIN=/privkey.pem
  POSTCONF_smtpd_tls_cert_file:  /letsencrypt/live/=DOMAIN=/fullchain.pem

volumes:
  - volume:
      host: /var/discourse/shared/mail-receiver/postfix-spool
      guest: /var/spool/postfix
# uncomment to support TLS
  - volume:
      host: /etc/letsencrypt/
      guest: /letsencrypt

Note that you can’t export as a volume only /etc/letsencrypt/live because the actual files are symlinks into ../../archive/... and those won’t resolve if you are more specific in the volume specification.

Prevent outgoing host email from interfering (Postfix)

If you have (or want) automated messages from your host server (via Postfix), the mail-receiver will conflict because it needs port 25 to operate. One solution is to disable the host Postfix from listening on port 25:

nano /etc/postfix/master.cf

and comment out the line that looks like this:

smtp inet n - y - - smtpd

Then service postfix reload. You may also need to restart the mail-receiver container.

With both the host Postfix and the mail-receiver running, do netstat -tulpn | grep :25 to confirm that docker-proxy is using port 25.

Block unwanted domains from sending to you

To stop email from unwanted domains from even reaching your Discourse, your mail-receiver.yml should look something like this:

  DISCOURSE_API_USERNAME: system

  POSTCONF_smtpd_sender_restrictions: 'texthash:/etc/postfix/shared/sender_access'

volumes:
  - volume:
      host: /var/discourse/shared/mail-receiver/postfix-spool
      guest: /var/spool/postfix
  - volume:
      host: /var/discourse/shared/mail-receiver/etc
      guest: /etc/postfix/shared
# uncomment to support TLS
#  - volume:
#      host: /var/discourse/shared/standalone/letsencrypt
#      guest: /letsencrypt

Then create /var/discourse/shared/mail-receiver/etc path, and within it create a sender_access file containing the domains to reject, like this:

qq.com REJECT
163.com REJECT

Rebuild and you’re golden!

DMARC support

DMARC support has been enabled by default in the discourse/mail-receiver:release image to more strongly validate incoming email. This is enabled since the timestamped image discourse/mail-receiver:20240720054629.

This functionality can be toggled via the INCLUDE_DMARC docker environment variable. If a more permissive incoming mail server configuration is preferred, set that environment variable to false and rebuild the image.

The last version without DMARC support is discourse/mail-receiver:20211208001915.

Further Reading

Last edited by @kelv 2024-07-22T03:53:51Z

Check documentPerform check on document:
95 « J'aime »

7 messages ont été déplacées vers un nouveau sujet : Le récepteur de courrier fonctionne-t-il avec arm ?

Ce conteneur semble encoder l’e-mail dans un paramètre nommé email :

Cela semble obsolète, conformément à /logs :

Avis de dépréciation : avertissement : le paramètre email est obsolète. toutes les requêtes POST vers cet itinéraire doivent être envoyées avec un paramètre email_encoded strictement encodé en base64 à la place. email a été reçu et est mis en file d'attente pour traitement (suppression dans Discourse 3.3.0)

Pourriez-vous mettre cela à jour avant de supprimer le paramètre obsolète ? :innocent:

6 « J'aime »

Quelques notes de la configuration :\n\n* Assurez-vous de sécuriser le nouveau fichier de configuration du conteneur avec : chmod o-rwx containers/mail-receiver.yml. Sinon, vous serez invité à le faire lors du démarrage du conteneur.\n* Lors de la création de la clé API, j’ai sélectionné les étendues « Tous les utilisateurs » et « Global ». Je ne sais pas si une clé plus restreinte fonctionnerait.\n* Le fichier d’exemple mail-receiver.yml a des paramètres TLS assez différents, vous voudrez donc utiliser les instructions ici plutôt que d’essayer de modifier l’exemple.\n* Il inclut également un paramètre smtpd_tls_security_level que j’ai décommenté. Je n’ai fait aucune recherche pour déterminer s’il est nécessaire ou si un autre paramètre que « may » serait préférable.\n* Si vous souhaitez configurer un e-mail pour une catégorie particulière, cela peut être fait dans /c/{category-name}/edit/settings. (Ceci est utile si vous souhaitez créer une sorte de catégorie de liste de diffusion.) Pour un groupe, vous pouvez configurer une adresse e-mail dans /g/{group-name}/manage/interaction.\n\nJe ne sais pas si cela aidera quelqu’un, mais cela m’aurait aidé. :wink:

4 « J'aime »

Vous devriez vraiment utiliser une clé API de moindre privilège, ces paramètres fonctionnent certainement :

Niveau utilisateur : Tous les utilisateurs
Portée : Granulaire
email—recevoir les emails

4 « J'aime »

D’après ce que je peux dire, ma configuration est correcte, mais il n’y a aucune trace des e-mails dans Discourse.

Les e-mails apparaissent dans le journal comme ceci :

Mar 18 17:20:41 [myserver]-mail-receiver postfix/smtpd[122]: NOQUEUE: reject: RCPT from [XXX].google.com[XXX.XX.XXX.XXX]: 554 5.7.1 <test004@www.[mysite].com>: Recipient address rejected: Mail to this address is not accepted. Check the address and try to send again?; from=<[sender]@gmail.com> to=<test004@www.[mysite].com> proto=ESMTP helo=<[XXX].google.com>
Mar 18 17:20:42 [myserver]-mail-receiver postfix/smtpd[122]: disconnect from [XXX].google.com[XXX.XX.XXX.XXX] ehlo=2 starttls=1 mail=1 rcpt=0/1 bdat=0/1 quit=1 commands=5/7

Et je reçois également une notification de rejet à l’adresse d’envoi. Rien ne reste dans la file d’attente et il n’y a aucune erreur de trace dans les journaux. J’ai vérifié trois fois que les URL correspondent toutes, et la page des paramètres de l’API indique que la clé est utilisée. Mais la liste des e-mails rejetés dans le panneau d’administration reste vide.

Des suggestions ?

L’erreur suggère que MAIL_DOMAIN n’est pas défini sur www.[mysite].com, ou qu’il n’y a pas de catégorie ou de groupe configuré pour recevoir les e-mails envoyés à test004@www.[mysite].com.

1 « J'aime »

Merci de votre réponse. J’ai vérifié MAIL_DOMAIN de toutes les manières imaginables, j’ai essayé toutes les combinaisons de la valeur de MAIL_DOMAIN et de l’adresse e-mail de destination. Quelle valeur dans la configuration de Discourse est-elle vérifiée par rapport à, par exemple, DISCOURSE_HOSTNAME, DISCOURSE_SMTP_DOMAIN, autre chose ?

Je suis un peu confus par votre deuxième suggestion étant donné cette ligne dans le message original :

Les rejets ne devraient-ils pas apparaître avant même que Discourse ne soit configuré pour en faire quoi que ce soit ? Les rebonds n’apparaissent pas non plus, j’ai testé en utilisant la méthode recommandée ici : Configurer VERP pour gérer les e-mails rebondis. Aucune trace de quoi que ce soit sur admin/email.

Existe-t-il un journal dans l’un ou l’autre conteneur qui montre (ou pourrait être configuré pour montrer) plus d’informations sur l’interaction entre mail-receiver et app ?

1 « J'aime »

Il existe deux types majeurs de rejets : ceux qui se produisent tôt et décident si l’e-mail doit être transmis à EmailReceiver de Discourse, et ceux qui se produisent pendant le traitement des e-mails par Discourse.

D’après mon expérience, les premiers n’apparaissent pas dans les journaux de Discourse, ce qui signifie que la plupart (voire tous ?) les rejets liés aux e-mails (DMARC échoué, mauvaise adresse, etc.) n’y apparaissent pas. Ceux qui apparaissent sont des choses comme un e-mail trop court, un utilisateur non autorisé à poster, etc.

Je ne suis pas sûr si quelque chose a changé depuis que ce paragraphe a été écrit, mais ce qui précède correspond à mon expérience depuis environ 2,5 ans, lorsque je l’ai configuré.

Si j’envoie un e-mail à test-reject@[my-instance], je reçois un avis de rejet générique de mon fournisseur de messagerie (pas de mail-receiver / Discourse) m’indiquant que l’adresse du destinataire a été rejetée. C’est parce que mail-receiver la rejette pendant l’interaction SMTP.

Les bounces et VERP sont liés aux e-mails que votre instance Discourse envoie plutôt qu’à ceux qu’elle reçoit, par exemple pour arrêter automatiquement d’envoyer des e-mails de notification à une adresse qui renvoie constamment des erreurs. Ils ne sont pas liés à mail-receiver.


Je soupçonne que votre citation du guide vous a induit en erreur et qu’en réalité, tout fonctionne probablement correctement. Envoyer à some-random-address@MAIL_DOMAIN ne sera pas accepté et n’apparaîtra pas dans les rejets, ce n’est donc pas un test très utile en soi (en dehors de s’assurer que mail-receiver reçoit bien les e-mails, ce que vous avez constaté).

Naviguez vers une catégorie existante ou créez-en une nouvelle, ouvrez sa configuration et allez dans l’onglet des paramètres. Près du bas, vous trouverez custom incoming email address. Définissez-la sur something@MAIL_DOMAIN, par exemple l’adresse test004 que vous avez essayée précédemment, enregistrez, puis essayez d’envoyer à cette adresse.

Cela devrait passer mail-receiver, vous devriez donc soit voir un nouveau message créé dans la catégorie, soit un rejet dans Discourse.

1 « J'aime »

Merci, c’est une clarification très utile, je vais la mettre en place et tester pour voir.

Concernant les bounces, j’ai été à nouveau confus par l’OP, car les bounces sont le premier point de la liste des raisons pour lesquelles vous pourriez vouloir suivre ce guide.

Donc, même avec cette configuration, et même si j’ai supprimé mes enregistrements MX de Mailgun, dois-je toujours configurer VERP de ce côté pour intercepter les bounces, etc. ? Eh bien zut, je pensais que la livraison directe était une solution de contournement à mes problèmes avec les webhooks de Mailgun, on dirait que je vais devoir recommencer à dépanner cela.

2 « J'aime »

Oh, désolé, vous avez raison, cela dit que vous pouvez utiliser mail-receiver pour les rebonds, je ne suis pas très familier avec le fonctionnement.

Mon mail-receiver ne reçoit pas les rebonds mais j’utilise les webhooks de Mailgun, peut-être que Mailgun change l’expéditeur de l’enveloppe pour qu’il reçoive les rebonds si les webhooks sont activés. (C’est-à-dire que si les webhooks étaient désactivés, peut-être que mon mail-receiver recevrait les rebonds à la place.)

1 « J'aime »

Oui, je suis à peu près sûr que ce n’est plus exact, puisque le rejet rapide a été implémenté en… (vérifie git log) mai 2017.

Sans voir votre configuration réelle, y compris la configuration du groupe/catégorie Discourse, il est vraiment difficile de dire ce qui ne va pas. Au moins 80% du temps, c’est une faute de frappe quelque part, cependant ; demandez à un collègue (pas besoin que ce soit quelqu’un de très technique) de regarder, et il repérera probablement où vous avez mis un l au lieu d’un i en environ cinq secondes. Ma femme fait cela pour moi régulièrement.

C’est le cas. Avec la livraison directe, votre fournisseur de messagerie sortante n’a pas besoin d’être impliqué du tout pour les e-mails entrants. Tout, qu’il s’agisse d’un nouveau sujet, d’une réponse ou d’un rejet, devrait aller directement à mail-receiver (et de là à Discourse pour traitement).

3 « J'aime »

Je suis à peu près sûr que c’est ce qui m’est arrivé avec ce problème exact la semaine dernière. J’ai finalement copié un autre fichier YML d’ailleurs et cela a fonctionné.

Cela semblait étrange, cependant, Matt. J’ai regardé dans les fichiers postfix et ils semblaient également corrects, mais il était indiqué que le nom d’hôte ne correspondait pas. Je jure que je l’ai copié/collé, mais peut-être ai-je fait l’erreur de penser que je pouvais taper.

1 « J'aime »

C’est bien que la reconnaissance vocale par IA va régler tout ça pour nous n’importe quel jour maintenant. :troll:

3 « J'aime »

[quote=“Simon Manning, post:476, topic:49487, username:Simon_Manning”]
Mon soupçon est que votre citation du guide vous a induit en erreur et qu’en fait, tout fonctionne probablement correctement.
[/quote]Vous aviez raison, la configuration d’un e-mail pour une catégorie et l’envoi d’e-mails là-bas ont fonctionné comme prévu, donc je me frappais la tête contre un mur parce que les rejets étaient silencieux.

Je suis content de le savoir maintenant, et j’espère que le guide sera mis à jour, bien que personnellement, je préférerais que cela fonctionne comme le décrit le guide. Par exemple, si les utilisateurs essaient d’envoyer un e-mail à une adresse et que cela échoue, cela pourrait m’aider à les informer ou à réaliser qu’il y a une demande pour communiquer avec une catégorie ou un groupe par e-mail. Il semble que sans cela, il n’y ait aucun moyen facile de voir ces e-mails.

[quote=“Matt Palmer, post:479, topic:49487, username:mpalmer”]
C’est le cas. Avec la livraison directe, votre fournisseur de messagerie sortante n’a pas besoin d’intervenir du tout pour la messagerie entrante. Tout, qu’il s’agisse d’un nouveau sujet, d’une réponse ou d’un rebond, devrait être envoyé directement à mail-receiver (et de là à Discourse pour traitement).
[/quote]Cela ne fonctionne toujours pas comme prévu. J’ai fait fonctionner les webhooks, donc je peux voir plusieurs rebonds, mais je sais qu’ils proviennent des webhooks de Mailgun car ils ont le problème décrit ici : “Discourse::NotFound” error when click “Email Type” field on admin/email/bounced

Je ne comprends pas vraiment comment Mailgun reçoit les rebonds en premier lieu, car je n’ai pas d’enregistrements MX pointant vers leurs serveurs, j’imagine qu’ils définissent un chemin de retour lorsqu’ils envoient l’e-mail sortant ?

Et je vois les rebonds dans les journaux de mail-receiver, mais ils n’atteignent pas app. Il semble qu’ils soient rejetés silencieusement. Voici une ligne dans les journaux que je peux relier à un rebond reçu via les webhooks :

NOQUEUE: reject: RCPT from mail-[id1].outbound.protection.outlook.com[XX.XX.XX.XX]: 450 4.7.1 <bounce+[id2]-[email]=[address].com@www.[mydomain].com>: Recipient address rejected: Internal error, API request failed; from=<> to=<bounce+[id#]-[email]=[address].com@www.[mydomain].com> proto=ESMTP helo=<[id3].outbound.protection.outlook.com>

Dois-je ajouter bounce+{%quelque chose}@www.mydomain.com comme une adresse autorisée quelque part pour qu’ils passent ?

2 « J'aime »

Oui, ils réécrivent probablement le chemin de retour (également appelé « enveloppe de l’expéditeur ») lorsque l’e-mail sortant transite par leurs serveurs. Il y a probablement un réglage quelque part pour désactiver cela, mais je n’ai pas utilisé Mailgun, donc je ne peux pas le dire avec certitude (ni où un tel réglage pourrait se trouver).

OK, ça, c’est une erreur entre mail-receiver et Discourse. Il devrait y avoir une ligne dans les journaux peu avant cela qui commence par « Failed to GET smtp_should_reject answer » qui vous en dira plus sur ce qui a échoué et pourquoi, et cela devrait correspondre à un message d’erreur quelconque dans les journaux de Discourse.

2 « J'aime »

Mar 21 17:02:21 discourse-smtp-fast-rejection[1149]: Échec de l'obtention de la réponse smtp_should_reject depuis https://www.mydomain.com/admin/email/smtp_should_reject.json : 400

Cela pourrait-il être lié à l’expéditeur nul, from=\u003c\u003e ? Je ne vois rien dans les journaux à ce sujet. Le 400 indique-t-il que smtp_should_reject.json n’existe pas ?

2 « J'aime »

Si cette ressource HTTP n’existait pas, ce serait un 404, pas un 400. Je ne pense pas qu’un expéditeur nul devrait poser problème, car c’est ainsi que tous les rejets seront livrés. Une clé API incorrecte devrait (je pense) renvoyer un 403, mais je ne peux pas le dire avec une certitude absolue de mémoire, donc cela vaut probablement au moins la peine de vérifier, juste au cas où. Si les journaux de Discourse n’indiquent rien sur la raison pour laquelle la requête était mauvaise, je crains que vous n’ayez probablement une session de débogage douloureuse - je n’ai pas de système activé pour mail-receiver avec lequel jouer facilement pour le moment. Ce serait un travail de conseil pour moi de comprendre ce qui se passe et de le résoudre pour vous, je le crains.

3 « J'aime »

Pour l’instant, cela ne semble rien casser, j’ai fait fonctionner les rebonds avec des webhooks et la plupart des rebonds ne génèrent pas d’e-mail (un autre sujet mentionne cette réponse Stack Overflow, ce qui correspond à ce que je vois). Et la réponse par e-mail fonctionne également comme prévu. Quelle que soit la défaillance, elle est rare et ne casse pas la fonction normale.

Je vais garder un œil dessus au cas où, et je reviendrai si je découvre quelque chose qui pourrait être utile à d’autres. Merci encore pour votre aide !

2 « J'aime »

@JammyDodger Pourrait-on renommer ceci de manière à permettre la recherche de « mail-receiver » pour le trouver ? Je n’ai généralement pas pu trouver ce sujet sans plusieurs tentatives depuis trois ans, lorsque « straightforward » a été supprimé du titre.

4 « J'aime »