الأدلة التقنية

بناء خادم بريد بيدك مع Postfix وDovecot على Ubuntu

نبني خادم بريد كاملاً من حزم Ubuntu دون Docker، ونضبط SPF وDKIM وDMARC بأنفسنا ونجرب انتحال النطاق لنرى الرفض بأعيننا، فتعرف دور كل مكون وأين تبحث عندما تتوقف رسالة.

بناء خادم بريد بيدك مع Postfix وDovecot على Ubuntu

في هذا الدليل سوف نبني خادم بريد Mail Server كاملاً بأيدينا على Ubuntu 24.04 من الحزم الرسمية Packages للتوزيعة، دون Docker ودون حزمة جاهزة All-in-one كما في دليل تثبيت mailcow، حيث يستقبل Postfix البريد ويرسله، ويخزنه Dovecot ويقدمه للمستخدمين، وتضيف أدوات صغيرة فوقهما فحص SPF وتوقيع DKIM وسياسة DMARC. والسبب أن الحزمة الجاهزة مريحة جداً حتى يحدث أول خطأ، فعندها تكتشف أنك لا تعرف أي مكون Component رفض الرسالة ولماذا، أما هنا فتعرف في النهاية دور كل سطر في الإعداد وأين تبحث عندما تتوقف رسالة.

وهذا الطريق يناسب من يريد أن يفهم البريد الإلكتروني Email من الداخل، ومن يدير نطاقاً واحداً أو بضعة نطاقات بعدد صغير من الصناديق على سيرفر صغير، ولكنه لا يقدم لوحة إدارة ولا بريداً عبر الويب Webmail، فإذا احتاج فريقك إلى ذلك من اليوم الأول فابدأ بـ mailcow. أما قرار استضافة البريد بنفسك أصلاً، والمقارنة بين الخيارات، وخطة نقل المؤسسة، فتجدها في دليل استضافة البريد الإلكتروني للمؤسسات.

في هذا المقال سوف نتناول:

  • كيف تمر الرسالة بين السيرفرات، وما دور كل مكون، ولماذا لا يكفي تثبيت Postfix وحده.
  • متطلبات السيرفر والمنفذ 25، وسجلات DNS كلها: A وAAAA وMX وPTR وSPF وDKIM وDMARC.
  • إعداد Dovecot لصناديق بريد افتراضية Virtual Mailboxes دون حسابات في النظام، وإعداد Postfix للاستقبال على المنفذ 25 والإرسال على المنفذ 587.
  • فحص SPF للرسائل الواردة، وتوقيع DKIM والتحقق منه بـ OpenDKIM، وتطبيق سياسة DMARC بـ OpenDMARC.
  • الاختبار الكامل: الإرسال والاستقبال والقراءة، ثم محاولة انتحال نطاقك ورؤية الرفض بعينك.
  • الحماية من الرسائل المزعجة وتخمين كلمات المرور، والنسخ الاحتياطي، والتحديث، وقراءة سجلات الأخطاء.

كيف تصل الرسالة من سيرفر إلى آخر؟

عندما يرسل شخص من Gmail رسالة إلى [email protected]، يسأل سيرفر Gmail نظام DNS عن سجل MX لنطاقك فيعرف أن بريده يستلمه mail.example.com، ثم يتصل به على المنفذ Port 25 ويسلمه الرسالة عبر بروتوكول SMTP. والبرنامج الذي يستقبل الرسائل وينقلها بين السيرفرات يسمى وكيل نقل البريد Mail Transfer Agent واختصاراً MTA، وهو هنا Postfix. بعد ذلك يسلم Postfix الرسالة إلى Dovecot عبر بروتوكول LMTP فيكتبها في صندوق المستخدم بصيغة Maildir، أي ملف لكل رسالة داخل مجلد، وعندما يفتح المستخدم برنامج البريد Mail Client في هاتفه يتصل بـ Dovecot عبر IMAP على المنفذ 993 ويقرأ الرسالة.

والاتجاه المعاكس يختلف قليلاً، فبرنامج المستخدم لا يرسل على المنفذ 25، وإنما على منفذ الإرسال Submission وهو 587، ويثبت هويته بكلمة المرور، فيسأل Postfix خدمة Dovecot هل كلمة المرور صحيحة (وهذا ما يسمى SASL)، ثم يمرر الرسالة إلى OpenDKIM فيوقعها بمفتاح نطاقك، وبعدها يتصل Postfix بسيرفر المستلم على المنفذ 25 ويسلمها. وفي الطريق يسأل كل طرف نظام DNS عدة أسئلة، والصورة التالية تبين ذلك كله:

مخطط يبين مرور البريد الوارد من سيرفر المرسل على المنفذ 25 عبر postscreen وفحص SPF في Postfix ثم OpenDKIM وOpenDMARC ثم LMTP إلى Dovecot وMaildir ثم القراءة عبر IMAP 993، ومرور البريد الصادر من برنامج المستخدم على المنفذ 587 مع كلمة المرور ثم توقيع OpenDKIM ثم سيرفر المستلم، مع سجلات DNS التي يسأل عنها كل طرف
البريد الوارد بالأخضر والصادر بالبرتقالي، وبالأزرق سجلات DNS التي يستعلم عنها كل طرف

في الصورة أعلاه لاحظ التالي:

  • Postfix هو الوحيد الذي يتحدث SMTP مع العالم، على المنفذ 25 للسيرفرات الأخرى وعلى المنفذ 587 لمستخدميك، ويقف أمامه postscreen الذي يصد البرامج الآلية Bots قبل أن تكلف Postfix شيئاً.
  • Dovecot يقوم بثلاث مهام: يستلم الرسائل من Postfix عبر LMTP ويكتبها على القرص، ويخدم برامج البريد عبر IMAP، ويتحقق من كلمات المرور لصالح Postfix، وبالتالي توجد قائمة المستخدمين في مكان واحد فقط.
  • Milter اختصار Mail Filter، وهو برنامج مستقل يمرر له Postfix الرسالة أثناء استقبالها فيعدلها أو يرفضها، وهنا عندنا اثنان: OpenDKIM للتوقيع والتحقق، وOpenDMARC لتطبيق سياسة DMARC.
  • فحص SPF يقوم به برنامج صغير ثالث هو policyd-spf، ويعمل بطريقة أخرى تسمى خادم السياسات Policy Server، حيث يسأله Postfix قبل قبول المستلم: هل هذا العنوان مسموح له بالإرسال باسم هذا النطاق؟

لماذا لا يكفي تثبيت Postfix وحده؟

أسهل طريقة يجربها الجميع أن تثبت الحزمة postfix وتختار Internet Site في نافذة الإعداد، فيعمل السيرفر خلال دقائق ويرسل فعلاً، ثم ترسل أول رسالة إلى حسابك في Gmail فتجدها في الرسائل المزعجة Spam، أو يرفضها Gmail برسالة خطأ تذكر عنوان IP الخاص بسيرفرك. والسبب أن السيرفر المستقبل لا يعرف عنك شيئاً، فلا توجد سجلات SPF وDKIM وDMARC تثبت أن سيرفرك يحق له الإرسال باسم نطاقك، وغالباً لا يوجد سجل PTR يعيد اسم سيرفرك عند الاستعلام العكسي، ومنذ فبراير 2024 تشترط Gmail هذه السجلات على من يرسل إليها، وقواعد Yahoo مشابهة.

وقد يتساءل البعض: إذا أضفت سجل SPF فقط، ألا يكفي ذلك؟ والإجابة لا، فالمنتحل يستطيع أن يجتاز SPF لنطاقه هو ويضع From: [email protected] في رسالته، ولذلك نحتاج DKIM وDMARC معه، وقد شرحنا ما يفعله كل واحد من الثلاثة ولماذا لا يكفي أحدها وحده في شرح SPF وDKIM وDMARC للمبتدئين، وفي هذا الدليل سوف نجرب هذا الانتحال على سيرفرنا ونرى من يوقفه.

والمشكلة الثانية في الإعداد البسيط هي الأمان، فإعداد المستخدمين الافتراضي في Postfix وDovecot يعتمد على حسابات النظام System Users، أي أن كل صندوق بريد هو مستخدم Linux يستطيع نظرياً الدخول عبر SSH، ومنفذ الإرسال غير مفعل، وكلمات المرور قد تمر دون تشفير إذا أخطأت في إعداد واحد. لذلك سوف نبني الإعداد الصحيح خطوة خطوة: صناديق افتراضية يملكها مستخدم نظام واحد لا يستطيع الدخول، وكلمات مرور لا تقبل إلا عبر TLS، وإرسال لا يسمح به إلا بعد المصادقة Authentication، وتوقيع وفحص لكل رسالة.

ما تحتاجه قبل أن تبدأ

السيرفر والمنفذ 25

  • سيرفر افتراضي VPS بعنوان IPv4 ثابت Static IP: يكفي معالج واحد وذاكرة 1 GB لعدد صغير من الصناديق، ثم احسب مساحة القرص Disk من عدد الصناديق وحجم البريد المتوقع. وابدأ بتأمين السيرفر كما في دليل تأمين خادم VPS من أول دخول قبل أي خطوة هنا.
  • المنفذ 25 الصادر مفتوح وسجل PTR قابل للتعديل: كثير من المزودين يحجبون المنفذ 25 افتراضياً، فمثلاً تحجب Hetzner المنفذين 25 و465 على سيرفرات Cloud ولا تفتحهما إلا بطلب بعد شهر من التسجيل ودفع أول فاتورة، وتحجب DigitalOcean منافذ SMTP على Droplets، وتقيد AWS المنفذ 25 على EC2 حتى تطلب رفع القيد. لذلك اسأل المزود قبل الشراء، وراجع دليل اختيار خادم VPS.
  • ليس من اتصال المنزل، وبعنوان IP نظيف: عناوين IP المنزلية مدرجة عادة في قوائم الحظر Blocklists كما شرحنا في دليل تشغيل خادمك من إنترنت المنزل، فافحص عنوان سيرفرك في Spamhaus قبل أن تبدأ، وبقية ما تجهزه قبل أي خادم بريد في دليل البريد للمؤسسات.

