يُرسل Discourse SMTP "EHLO localhost" بدلاً من النطاق، مما يكسر Google smtp-relay

بعض السياق هنا: Emails have stopped sending - end of file reached

قبل حوالي أسبوع (13 يناير 2021)، بدأت رسائل البريد الإلكتروني في الفشل عند الإرسال عبر خادم Google’s smtp-relay.gmail.com (وهو استخدام مسموح به ومقصود لمستخدمي Google Apps).

أبلغ Sidekiq عن الفشل مع أخطاء EOFErrors:

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

كما أبلغت سجلات /logs عن المهام الفاشلة أيضًا:

Job exception: end of file reached

متوفر تتبع الأخطاء (Backtrace) في المنشور الآخر.

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

كشفت التحقيقات أن تثبيتات Discourse المحدثة تتصل بخوادم SMTP الوسيطة باستخدام ‘EHLO localhost’ - وقد بدأ Google في رفض هذه الطلبات منذ حوالي أسبوع.

من tcpdump على نسخة إنتاجية:

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..

ومحاكاة الأمر عبر telnet تعطي نفس النتيجة:

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.

ومع ذلك، فإن استخدام ehlo محدد بالنطاق يعمل بشكل صحيح:

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

======

بناءً على السجلات، حددت الملف الذي يجب تعديله لاختبار الإصلاح (داخل صورة Docker):

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

بتغيير

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

إلى

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

تم حل المشكلة (بعد إعادة تشغيل النسخة). الآن يتم إرسال EHLO مع سلسلة النطاق، وتُرسَل رسائل البريد الإلكتروني بشكل صحيح من نسختي.

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

السلوك المطلوب: عند إرسال البريد الإلكتروني، يستخدم التثبيت الافتراضي لـ Discourse اسم النطاق المُهيأ للاتصال الأولي بخادم SMTP. بديلاً عن ذلك، توجد خيار إعداد لتجاوز النطاق المُرسل. إذا كان هذا الخيار موجودًا، فلم أتمكن من العثور عليه عبر البحث.

5 إعجابات

أعتقد أنني رأيت نفس الخطأ من أشخاص آخرين (قد لا يكونون هم أيضًا يستخدمون Google Domains).

الحل طويل الأجل هو إضافة بعض الحيل إلى ملف app.yml الخاص بك للقيام بإعادة الكتابة نيابةً عنك. ولكن نأمل أن يأتي الإصلاح الحقيقي بدلاً من ذلك.

إذا كان هناك طريقة لإصلاح المشكلة عبر ملف app.yml، فأنا مهتم بالتأكيد — ففرض كتابة النطاق يدويًا في الكود لضمان عمل البريد الإلكتروني ليس حلاً “صحيحًا” بوضوح، لكنه يوضح مكان حل المشكلة بشكل دائم أكثر.

هل هناك سبب لعدم استخدام النطاق المconfigured ببساطة في أمر ehlo؟ سيكون ذلك “أصح” من استخدام localhost.

عمل تحقيقي رائع @Syonyk!

هل يمكنك مشاركة إعدادات SMTP الخاصة بملف app.yml؟

إعجابَين (2)

لا يوجد شيء هناك بخلاف الإعدادات المطلوبة العادية.

  DISCOURSE_SMTP_ADDRESS: smtp-relay.gmail.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: [اسم المستخدم للبريد الإلكتروني]
  DISCOURSE_SMTP_PASSWORD: [كلمة المرور]

هل يمكنك محاولة إضافة سطر جديد يحتوي على

DISCOURSE_SMTP_DOMAIN: conversation.sevarg.net

ثم المحاولة مرة أخرى؟

3 إعجابات

لقد أضفت هذا السطر وأعدت بناء التطبيق (هل توجد طريقة لتجاوز هذه الخطوة؟).

الآن أرى “النطاق” في إعدادات البريد الإلكتروني، وقد عادت ملف smtp.rb إلى احتوائه على localhost كافتراضي، ويبدو أن الرسائل تُرسل بشكل صحيح - أستطيع إرسال رسائل اختبار ويتم نقلها بشكل سليم.

لذا فإن هذا يحل المشكلة، حسب ما أستطيع تحديده. هل يمكن إضافة هذه المعلومة إلى الوثائق أو إلى سير العمل الخاص بالإعداد في مكان ما؟ لقد بحثت لفترة طويلة عن مثل هذا الإعداد ولم أجد هذا الخيار - وحتى مع معرفتي بخيار التكوين، هناك ذكر ضئيل جدًا له.

إعجابَين (2)

يمكن إضافته إلى هذا الكتلة في ملف العينة الافتراضي app.yml:

هل تعتبر ذلك مفيدًا؟

إعجاب واحد (1)

طالما أن الأمر موثق في مكان ما، فأنا سعيد.

ومع ذلك، إذا لم يتم تعيينه، هل يمكن للكود استخدام قيمة DISCOURSE_HOSTNAME (وهو ما سيكون صحيحًا في جميع الحالات البسيطة تقريبًا)؟ إرسال ‘localhost’ في EHLO يعتبر عمومًا خطأ (على الأقل عند التحدث إلى خادم ليس على localhost).

أعتقد أن إضافتها إلى standalone.yml و web_only.yml فكرة جيدة.

أتفق على أنه يجب أن يكون الافتراضي على الأرجح DISCOURSE_HOSTNAME. فهذا يبدو بالتأكيد أفضل من localhost. هل تغير هذا مؤخرًا؟

