Configura email in arrivo per consegna diretta su siti self-hosted con 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 Mi Piace

7 post sono stati divisi in un nuovo argomento: Il ricevitore di posta funziona con arm?

Questo container sembra codificare l’email in un parametro chiamato email:

Questo sembra essere deprecato, come indicato in /logs:

Avviso di deprecazione: warning: il parametro email è deprecato. tutte le richieste POST a questo percorso dovrebbero essere inviate con un parametro email_encoded codificato in base64 strict invece. email è stato ricevuto ed è in coda per l'elaborazione (rimozione in Discourse 3.3.0)

Potresti aggiornare questo prima di rimuovere il parametro deprecato? :innocent:

6 Mi Piace

Alcune note dall’impostazione di questo:\n\n* Assicurati di proteggere il nuovo file di configurazione del container con: chmod o-rwx containers/mail-receiver.yml. In caso contrario, ti verrà richiesto di farlo quando avvierai il container.\n* Quando crei la chiave API, ho selezionato l’ambito “Tutti gli utenti” e “Globale”. Non so se una chiave più restrittiva funzionerebbe.\n* Il file di esempio mail-receiver.yml ha impostazioni TLS piuttosto diverse, quindi vorrai usare le istruzioni qui piuttosto che cercare di modificare l’esempio.\n* Include anche un’impostazione smtpd_tls_security_level che ho decommentato. Non ho fatto ricerche per capire se è necessaria o se starei meglio con un’impostazione diversa da “may”.\n* Se vuoi impostare un’e-mail per una particolare categoria, puoi farlo in /c/{category-name}/edit/settings. (Questo è utile se vuoi creare una sorta di categoria di mailing list.) Per un gruppo, puoi impostare un indirizzo e-mail in /g/{group-name}/manage/interaction.\n\nNon so se qualcosa di tutto ciò aiuterà altri, ma avrebbe aiutato me. :wink:

4 Mi Piace

Dovresti davvero usare una chiave API con privilegi minimi, queste impostazioni funzionano sicuramente:

Livello utente: Tutti gli utenti
Ambito: Granulare
email—ricevi email

4 Mi Piace

Per quanto ne so, la mia configurazione è corretta, ma non c’è traccia di nessuna delle email in Discourse.

Le email compaiono nel log in questo modo:

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

E ricevo anche una notifica di rifiuto all’indirizzo del mittente. Non rimane nulla in coda e non ci sono errori di traccia nei log. Ho ricontrollato tre volte che gli URL corrispondano e la pagina delle impostazioni API mostra che la chiave viene utilizzata. Ma l’elenco delle email rifiutate nel pannello di amministrazione rimane vuoto.

Qualche suggerimento?

L’errore suggerisce che MAIL_DOMAIN non è impostato su www.[mysite].com, oppure non esiste una categoria o un gruppo configurato per ricevere email inviate a test004@www.[mysite].com.

1 Mi Piace

Grazie per la tua risposta. Ho controllato MAIL_DOMAIN in ogni modo immaginabile, ho provato ogni combinazione di valore MAIL_DOMAIN e indirizzo email di destinazione. Con quale valore nella configurazione di Discourse viene confrontato, ad esempio DISCOURSE_HOSTNAME, DISCOURSE_SMTP_DOMAIN, qualcos’altro?

Sono un po’ confuso dal tuo secondo suggerimento data questa riga nell’OP:

I rifiuti non dovrebbero apparire anche prima che Discourse sia configurato per fare qualcosa con essi? Anche i bounce non compaiono, ho testato utilizzando il metodo consigliato qui: Configure VERP to handle bouncing e-mails. Nessuna traccia di nulla su admin/email.

C’è un log in uno dei container che mostra (o potrebbe essere configurato per mostrare) maggiori informazioni sull’interazione tra mail-receiver e app?

1 Mi Piace

Esistono due tipi principali di rifiuti, quelli che si verificano precocemente e decidono se l’e-mail debba essere passata a Discourse’s EmailReceiver o meno, e quelli durante l’elaborazione delle e-mail di Discourse.

Secondo la mia esperienza, i primi non compaiono nei log di Discourse, il che significa che la maggior parte (tutti?) i rifiuti relativi alle e-mail (DMARC fallito, indirizzo errato, ecc.) non compaiono lì. Quelli che compaiono sono cose come e-mail troppo corte, utente non autorizzato a pubblicare, ecc.

