Configurar email de entrada para entrega direta em sites hospedados por você com 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 curtidas

7 posts were split to a new topic: Does the mail-receiver work with arm?

Este contêiner parece codificar o e-mail em um parâmetro chamado email:

Isso parece estar obsoleto, de acordo com /logs:

Aviso de depreciação: aviso: o parâmetro email está obsoleto. todas as requisições POST para esta rota devem ser enviadas com um parâmetro email_encoded codificado em base64 estrito em vez disso. email foi recebido e está na fila para processamento (remoção no Discourse 3.3.0)

Você poderia atualizar isso antes de remover o parâmetro obsoleto? :innocent:

6 curtidas

Algumas notas da configuração:

  • Certifique-se de proteger o novo arquivo de configuração do contêiner com: chmod o-rwx containers/mail-receiver.yml. Se não o fizer, você será solicitado a fazer isso ao inicializar o contêiner.
  • Ao criar a chave da API, selecionei “Todos os Usuários” e o escopo “Global”. Não sei se uma chave mais restrita funcionaria.
  • O arquivo de exemplo mail-receiver.yml tem configurações TLS bem diferentes, então você vai querer usar as instruções aqui em vez de tentar editar o exemplo.
  • Ele também inclui uma configuração smtpd_tls_security_level que descomentei. Não pesquisei para descobrir se é necessária ou se seria melhor com uma configuração diferente de “may”.
  • Se você quiser configurar um e-mail para uma categoria específica, isso pode ser feito em /c/{category-name}/edit/settings. (Isso é útil se você quiser criar uma categoria de lista de e-mails). Para um grupo, você pode configurar um endereço de e-mail em /g/{group-name}/manage/interaction.

Não sei se algo disso ajudará outras pessoas, mas teria me ajudado. :wink:

4 curtidas

Você realmente deveria usar uma chave de API de privilégio mínimo, estas configurações definitivamente funcionam:

Nível do usuário: Todos os usuários
Escopo: Granular
email—receber emails

4 curtidas

Pelo que consigo verificar, minha configuração está correta, mas não há registro de nenhum dos e-mails no Discourse.

Os e-mails aparecem no log assim:

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 eu também recebo uma notificação de rejeição no endereço remetente. Nada permanece na fila e não há erros de rastreamento nos logs. Verifiquei três vezes que os URLs correspondem e a página de configurações da API mostra que a chave está sendo usada. Mas a lista de e-mails rejeitados no painel de administração permanece vazia.

Alguma sugestão?

O erro sugere que MAIL_DOMAIN não está definido como www.[mysite].com, ou então não há nenhuma categoria ou grupo configurado para receber e-mails enviados para test004@www.[mysite].com.

1 curtida

Obrigado pela sua resposta. Verifiquei o MAIL_DOMAIN de todas as maneiras que consigo imaginar, tentei todas as combinações de valores de MAIL_DOMAIN e endereço de e-mail de destino. Qual valor na configuração do Discourse é verificado em relação a, por exemplo, DISCOURSE_HOSTNAME, DISCOURSE_SMTP_DOMAIN, algo mais?

Fiquei um pouco confuso com sua segunda sugestão, dada esta linha no OP:

As rejeições não deveriam aparecer mesmo antes do Discourse ser configurado para fazer algo com elas? As devoluções também não estão aparecendo, testei usando o método recomendado aqui: Configurar VERP para lidar com e-mails devolvidos. Não há nenhum rastro de nada em admin/email.

Existe algum log em algum dos contêineres que mostre (ou possa ser configurado para mostrar) mais informações sobre a interação entre mail-receiver e app?

1 curtida

Existem dois tipos principais de rejeições: as que ocorrem cedo e decidem se o e-mail deve ser passado para o EmailReceiver do Discourse, e as que ocorrem durante o processamento de e-mail do Discourse.

Na minha experiência, as primeiras não aparecem nos logs do Discourse, o que significa que a maioria (todas?) das rejeições relacionadas a e-mail (DMARC falhou, endereço errado, etc.) não aparecem lá. As que aparecem são coisas como e-mail muito curto, usuário não autorizado a postar, etc.

Não tenho certeza se algo mudou desde que esse parágrafo foi escrito, mas essa tem sido minha experiência há cerca de 2,5 anos, quando o configurei.

