Refatorando os e-mails: saída da tarefa rake de teste

Revisei recentemente os emails:task e o código relacionado com o objetivo de testar todos os caminhos de falha e retocar o texto de erro.

Também descobri que os efeitos de definir DISCOURSE_SMTP_ENABLE_STARTTLS=false são (na maioria) nulos. Definir isso não desativou realmente o STARTTLS e, de fato, ele pode coexistir perfeitamente com TLS no momento da conexão (DISCOURSE_SMTP_FORCE_TLS=true).

Então eu:

Antes de mesclar isso, acho que um aviso de administrador no painel para quando DISCOURSE_SMTP_ENABLE_STARTTLS=false for definido é adequado. Imagino que haja pelo menos um auto-hospedeiro que definiu isso, mas não precisa e está realmente confiando no STARTTLS.

4 curtidas

Isso parece um bom trabalho! Uma coisa que eu (acho que) notei é que a tarefa rake não usa o mesmo código que o envio real (como da página de teste de e-mail /admin/email). Tenho quase certeza de que tive um caso em que funcionou no UX, mas não na tarefa rake (ou talvez tenha sido o contrário?)

Enquanto isso está fresco em sua mente, se você pudesse ver que, pelo menos quando realmente envia, o faz usando o mesmo código que o Discourse, seria ótimo.

2 curtidas

Também estamos trabalhando nisso, e em melhorar o log quando um trabalho de e-mail em fila falha. :+1:

4 curtidas

Há algo que eu precise fazer no fórum que você hospeda?

4 curtidas

Não, este alerta não deve aparecer em nossa hospedagem. Vamos corrigir, obrigado pelo relatório como sempre.

5 curtidas

@supermathie vale a pena enviar uma mensagem privada para todos os administradores em todos os sites que têm essa variável definida? Nosso sistema atual de verificação de problemas fará isso, e não tenho certeza se é garantido aqui, dado que, na maioria das vezes, isso tem um efeito nil. Idealmente, eu gostaria de mostrar isso apenas no painel sem notificar os usuários administradores, não tenho certeza se nossa estrutura atual de verificação de problemas suporta esse caso de uso.

Eu acho que sim - acho extremamente provável que, dada a confusão de muitos administradores com a configuração de e-mail, alguém tenha essa variável definida quando na verdade depende do starttls.

Ninguém deveria tê-la definida, provavelmente.

Prefiro ter um aviso espúrio do que quebrar silenciosamente a configuração de e-mail de alguém.

A alternativa é remover a verificação e neutralizar a variável para que ela não faça nada.

1 curtida

Seria bom se o aviso não aparecesse quando o servidor SMTP de saída for localhost (ou seja, corresponder ao nome de domínio do Discourse), pois o TLS entre o contêiner Docker e o host não é necessário, já que eles estão na mesma máquina.

1 curtida

Neste caso, você pode remover a variável do ambiente.

Ele só usará STARTTLS se for oferecido.

1 curtida

Eu tive o caso na versão atual em que a linha comentada (o padrão é true) juntamente com force_tls bloqueava o envio de e-mail. Eu a comentei e a defini como false. O envio de e-mail agora funciona, mas recebo esta mensagem no backend

Depende um pouco da porta em que seu provedor de e-mail está rodando… ambas as configurações não funcionam e definitivamente têm impacto.

Notei isso também. Após atualizar o Discourse em 28 de novembro, comecei a ver falhas na entrega de e-mails com a mensagem de erro Job exception: :enable_starttls and :tls are mutually exclusive. Set :tls if you're on an SMTPS connection. Set :enable_starttls if you're on an SMTP connection and using STARTTLS for secure TLS upgrade.

