Discourse SMTP envia \"EHLO localhost\" em vez do domínio, quebrando o Google smtp-relay

Alguns contextos aqui: Emails have stopped sending - end of file reached

Cerca de uma semana atrás (13 de janeiro de 2021), os e-mails começaram a falhar ao serem enviados através do servidor smtp-relay.gmail.com do Google (que é um uso permitido e pretendido para usuários do Google Apps).

O Sidekiq relatou as falhas com EOFErrors:

Jobs::HandledExceptionWrapper: Wrapped EOFError: end of file reached

E os /logs também relataram as tarefas falhas:

Job exception: end of file reached

O backtrace está disponível no outro post.

===================

A investigação revelou que instalações atualizadas do Discourse estão se conectando aos relays SMTP com ‘EHLO localhost’ — e o Google começou a rejeitar isso há cerca de uma semana.

A partir de tcpdump em uma instância de produção:

0x0030:  d10f f8e4 4548 4c4f 206c 6f63 616c 686f  ....EHLO.localho
	0x0040:  7374 0d0a                                st..
...
	0x0030:  de62 f0c3 3432 3120 342e 372e 3020 5472  .b..421.4.7.0.Tr
	0x0040:  7920 6167 6169 6e20 6c61 7465 722c 2063  y.again.later,.c
	0x0050:  6c6f 7369 6e67 2063 6f6e 6e65 6374 696f  losing.connectio
	0x0060:  6e2e 2028 4548 4c4f 2920 6a31 3673 6d34  n..(EHLO).j16sm4
	0x0070:  3831 3932 3976 736d 2e31 202d 2067 736d  81929vsm.1.-.gsm
	0x0080:  7470 0d0a                                tp..

E replicando com telnet, obtém-se o mesmo resultado:

root@conversation:~# telnet smtp-relay.gmail.com 587
Trying 74.125.137.28...
Connected to smtp-relay.gmail.com.
Escape character is '^]'.
220 smtp-relay.gmail.com ESMTP ls8sm507258pjb.6 - gsmtp
ehlo localhost.localdomain
421 4.7.0 Try again later, closing connection. (EHLO) ls8sm507258pjb.6 - gsmtp
Connection closed by foreign host.

No entanto, um ehlo específico do domínio funciona corretamente:

root@conversation:~# telnet smtp-relay.gmail.com 587
Trying 74.125.137.28...
Connected to smtp-relay.gmail.com.
Escape character is '^]'.
220 smtp-relay.gmail.com ESMTP p10sm668563uaw.3 - gsmtp
ehlo conversation.sevarg.net
250-smtp-relay.gmail.com at your service, [64.227.96.27]
250-SIZE 157286400
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8

======

Com base nos logs, identifiquei o arquivo a ser modificado para testar a correção (na imagem docker):

/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/smtp.rb

Alterando