Se eu enviar um e-mail para test-reject@[my-instance], recebo um aviso genérico de devolução do meu provedor de e-mail (não do mail-receiver / Discourse) informando que o endereço do destinatário foi rejeitado. Isso ocorre porque o mail-receiver o está rejeitando durante a interação SMTP.

Devoluções e VERP estão relacionados a e-mails que sua instância do Discourse está enviando, em vez de receber, por exemplo, para parar automaticamente de enviar e-mails de notificação para um endereço que está consistentemente devolvendo. Eles não se relacionam com o mail-receiver.


Minha suspeita é que sua citação do guia o confundiu e, na verdade, tudo provavelmente está funcionando corretamente. Enviar para some-random-address@MAIL_DOMAIN não será aceito e não aparecerá nas rejeições, portanto, não é um teste muito útil por si só (além de garantir que o mail-receiver está recebendo e-mails, o que você viu que está).

Navegue até uma categoria existente ou crie uma nova, abra sua configuração e vá para a aba de configurações. Perto do final, você encontrará endereço de e-mail de entrada personalizado. Defina-o para something@MAIL_DOMAIN, por exemplo, o endereço test004 que você tentou anteriormente, salve e, em seguida, tente enviar para esse endereço.

Isso deve passar pelo mail-receiver, então você deverá ver uma nova postagem criada na categoria ou uma rejeição no Discourse.

1 curtida

Obrigado, este é um esclarecimento muito útil, vou configurá-lo e testá-lo para ver.

Para bounces, fiquei novamente confuso com o OP, já que bounces são o primeiro ponto na lista de razões pelas quais você pode querer seguir este guia.

Então, mesmo com isso configurado, e mesmo que eu tenha removido meus registros MX do Mailgun, ainda preciso configurar o VERP desse lado para capturar bounces, etc.? Bem, droga, pensei que a entrega direta fosse uma solução alternativa para meus problemas com os webhooks do Mailgun, parece que terei que começar a solucionar isso novamente.

2 curtidas

Ah, desculpas, você está certo que diz que pode usar o mail-receiver para devoluções, não estou muito familiarizado com como isso funciona.

Meu mail-receiver não recebe devoluções, mas estou usando webhooks do Mailgun, talvez o Mailgun esteja mudando o remetente do envelope para que ele receba as devoluções se os webhooks estiverem ativados. (Ou seja, se os webhooks estivessem desativados, talvez meu mail-receiver estivesse recebendo as devoluções em vez disso.)

1 curtida

Sim, tenho certeza que isso agora está incorreto, já que a rejeição rápida foi implementada em… (verifica git log) maio de 2017.

Sem ver sua configuração real, incluindo a configuração do grupo/categoria do Discourse, é realmente difícil dizer o que está dando errado. Pelo menos 80% das vezes é um erro de digitação em algum lugar; peça a um colega (não precisa ser alguém muito técnico) para dar uma olhada, e ele provavelmente notará onde você colocou um l em vez de um i em cerca de cinco segundos. Minha esposa faz isso por mim regularmente.

É. Com a entrega direta, seu provedor de e-mail de saída não precisa se envolver em nada para e-mails de entrada. Tudo, seja um novo tópico, uma resposta ou um bounce, deve ir diretamente para mail-receiver (e daí para o Discourse para processamento).

3 curtidas

Tenho quase certeza que foi o que aconteceu comigo com esse mesmo problema na semana passada. Finalmente copiei outro arquivo YML de outro lugar e funcionou.

Pareceu estranho, porém, Matt. Eu olhei nos arquivos postfix e eles também pareciam corretos, mas dizia que o nome do host não correspondia. Juro que copiei/colei, mas talvez eu tenha cometido o erro de pensar que poderia digitar.

1 curtida

Ainda bem que o reconhecimento de voz por IA vai consertar tudo isso para nós a qualquer dia. :troll:

3 curtidas

[quote=“Simon Manning, post:476, topic:49487, username:Simon_Manning”]
Minha suspeita é que sua citação do guia o confundiu e, na verdade, tudo provavelmente está funcionando corretamente.
[/quote]Você estava correto, configurar um e-mail para uma categoria e enviar e-mail para lá funcionou como esperado, então eu estava apenas batendo minha cabeça contra a parede porque as rejeições eram silenciosas.

