Refonte des e-mails : sortie de la tâche rake de test

J’ai récemment examiné les e-mails : tâche et le code associé dans le but de tester chacun des chemins d’échec et de retoucher le texte d’erreur.

J’ai également découvert que les effets de la définition de DISCOURSE_SMTP_ENABLE_STARTTLS=false sont (principalement) nuls. La définition de cette option n’a pas réellement désactivé STARTTLS et, en fait, elle peut coexister sans problème avec TLS au moment de la connexion (DISCOURSE_SMTP_FORCE_TLS=true).

J’ai donc :

Avant de fusionner cela, je pense qu’un avertissement d’administrateur dans le tableau de bord lorsqu’une valeur est définie pour DISCOURSE_SMTP_ENABLE_STARTTLS=false est approprié. J’imagine qu’il y a au moins un auto-hébergeur qui l’a défini mais n’en a pas besoin et s’appuie en fait sur STARTTLS.

4 « J'aime »

Cela semble être du bon travail ! Une chose que j’ai (je pense) remarquée, c’est que la tâche rake n’utilise pas réellement le même code que l’envoi réel (comme depuis la page de test d’e-mail /admin/email). Je suis à peu près sûr d’avoir eu un cas où cela a fonctionné dans l’UX mais pas dans la tâche rake (ou peut-être était-ce l’inverse ?)

Tant que c’est frais dans votre esprit, si vous pouviez vérifier qu’au moins lorsqu’il envoie réellement, il le fait en utilisant le même code que Discourse, ce serait formidable.

2 « J'aime »

Nous travaillons également sur cela, et sur l’amélioration de la journalisation en cas d’échec d’un travail d’e-mail mis en file d’attente. :+1:

4 « J'aime »

Y a-t-il quelque chose que je doive faire sur le forum que vous hébergez ?

4 « J'aime »

Non, cette alerte ne devrait pas s’afficher sur notre hébergement. Nous allons corriger cela, merci pour le rapport comme toujours.

5 « J'aime »

@supermathie est-ce que cela vaut la peine d’envoyer un message privé à chaque administrateur sur chaque site où cette variable est définie ? Notre système actuel de vérification des problèmes le fera, et je ne suis pas sûr que ce soit justifié ici, étant donné que, dans la plupart des cas, cela a un effet nil. Idéalement, je voudrais seulement montrer cela sur le tableau de bord sans notifier les utilisateurs administrateurs, je ne suis pas sûr que notre structure actuelle de vérification des problèmes prenne en charge ce cas d’utilisation.

Je pense que oui - je trouve extrêmement probable que, étant donné la confusion de nombreux administrateurs concernant la configuration de la messagerie, quelqu’un ait cette variable définie alors qu’il dépend en fait de starttls.

Personne ne devrait l’avoir définie, probablement.

Je préférerais un avertissement superflu plutôt que de casser silencieusement la configuration de messagerie de quelqu’un.

L’alternative est de supprimer la vérification et de neutraliser la variable pour qu’elle ne fasse rien du tout.

1 « J'aime »

Il serait agréable que l’avertissement n’apparaisse pas lorsque le serveur SMTP sortant est localhost (c’est-à-dire qu’il correspond au nom de domaine de Discourse), car le TLS entre le conteneur Docker et l’hôte n’est pas nécessaire puisqu’il s’agit de la même machine.

1 « J'aime »

Dans ce cas, vous pouvez supprimer la variable de l’environnement.

Il n’utilisera STARTTLS que s’il est proposé.

1 « J'aime »

J’ai rencontré dans la version actuelle le cas où la ligne commentée (la valeur par défaut est true) bloquait l’envoi d’e-mails avec force_tls. Je l’ai donc décommentée et mise à false. L’envoi d’e-mails fonctionne maintenant, mais j’ai ce message dans le backend…

Cela dépend un peu du port sur lequel votre fournisseur de messagerie est configuré… les deux ne fonctionnent pas et ont certainement des conséquences.

J’ai également remarqué cela. Après avoir mis à jour Discourse le 28 novembre, j’ai commencé à voir des échecs de livraison de courrier avec le message d’erreur 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.

Trace de la pile
/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'

Tout comme Johann Christoph, j’avais auparavant DISCOURSE_SMTP_ENABLE_START_TLS commenté et DISCOURSE_SMTP_FORCE_TLS défini sur true pour le TLS implicite, et cela fonctionnait à merveille. Mais maintenant, je dois définir explicitement DISCOURSE_SMTP_ENABLE_START_TLS sur false pour que la livraison des e-mails fonctionne, ce qui déclenche bien sûr le message susmentionné dans le tableau de bord d’administration.

1 « J'aime »

C’est force_tls qui empêche l’envoi d’e-mails - cela force TLS au moment de la connexion et n’est probablement adapté qu’au port 465.

Une licorne !

Quels paramètres SMTP utilisez-vous (moins le nom d’utilisateur/mot de passe) ?

Port 465 avec ENABLE_START_TLS=false, FORCE_TLS=true et OPEN_TIMEOUT=10. Ce dernier est dû à des erreurs d’expiration de délai étranges que j’ai commencé à voir après une mise à jour système il y a environ trois ans, mais je me coupe la langue si c’est lié au problème décrit ci-dessus.

J’ai examiné l’historique des commits entre-temps et j’ai remarqué que le gem mail a été mis à jour vers la version 2.9.0 juste trois jours avant la mise à jour qui a introduit le problème de mon côté (#36254). Il y a une entrée dans les notes de version qui dit « SMTP: refactor and accept starttls :always and :auto by @eval in #1536 ». Je ne connais pas grand-chose à Ruby, mais ce changement dans la PR référencée me semble un peu suspect :

Étant donné que DISCOURSE_SMTP_ENABLE_START_TLS est documenté comme étant vrai par défaut (c’est-à-dire s’il est commenté), est-il possible que ce soit la racine du problème ?

Comme je l’ai écrit, j’ai défini force_TLS :

DISCOURSE_SMTP_FORCE_TLS = true

et ENABLE_START_TLS explicitement à false… ensuite l’envoi d’e-mails fonctionne - si le serveur de messagerie fonctionne sur 465.

Pour les serveurs de messagerie qui fonctionnent sur 567 - il faut que ce soit exactement l’inverse.

Depuis, j’ai bien un avertissement sur le tableau de bord dans le backend… mais l’envoi d’e-mails fonctionne sans problème.

Pour pouvoir définir DISCOURSE_SMTP_ENABLE_START_TLS sur false, il faut le commenter — car il est défini sur true par défaut. C’est exactement ce qui avait causé mon problème. Cependant, cela n’est apparu qu’avec la dernière version (2025.12.0-latest).

Wow, c’était un mauvais changement.

enable_starttls EXIGERA l’utilisation de starttls, mais enable_starttls_auto est opportuniste - il ne négociera TLS que s’il est proposé.

Et si le serveur de messagerie était connecté via TLS initial, il n’offrira pas 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

Pourquoi diable auraient-ils fait ça ? :facepalm:
La difficulté ici est que nous n’aurions jamais dû proposer cette configuration en premier lieu, cela aurait dû être quelque chose comme :

DISCOURSE_SMTP_TLS_MODE = starttls_auto # [ none | starttls | starttls_auto (défaut) | tls ]

Dans l’intérêt d’essayer de faire faire moins de configuration aux gens, et non plus, je pense que cette approche est la meilleure :

1 « J'aime »

Ceci est maintenant fusionné, on n’a plus besoin de désactiver explicitement STARTTLS, Discourse s’en chargera si TLS est activé.

Oui, cela fonctionne à nouveau correctement. Merci !