DEFAULTS = {
      :address              => 'localhost',
      :port                 => 25,
      :domain               => 'localhost.localdomain',

para

    DEFAULTS = {
      :address              => 'conversation.sevarg.net',
      :port                 => 25,
      :domain               => 'conversation.sevarg.net',

resolveu o problema (após reiniciar a instância). O EHLO agora é enviado com a string do domínio, e os e-mails agora são enviados corretamente da minha instância.

================

Comportamento desejado: Ao enviar e-mail, a instalação padrão do Discourse usa o nome de domínio configurado para a conexão inicial com o servidor SMTP. Alternativamente, existe uma opção de configuração para substituir o domínio enviado. Se isso existir, não consegui encontrá-lo através da pesquisa.

5 curtidas

Acredito que já vi esse mesmo erro em relatos de outras pessoas (que talvez também não estivessem usando o Google Domains).

Uma solução de longo prazo é adicionar alguma mágica ao seu app.yml que faça essa reescrita automaticamente. Mas, esperamos que uma Correção Definitiva esteja chegando em breve.

Se houver uma maneira de corrigir isso com o app.yml, com certeza estou interessado — deixar o domínio fixo no código para que o e-mail funcione claramente não é uma solução “adequada”, mas isso demonstra onde resolver o problema de forma mais permanente.

Há algum motivo para ele não usar simplesmente o domínio configurado para o ehlo? Isso seria “mais correto” do que localhost.

Ótimo trabalho de investigação @Syonyk!

Você poderia compartilhar as configurações SMTP do seu arquivo app.yml?

2 curtidas

Não há nada além das configurações padrão necessárias.

  DISCOURSE_SMTP_ADDRESS: smtp-relay.gmail.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: [nome de usuário]
  DISCOURSE_SMTP_PASSWORD: [senha]

Você pode tentar adicionar uma nova linha com

DISCOURSE_SMTP_DOMAIN: conversation.sevarg.net

e tentar novamente?

3 curtidas

Adicionei essa linha e recompilarei o aplicativo (existe uma maneira de contornar essa etapa?).

Agora vejo “domain” nas minhas configurações de e-mail, o arquivo smtp.rb foi revertido para ter localhost como padrão, e os e-mails parecem ser enviados corretamente — consigo enviar mensagens de teste e elas são transmitidas adequadamente.

Portanto, isso resolve o problema, pelo que pude constatar. Seria possível adicionar isso à documentação ou ao fluxo de configuração em algum lugar? Procurei por bastante tempo por essa configuração e não encontrei essa opção — mesmo sabendo da existência dessa opção de configuração, há muito pouca menção a ela.

2 curtidas

Isso pode ser adicionado a este bloco no arquivo de exemplo padrão app.yml:

Você considera isso útil?

1 curtida

Desde que esteja documentado em algum lugar, estou feliz.

No entanto, se não estiver definido, o código poderia usar o valor de DISCOURSE_HOSTNAME (o que estaria correto na maioria dos casos simples)? Enviar ‘localhost’ em EHLO geralmente está errado (pelo menos ao se comunicar com um servidor que não esteja em localhost).

Acho que adicionar isso ao standalone.yml e ao web_only.yml é uma boa ideia.

Concordo que provavelmente deve usar DISCOURSE_HOSTNAME como padrão. Isso certamente parece melhor do que localhost. Isso mudou recentemente?

O ponto aqui é que isso está funcionando para todas as dezenas de milhares de instâncias do Discourse que atualmente não dependem do Google para e-mail. Alterar um padrão como esse pode quebrar tudo para todos, enquanto resolve apenas para os usuários do Google Apps, que são comparativamente poucos.

Vou adicionar isso ao exemplo, e também deveríamos ter um tópico howto chamado “Usando o Google Apps para e-mail de saída” que documente isso. Alguém pode assumir essa tarefa?

4 curtidas

Certo. Concordo que provavelmente faz sentido pecar pelo lado da cautela, mas meu palpite é que qualquer servidor de e-mail que aceite localhost também aceitaria DISCOURSE_HOSTNAME, mas eu não tenho, tipo, dados. :wink: Ter isso nos modelos padrão provavelmente é “Bom o Suficiente”.

1 curtida

Sim, mas enviar ‘localhost’ (para um host remoto) também está errado, conforme o RFC.

O destaque é meu.

RFCs mais antigos afirmam que o servidor não deve rejeitar clientes com base na string EHLO, o que o Google parece estar fazendo, mas não vejo essa formulação no RFC 5321.

Eu esperaria que qualquer servidor de e-mail remoto que tolera localhost também tolerasse (e preferisse) um FQDN, conforme exigido pelo RFC. Entendo o desejo de não quebrar nada, mas, conforme interpreto os RFCs relevantes, o Discourse está simplesmente errado por padrão, e o fato de funcionar é resultado de servidores SMTP remotos excessivamente permissivos.

1 curtida

Ficarei feliz em mesclar um PR em ./discourse-setup que o configure por padrão para o mesmo valor de DISCOURSE_HOSTNAME, desde que seja comprovado que não causa problemas com os serviços SMTP mais comuns que recomendamos para uso.

2 curtidas

Não posso testar a entrega completa de ponta a ponta de e-mail porque não tenho contas, mas:

Mailgun

% nc smtp.mailgun.com 587
220 Mailgun Influx pronto
ehlo conversation.sevarg.net
250-smtp-out-n04.prod.us-west-2.postgun.com
250-AUTH PLAIN LOGIN
...

Sendgrid

% nc smtp.sendgrid.net 587
220 SG ESMTP service pronto em ismtpd0021p1las1.sendgrid.net
ehlo conversation.sevarg.net
250-smtp.sendgrid.net
250-8BITMIME
...

Mailjet

 % nc smtp.mailjet.com 587
220 in.mailjet.com ESMTP Mailjet
ehlo conversation.sevarg.net
250-smtpin.mailjet.com
250-PIPELINING
...

ElasticMail

… não responde a nenhum tipo de helo ou ehlo, realmente. o.O Localhost ou qualquer domínio real.

Acho que configurá-lo durante a instalação é a resposta certa, porque pelo menos estará disponível para que as pessoas saibam e possam modificar se necessário.

6 curtidas

Há outro problema relacionado: o discourse-doctor não parece definir corretamente o domínio e ainda falhará quando a instalação real puder enviar e-mails.

Com a configuração funcional, o discourse-doctor ainda relata a falha de fim de arquivo.

======================================== ERRO ========================================
                                    ERRO INESPERADO

fim de arquivo alcançado

====================================== SOLUÇÃO =======================================
Este não é um erro comum. Não existe uma solução recomendada!

Por favor, relate a mensagem de erro exata acima em https://meta.discourse.org/
(E uma solução, se você encontrar uma!)
=======================================================================================

Não há menção a SMTP_DOMAIN no script de teste.

root@conversation:/var/discourse# grep SMTP_DOMAIN discourse-doctor
root@conversation:/var/discourse#

E o tcpdump indica que a execução do discourse-doctor ainda envia ‘localhost’ no EHLO. Isso também precisa ser corrigido.

	0x0030:  cccd b12c 4548 4c4f 206c 6f63 616c 686f  ...,EHLO.localho
	0x0040:  7374 0d0a                                st..
...
	0x0030:  e247 1aa5 3432 3120 342e 372e 3020 5472  .G..421.4.7.0.Tr
	0x0040:  7920 6167 6169 6e20 6c61 7465 722c 2063  y.again.later,.c
	0x0050:  6c6f 7369 6e67 2063 6f6e 6e65 6374 696f  losing.connectio
	0x0060:  6e2e 2028 4548 4c4f 2920 6e6d 3773 6d31  n..(EHLO).nm7sm1
	0x0070:  3032 3832 3139 706a 622e 3620 2d20 6773  028219pjb.6.-.gs
	0x0080:  6d74 700d 0a                             mtp..

Isso não é o discourse-doctor, mas sim o emails.rake:

Ah, e parece que ele usa localhost. Acredito que deveria referenciar #{ENV["DISCOURSE_SMTP_PORT"]} — ou, por enquanto, DISCOURSE_SMTP_DOMAIN.

@falco temos certeza de que isso sempre foi assim e não é uma regressão recente?

Lembro-me de problemas relacionados ao envio de

ehlo {domínio inválido}

desde muito tempo atrás, então ficaria muito surpreso se tivéssemos estado enviando incorretamente

ehlo localhost

por um longo tempo.

1 curtida

Desculpe, apenas estou relatando o que vejo. Tenho quase certeza de que ‘localhost’ ali também está errado. Meu desenvolvimento web é majoritariamente antigo; faz mais de uma década que não trabalho profissionalmente com essas coisas. Chegar até onde cheguei levou algum tempo; Docker, Ruby e afins são todos bastante novos para mim. tcpdump, por outro lado… conheço bem essa ferramenta. :wink:

Ainda acho que enviar ‘localhost’ para um servidor remoto, em qualquer situação, é um comportamento errado, no entanto.

1 curtida

Por que não, estamos enfrentando exatamente o mesmo problema desde 13 de janeiro. O último e-mail enviado com sucesso foi em 13 de janeiro e também estamos usando smtp-relay.gmail.com — ainda não descobrimos como contornar isso (sem modificar o código-fonte).