Fico feliz em saber agora, e espero que o guia seja atualizado, embora pessoalmente eu preferisse que funcionasse como o guia descreve. Por exemplo, se os usuários estiverem tentando enviar e-mail para algum endereço e estiver falhando, isso pode me ajudar a informá-los ou a perceber que há demanda para se comunicar com uma categoria ou grupo por e-mail. Parece que, sem isso, não há uma maneira fácil de ver esses e-mails.

[quote=“Matt Palmer, post:479, topic:49487, username:mpalmer”]
É. Com entrega direta, seu provedor de e-mail de saída não precisa se envolver em nada para e-mails de entrada. Tudo, seja um novo tópico, uma resposta ou um bounce, deve ir diretamente para mail-receiver (e daí para o Discourse para processamento).
[/quote]Isso ainda não está funcionando como esperado. Consegui fazer os webhooks funcionarem, então posso ver vários bounces, mas sei que eles são de webhooks do Mailgun porque eles têm o problema descrito aqui: “Discourse::NotFound” error when click “Email Type” field on admin/email/bounced

Eu realmente não entendo como o Mailgun está recebendo os bounces em primeiro lugar, já que não tenho nenhum registro MX apontando para os servidores deles, presumo que eles estejam definindo um caminho de retorno enquanto enviam o e-mail de saída?

E eu vejo os bounces nos logs do mail-receiver, mas eles não estão chegando ao app. Parece que eles estão sendo rejeitados silenciosamente. Aqui está uma linha nos logs que posso conectar a um bounce recebido através dos 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>

Preciso adicionar bounce+{%something}@www.mydomain.com como um endereço permitido em algum lugar para que eles passem?

2 curtidas

Sim, eles provavelmente estão reescrevendo o caminho de retorno (também conhecido como “envelope from”) quando o e-mail de saída passa por seus servidores. Provavelmente há uma configuração em algum lugar para desativar isso, mas eu não usei o Mailgun, então não posso dizer com certeza (ou onde tal configuração estaria).

Ok, esse é um erro entre o mail-receiver e o Discourse. Deverá haver uma linha nos logs logo antes disso que começa com “Failed to GET smtp_should_reject answer” que lhe dirá mais sobre o que falhou e porquê, e isso deve correlacionar-se com uma mensagem de erro de algum tipo nos logs do Discourse.

2 curtidas

Mar 21 17:02:21 discourse-smtp-fast-rejection[1149]: Falha ao obter a resposta smtp_should_reject de https://www.mydomain.com/admin/email/smtp_should_reject.json: 400

Poderia estar relacionado ao remetente nulo, from=\u003c\u003e? Não vejo nada nos logs sobre isso. O 400 está dizendo que smtp_should_reject.json não existe?

2 curtidas

Se esse recurso HTTP não existisse, seria um 404, não um 400. Eu acho que um remetente nulo não deveria ser um problema, pois é assim que todos os bounces serão entregues. Uma chave de API incorreta deveria (eu acho) retornar um 403, mas não posso afirmar isso com certeza absoluta de imediato, então isso provavelmente vale a pena verificar, só por precaução. Se os logs do Discourse não estiverem dando nenhuma indicação sobre o motivo pelo qual a solicitação foi inválida, receio que você provavelmente terá uma sessão de depuração dolorosa - eu não tenho um sistema habilitado para mail-receiver para brincar facilmente no momento. Seria um trabalho de consultoria para eu chegar ao fundo do que está acontecendo e consertar para você, receio.

3 curtidas

Por enquanto, não parece estar quebrando nada, consegui que os bounces funcionassem com webhooks e a maioria dos bounces não gera um e-mail (outro tópico mencionou esta resposta do stackoverflow, o que corresponde ao que estou vendo). E a resposta por e-mail também está funcionando como esperado. Qualquer que seja a falha, é rara e não está quebrando a função normal.

Vou ficar de olho, caso a caso, e reportarei se descobrir algo que possa ser útil para outros. Obrigado novamente pela sua ajuda!

2 curtidas

@JammyDodger Poderia ser renomeado para algo que permita pesquisar por “mail-receiver” para encontrá-lo? Eu, na maioria das vezes, não consegui encontrar este tópico sem várias tentativas desde três anos atrás, quando “straightforward” foi removido do título.

4 curtidas