Backtrace
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/network/delivery_methods/smtp.rb:159:in `build_smtp_session'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/network/delivery_methods/smtp.rb:154:in `start_smtp_session'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/network/delivery_methods/smtp.rb:108:in `deliver!'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/message.rb:269:in `deliver!'
/usr/local/lib/ruby/3.3.0/delegate.rb:87:in `method_missing'
/var/www/discourse/lib/email/sender.rb:296:in `send'
/var/www/discourse/app/jobs/regular/user_email.rb:80:in `send_user_email'
/var/www/discourse/app/jobs/regular/user_email.rb:40:in `execute'
/var/www/discourse/app/jobs/base.rb:318:in `block (2 levels) in perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-7.0.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-7.0.0/lib/rails_multisite/connection_management.rb:17:in `with_connection'
/var/www/discourse/app/jobs/base.rb:305:in `block in perform'
/var/www/discourse/app/jobs/base.rb:301:in `each'
/var/www/discourse/app/jobs/base.rb:301:in `perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:220:in `execute_job'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:185:in `block (4 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:180:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/suppress_user_email_errors.rb:6:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/discourse_event.rb:6:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/pausable.rb:131:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job/interrupt_handler.rb:9:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:26:in `track'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:134:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:173:in `invoke'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:184:in `block (3 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:145:in `block (6 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:118:in `local'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:144:in `block (5 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/config.rb:39:in `block in <class:Config>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:139:in `block (4 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:281:in `stats'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:134:in `block (3 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:15:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:133:in `block (2 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:85:in `global'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:132:in `block in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:40:in `prepare'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:131:in `dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:183:in `block (2 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `block in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:86:in `process_one'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:76:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:10:in `watchdog'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:19:in `block in safe_thread'

Assim como Johann Christoph, eu anteriormente tinha DISCOURSE_SMTP_ENABLE_START_TLS comentado e DISCOURSE_SMTP_FORCE_TLS definido como true para TLS implícito e funcionava perfeitamente. Mas agora preciso definir explicitamente DISCOURSE_SMTP_ENABLE_START_TLS como false para que a entrega de e-mail funcione, o que, é claro, aciona a mensagem mencionada no painel de administração.

1 curtida

Será o force_tls que está impedindo o envio de e-mail - ele força o TLS no momento da conexão e provavelmente só é adequado para a porta 465.

Um unicórnio!

Quais parâmetros SMTP você está usando (exceto nome de usuário/senha)?

Porta 465 com ENABLE_START_TLS=false, FORCE_TLS=true e OPEN_TIMEOUT=10. Este último é devido a estranhos erros de tempo limite que comecei a ver após uma atualização do sistema há cerca de três anos, mas eu comeria meu chapéu se isso estivesse relacionado ao problema descrito acima.

Enquanto isso, dei uma olhada no histórico de commits e notei que a gem mail foi atualizada para a versão 2.9.0 apenas três dias antes da atualização que introduziu o problema do meu lado (#36254). Há uma entrada nas notas de lançamento disso que diz “SMTP: refactor and accept starttls :always and :auto by @eval in #1536”. Eu não sei muito sobre Ruby, mas essa mudança no PR referenciado parece meio suspeita para mim:

Dado que DISCOURSE_SMTP_ENABLE_START_TLS é documentado como sendo true por padrão (ou seja, se comentado), é possível que esta seja a raiz do problema?

Como escrevi - eu defini o force_TLS:

DISCOURSE_SMTP_FORCE_TLS = true

e ENABLE_START_TLS explicitamente como false… então o envio de e-mail funciona - desde que o servidor de e-mail esteja rodando na porta 465.

Para servidores de e-mail que rodam na porta 567 - deve ser exatamente o oposto.

Desde então, tenho um aviso no painel de controle no backend… mas o envio de e-mail funciona sem problemas.

Para poder definir DISCOURSE_SMTP_ENABLE_START_TLS como false, você precisa descomentá-lo — pois, caso contrário, ele estará definido como true por padrão. Foi exatamente isso que causou o meu problema. No entanto, isso só ocorreu com a compilação mais recente (2025.12.0-latest).

Uau, esta foi uma mudança ruim.

enable_starttls irá EXIGIR o uso de starttls, mas enable_starttls_auto é oportunista - ele só negociará tls se for oferecido.

E se o servidor de e-mail foi conectado via TLS inicial, ele não oferecerá starttls:

○ → openssl s_client -connect localhost:5587 -starttls smtp
250 CHUNKING
EHLO localhost
250-testmailrelay
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 CHUNKING

Por que diabos eles fariam isso? :facepalm:
A dificuldade aqui é que nunca deveríamos ter oferecido essa configuração em primeiro lugar, deveria ter sido algo como:

DISCOURSE_SMTP_TLS_MODE = starttls_auto # [ none | starttls | starttls_auto (padrão) | tls ]

No interesse de tentar fazer com que as pessoas façam menos configuração, e não mais, acho que esta abordagem é a melhor:

1 curtida

Isso agora foi mesclado, não é mais necessário desabilitar explicitamente o STARTTLS, o Discourse cuidará disso se o TLS estiver ativado.

Sim, funciona corretamente novamente. Obrigado!