Non sono sicuro se qualcosa sia cambiato da quando è stato scritto quel paragrafo, ma quanto sopra è stata la mia esperienza negli ultimi ~2,5 anni da quando l’ho configurato.

Se invio un’e-mail a test-reject@[my-instance], ricevo un avviso di rimbalzo generico dal mio provider di posta elettronica (non mail-receiver / Discourse) che mi informa che l’indirizzo del destinatario è stato rifiutato. Questo perché mail-receiver lo sta rifiutando durante l’interazione SMTP.

I rimbalzi e VERP sono correlati alle e-mail che la tua istanza di Discourse sta inviando piuttosto che ricevendo, ad esempio per interrompere automaticamente l’invio di e-mail di notifica a un indirizzo che rimbalza costantemente. Non sono correlati a mail-receiver.


Il mio sospetto è che la tua citazione dalla guida ti abbia tratto in inganno e in realtà tutto stia probabilmente funzionando correttamente. Inviare a some-random-address@MAIL_DOMAIN non verrà accettato e non apparirà nei rifiuti, quindi non è un test molto utile di per sé (oltre a garantire che mail-receiver stia ricevendo e-mail, cosa che hai visto che sta facendo).

Naviga in una categoria esistente o creane una nuova, apri la sua configurazione e vai alla scheda delle impostazioni. Vicino al fondo troverai custom incoming email address. Impostalo su something@MAIL_DOMAIN, ad esempio l’indirizzo test004 che hai provato in precedenza, salva e poi prova a inviare a quell’indirizzo.

Ciò dovrebbe superare mail-receiver, quindi dovresti vedere un nuovo post creato nella categoria o un rifiuto in Discourse.

1 Mi Piace

Grazie, questa è una chiarificazione molto utile, la imposterò e la testerò per vedere.

Per i bounce, sono stato nuovamente confuso dall’OP, poiché i bounce sono il primo punto nell’elenco dei motivi per cui potresti voler seguire questa guida.

Quindi, anche con questa configurazione, e anche se ho rimosso i miei record MX di Mailgun, devo ancora configurare VERP su quel lato per catturare i bounce ecc.? Beh, accidenti, pensavo che la consegna diretta fosse una soluzione ai miei problemi con le webhook di Mailgun, sembra che dovrò ricominciare a risolvere i problemi.

2 Mi Piace

Oh, mi scuso, hai ragione sul fatto che dice che puoi usare mail-receiver per i rimbalzi, non ho molta familiarità con il suo funzionamento.

Il mio mail-receiver non riceve i rimbalzi ma sto usando i webhook di Mailgun, forse Mailgun sta cambiando il mittente della busta in modo che riceva i rimbalzi se i webhook sono abilitati. (Cioè, se i webhook fossero disabilitati, forse il mio mail-receiver li starebbe ricevendo invece.)

1 Mi Piace

Sì, sono abbastanza sicuro che ora sia inaccurato, dato che il rifiuto rapido è stato implementato nel… (controlla git log) maggio 2017.

Senza vedere la tua configurazione effettiva, inclusa la configurazione del gruppo/categoria di Discourse, è davvero difficile dire cosa stia andando storto. Almeno l’80% delle volte si tratta di un errore di battitura da qualche parte; chiedi a un collega (non deve essere qualcuno molto tecnico) di dargli un’occhiata, e probabilmente individuerà dove hai messo una l invece di una i in circa cinque secondi. Mia moglie lo fa regolarmente per me.

Lo è. Con la consegna diretta, il tuo provider di posta in uscita non ha bisogno di essere coinvolto affatto per la posta in arrivo. Tutto, che si tratti di un nuovo argomento, una risposta o un rimbalzo, dovrebbe andare direttamente a mail-receiver (e da lì a Discourse per l’elaborazione).

3 Mi Piace

Sono abbastanza sicuro che sia successo anche a me con questo stesso problema la settimana scorsa. Alla fine ho copiato un altro file YML da qualche altra parte e ha funzionato.

Sembrava strano, però, Matt. Ho controllato i file postfix e anche quelli sembravano a posto, ma diceva che l’hostname non corrispondeva. Giuro che l’ho copiato/incollato, ma forse ho commesso l’errore di pensare di poter digitare.

1 Mi Piace

È un bene che il riconoscimento vocale AI risolverà tutto questo per noi, da un giorno all’altro. :troll:

3 Mi Piace

Avevi ragione, configurare un’email per una categoria e inviare lì l’email ha funzionato come previsto, quindi mi stavo solo sbattendo la testa contro il muro perché i rifiuti erano silenziosi.

Sono contento di saperlo ora, e spero che la guida venga aggiornata, anche se personalmente preferirei che funzionasse come descritto nella guida. Ad esempio, se gli utenti stanno cercando di inviare email a un indirizzo e questo non funziona, potrebbe aiutarmi a informarli o a rendermi conto che c’è una domanda di comunicazione con una categoria o un gruppo via email. Sembra che senza questo, non ci sia un modo semplice per vedere quelle email.

Questo ancora non funziona come previsto. Ho fatto funzionare i webhook, quindi posso vedere diversi rimbalzi, ma so che provengono dai webhook di Mailgun perché hanno il problema descritto qui: “Errore Discourse::NotFound” quando si fa clic sul campo “Tipo di email” in admin/email/bounced

Non capisco davvero come Mailgun riceva i rimbalzi in primo luogo, dato che non ho record MX che puntano ai loro server, presumo che impostino un percorso di ritorno mentre inviano l’email in uscita?

E vedo i rimbalzi nei log di mail-receiver, ma non arrivano ad app. Sembra che vengano rifiutati silenziosamente. Ecco una riga nei log che posso collegare a un rimbalzo ricevuto tramite i webhook:

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>

Devo aggiungere bounce+{%something}@www.mydomain.com come indirizzo in whitelist da qualche parte in modo che vengano accettati?

2 Mi Piace

Sì, probabilmente riscrivono il percorso di ritorno (noto anche come “envelope from”) quando l’email in uscita passa attraverso i loro server. Probabilmente c’è un’impostazione da qualche parte per disattivare tale funzione, ma non ho usato Mailgun, quindi non posso dirlo con certezza (né dove si troverebbe tale impostazione).

OK, questo è un errore tra mail-receiver e Discourse. Dovrebbe esserci una riga nei log poco prima che inizi con “Failed to GET smtp_should_reject answer” che ti dirà di più su cosa è fallito e perché, e questo dovrebbe correlare con un messaggio di errore di qualche tipo nei log di Discourse.

2 Mi Piace

Mar 21 17:02:21 discourse-smtp-fast-rejection[1149]: Impossibile ottenere la risposta da smtp_should_reject da https://www.mydomain.com/admin/email/smtp_should_reject.json: 400

Potrebbe essere correlato al mittente nullo, from=\u003c\u003e? Non vedo nulla nei log al riguardo. Il 400 indica che smtp_should_reject.json non esiste?

2 Mi Piace

Se quella risorsa HTTP non esistesse, sarebbe un 404, non un 400. Non credo che un mittente nullo dovrebbe essere un problema, perché è così che verranno recapitate tutte le notifiche di rimbalzo. Una chiave API errata dovrebbe (credo) restituire un 403, ma non posso dirlo con assoluta certezza a memoria, quindi vale probabilmente la pena verificarlo, non si sa mai. Se i log di Discourse non danno indicazioni sul motivo per cui la richiesta non è andata a buon fine, temo che dovrai affrontare una sessione di debugging dolorosa: al momento non dispongo di un sistema abilitato per mail-receiver con cui poter giocare facilmente. Per arrivare alla radice del problema e risolverlo per te, temo che sarebbe un lavoro di consulenza per me.

3 Mi Piace

Per ora, non sembra che stia rompendo nulla, ho fatto funzionare i bounce con le webhook e la maggior parte dei bounce non genera un’e-mail (un altro argomento menzionato questa risposta di stackoverflow, che corrisponde a ciò che sto vedendo). E la risposta via e-mail funziona come previsto. Qualunque sia il fallimento, è raro e non interrompe la normale funzionalità.

Terrò d’occhio la situazione, non si sa mai, e ti farò sapere se scopro qualcosa che potrebbe essere utile ad altri. Grazie ancora per il tuo aiuto!

2 Mi Piace

@JammyDodger Potrebbe essere rinominato in qualcosa che permetta di cercare “mail-receiver” per trovarlo? Non sono riuscito a trovare questo argomento senza diversi tentativi da tre anni fa, quando “straightforward” è stato rimosso dal titolo.

4 Mi Piace