اسم السيرفر والمنافذ

هنا اسمان يخلط بينهما كثيرون: اسم السيرفر مثل mail.example.com، وبه يعرف السيرفر نفسه في التحية HELO وله تصدر الشهادة Certificate وإليه يشير سجل PTR، ونطاق البريد مثل example.com وهو ما يظهر بعد @ في العناوين. ابدأ بضبط اسم السيرفر:

sudo hostnamectl set-hostname mail.example.com
hostname -f

ثم افتح المنافذ التي سوف نستخدمها في جدار الحماية Firewall:

المنفذالخدمةالاستخدام
25/tcpSMTPاستقبال البريد من السيرفرات الأخرى (وخروجه إليها)
587/tcpSubmissionإرسال البريد من برامج المستخدمين بعد STARTTLS وكلمة المرور
993/tcpIMAPSقراءة البريد عبر TLS
80/tcpHTTPإصدار شهادة Let's Encrypt وتجديدها عبر التحقق من ملكية النطاق عبر HTTP (HTTP Challenge)
sudo ufw allow 25,587,993,80/tcp
sudo ufw status

ولاحظ أننا لا نفتح المنفذ 143 (IMAP دون تشفير) ولا المنفذين 110 و995 (POP3)، والسبب أن كل خدمة لا تحتاجها هي باب إضافي يطرقه المخترقون. وكذلك يوصي RFC 8314 بالمنفذ 465 مع TLS مباشر Implicit TLS للإرسال، ونكتفي هنا بالمنفذ 587 لأن كل برامج البريد تدعمه، وتستطيع إضافة 465 لاحقاً بخدمة submissions في master.cf.

سجلات DNS

سجلات DNS هي هوية سيرفرك أمام السيرفرات الأخرى، وقد شرحنا أنواعها بالتفصيل في شرح DNS وسجلاته للمبتدئين، وعرفناها باختصار في دليل مصطلحات الاستضافة الذاتية. أضف السجلات التالية في لوحة DNS الخاصة بنطاقك، وضع عنوان سيرفرك مكان 203.0.113.10 و2001:db8::10، أما سجل DKIM فسوف نضيفه بعد إنشاء المفتاح:

الاسمالنوعالقيمةالغرض
mail.example.comA203.0.113.10عنوان السيرفر
mail.example.comAAAA2001:db8::10فقط إذا كان IPv6 مفعلاً لديك، ومعه PTR خاص به
example.comMX10 mail.example.comأين تسلم السيرفرات الأخرى بريد النطاق، والقيمة اسم وليست عنوان IP
example.comTXTv=spf1 mx -allSPF: السيرفرات المذكورة في MX وحدها ترسل باسم النطاق
mail.example.comTXTv=spf1 a -allSPF لاسم السيرفر نفسه الذي يظهر في HELO
_dmarc.example.comTXTv=DMARC1; p=none; rua=mailto:[email protected]سياسة DMARC وعنوان التقارير
203.0.113.10PTRmail.example.comالاستعلام العكسي، ويضبط من لوحة مزود السيرفر وليس من لوحة DNS

في الجدول أعلاه لاحظ التالي:

  • PTR أو rDNS: تجده في لوحة مزود ال VPS باسم Reverse DNS، ويجب أن يعيد الاسم mail.example.com نفسه الذي يشير سجله A إلى العنوان نفسه، وهذا ما تسميه Gmail سجلات DNS في الاتجاهين Forward-confirmed reverse DNS، وبدونه ترفض كثير من السيرفرات الاتصال أصلاً.
  • SPF: سجل واحد فقط لكل نطاق كما يشترط RFC 7208، فإذا كنت ترسل أيضاً عبر خدمة أخرى مثل نشرة بريدية أو نظام تذاكر فأضفها إلى السجل نفسه، والسبب أن وجود سجلين يجعل النتيجة PermError فيفشل الفحص، ومعنى كل جزء في السجل مثل -all و~all تجده في شرح SPF وDKIM وDMARC.
  • SPF لاسم السيرفر: بعض السيرفرات تفحص الاسم الذي يقدمه سيرفرك في HELO، وخصوصاً في رسائل الارتداد Bounces التي يكون عنوان مغلفها فارغاً، لذلك أضف له سجلاً خاصاً كما يوصي RFC 7208.
  • DMARC: ابدأ بالقيمة p=none واقرأ التقارير التي تصل إلى [email protected]، ثم انتقل إلى p=quarantine ثم p=reject على مراحل، ولا تضع الوسم pct في السجلات الجديدة، فقد حذفه المعيار الحالي RFC 9989.

بعد نشر السجلات تحقق منها من أي جهاز:

dig +short A mail.example.com
dig +short MX example.com
dig +short TXT example.com
dig +short TXT _dmarc.example.com
dig +short -x 203.0.113.10

تثبيت الحزم

كل ما نحتاجه موجود في مستودعات Ubuntu 24.04 الرسمية Repositories. وعند تثبيت postfix سوف تظهر نافذة تسأل عن نوع الإعداد، فنجيب عنها مسبقاً بالأمر debconf-set-selections حتى لا تتوقف عندها، والسبب أننا سوف نستبدل ملف الإعداد كاملاً بعد قليل:

echo "postfix postfix/main_mailer_type select Internet Site" | sudo debconf-set-selections
echo "postfix postfix/mailname string mail.example.com" | sudo debconf-set-selections
sudo apt update
sudo apt install postfix dovecot-imapd dovecot-lmtpd opendkim opendkim-tools \
  postfix-policyd-spf-python opendmarc fail2ban certbot swaks

والإصدارات التي تثبتها هذه الأوامر وقت كتابة الدليل هي Postfix 3.8.6 وDovecot 2.3.21 وOpenDKIM 2.11.0~beta2 وOpenDMARC 1.4.2 وpolicyd-spf 3.0.4، ولاحظ أن الحزم postfix وdovecot-* في المستودع main فتصلها التحديثات الأمنية من Ubuntu خمس سنوات، أما opendkim وopendmarc وpostfix-policyd-spf-python وfail2ban ففي المستودع universe الذي يصونه المجتمع، وتغطيه التحديثات الأمنية الموسعة ESM عبر Ubuntu Pro المجاني للاستخدام الشخصي حتى خمسة أجهزة.

شهادة TLS من Let's Encrypt

يحتاج Postfix وDovecot إلى شهادة صالحة للاسم mail.example.com، وأبسط طريقة على سيرفر لا يوجد فيه خادم ويب Web Server هي أن يشغل certbot خادم ويب مؤقتاً على المنفذ 80 للتحقق ثم يغلقه:

sudo certbot certonly --standalone -d mail.example.com \
  --agree-tos -m [email protected] --no-eff-email \
  --deploy-hook "systemctl reload postfix dovecot"

تحفظ الشهادة في /etc/letsencrypt/live/mail.example.com/: الملف fullchain.pem وفيه الشهادة مع السلسلة Chain، والملف privkey.pem وفيه المفتاح الخاص Private Key. والخيار --deploy-hook يحفظه certbot مع إعداد الشهادة، فبعد كل تجديد تلقائي يعيد تحميل Postfix وDovecot حتى يقرآ الشهادة الجديدة، والسبب أن الخدمتين تقرآن الملف عند التشغيل فقط، وبدون هذا الخيار سوف تستمران في تقديم الشهادة القديمة حتى تنتهي صلاحيتها. وتستطيع تجربة التجديد دون إصدار حقيقي:

sudo certbot renew --dry-run
💡
إذا كان على السيرفر خادم ويب يستخدم المنفذ 80، فاستخدم الطريقة --webroot بدلاً من --standalone، أو أصدر الشهادة عبر التحقق من ملكية النطاق عبر ال DNS (DNS Challenge) بإضافة certbot الخاصة بمزود DNS لديك.

صناديق بريد دون حسابات في النظام

الإعداد الافتراضي يجعل كل صندوق بريد مستخدماً في Linux، وهذا غير مناسب إطلاقاً لخادم بريد، فكل مستخدم بريد يصبح حساباً في النظام له كلمة مرور قد تفتح SSH، وإضافة صندوق تحتاج إلى صلاحيات root. والحل الأنسب هو الصناديق الافتراضية Virtual Mailboxes: مستخدم نظام واحد اسمه vmail لا يستطيع الدخول ويملك كل الرسائل على القرص، وقائمة المستخدمين وكلمات مرورهم في ملف نصي يقرأه Dovecot وحده. الآن سوف ننشئ هذا المستخدم برقم ثابت 5000 حتى يبقى الرقم نفسه إذا نقلت البريد إلى سيرفر آخر:

sudo groupadd -g 5000 vmail
sudo useradd -u 5000 -g vmail -s /usr/sbin/nologin -d /var/vmail -M vmail
sudo install -d -o vmail -g vmail -m 750 /var/vmail

ثم نولد الهاش Hash لكلمة مرور أول صندوق بخوارزمية ARGON2ID، وهي دالة اشتقاق مفاتيح Key Derivation Function بطيئة عمداً وتحتاج إلى ذاكرة كبيرة في كل محاولة، والسبب أن من يسرق ملف المستخدمين سوف يحتاج إلى وقت طويل جداً لتجربة كل كلمة مرور. ونفذ الأمر دون أن تكتب كلمة المرور فيه حتى لا تبقى في سجل الأوامر History، فسوف يطلبها منك مرتين:

sudo doveadm pw -s ARGON2ID

والمخرج سوف يكون سطراً مثل التالي:

{ARGON2ID}$argon2id$v=19$m=65536,t=3,p=1$6pGgoXZvZ5ZQAZ56OSkoBA$OtCqVjFchJ2sQtyJkgjv1/l1c6we1Zei8iyVlJYlu/Q

انسخ السطر كاملاً وأنشئ ملف المستخدمين /etc/dovecot/users، حيث يحمل كل سطر العنوان الكامل ثم نقطتين ثم الهاش:

sudo nano /etc/dovecot/users
[email protected]:{ARGON2ID}$argon2id$v=19$m=65536,t=3,p=1$6pGgoXZvZ5ZQAZ56OSkoBA$OtCqVjFchJ2sQtyJkgjv1/l1c6we1Zei8iyVlJYlu/Q
sudo chown root:dovecot /etc/dovecot/users
sudo chmod 640 /etc/dovecot/users

وبهذا الشكل يقرأ الملف root ومجموعة dovecot فقط، ولا يستطيع أي مستخدم آخر على السيرفر أن يرى حتى الهاشات.

إعداد Dovecot

يأتي Dovecot في Ubuntu بملف رئيسي /etc/dovecot/dovecot.conf يضم أكثر من عشرين ملفاً في conf.d، وتعديل الإعداد موزعاً عليها يجعل من الصعب أن تعرف ما المفعل فعلاً. لذلك سوف نكتب الإعداد كله في ملف واحد بدلاً من الملف الرئيسي، فيصبح كل ما يفعله Dovecot أمامك في صفحة واحدة. احتفظ بنسخة من الملف الأصلي ثم اكتب الملف الجديد:

sudo cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig
sudo nano /etc/dovecot/dovecot.conf
# /etc/dovecot/dovecot.conf: the whole configuration in one file
protocols = imap lmtp
listen = *, ::

# Virtual users: one system user (vmail) owns every mailbox
mail_location = maildir:/var/vmail/%d/%n/Maildir
mail_uid = vmail
mail_gid = vmail
first_valid_uid = 5000
last_valid_uid = 5000
mail_privileged_group = vmail

namespace inbox {
  inbox = yes
  separator = /
  mailbox Drafts {
    special_use = \Drafts
    auto = subscribe
  }
  mailbox Junk {
    special_use = \Junk
    auto = subscribe
  }
  mailbox Sent {
    special_use = \Sent
    auto = subscribe
  }
  mailbox Trash {
    special_use = \Trash
    auto = subscribe
  }
}

# TLS
ssl = required
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
ssl_min_protocol = TLSv1.2

# Authentication: passwd-file, no system accounts
auth_mechanisms = plain login
auth_username_format = %Lu
disable_plaintext_auth = yes

passdb {
  driver = passwd-file
  args = scheme=ARGON2ID /etc/dovecot/users
}
userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/vmail/%d/%n
}