المسألة هنا هي أن هذا يعمل لجميع عشرات الآلاف من نسخ Discourse التي لا تعتمد حاليًا على Google للبريد الإلكتروني. تغيير افتراضي مثل هذا قد يكسر عمل الجميع بينما يصلح الأمر لمستخدمي Google Apps، وهم عدد قليل نسبيًا.

سأضيفه إلى العينة، ويجب أن يكون لدينا أيضًا موضوع howto بعنوان “استخدام Google Apps للبريد الصادر” يوثق ذلك. هل يمكن لأي شخص تولي هذا؟

4 إعجابات

حسنًا، أوافق على أن الحذر هو الأرجح، لكنني أظن أن أي خادم بريد يقبل localhost سيقبل أيضًا DISCOURSE_HOSTNAME، لكن لا أملك أي بيانات تدعم ذلك. :wink: وجوده في القوالب القياسية على الأرجح كافٍ ومقبول.

إعجاب واحد (1)

نعم، لكن إرسال ‘localhost’ (إلى مضيف بعيد) خاطئ أيضًا وفقًا لـ RFC.

التشديد مني.

تذكر وثائق RFC الأقدم أن الخادم لا ينبغي أن يرفض العملاء بناءً على سلسلة EHLO، وهو ما يبدو أن Google تفعله، لكنني لا أرى هذه الصياغة في 5321.

أتوقع أن أي خادم بريد بعيد يتسامح مع localhost سيتسامح (ويفضل) اسم نطاق كامل (FQDN) كما تتطلب وثائق RFC. أنا أفهم الرغبة في عدم تعطيل الأشياء، ولكن كما أفهم وثائق RFC ذات الصلة، فإن Discourse خاطئة افتراضيًا ببساطة، وأن عملها هو نتيجة لكون خوادم SMTP البعيدة متساهلة بشكل مفرط.

إعجاب واحد (1)

يسعدني دمج طلب السحب (PR) إلى ./discourse-setup ليتم تعيينه افتراضيًا بنفس قيمة DISCOURSE_HOSTNAME، بشرط إثبات أنه غير ضار مع خدمات SMTP الأكثر شيوعًا التي نوصي الناس باستخدامها.

إعجابَين (2)

لا يمكنني اختبار تسليم البريد بالكامل من البداية إلى النهاية لأنني لا أملك حسابات، لكن:

Mailgun

% nc smtp.mailgun.com 587
220 Mailgun Influx ready
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 ready at 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

… لا يستجيب على الإطلاق لرسالة helo أو ehlo من أي نوع، حقًا. o.O سواء من localhost أو أي نطاقات حقيقية.

أعتقد أن ضبط ذلك أثناء الإعداد هو الحل الصحيح، لأنه على الأقل يكون متاحًا للناس ليعرفوا به ويقومون بتعديله إذا لزم الأمر.

6 إعجابات

هناك مشكلة أخرى ذات صلة: لا يبدو أن discourse-doctor يضبط النطاق (domain) بشكل صحيح، وسيستمر في الفشل حتى عندما تكون التثبيت الفعلي قادرًا على إرسال البريد.

مع الإعدادات العاملة، لا يزال discourse-doctor يُبلغ عن فشل نهاية الملف.

======================================== ERROR ========================================
                                    UNEXPECTED ERROR

end of file reached

====================================== SOLUTION =======================================
This is not a common error. No recommended solution exists!

Please report the exact error message above to https://meta.discourse.org/
(And a solution, if you find one!)
=======================================================================================

لا يوجد أي ذكر لـ SMTP_DOMAIN في سكريبت الاختبار.

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

وتشير tcpdump إلى أن تشغيل discourse-doctor لا يزال يرسل ‘localhost’ في EHLO. ويجب إصلاح هذا أيضًا.

	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..

هذا ليس discourse-doctor بل emails.rake:

آه، ويبدو أنه يستخدم localhost. أظن أنه يجب أن يشير إلى #{ENV["DISCOURSE_SMTP_PORT"]}–أو DISCOURSE_SMTP_DOMAIN مؤقتًا.

@falco هل نحن متأكدون من أن هذا كان دائمًا هو الحال وليس انتكاسة حديثة؟

أتذكر مشاكل تتعلق بالإرسال

ehlo {invalid domain}

من وقت بعيد جدًا، لذا سأكون مندهشًا جدًا لو كنا نرسل بشكل خاطئ

ehlo localhost

لفترة طويلة؟

إعجاب واحد (1)

آسف، أنا فقط أبلغ عما أراه. أنا متأكد تقريبًا من أن ‘localhost’ هناك خاطئ أيضًا. تطوير الويب الخاص بي قديم إلى حد كبير، لقد مر أكثر من عقد منذ أن عملت مع هذه الأدوات بشكل احترافي. الوصول إلى حيث وصلت استغرق بعض الوقت، فـ Docker و Ruby وما شابهها كلها جديدة نسبيًا بالنسبة لي. أما tcpdump، فمن ناحية أخرى… أنا أعرف هذه الأداة. :wink:

لا أزال أعتقد أن إرسال ‘localhost’ إلى خادم بعيد، في أي حالة، هو سلوك خاطئ.

إعجاب واحد (1)

على الرغم من ذلك، نواجه نفس المشكلة تمامًا اعتبارًا من 13 يناير. آخر بريد إلكتروني تم إرساله بنجاح كان في 13 يناير، ونحن نستخدم أيضًا smtp-relay.gmail.com — لم نتمكن بعد من إيجاد حل لهذه المشكلة (دون تعديل كود المصدر)