service imap-login {
  inet_listener imap {
    port = 0
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

protocol lmtp {
  postmaster_address = [email protected]
}

في الإعداد أعلاه لاحظ التالي:

  • mail_location يضع بريد [email protected] في /var/vmail/example.com/info/Maildir، حيث %d هو النطاق و%n هو الجزء قبل @، وصيغة Maildir تحفظ كل رسالة في ملف مستقل، فلا يفسد صندوق كامل إذا انقطع التيار أثناء الكتابة، ويسهل نسخه احتياطياً.
  • first_valid_uid وlast_valid_uid يمنعان Dovecot من الكتابة بأي هوية غير vmail، فحتى لو أخطأت في إعداد مستخدم لن يكتب Dovecot بصلاحيات root.
  • الكتلة namespace inbox تنشئ المجلدات الأساسية تلقائياً وتعلن نوع كل مجلد Special Use، فيعرف Thunderbird والهاتف أين يحفظان الرسائل المرسلة والمحذوفة دون إعداد يدوي.
  • ssl = required مع disable_plaintext_auth = yes يعنيان أن كلمة المرور لا تقبل أبداً على اتصال غير مشفر، والعلامة < قبل المسار تعني اقرأ محتوى الملف وليس النص نفسه.
  • passwd-file يقرأ كلمات المرور من الملف الذي أنشأناه، وuserdb static يعطي كل المستخدمين الهوية نفسها vmail، فلا نحتاج إلى أن نكتب لكل مستخدم رقماً ومجلداً.
  • port = 0 يغلق IMAP غير المشفر على المنفذ 143، ويبقى المنفذ 993 وحده.
  • المقبسان Sockets في /var/spool/postfix/private/ هما نقطة الالتقاء مع Postfix: الأول لتسليم الرسائل عبر LMTP، والثاني ليسأل Postfix عن كلمات المرور كما في دليل Dovecot الرسمي لربط SASL. ووضعناهما داخل مجلد Postfix لأن خدمات Postfix تعمل في بيئة معزولة Chroot داخل /var/spool/postfix ولا ترى ملفات خارجه.

أعد تشغيل Dovecot ثم اختبر كلمة المرور ومعلومات المستخدم:

sudo systemctl restart dovecot
sudo doveadm auth test [email protected]
sudo doveadm user [email protected]

والمخرج سوف يكون كما يلي:

passdb: [email protected] auth succeeded
extra fields:
  [email protected]
field	value
uid	5000
gid	5000
home	/var/vmail/example.com/info
mail	maildir:/var/vmail/example.com/info/Maildir

إعداد Postfix

إعداد Postfix في ملفين: main.cf للإعدادات العامة، وmaster.cf للخدمات التي تعمل وعلى أي منافذ. وكما فعلنا مع Dovecot سوف نكتب main.cf كاملاً بدلاً من تعديل الملف الذي ولده المثبت، والسبب أن إضافة الإعدادات في آخر الملف تترك قيماً مكررة يحذرك منها Postfix في كل أمر، ولا تعرف أي القيمتين هي المستخدمة.

الملف main.cf

sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.orig
sudo nano /etc/postfix/main.cf
# /etc/postfix/main.cf
compatibility_level = 3.6
smtpd_banner = $myhostname ESMTP
biff = no
append_dot_mydomain = no
readme_directory = no

# Server identity
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = localhost
mynetworks = 127.0.0.0/8 [::1]/128
inet_interfaces = all
inet_protocols = all
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
recipient_delimiter = +
message_size_limit = 52428800
mailbox_size_limit = 0

# Virtual domains and mailboxes, delivered to Dovecot over LMTP
virtual_mailbox_domains = example.com
virtual_mailbox_maps = hash:/etc/postfix/vmailbox
virtual_alias_maps = hash:/etc/postfix/virtual
virtual_transport = lmtp:unix:private/dovecot-lmtp

# TLS for incoming connections (port 25 and 587)
smtpd_tls_chain_files = /etc/letsencrypt/live/mail.example.com/privkey.pem, /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_security_level = may
smtpd_tls_protocols = >=TLSv1.2
smtpd_tls_mandatory_protocols = >=TLSv1.2
smtpd_tls_loglevel = 1

# TLS for outgoing connections to other servers
smtp_tls_CApath = /etc/ssl/certs
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# SASL: Postfix asks Dovecot to check passwords (enabled on 587 only)
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = no

# Who may relay, and the SPF check for incoming mail
smtpd_helo_required = yes
disable_vrfy_command = yes
smtpd_forbid_bare_newline = normalize
smtpd_forbid_bare_newline_exclusions = $mynetworks
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policyd-spf
policyd-spf_time_limit = 3600

# Milters: OpenDKIM (sign and verify) then OpenDMARC
milter_default_action = accept
smtpd_milters = inet:127.0.0.1:8891, inet:127.0.0.1:8893
non_smtpd_milters = inet:127.0.0.1:8891

# postscreen in front of port 25
postscreen_access_list = permit_mynetworks
postscreen_blacklist_action = drop
postscreen_greet_action = enforce
postscreen_dnsbl_sites = zen.spamhaus.org=127.0.0.[2..11]*3, bl.spamcop.net=127.0.0.2*2
postscreen_dnsbl_threshold = 3
postscreen_dnsbl_action = enforce

# Limits per client IP
smtpd_client_connection_rate_limit = 30
smtpd_client_message_rate_limit = 60
smtpd_client_recipient_rate_limit = 200
smtpd_soft_error_limit = 10
smtpd_hard_error_limit = 20

في الإعداد أعلاه لاحظ التالي:

  • mydestination = localhost مع virtual_mailbox_domains = example.com: النطاق example.com لا يسلم لحسابات النظام، وإنما يعامل نطاقاً افتراضياً يسلم بريده إلى Dovecot عبر virtual_transport، ولا تضع النطاق نفسه في الإعدادين معاً أبداً كما ينبه دليل النطاقات الافتراضية.
  • virtual_mailbox_maps قائمة العناوين الموجودة فعلاً، وبها يرفض Postfix الرسالة إلى عنوان غير موجود أثناء الاتصال برمز 550، بدلاً من أن يقبلها ثم يرسل رسالة ارتداد إلى عنوان قد يكون مزيفاً، وهذا ما يسمى Backscatter ويضع سيرفرك في قوائم الحظر.
  • smtpd_tls_chain_files هي الطريقة الحديثة لتحديد المفتاح ثم الشهادة في سطر واحد، وsmtpd_tls_security_level = may على المنفذ 25 تعني أن TLS متاح ولكنه ليس إجبارياً، والسبب أن بعض السيرفرات القديمة لا تدعمه، وRFC 3207 يمنع إجبار TLS على سيرفر MX عام. أما على المنفذ 587 فسوف نجعله إجبارياً في master.cf.
  • smtpd_sasl_auth_enable = no هنا، ثم نفعله على المنفذ 587 فقط، فلا يقبل المنفذ 25 كلمات مرور إطلاقاً، وبالتالي لا يستطيع المخترق تخمين كلمات المرور عليه.
  • smtpd_relay_restrictions أهم سطر في الملف: لا يمرر Postfix رسالة إلى نطاق خارجي إلا إذا جاءت من السيرفر نفسه أو من مستخدم سجل دخوله، وبدونه يصبح سيرفرك مرحلاً مفتوحاً Open Relay يرسل من خلاله أي شخص في العالم، وتجده في قوائم الحظر خلال ساعات.
  • smtpd_forbid_bare_newline = normalize يغلق هجوم تهريب SMTP (SMTP Smuggling) كما سيأتي في قسم الحماية، وقيمته الافتراضية في Postfix 3.8 هي no، لذلك نكتبها صراحة.
  • milter_default_action = accept يعني أنه إذا توقف OpenDKIM أو OpenDMARC يستمر Postfix في قبول البريد دون فحص بدلاً من أن يرفض كل شيء، وهذا اختيار للتوافرية Availability على حساب الفحص، ويمكنك أن تجعلها tempfail إذا كنت تفضل أن تتأخر الرسائل على أن تمر دون توقيع.
  • OpenDKIM وOpenDMARC يستمعان على 127.0.0.1 عبر TCP بدلاً من المقبس الافتراضي في /run، والسبب نفسه: خدمة smtpd تعمل في Chroot ولا ترى /run.

قوائم العناوين

الآن سوف ننشئ ثلاث قوائم صغيرة: العناوين الموجودة، والأسماء المستعارة Aliases، وأي عنوان يحق لكل مستخدم أن يرسل باسمه. ولاحظ أن القيمة في vmailbox لا يستخدمها Postfix عند التسليم عبر LMTP، فيكفي أي نص مثل OK، والمهم وجود العنوان في القائمة:

sudo tee /etc/postfix/vmailbox >/dev/null <<'EOF'
[email protected]    OK
EOF

sudo tee /etc/postfix/virtual >/dev/null <<'EOF'
[email protected]  [email protected]
[email protected]       [email protected]
[email protected]       [email protected]
[email protected]        [email protected]
EOF

sudo tee /etc/postfix/sender_login >/dev/null <<'EOF'
[email protected]    [email protected]
EOF

sudo postmap /etc/postfix/vmailbox /etc/postfix/virtual /etc/postfix/sender_login
sudo newaliases

العنوان postmaster يجب أن يستقبل البريد في كل نطاق كما يشترط RFC 5321، وabuse يوصي به RFC 2142، وإليهما تصل الشكاوى وتقارير المشكلات، وdmarc لتقارير DMARC، وroot حتى تصلك رسائل النظام مثل أخطاء cron. وكل مرة تعدل فيها ملفاً من هذه الملفات نفذ postmap عليه، والسبب أن Postfix يقرأ النسخة المفهرسة .db وليس الملف النصي.

⚠️
قائمة المستخدمين الآن في مكانين: /etc/dovecot/users لكلمات المرور و/etc/postfix/vmailbox للعناوين. عند إضافة صندوق جديد أضفه في الملفين ثم نفذ postmap، وعند حذف صندوق احذفه من الملفين، وإلا قبل Postfix رسائل لصندوق لا يعرفه Dovecot فترتد بعد قبولها.

الملف master.cf: المنفذ 587 وpostscreen

افتح /etc/postfix/master.cf، وفي أوله ستجد خدمة smtp مفعلة وتحتها أربعة أسطر معطلة لـ postscreen. عطل السطر الأول وفعل الأربعة حتى تصبح بداية الملف كما يلي:

sudo nano /etc/postfix/master.cf
#smtp      inet  n       -       y       -       -       smtpd
smtp      inet  n       -       y       -       1       postscreen
smtpd     pass  -       -       y       -       -       smtpd
dnsblog   unix  -       -       y       -       0       dnsblog
tlsproxy  unix  -       -       y       -       0       tlsproxy

ثم أضف في آخر الملف خدمة الإرسال submission وخدمة فحص SPF:

submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_sender_login_maps=hash:/etc/postfix/sender_login
  -o smtpd_sender_restrictions=reject_sender_login_mismatch
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=
  -o milter_macro_daemon_name=ORIGINATING
policyd-spf  unix  -    n       n       -       0       spawn
  user=policyd-spf argv=/usr/bin/policyd-spf

في الإعداد أعلاه لاحظ التالي:

  • الآن يستقبل postscreen كل اتصال على المنفذ 25، ولا يمرره إلى smtpd إلا بعد أن يجتاز فحوصه، كما سيأتي في قسم الحماية.
  • على المنفذ 587 smtpd_tls_security_level=encrypt يجعل TLS إجبارياً، وsmtpd_tls_auth_only=yes يخفي أمر AUTH حتى يبدأ التشفير، فلا يرسل أي برنامج كلمة المرور نصاً واضحاً ولو أخطأ المستخدم في إعداده.
  • smtpd_sender_login_maps مع reject_sender_login_mismatch يمنعان المستخدم الذي سجل دخوله بحساب info من أن يرسل باسم [email protected]، والسبب أن الحساب المسروق يجب ألا يستطيع انتحال زملائه.
  • smtpd_recipient_restrictions= فارغة على 587 لأن رسائل المستخدمين لا تحتاج إلى فحص SPF، فهي من سيرفرنا نفسه.
  • خدمة policyd-spf يشغلها Postfix عند الطلب بمستخدم policyd-spf الذي أنشأته الحزمة، كما في دليل خوادم السياسات.

فحص SPF للرسائل الواردة

سجل SPF الذي أضفناه يحمي نطاقك عند الآخرين، أما فحص الرسائل التي تصلك فيقوم به policyd-spf. وملف إعداده /etc/postfix-policyd-spf-python/policyd-spf.conf يأتي بقيم مناسبة، ونضيف إليه سطرين حتى يكتب النتيجة بصيغة Authentication-Results التي يقرأها OpenDMARC:

sudo nano /etc/postfix-policyd-spf-python/policyd-spf.conf
debugLevel = 1
TestOnly = 1
HELO_reject = Fail
Mail_From_reject = Fail
PermError_reject = False
TempError_Defer = False
skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
Header_Type = AR
Authserv_Id = mail.example.com

في الإعداد أعلاه لاحظ التالي:

  • TestOnly = 1 اسم مضلل، فالقيمة 1 هي التي تفعل الرفض الحقيقي، والقيمة 0 هي وضع الاختبار الذي يضيف الترويسة Header فقط، فلا تغيرها ظناً أنك تلغي وضع الاختبار.
  • Mail_From_reject = Fail يرفض الرسالة عندما تكون نتيجة SPF هي fail فقط، أي عندما يقول صاحب النطاق صراحة -all، أما softfail فتمر مع ترويسة تسجل النتيجة.
  • Header_Type = AR مع Authserv_Id تجعل النتيجة ترويسة Authentication-Results باسم سيرفرك، فيثق بها OpenDMARC لاحقاً.

توقيع DKIM: الختم الذي لا يمكن تزويره

في هذا القسم سوف يوقع OpenDKIM كل رسالة تخرج من سيرفرك بالمفتاح الخاص Private Key الذي لا يغادره، ويضع التوقيع في ترويسة DKIM-Signature كما في RFC 6376، وننشر المفتاح العام Public Key في سجل TXT ليتحقق منه السيرفر المستقبل. وقد شرحنا فكرة التوقيع الرقمي وما يثبته DKIM وما لا يثبته في شرح SPF وDKIM وDMARC للمبتدئين.

لماذا OpenDKIM وليس Rspamd؟

هناك أداتان شائعتان للتوقيع مع Postfix. الأولى Rspamd، وهو نظام كامل لمكافحة الرسائل المزعجة يوقع DKIM ويفحص SPF وDMARC ويتعلم من الرسائل، وهو الذي يستخدمه mailcow، ولكنه أثقل بكثير ويحتاج إلى Redis، ويوصي مطوروه بحزمهم الرسمية بدلاً من حزمة التوزيعة، والسبب واضح عند المقارنة: Ubuntu 24.04 يقدم الإصدار 3.8.1 بينما الإصدار الحالي 4.2.1. والثانية OpenDKIM، وهو برنامج صغير بلغة C يفعل شيئاً واحداً هو التوقيع والتحقق، ويأتي في مستودعات Ubuntu جاهزاً.

واخترنا OpenDKIM في هذا الدليل لأن هدفه أن ترى كل مكون منفصلاً وتفهم دوره، ولأنه الأخف على سيرفر صغير، ولكن عليك أن تعرف حالته بصراحة: آخر إصدار منشور منه هو 2.11.0 Beta2 في نوفمبر 2018، والحزمة في Ubuntu مبنية عليه، بينما يستمر الفرع develop في GitHub في تلقي إصلاحات حتى 2026 دون إصدار جديد. وهذا مقبول لأداة صغيرة ناضجة يصونها Debian وUbuntu، فإذا أردت لاحقاً فلتراً حقيقياً للرسائل المزعجة فالانتقال إلى Rspamd هو الخطوة الطبيعية، وهو يحل محل OpenDKIM وOpenDMARC وpolicyd-spf معاً.

إنشاء المفتاح ونشره

نولد زوج المفاتيح بطول 2048 بت، والمحدد Selector هو اسم المفتاح داخل النطاق، وهنا mail، وبه تستطيع لاحقاً تدوير المفاتيح Key Rotation بنشر مفتاح جديد بمحدد آخر دون أن تتعطل الرسائل القديمة:

sudo mkdir -p /etc/opendkim/keys/example.com
sudo opendkim-genkey -b 2048 -d example.com -s mail -D /etc/opendkim/keys/example.com
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod 700 /etc/opendkim/keys/example.com
sudo cat /etc/opendkim/keys/example.com/mail.txt

والمخرج سوف يكون كما يلي:

mail._domainkey	IN	TXT	( "v=DKIM1; h=sha256; k=rsa; "
	  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkU7h3AxshIPmmJFDUZf/PIzAteClMy1JsdmU4Fz/7vdrncSPPx8tPN9l42tNeOcMiT5/+xoaxdceSfX3OjPNvJKGQoVu2M+AjZ7pbF1+FORYuxqcsjTIGoItLzr3sfxRA0anXbcFT+p08/FigxM2GV9wUlyDM9QBkf3fWJ+CkL6Yq0BBXUtMddTR9zFYdL9brtVYXLQ+XTLk6s"
	  "ArEdnZ+v3alTC6icstA/qNcNyhiqg0T438o609WySHCr41eIWobYdl7rd8X797utnOgcV5tge3AaEg4Mc+MKCqDjdjKJCODoMS2gMHIv/QzqZFJVLBziSNhY5IpfUhnxfZRQqNPwIDAQAB" )  ; ----- DKIM key mail for example.com

أضف سجل TXT باسم mail._domainkey.example.com، وقيمته النصوص التي بين علامات التنصيص متتالية دون فواصل، أي تبدأ بـ v=DKIM1; h=sha256; k=rsa; p=MIIB… وتنتهي بـ …IDAQAB. ولاحظ أن النص طويل، وسجل TXT لا يقبل أكثر من 255 حرفاً في السلسلة الواحدة، لذلك قسمه opendkim-genkey إلى سلسلتين، وبعض لوحات DNS تقسمه بنفسها إذا لصقته كاملاً، ولا مشكلة في ذلك. أما الملف mail.private فهو المفتاح الخاص، وإذا تسرب يستطيع حامله أن يوقع رسائل باسم نطاقك، لذلك صلاحياته للمستخدم opendkim وحده.

إعداد OpenDKIM

sudo cp /etc/opendkim.conf /etc/opendkim.conf.orig
sudo nano /etc/opendkim.conf
# /etc/opendkim.conf
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes
UserID                  opendkim
UMask                   007
PidFile                 /run/opendkim/opendkim.pid
Socket                  inet:[email protected]

# sign mail from our domain, verify everything else
Mode                    sv
Domain                  example.com
Selector                mail
KeyFile                 /etc/opendkim/keys/example.com/mail.private
InternalHosts           127.0.0.1, ::1
Canonicalization        relaxed/simple
OversignHeaders         From
AuthservID              mail.example.com
TrustAnchorFile         /usr/share/dns/root.key

في الإعداد أعلاه لاحظ التالي:

  • Mode sv يعني التوقيع Sign والتحقق Verify معاً، فيوقع OpenDKIM الرسائل التي من نطاقك إذا جاءت من InternalHosts أو من مستخدم سجل دخوله على المنفذ 587، ويتحقق من توقيع كل رسالة واردة.
  • Domain وSelector وKeyFile تكفي لنطاق واحد، وإذا أضفت نطاقات أخرى فاستخدم الخيارين KeyTable وSigningTable الموضحين في توثيق opendkim.conf.
  • Canonicalization relaxed/simple يتسامح مع تغيير المسافات وحالة الأحرف في الترويسات، والسبب أن بعض السيرفرات في الطريق تعيد ترتيب المسافات فيفشل التوقيع دون أن يغير أحد المعنى.
  • OversignHeaders From يمنع أي طرف في الطريق من إضافة ترويسة From ثانية إلى رسالة موقعة.
  • TrustAnchorFile يجعل OpenDKIM يتحقق من توقيع DNSSEC للمفتاح إذا كان نطاق المرسل يدعمه، ولذلك سوف ترى كلمة unprotected أو not secure في النتائج عندما لا يكون النطاق موقعاً بـ DNSSEC، وهي معلومة وليست خطأ.

أعد تشغيل OpenDKIM ثم اختبر أن المفتاح المنشور في DNS يطابق المفتاح الخاص، وانتظر قبل ذلك حتى ينتشر السجل:

sudo systemctl restart opendkim
sudo opendkim-testkey -d example.com -s mail -vvv

والمخرج سوف يكون كما يلي:

opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: key loaded from /etc/opendkim/keys/example.com/mail.private
opendkim-testkey: checking key 'mail._domainkey.example.com'
opendkim-testkey: key not secure
opendkim-testkey: key OK

السطر key OK هو المهم، وkey not secure معناه أن نطاقك غير موقع بـ DNSSEC فقط.

DMARC: ماذا يفعل المستقبل عند الفشل؟

بعد SPF وDKIM يبقى سؤال: ماذا يفعل السيرفر المستقبل إذا فشلا؟ وهنا يأتي DMARC بسياستك Policy (none أو quarantine أو reject) وشرط التطابق Alignment بين النطاق الظاهر في From والنطاق الذي اجتاز SPF أو وقع بـ DKIM، وهذا الشرط هو الذي سوف يوقف رسالة المنتحل في قسم الاختبار، وتفاصيله في شرح SPF وDKIM وDMARC.

وسجلك أضفناه في قسم DNS، أما تطبيق DMARC على الرسائل التي تصلك أنت فاختياري ويقوم به OpenDMARC، وآخر إصدار منه 1.4.2 في ديسمبر 2021، ويقرأ نتائج SPF وDKIM التي كتبها policyd-spf وOpenDKIM ثم يطبق سياسة نطاق المرسل:

sudo cp /etc/opendmarc.conf /etc/opendmarc.conf.orig
sudo nano /etc/opendmarc.conf
# /etc/opendmarc.conf
Syslog                  true
UserID                  opendmarc
UMask                   0002
PidFile                 /run/opendmarc/opendmarc.pid
Socket                  inet:[email protected]
AuthservID              mail.example.com
TrustedAuthservIDs      mail.example.com
PublicSuffixList        /usr/share/publicsuffix/public_suffix_list.dat
IgnoreAuthenticatedClients true
RejectFailures          true
RequiredHeaders         true

في الإعداد أعلاه لاحظ التالي:

  • TrustedAuthservIDs يجعل OpenDMARC يثق بترويسات Authentication-Results التي كتبها سيرفرك فقط، والسبب أن المرسل يستطيع أن يضع في رسالته ترويسة مزيفة تقول spf=pass.
  • RejectFailures true يرفض الرسالة فعلاً عندما تكون سياسة نطاق المرسل p=reject وتفشل الرسالة، وبدونه يكتفي OpenDMARC بكتابة النتيجة. ولاحظ أن الرسائل المحولة Forwarded وبعض القوائم البريدية Mailing Lists قد تفشل في DMARC لأنها تغير الرسالة، فإذا لاحظت رفضاً لرسائل مشروعة من قائمة بريدية فاجعلها false.
  • RequiredHeaders true يرفض الرسائل المشوهة التي لا تحمل ترويسة From واحدة أو Date واحدة كما يشترط معيار الرسائل، وهي غالباً رسائل مزعجة.
  • IgnoreAuthenticatedClients true يتجاوز رسائل مستخدميك على المنفذ 587، فهي لم توقع بعد عند وصولها إلى OpenDMARC.

تشغيل الخدمات والتحقق من الإعداد

الآن تحقق من إعداد Postfix، فإذا لم يطبع الأمر postfix check شيئاً فالإعداد سليم، ثم أعد تشغيل كل الخدمات:

sudo postfix check
sudo systemctl restart opendkim opendmarc dovecot postfix
sudo systemctl --no-pager status postfix dovecot opendkim opendmarc | grep -E "●|Active:"
sudo ss -tlnp | grep -E ":(25|587|993|8891|8893) "

وفي مخرج ss سوف ترى المنافذ 25 و587 و993 على كل العناوين، والمنفذين 8891 و8893 على 127.0.0.1 فقط:

LISTEN 0      100          0.0.0.0:993        0.0.0.0:*    users:(("dovecot",pid=277,fd=36))
LISTEN 0      100          0.0.0.0:587        0.0.0.0:*
LISTEN 0      100          0.0.0.0:25         0.0.0.0:*
LISTEN 0      4096       127.0.0.1:8891       0.0.0.0:*
LISTEN 0      4096       127.0.0.1:8893       0.0.0.0:*

وكل ما يحدث بعد ذلك يسجل في /var/log/mail.log، فافتح نافذة ثانية وتابعه أثناء الاختبار:

sudo tail -f /var/log/mail.log

الاختبار: إرسال واستقبال وقراءة

الإرسال عبر المنفذ 587

الأداة swaks تتحدث SMTP مثل برنامج البريد تماماً وتعرض كل سطر في الحوار، لذلك هي أفضل أداة للاختبار. نفذ الأمر التالي من جهازك أو من السيرفر نفسه، وضع مكان [email protected] حساباً خارجياً تملكه، ولاحظ أننا لم نكتب كلمة المرور في الأمر، فسوف يسألك عنها swaks ولا يظهرها على الشاشة بسبب --protect-prompt، ولا يطبعها في الحوار بسبب --auth-hide-password:

swaks --server mail.example.com --port 587 --tls \
  --auth LOGIN --auth-user [email protected] --protect-prompt --auth-hide-password \
  --from [email protected] --to [email protected] \
  --header "Subject: Test from example.com" --body "Hello from Postfix and Dovecot"

والمخرج سوف يكون كما يلي (أهم الأسطر):

<-  220 mail.example.com ESMTP
 -> EHLO mx.example.net
<-  250-STARTTLS
 -> STARTTLS
<-  220 2.0.0 Ready to start TLS
=== TLS started with cipher TLSv1.3:TLS_AES_256_GCM_SHA384:256
 ~> EHLO mx.example.net
<~  250-AUTH PLAIN LOGIN
<~  235 2.7.0 Authentication successful
 ~> MAIL FROM:<[email protected]>
<~  250 2.1.0 Ok
<~  250 2.1.5 Ok
<~  250 2.0.0 Ok: queued as DA5CC1AC347

في المخرج أعلاه لاحظ التالي: السيرفر لم يعلن AUTH في قائمة الأوامر الأولى، وإنما أعلنه فقط بعد STARTTLS، وهذا أثر smtpd_tls_auth_only. وفي السجل سوف ترى الرسالة تمر بكل المراحل، من تسجيل الدخول إلى التوقيع إلى التسليم:

postfix/submission/smtpd[817]: DA5CC1AC347: client=mx.example.net[198.51.100.25], sasl_method=LOGIN, [email protected]
opendkim[383]: DA5CC1AC347: DKIM-Signature field added (s=mail, d=example.com)
postfix/qmgr[808]: DA5CC1AC347: from=<[email protected]>, size=483, nrcpt=1 (queue active)
postfix/smtp[824]: DA5CC1AC347: to=<[email protected]>, relay=mx.example.net[198.51.100.25]:25, delay=0.18, delays=0.12/0.02/0.01/0.03, dsn=2.0.0, status=sent (250 OK)

وإذا أرسلت إلى حساب في Gmail فافتح الرسالة واختر Show original، وتأكد أن نتيجة SPF وDKIM وDMARC هي PASS، وللتقييم الكامل أرسل رسالة إلى أداة مثل mail-tester.com التي تعطيك درجة من 10 مع سبب كل نقطة مخصومة. وإذا أردت أن تتحقق من التوقيع بنفسك، فاحفظ الرسالة كما وصلت (في Gmail: Download original) ثم افحصها بالأداة dkimverify من الحزمة python3-dkim، فهي تجلب المفتاح العام من DNS وتعيد حساب التوقيع:

sudo apt install python3-dkim
dkimverify < original_msg.eml
signature ok

الاستقبال عبر المنفذ 25 والقراءة عبر IMAP

أرسل الآن رسالة إلى [email protected] من حساب خارجي، أو من سيرفر آخر تملكه بالأداة swaks على المنفذ 25 دون كلمة مرور، كما يفعل أي سيرفر بريد في العالم:

swaks --server mail.example.com --port 25 --from [email protected] --to [email protected]

وفي سجل سيرفرك سوف ترى الرسالة تمر بكل حارس من الحراس الذين أعددناهم بالترتيب: postscreen ثم فحص SPF ثم OpenDKIM ثم OpenDMARC ثم التسليم إلى Dovecot، والسطور التالية لرسالة من نطاق يوقع رسائله بـ DKIM:

postfix/postscreen[833]: CONNECT from [198.51.100.25]:33866 to [203.0.113.10]:25
postfix/postscreen[833]: PASS NEW [198.51.100.25]:33866
policyd-spf[838]: : prepend Authentication-Results: mail.example.com; spf=pass (sender SPF authorized) smtp.mailfrom=example.net (client-ip=198.51.100.25; helo=mx.example.net; [email protected]; receiver=example.com)
opendkim[383]: 9199B1AC2C7: DKIM verification successful
opendkim[383]: 9199B1AC2C7: s=s1 d=example.net a=rsa-sha256 SSL
opendmarc[898]: 9199B1AC2C7: example.net pass
dovecot: lmtp([email protected])<849><QJhLMqC5wmpRAwAAYIgGOA>: msgid=<[email protected]>: saved mail to INBOX
postfix/lmtp[848]: 9199B1AC2C7: to=<[email protected]>, relay=mail.example.com[private/dovecot-lmtp], delay=0.52, delays=0.17/0.03/0.06/0.26, dsn=2.0.0, status=sent (250 2.0.0 <[email protected]> QJhLMqC5wmpRAwAAYIgGOA Saved)

تأكد بعد ذلك أن IMAP يعمل عبر TLS، فالأمر التالي يتصل بالمنفذ 993 ويطبع تحية Dovecot ثم يخرج:

printf "a1 LOGOUT\r\n" | openssl s_client -connect mail.example.com:993 -quiet 2>/dev/null
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN AUTH=LOGIN] Dovecot (Ubuntu) ready.
* BYE Logging out
a1 OK Logout completed.

ثم اقرأ الرسالة كما يقرؤها برنامج البريد، والسكربت التالي بلغة Python يسجل الدخول عبر IMAP ويطبع ترويسات النتائج لكل رسالة في الصندوق:

import imaplib, ssl, getpass

M = imaplib.IMAP4_SSL("mail.example.com", 993, ssl_context=ssl.create_default_context())
M.login("[email protected]", getpass.getpass())
M.select("INBOX")
typ, data = M.search(None, "ALL")
for num in data[0].split():
    typ, msg = M.fetch(num, "(BODY.PEEK[HEADER.FIELDS (AUTHENTICATION-RESULTS FROM SUBJECT)])")
    print(msg[0][1].decode())
M.logout()

والمخرج لرسالة موقعة من example.net سوف يكون كما يلي:

Authentication-Results: mail.example.com; dmarc=pass (p=reject dis=none) header.from=example.net
Authentication-Results: mail.example.com;
	dkim=pass (2048-bit key; unprotected) header.d=example.net [email protected] header.a=rsa-sha256 header.s=s1 header.b=vei0flL0;
	dkim-atps=neutral
Authentication-Results: mail.example.com; spf=pass (sender SPF authorized) smtp.mailfrom=example.net (client-ip=198.51.100.25; helo=mx.example.net; [email protected]; receiver=example.com)
From: Alice <[email protected]>
Subject: Hello from example.net

في المخرج أعلاه لاحظ التالي: كل حارس كتب نتيجته في ترويسة Authentication-Results مستقلة تبدأ باسم سيرفرك، وتقرأ من الأسفل إلى الأعلى بترتيب حدوثها: SPF أولاً ثم DKIM ثم DMARC الذي بنى قراره على الاثنين. وهذه الترويسات نفسها هي ما تقرؤه في Gmail عندما تختار Show original، فإذا وصلتك رسالة مشبوهة يوماً فهذا أول مكان تنظر فيه. ويستطيع المستخدم الآن إضافة الحساب في Thunderbird أو الهاتف بهذه القيم: IMAP على mail.example.com بالمنفذ 993 مع SSL/TLS، وSMTP بالمنفذ 587 مع STARTTLS، واسم المستخدم هو العنوان الكامل.

ماذا لو حاول أحدهم انتحال نطاقك؟

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

أولاً: رسالة على المنفذ 25 عنوان مغلفها [email protected] من سيرفر ليس في سجل SPF لنطاقك:

swaks --server mail.example.com --from [email protected] --to [email protected]
<** 550 5.7.23 <[email protected]>: Recipient address rejected: Message rejected due to: SPF fail - not authorized. Please see http://www.openspf.net/Why?s=mfrom;[email protected];ip=198.51.100.25;r=example.com

رفضها policyd-spf قبل أن تبدأ الرسالة نفسها، لأن سجلك يقول -all. فيحاول المخترق الحيلة الثانية: يضع نطاقه هو في المغلف حتى يجتاز SPF، ويضع نطاقك في From الظاهر:

swaks --server mail.example.com --from [email protected] --to [email protected] \
  --header "From: CEO <[email protected]>" --header "Subject: urgent transfer"
<** 550 5.7.1 rejected by DMARC policy for example.com

اجتازت الرسالة SPF فعلاً، ولكن OpenDMARC لاحظ أن النطاق الظاهر example.com لا يطابق النطاق الذي اجتاز SPF ولا يوجد توقيع DKIM منه، فطبق سياسة p=reject المنشورة لنطاقك. ولاحظ أن هذا يحدث فقط بعد أن تنتقل بسياستك إلى reject، أما مع p=none فتمر الرسالة وتسجل النتيجة dmarc=fail، وهذا هو سبب الانتقال إلى reject بعد أن تراجع التقارير.

ثالثاً: رسالة موقعة توقيعاً صحيحاً، ثم عدل أحدهم جملة واحدة في جسمها في الطريق، فتصل مع النتيجة التالية:

Authentication-Results: mail.example.com;
	dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=example.net [email protected] header.a=rsa-sha256 header.s=s1 header.b=vei0flL0;

رابعاً: محاولة استخدام سيرفرك مرحلاً مفتوحاً إلى نطاق خارجي دون تسجيل دخول، ثم محاولة مستخدم مسجل أن يرسل باسم زميله، ثم كلمة مرور خاطئة، ثم محاولة المصادقة دون TLS:

<** 554 5.7.1 <[email protected]>: Relay access denied
<~* 553 5.7.1 <[email protected]>: Sender address rejected: not owned by user [email protected]
<~* 535 5.7.8 Error: authentication failed: (reason unavailable)
*** Host did not advertise authentication

والأخير يعني أن السيرفر لم يعرض المصادقة أصلاً على اتصال غير مشفر. وأخيراً رسالة إلى عنوان غير موجود، فترفض أثناء الاتصال دون Backscatter:

<** 550 5.1.1 <[email protected]>: Recipient address rejected: User unknown in virtual mailbox table

الحماية من الرسائل المزعجة وتخمين كلمات المرور

postscreen: حارس البوابة

معظم الرسائل المزعجة تأتي من برامج آلية مستعجلة لا تتبع بروتوكول SMTP بدقة، فلا تنتظر تحية السيرفر قبل أن تتكلم، أو ترسل عدة أوامر دفعة واحدة. والإعداد postscreen_greet_action = enforce يجعل postscreen ينتظر بضع ثوان قبل التحية، فإذا تكلم العميل قبلها سجل عليه مخالفة ورفض رسائله، وهذا ما يحدث مع برنامج آلي يرسل EHLO فوراً:

postfix/postscreen[833]: CONNECT from [192.0.2.30]:53736 to [203.0.113.10]:25
postfix/postscreen[833]: PREGREET 10 after 0 from [192.0.2.30]:53736: EHLO bot\r\n
postfix/postscreen[833]: NOQUEUE: reject: RCPT from [192.0.2.30]:53736: 550 5.5.1 Protocol error; from=<[email protected]>, to=<[email protected]>, proto=ESMTP, helo=<bot>

والعميل الذي يجتاز الفحوص يحفظه postscreen في ذاكرة مؤقتة Cache فلا ينتظر في المرات التالية، وهذا معنى PASS NEW الذي رأيناه في الاختبار.

قوائم الحظر DNSBL ومحاذيرها

السطر postscreen_dnsbl_sites يسأل قوائم حظر عامة DNS Blocklists عن عنوان كل عميل، ويعطي كل قائمة وزناً، فإذا بلغ المجموع postscreen_dnsbl_threshold رفض العميل، وهنا يكفي ظهور العنوان في Spamhaus ZEN وحده (الوزن 3)، أما SpamCop فلا يكفي وحده (الوزن 2). ولكن هذه القوائم لها محاذير يجب أن تعرفها قبل أن تعتمد عليها:

  • تمنع Spamhaus الاستعلام المجاني من خوادم DNS العامة مثل 8.8.8.8 و1.1.1.1 ومن خوادم كثير من المزودين، وترد عليها برموز خطأ مثل 127.255.255.254 بدلاً من النتيجة. والخطير أن سيرفراً يعامل أي رد على أنه وجود في القائمة سوف يرفض البريد كله.
  • لذلك كتبنا الرموز المقبولة صراحة zen.spamhaus.org=127.0.0.[2..11]، فلا يحسب postscreen إلا رموز الحظر الحقيقية، وتصبح رموز الخطأ بلا أثر.
  • وإذا كان سيرفرك يستخدم خادم DNS عاماً أو خادم مزود محظوراً، فلن تعمل Spamhaus، والحل أن تثبت خادم DNS محلياً مثل unbound يستعلم مباشرة، أو أن تسجل في خدمة Spamhaus المجانية DQS وتستخدم نطاقها الخاص.

حدود المعدل Rate Limits

القيم smtpd_client_*_rate_limit تحدد عدد الاتصالات والرسائل والمستلمين لكل عنوان IP في الدقيقة، ويحسبها Postfix عبر خدمة anvil، ودليل الضبط يشرح كيف تختار القيم. ولاحظ أنها تطبق على عنوان IP وليس على المستخدم، فإذا سرق أحدهم كلمة مرور أحد مستخدميك وأرسل من عناوين كثيرة فلن توقفه هذه الحدود، وهذا ما تفعله fail2ban وكلمات المرور القوية، وإذا احتجت إلى حدود لكل مستخدم فهي من الأسباب الوجيهة للانتقال إلى Rspamd.

تهريب SMTP وإصلاحه

نهاية الرسالة في SMTP هي سطر فيه نقطة وحدها بين نهايتي سطر قياسيتين <CR><LF>.<CR><LF>، ولكن Postfix مثل غيره كان يقبل نهاية السطر غير القياسية <LF> وحدها، توافقاً مع برامج قديمة. واستغل باحثون هذا الاختلاف بين السيرفرات لتهريب رسالة مزيفة داخل رسالة حقيقية، والإعداد smtpd_forbid_bare_newline = normalize في main.cf يغلق هذا الباب.

📌
في 18 ديسمبر 2023 نشرت شركة SEC Consult هجوم تهريب SMTP، الذي يجعل سيرفراً مثل Postfix يرى رسالتين حيث أرسل سيرفر آخر رسالة واحدة، فتمر الرسالة المهربة باسم أي عنوان في نطاقات السيرفر المرسل وتجتاز فحص DMARC المبني على SPF. وبحسب صفحة Postfix عن الهجوم لم تصل التفاصيل إلى مطوري Postfix قبل النشر، فأصبح الهجوم ثغرة دون إصلاح Zero-day قبل عطلة نهاية العام بأيام، فنشر Postfix الإصدارات 3.8.4 و3.7.9 و3.6.13 و3.5.23 في 22 ديسمبر، وسجلت الثغرة باسم CVE-2023-51764، ثم جاء الإصلاح النهائي في 3.8.5 في يناير 2024. ولكن الإعداد بقي معطلاً افتراضياً في الإصدارات الأقدم من 3.9، ومنها 3.8.6 في Ubuntu 24.04، والدرس: بعد كل تحديث أمني اقرأ إعلانه كاملاً، وفعل بنفسك ما يطلبه في الإعداد، والسبب أن بعض الإصلاحات تصل معطلة حفاظاً على التوافق.

fail2ban: إيقاف تخمين كلمات المرور

بعد أيام من تشغيل السيرفر سوف ترى في السجل محاولات دخول فاشلة على المنفذين 587 و993 من عناوين في كل مكان، فهذه برامج تجرب كلمات مرور شائعة على كل خادم بريد تجده. والحزمة fail2ban تقرأ السجل وتحظر العنوان في جدار الحماية بعد عدد من المحاولات الفاشلة، وفيها سجون جاهزة Jails لـ Postfix وDovecot، وكل ما علينا أن نفعلها:

sudo nano /etc/fail2ban/jail.d/mail.local
[postfix-sasl]
enabled  = true
backend  = auto
logpath  = /var/log/mail.log
maxretry = 5
findtime = 10m
bantime  = 1h

[dovecot]
enabled  = true
backend  = auto
logpath  = /var/log/mail.log
maxretry = 5
findtime = 10m
bantime  = 1h
sudo systemctl restart fail2ban
sudo fail2ban-client status postfix-sasl

وقبل أن تعتمد عليها تأكد أن فلاترها تطابق سطور السجل عندك فعلاً، فالأمر fail2ban-regex يمرر السجل على الفلتر ويعد المطابقات، وبعد محاولتي دخول فاشلتين على 587 ومحاولة على 993 سوف ترى:

sudo fail2ban-regex /var/log/mail.log "postfix[mode=auth]" | grep -E "^Lines"
sudo fail2ban-regex /var/log/mail.log dovecot | grep -E "^Lines"
Lines: 183 lines, 0 ignored, 2 matched, 181 missed
Lines: 184 lines, 0 ignored, 1 matched, 183 missed

ولاحظ أن Ubuntu 24.04 يجعل الطريقة الافتراضية لقراءة السجلات backend = systemd في الملف defaults-debian.conf، ولكننا حددنا للسجنين الملف /var/log/mail.log الذي يكتبه rsyslog، والسبب أن هذا هو الملف نفسه الذي نختبر عليه الفلتر بالأمر fail2ban-regex، فنضمن أن ما اختبرناه هو ما يعمل. وتأكد أيضاً أن عنوانك الثابت في ignoreip إذا كنت تخطئ في كلمة المرور كثيراً، حتى لا تحظر نفسك.

MTA-STS وTLS-RPT (اختياري)

الإعداد smtpd_tls_security_level = may يعني أن السيرفر المرسل يستخدم TLS إن استطاع، ولكن المخترق في منتصف الطريق يستطيع أن يحذف إعلان STARTTLS فيرسل السيرفر الرسالة دون تشفير. والمعيار MTA-STS يغلق ذلك، حيث تعلن فيه أن بريد نطاقك يجب أن يصل عبر TLS بشهادة صالحة فقط، فتلتزم بذلك السيرفرات التي تدعمه مثل Gmail. ويحتاج إلى ثلاثة أشياء: ملف سياسة على https://mta-sts.example.com/.well-known/mta-sts.txt يقدمه أي خادم ويب بشهادة صالحة، وسجلي TXT:

version: STSv1
mode: testing
mx: mail.example.com
max_age: 86400
الاسمالنوعالقيمة
_mta-sts.example.comTXTv=STSv1; id=20261004
_smtp._tls.example.comTXTv=TLSRPTv1; rua=mailto:[email protected]

السجل الثاني هو TLS-RPT، وبه تصلك تقارير يومية عن فشل TLS عند السيرفرات الأخرى، فأضف [email protected] إلى ملف virtual. وابدأ بالوضع testing وراجع التقارير، ثم انتقل إلى enforce، وغير قيمة id في كل مرة تعدل فيها ملف السياسة حتى تعرف السيرفرات أنه تغير.

النسخ الاحتياطي والاستعادة

خادم البريد هذا كله ملفات، وهذه ميزة كبيرة عند النسخ الاحتياطي Backup، فلا توجد قاعدة بيانات تحتاج إلى أداة خاصة. والمهم أن تنسخ ثلاثة أشياء: الرسائل في /var/vmail، والإعداد في /etc، ومفاتيح DKIM، والسبب أن خسارة المفتاح الخاص تعني أن تنشئ مفتاحاً جديداً وتغير سجل DNS، وخلال ذلك تفشل كل رسائلك في DKIM:

sudo tar czf /root/mail-backup-$(date +%F).tar.gz -C / \
  var/vmail etc/postfix etc/dovecot etc/opendkim etc/opendkim.conf etc/opendmarc.conf \
  etc/postfix-policyd-spf-python etc/fail2ban/jail.d/mail.local etc/letsencrypt

نسخ Maildir والخدمات تعمل آمن، والسبب أن Dovecot يكتب كل رسالة في ملف مؤقت ثم ينقله إلى مكانه دفعة واحدة، فلا تجد في النسخة رسالة نصف مكتوبة، وأسوأ ما يحدث أن ملفات الفهرس Index تحتاج إلى إعادة بناء فيعيد Dovecot بناءها تلقائياً. ولكن تذكر أن النسخة على قرص السيرفر نفسه لا تحميك إذا تعطل السيرفر، لذلك انقلها يومياً إلى تخزين خارجي Offsite Storage بأداة مثل restic أو rsync، واحذف النسخ المحلية القديمة. وللجدولة اليومية أضف مهمة cron:

sudo crontab -e
30 2 * * * tar czf /root/mail-backup-$(date +\%F).tar.gz -C / var/vmail etc/postfix etc/dovecot etc/opendkim etc/opendkim.conf etc/opendmarc.conf etc/postfix-policyd-spf-python etc/fail2ban/jail.d/mail.local etc/letsencrypt && find /root -name 'mail-backup-*.tar.gz' -mtime +7 -delete

وللاستعادة Restore على سيرفر جديد ثبت الحزم نفسها على الإصدار نفسه من Ubuntu، وأنشئ المستخدم vmail بالرقم 5000 كما فعلنا، ثم أوقف الخدمات وفك النسخة وأعد تشغيلها:

sudo systemctl stop postfix dovecot opendkim opendmarc
sudo tar xzf mail-backup-2026-10-04.tar.gz -C /
sudo chown -R vmail:vmail /var/vmail
sudo postmap /etc/postfix/vmailbox /etc/postfix/virtual /etc/postfix/sender_login
sudo systemctl start opendkim opendmarc dovecot postfix
🛑
في النسخة الاحتياطية المفتاح الخاص لـ DKIM والمفتاح الخاص للشهادة وهاشات كلمات المرور، فمن يحصل عليها يستطيع أن يوقع رسائل باسم نطاقك. لذلك شفر النسخة قبل أن تخرج من السيرفر (restic يشفر افتراضياً)، ولا تضعها في مجلد مشترك أو تخزين عام. واختبر الاستعادة دورياً على سيرفر مؤقت، فالنسخة التي لم تختبر استعادتها لا تعرف هل تعمل.

وإذا كنت تنقل البريد من سيرفر آخر وليس من نسخة احتياطية، فطريقة imapsync التي شرحناها في دليل ترحيل البريد بين خادمي mailcow تعمل هنا أيضاً، لأنها تتحدث IMAP مع الطرفين ولا تهتم بما خلفهما.

التحديث إلى إصدار أحدث

كل المكونات هنا حزم من Ubuntu، فتحديثها هو تحديث النظام نفسه، وتطبق unattended-upgrades المثبتة افتراضياً التحديثات الأمنية تلقائياً، وتستطيع أن تحدث يدوياً وتعيد تشغيل الخدمات:

sudo apt update
sudo apt upgrade
sudo postfix check
sudo systemctl restart postfix dovecot opendkim opendmarc

أما الترقية إلى إصدار Ubuntu التالي فتحتاج إلى تخطيط، والسبب أن Ubuntu 26.04 يأتي بالإصدار Dovecot 2.4، وقد تغيرت فيه صيغة الإعداد تغييراً كبيراً، فكثير من الإعدادات في الملف الذي كتبناه تغير اسمها أو طريقة كتابتها، ولن يعمل Dovecot بالملف القديم بعد الترقية. لذلك لا تقم بالترقية على سيرفر الإنتاج مباشرة، بل جهز سيرفراً جديداً بالإصدار الجديد واكتب عليه إعداد Dovecot بالصيغة الجديدة، ثم انقل البريد إليه بالنسخة الاحتياطية.

مشكلات شائعة وحلولها

كل مشكلة في هذا الإعداد تترك سطراً في /var/log/mail.log، ومعرفة السطر نصف الحل. وإذا احتجت إلى تفاصيل أكثر من مكون محدد فاتبع دليل التشخيص في Postfix، وفيما يلي أكثر السطور التي سوف تراها ومعناها.

connect to …:25: Connection timed out

postfix/smtp[2190]: connect to mx.example.net[198.51.100.25]:25: Connection timed out

إذا ظهر سطر بهذا الشكل فالرسائل الصادرة تبقى في الطابور Queue ولا تخرج، والسبب في الغالب أن المزود يحجب المنفذ 25 الصادر، فتأكد بالأمر التالي من السيرفر نفسه، وإذا انتهت المهلة دون اتصال فاطلب فتح المنفذ من الدعم الفني:

nc -vz -w 5 gmail-smtp-in.l.google.com 25
sudo postqueue -p

وإذا رفض المزود فتحه، فالحل أن ترسل عبر خادم وسيط SMTP Relay مثل Amazon SES بالإعداد relayhost، وأضف الوسيط إلى سجل SPF ووقع رسائلك بـ DKIM كما هي.

warning: connect to Milter service … Connection refused

OpenDKIM أو OpenDMARC متوقف، وبسبب milter_default_action = accept تمر الرسائل دون توقيع ودون فحص، فلن تلاحظ المشكلة إلا عندما تفشل رسائلك في DKIM. راجع حالة الخدمة وسجلها بالأمر systemctl status opendkim، وأكثر الأسباب شيوعاً خطأ في صلاحيات ملف المفتاح أو خطأ كتابي في opendkim.conf.

Relay access denied

إذا ظهر لمستخدم يرسل من برنامجه، فهو يرسل على المنفذ 25 أو دون تسجيل دخول، فاضبط في برنامجه المنفذ 587 مع STARTTLS واسم المستخدم الكامل. وإذا ظهر لسيرفر خارجي يرسل إلى نطاقك، فالنطاق ليس في virtual_mailbox_domains.

SASL LOGIN authentication failed

كلمة المرور خاطئة، أو أن الهاش في /etc/dovecot/users نسخ ناقصاً، فاختبر بالأمر doveadm auth test مباشرة. وإذا ظهر في السجل warning: SASL: Connect to private/auth failed: No such file or directory، فالمقبس غير موجود لأن Dovecot متوقف أو أن مسار unix_listener في إعداده لا يطابق smtpd_sasl_path.

الرسالة مقبولة ولكنها لا تصل إلى الصندوق

ابحث عن رقم الرسالة في السجل، فإذا ظهر سطر postfix/lmtp مع status=deferred فالمشكلة بين Postfix وDovecot. والسبب الأشهر أن المقبس private/dovecot-lmtp لا يملكه المستخدم postfix، أو أن صلاحيات /var/vmail لا تسمح لـ vmail بالكتابة. وإذا ظهر User doesn't exist فالعنوان موجود في vmailbox وليس في /etc/dovecot/users.

milter-reject: END-OF-MESSAGE … 5.7.1 Command rejected

opendmarc[328]: 798D11AC353: RFC5322 requirement error: not exactly one Date field
postfix/cleanup[823]: 798D11AC353: milter-reject: END-OF-MESSAGE from mx.example.net[198.51.100.25]: 5.7.1 Command rejected; from=<[email protected]> to=<[email protected]>

رفض OpenDMARC الرسالة لأنها مشوهة، والسطر الذي قبله يقول السبب: لا توجد ترويسة Date. وهذا يحدث كثيراً مع السكربتات والأجهزة التي ترسل البريد بنفسها مثل الطابعات وأنظمة المراقبة، فأصلح المرسل حتى يضيف الترويسات القياسية، أو اجعل RequiredHeaders false إذا لم تستطع.

رسائلي تصل إلى مجلد الرسائل المزعجة

  • افتح الرسالة في Gmail واختر Show original، فإذا كانت نتيجة DKIM fail أو none فالمفتاح في DNS ناقص أو بمسافات زائدة، فقارنه بالأمر opendkim-testkey.
  • تأكد أن dig +short -x 203.0.113.10 يعيد mail.example.com، وأن السجل A لهذا الاسم يعيد العنوان نفسه.
  • السيرفر الجديد ليس له تاريخ إرسال، فالسيرفرات الكبيرة تعامله بحذر، لذلك ابدأ بحجم صغير ولا ترسل منه حملات تسويقية، بل خصص لها أداة مستقلة مثل listmonk مع خدمة إرسال ونطاق فرعي Subdomain، والسبب أن شكاوى حملة واحدة قد تضر بريد فريقك اليومي.
  • افحص عنوانك في قوائم الحظر، واطلب رفعه منها إذا وجدته، ولخدمات Microsoft سجل عنوانك في Microsoft SNDS.

الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

  • خادم البريد مكونات منفصلة: Postfix ينقل البريد، وDovecot يخزنه ويقدمه ويتحقق من كلمات المرور، وpolicyd-spf وOpenDKIM وOpenDMARC يفحصون الرسائل ويوقعونها، وكل مكون يكتب ما فعله في /var/log/mail.log.
  • Postfix وحده يرسل، ولكن رسائله تصل إلى الرسائل المزعجة، لذلك اضبط PTR وSPF وDKIM وDMARC قبل أول رسالة، وابدأ DMARC بالقيمة p=none ثم انتقل إلى reject.
  • استخدم صناديق افتراضية يملكها مستخدم واحد لا يستطيع الدخول، وكلمات مرور ARGON2ID، ولا تقبل كلمة مرور إلا على 587 و993 بعد TLS.
  • جرب الرفض كما جربت القبول: انتحال المغلف، وانتحال From، والمرحل المفتوح، والإرسال باسم زميل.
  • فعل smtpd_forbid_bare_newline وfail2ban، وانسخ /var/vmail ومفاتيح DKIM والإعداد إلى تخزين خارجي مشفر، ولا ترق إلى Ubuntu 26.04 دون إعادة كتابة إعداد Dovecot للإصدار 2.4.

وإذا أردت أن تبني خادم البريد نفسه في برنامج واحد يجمع هذه المكونات فاقرأ دليل تثبيت Stalwart، ولتختار بين الطرق الثلاث: البناء اليدوي وmailcow وStalwart، اقرأ مقارنة خوادم البريد ونقاط القوة ونقاط الضعف في كل منها.

تدريب عملي

  • أضف صندوقاً ثانياً [email protected] في الملفين، ثم أرسل منه بحساب info ولاحظ رسالة الرفض، ثم أضف السطر المناسب في sender_login حتى يرسل info باسم sales.
  • أوقف OpenDKIM مؤقتاً وأرسل رسالة إلى Gmail، ثم قارن Show original مع رسالة أرسلتها وهو يعمل، وابحث في السجل عن السطر الذي يخبرك بالمشكلة.

سجل التحديثات

  • أكتوبر 2026: كتابة الدليل واختباره على Ubuntu 24.04 مع Postfix 3.8.6 وDovecot 2.3.21 وOpenDKIM 2.11.0~beta2 وOpenDMARC 1.4.2 وpolicyd-spf 3.0.4.
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.