Docker

راه‌اندازی ایمیل سرور با Stalwart و Docker؛ تجربه من تو PXLAds

تجربهٔ راه‌اندازی ایمیل PXLAds با Stalwart روی VPS: از نصب Docker و تنظیم DNS تا خطای یاهو و ایمیلی که Gmail پذیرفت، اما به Spam فرستاد.

راه‌اندازی ایمیل سرور با Stalwart و Docker؛ تجربه من تو PXLAds

وقتی داشتم روی PXLAds کار می‌کردم، کنار بخش‌های مختلف پروژه یه سرویس ایمیل هم روی دامنه خودم راه انداختم. چیزی که می‌خواستم این بود که آدرسی مثل info@pxlads.ir داشته باشم و ارسال و دریافت ایمیل‌ها رو روی زیرساخت خودم مدیریت کنم.

برای این بخش از Stalwart + Docker روی یه VPS تو هلند استفاده کردم. سرویس بالا اومد، صندوق ایمیل ساخته شد و تنظیمات DNS رو هم انجام دادم. ولی بعد از این‌ها تازه چالش‌های ارسال خودشون رو نشون دادن. یه بار Gmail به PTR ایراد می‌گرفت، Yahoo ایمیل رو با خطای TSS09 برمی‌گردوند و بعد هم ایمیلی که تو لاگ سرور با موفقیت ارسال شده بود، تو Gmail رفت داخل Spam.

گفتم تجربه راه‌اندازی ایمیل سرور با Stalwart رو اینجا بنویسم؛ هم بخش نصب و تنظیماتش، هم چیزهایی که موقع ارسال به Gmail و Yahoo باهاشون برخورد کردم. چون بالا اومدن سرویس فقط یه قسمت از این مسیره.

توی این پست چی می‌خونید؟

سرویس ایمیل من چه شکلیه؟

برای PXLAds از Stalwart استفاده کردم که SMTP، IMAP و JMAP رو همراه با یه پنل مدیریت وب در اختیارمون می‌ذاره. توی این نوشته بیشتر با SMTP برای ارسال و IMAP برای دسترسی به صندوق کار داریم. راهنمای شروع Stalwart

سرویس ایمیل روی VPS هلند قرار داره. بخش وب پروژه مسیر جداگانه‌ای داره؛ لازم نیست سایت و ایمیل حتماً روی یه سرور باشن. رکورد MX مشخص می‌کنه ایمیل‌های دامنه باید به کجا برسن.

برای نمونه‌های پایین از example.com و IP مستندسازی 203.0.113.10 استفاده کردم. این‌ها رو باید با دامنه و IP خودتون جایگزین کنید. نمونه نصب هم برای یه VPS تازه و Stalwart 0.16 نوشته شده؛ استقرار واقعی من Caddy هم داره که تفاوتش رو جلوتر میگم.

قبل از نصب؛ VPS، پورت ۲۵ و تنظیم DNS

برای شروع به یه دامنه، دسترسی به DNS، یه VPS با IP ثابت و دسترسی SSH نیاز دارید. Docker و Compose هم باید روی سرور نصب باشن. اگه هنوز نصبشون نکردید، راهنمای رسمی Docker برای Ubuntu مسیر نصب رو توضیح داده.

قبل از خرید VPS حتماً از ارائه‌دهنده بپرسید که پورت ۲۵ ورودی و خروجی باز هست یا نه، و امکان تنظیم PTR رو داره یا نه. باز بودن پورت ۵۸۷ به‌تنهایی برای ارسال مستقیم به سرورهای ایمیل کافی نیست؛ ارتباط بین خود سرورهای ایمیل معمولاً روی پورت ۲۵ انجام میشه. استاندارد SMTP

مشخصات نمونه ما این‌هاست:

مورد مقدار نمونه
دامنه example.com
سرور ایمیل mail.example.com
IPv4 203.0.113.10
صندوق ایمیل info@example.com

رکوردهای A و MX

توی پنل DNS این رکوردها رو اضافه کنید:

نوع نام مقدار اولویت
A mail 203.0.113.10 —
MX @ mail.example.com. 10

اگه DNS شما گزینه Cloud یا Proxy داره، برای mail خاموشش کنید تا این نام به IP واقعی سرور برسه. پروکسی معمول وب برای SMTP و IMAP کافی نیست. دامنه اصلی سایت همچنان می‌تونه پشت CDN بمونه.

PTR رو باید ارائه‌دهنده IP تنظیم کنه

PTR همون Reverse DNS هست. با رکورد A میگیم mail.example.com چه IPای داره؛ با PTR مسیر برعکس رو مشخص می‌کنیم:

mail.example.com → 203.0.113.10
203.0.113.10 → mail.example.com

این بخش معمولاً دست پنل DNS دامنه نیست و باید از ارائه‌دهنده VPS بخواید انجامش بده. برای من هم پشتیبانی سرور PTR رو تنظیم کرد. بعد از اعلام پشتیبانی، نتیجه رو از DNS عمومی بررسی کردم:

dig @1.1.1.1 +short A mail.example.com
dig @1.1.1.1 +short MX example.com
dig @1.1.1.1 +short -x 203.0.113.10

باید هم IP به نام درست برگرده، هم اون نام به همون IP. Google هم DNS مستقیم و معکوس معتبر رو جزو الزامات فرستنده‌ها آورده. اگه از IPv6 ارسال می‌کنید، اون مسیر هم تنظیمات خودش رو لازم داره؛ نبودن AAAA به این معنی نیست که ارسال خروجی IPv6 خاموش شده. الزامات ارسال به Gmail

نصب Stalwart با Docker Compose

روی سرور یه پوشه می‌سازیم:

mkdir -p ~/stalwart-mail
cd ~/stalwart-mail

داخلش فایل compose.yaml رو با این محتوا قرار میدیم:

services:
  stalwart:
    image: stalwartlabs/stalwart:v0.16
    restart: unless-stopped
    ports:
      - "25:25"
      - "465:465"
      - "587:587"
      - "993:993"
      - "443:443"
      - "127.0.0.1:8080:8080"
    volumes:
      - stalwart_config:/etc/stalwart
      - stalwart_data:/var/lib/stalwart
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

volumes:
  stalwart_config:
  stalwart_data:

مسیرهای volume توی این نمونه مربوط به نسخه ۰٫۱۶ هستن. تگ v0.16 هم نسخه دقیق و تغییرناپذیر نیست؛ برای محیط اصلی، تگ دقیق یا digest بررسی‌شده خودتون رو ثابت کنید. اگه از نسخه قدیمی‌تر میاید، راهنمای مهاجرت همون نسخه رو بخونید. نصب Stalwart با Docker

توی این فایل فرض کردم پورت ۴۴۳ آزاد هست. اگه Caddy یا Nginx روی سرور دارید، قبل از اجرا مسیر HTTPS رو مشخص کنید تا سر اشغال پورت به مشکل نخورید.

سرویس رو بالا میاریم و لاگ رو می‌بینیم:

docker compose up -d
docker compose logs --tail=100 stalwart

اولین اجرا اطلاعات ورود موقت مدیر رو توی لاگ bootstrap می‌نویسه. این اطلاعات رو نگه دارید و از کامپیوتر خودتون یه تونل SSH بزنید؛ نام کاربر و پورت رو با اطلاعات سرورتون عوض کنید:

ssh -p 22 -L 8080:127.0.0.1:8080 user@203.0.113.10

حالا توی مرورگر، http://127.0.0.1:8080/admin رو باز کنید. توی راه‌انداز، hostname رو mail.example.com و دامنه رو example.com بذارید. تولید DKIM و دریافت خودکار گواهی رو هم فعال کنید. توی حالت DNS دستی، صدور خودکار گواهی به دسترسی مستقیم پورت ۴۴۳ نیاز داره. اطلاعات مدیر دائمی رو ذخیره کنید و بعد از تکمیل راه‌انداز سرویس رو restart کنید. جزئیات راه‌انداز

docker compose restart stalwart

بعد از فعال‌شدن HTTPS، از https://mail.example.com/admin وارد بشید و حساب ایمیل رو بسازید. دسترسی HTTP مربوط به نصب رو هم غیرفعال کنید. توی نمونه بالا، ۸۰۸۰ از اول فقط روی loopback سرور منتشر شده تا از طریق تونل بهش برسیم.

پورت‌ها و اتصال برنامه ایمیل

برای اتصال Apple Mail، Thunderbird یا اپلیکیشن خودتون، تنظیمات معمول این شکلیه:

کاربرد سرور پورت و اتصال
دریافت با IMAP mail.example.com 993 با SSL/TLS
ارسال با SMTP mail.example.com 465 با SSL/TLS
روش جایگزین ارسال mail.example.com 587 با STARTTLS

نام کاربری معمولاً آدرس کامل حساب، مثل info@example.com هست. احراز هویت ارسال باید فعال باشه. دریافت پیام برای صندوق‌های دامنه خودمون با اجازه ارسال آزاد برای همه فرق داره؛ سرور نباید به Open Relay تبدیل بشه. سرویس‌هایی که لازم ندارید، مثل POP3، رو هم منتشر نکنید. تنظیمات امنیتی Stalwart

یه نکته سمت Docker هم هست: پورت‌های publishشده ممکنه قواعد UFW رو دور بزنن. برای بررسی دسترسی‌ها فقط به ufw status تکیه نکنید. ملاحظات فایروال Docker

اگه Reverse Proxy داشته باشیم

توی استقرار من Caddy هم هست، ولی نمونه این پست مستقیماً پورت ۴۴۳ رو به Stalwart داده. با Reverse Proxy باید مشخص کنید گواهی رو چه سرویسی می‌گیره و خود SMTP و IMAP چه گواهی‌ای ارائه میدن. HTTPS درست پنل، به‌تنهایی درست بودن TLS ایمیل رو ثابت نمی‌کنه.

بسته به معماری، میشه از DNS-01 برای صدور گواهی استفاده کرد یا گواهی رو با یه مسیر مشخص برای نصب و تمدید در اختیار Stalwart گذاشت. مدیریت گواهی در Stalwart

برای بررسی STARTTLS با OpenSSL می‌تونید این دستور رو اجرا کنید:

openssl s_client \
  -starttls smtp \
  -connect mail.example.com:587 \
  -servername mail.example.com \
  -verify_hostname mail.example.com \
  -verify_return_error </dev/null

روی سیستمی که CAهای معتبر رو می‌شناسه، بررسی باید موفق بشه و گواهی نام mail.example.com رو پوشش بده. این تست رو برای پورت‌های مورد استفاده دیگه هم متناسب با نوع اتصالشون انجام بدید.

تنظیم SPF ، DKIM و DMARC

بعد از بالا اومدن سرویس، این سه تا بخش رو داریم که هر کدوم یه کار متفاوت انجام میدن.

SPF؛ کدوم سرور اجازه ارسال داره؟

اگه فقط همین VPS از طرف دامنه ارسال می‌کنه، یه TXT برای ریشه دامنه می‌تونه این باشه:

Name: @
Value: v=spf1 ip4:203.0.113.10 -all

من توی تنظیمات خودم از v=spf1 mx -all استفاده کردم. این هم وقتی IP فرستنده جزو آدرس‌های MX دامنه باشه، می‌تونه درست باشه. صرفاً عوض‌کردن mx با ip4 قرار نیست مشکل Spam رو رفع کنه.

برای هر نام DNS فقط یه رکورد SPF داشته باشید. اگه سرویس دیگه‌ای هم از طرف شما ایمیل می‌فرسته، باید مجوزش توی همون سیاست واحد بیاد. قبل از -all همه مسیرهای ارسال رو مشخص کنید؛ این قسمت منابع خارج از سیاست رو غیرمجاز اعلام می‌کنه. SPF معمولاً دامنه envelope sender رو بررسی می‌کنه، نه فقط آدرسی که کاربر توی From می‌بینه. استاندارد SPF

DKIM؛ امضای پیام

Stalwart برای دامنه کلید می‌سازه و رکورد DNS لازم رو بهتون میده. اون رکورد رو از تنظیمات دامنه خودتون بردارید؛ ساختارش شبیه اینه:

Name: <selector>._domainkey
Value: v=DKIM1; k=rsa; p=<PUBLIC_KEY_FROM_STALWART>

این نمونه رو با همین placeholderها منتشر نکنید. selector و کلید عمومی باید مال نصب خودتون باشه؛ کلید خصوصی هم باید روی زیرساخت خودتون بمونه. گیرنده از selector توی هدر پیام استفاده می‌کنه تا کلید عمومی رو پیدا و امضا رو بررسی کنه. استاندارد DKIM

DMARC؛ نتیجه بررسی‌ها با دامنه فرستنده جور درمیاد؟

برای شروع پایش یه دامنه تازه، میشه از این TXT استفاده کرد:

Name: _dmarc
Value: v=DMARC1; p=none; rua=mailto:dmarc@example.com

صندوق یا alias آدرس dmarc@example.com رو هم بسازید تا گزارش‌ها رو بگیره. بعد از بررسی گزارش‌ها و درست بودن همه فرستنده‌های مجاز، می‌تونید سیاست رو به‌صورت کنترل‌شده به quarantine یا reject تغییر بدید.

برای عبور DMARC، حداقل یکی از SPF یا DKIM باید هم موفق باشه، هم با دامنه From هم‌راستا باشه. p=none امکان مشاهده و گزارش‌گرفتن میده؛ قرار نیست به‌خودی‌خود اعتبار IP رو درست کنه. استاندارد DMARC

روی PXLAds الان p=reject فعاله، ولی برای یه دامنه تازه بهتره قبل از سخت‌گیرانه‌کردن سیاست، مسیرهای ارسالش رو بشناسیم.

تجربه ارسال به Gmail و Yahoo

بعد از این تنظیمات، یه پیام با عنوان مشخص و چند جمله طبیعی به صندوقی که کنترلش دست خودتونه بفرستید. از همون صندوق هم جواب بدید تا دریافت روی سرورتون رو تست کنید.

برای پیدا کردن ارسال توی لاگ، آدرس گیرنده واقعی رو جای نمونه پایین بذارید:

docker compose logs --since=30m stalwart 2>&1 \
  | grep -F 'recipient@example.net'

بعد با شناسه صف یا نشست، ادامه همون ارسال رو دنبال کنید. این دستور برای حالتیه که لاگ به Console میره؛ اگه مقصدش رو تغییر دادید، فایل مربوط یا Telemetry رو ببینید.

PTR درست شد، ولی Yahoo همچنان ایمیل رو رد می‌کرد

توی روزهای اول، Gmail خطای PTR ناقص یا نامنطبق می‌داد. بعد از تنظیم Reverse DNS و بررسی تطابقش با A، ارسال‌های بعدی به Gmail پذیرفته شدن. ولی Yahoo همچنان این خطا رو می‌داد:

553 5.7.2 [TSS09]

توی بررسی لاگ و اتصال مشخص شد که ارتباط و STARTTLS انجام میشن، ولی Yahoo موقع MAIL FROM ارسال رو رد می‌کنه. یعنی هنوز نرسیده بودیم به جایی که بدنه و هدرهای پیام رو تحویل بدیم.

برای همین، از این خطا نمیشه نتیجه گرفت که متن اون پیام یا کلید DKIM مشکل داشته. چیزی که مشخص بود، محدودیت Yahoo برای IP فرستنده بود. دلیل اولیه این تصمیم رو هم از لاگ سرور خودم نمی‌شد فهمید.

برای پیگیری، درخواست بررسی رو توی Yahoo Sender Support ثبت کردم. تا زمان نوشتن این پست، رفع محدودیت تأیید نشده. توی پاسخ سرور هم صریحاً گفته شده بود که تکرار ارسال موفق نمیشه، پس ارسال پشت‌سرهم کمکی به بررسی این مورد نمی‌کرد.

این قسمت برای من مشخص کرد که درست‌شدن PTR لزوماً به معنی تغییر فوری تصمیم سرویس مقصد نیست.

Gmail پیام رو گرفت، ولی فرستاد داخل Spam

توی یه تست دیگه، Gmail این پاسخ رو به سرور داد:

250 OK ... - gsmtp

توی لاگ، تحویل کامل شده بود. ولی وقتی صندوق مقصد بررسی شد، پیام داخل Spam بود. در واقع سرور من وظیفه تحویل به Gmail رو انجام داده بود؛ اینکه Gmail پیام رو توی کدوم پوشه قرار بده، مرحله بعدیه.

توی گزارش‌های تجمیعی قبلی Google، ارسال مستقیم از IP من با SPF و امضای RSA معتبر ثبت شده بود. ولی اون گزارش‌ها نتیجه همین پیام جدید رو ثابت نمی‌کنن. برای این پیام باید نسخه دریافت‌شده رو توی Gmail باز کنیم و از Show original وضعیت SPF، DKIM و DMARC رو ببینیم. توضیحی که Gmail بالای پیام Spam نشون میده هم می‌تونه کمک کنه.

اعتبار IP و دامنه، الگوی ارسال و بازخورد گیرنده‌ها از چیزهایی هستن که روی تحویل اثر دارن. با این حال، فقط از 250 OK نمیشه فهمید دقیقاً کدوم عامل باعث Spam شده. راهنمای فرستنده‌های Gmail

پیام تست من عنوانی شبیه TEST و یه متن خیلی کوتاه داشت. برای اینکه ببینم Gmail پیام رو می‌گیره کافی بود، ولی نمونه خوبی از یه ایمیل واقعی کسب‌وکار نبود. این هم به معنی اثبات علت Spam نیست؛ برای نتیجه قطعی‌تر هنوز هدر همون پیام لازمه.

اگه پیام معتبره، گیرنده می‌تونه Not spam رو بزنه. این کار به تجربه همون صندوق کمک می‌کنه، ولی نباید ازش نتیجه بگیریم که مشکل برای تمام گیرنده‌ها رفع شده. راهنمای Spam در Gmail

گزارش‌های Google و نگهداری سرویس

بعد از راه‌اندازی، ایمیل‌هایی هم از Google برای گزارش DMARC و TLS می‌رسیدن. اول باید حواسمون باشه که خود این ایمیل‌ها خطا نیستن. وقتی توی DNS آدرس گزارش‌گیری گذاشتیم، داریم از سرویس‌های پشتیبان این استانداردها می‌خوایم نتیجه بررسی‌ها رو برامون بفرستن.

MTA-STS و TLS-RPT

MTA-STS به سرورهایی که ازش پشتیبانی می‌کنن میگه MXهای معتبر دامنه چی هستن و اتصال TLS رو چطور ارزیابی کنن. یه سیاست اولیه برای مشاهده می‌تونه این باشه:

version: STSv1
mode: testing
mx: mail.example.com
max_age: 604800

این متن باید با گواهی معتبر از این آدرس در دسترس باشه:

https://mta-sts.example.com/.well-known/mta-sts.txt

خود mta-sts.example.com هم باید به میزبانی که این فایل رو ارائه میده resolve بشه. برای کشف سیاست، TXT زیر رو می‌ذاریم:

Name: _mta-sts
Value: v=STSv1; id=2026091401

با تغییر سیاست، id رو هم عوض کنید. حالت testing برای بررسیه؛ بعد از اطمینان از مسیرها میشه درباره enforce تصمیم گرفت. این بخش روی امنیت انتقال ورودی کار می‌کنه و درمان مستقیم Spam ایمیل خروجی نیست. استاندارد MTA-STS

برای گزارش TLS هم میشه یه صندوق یا alias ساخت و این رکورد رو گذاشت:

Name: _smtp._tls
Value: v=TLSRPTv1; rua=mailto:tls-reports@example.com

داخل گزارش، تعداد ارتباط‌های موفق و خطاها رو ببینید. TLS-RPT درباره انتقال به دامنه گزارش‌شده است؛ از روی اون نمی‌تونیم بفهمیم ایمیل خروجی ما توی Inbox گیرنده قرار گرفته یا نه. استاندارد TLS-RPT

چیزهایی که بعد از نصب هم باید بهشون برسیم

وقتی سرویس بالا اومد، چند تا کار همچنان باقی می‌مونه:

  • گرفتن پشتیبان از داده‌ها و تنظیمات، همراه با اطلاعات لازم برای بازیابی کلیدها؛
  • نگه‌داشتن یه نسخه رمزگذاری‌شده بیرون از همون VPS؛
  • تست بازیابی توی یه محیط جداگانه؛
  • بررسی فضای دیسک، صف ارسال، انقضای گواهی و خطاهای ورود؛
  • خوندن تغییرات و راهنمای مهاجرت قبل از آپدیت نسخه.

Volumeهای Docker کمک می‌کنن با بازسازی کانتینر داده‌ها از بین نرن، ولی جای backup رو نمی‌گیرن. روش پشتیبان‌گیری هم به storage backend بستگی داره. کپی معمولی فایل‌های یه دیتابیس زنده رو بدون بررسی سازگاری، پشتیبان مطمئن فرض نکنید. ابزار export/import داخلی Stalwart هم برای کار با دیتابیس الزام توقف سرویس داره. ملاحظات نگهداری دیتابیس

هنوز این بخش از پروژه چالش‌های خودش رو داره

فعلاً سرویس ایمیل PXLAds راه افتاده، ولی پیگیری محدودیت Yahoo و بررسی دقیق‌تر Spam شدن اون پیام Gmail هنوز باقی مونده. برای همین نمی‌خوام این تجربه رو به‌عنوان روشی برای رسیدن قطعی همه ایمیل‌ها به Inbox معرفی کنم.

چیزی که تا اینجا برام مفید بوده، جدا بررسی‌کردن هر بخشه: DNS درست resolve میشه؟ اتصال TLS برقراره؟ سرور مقصد پیام رو پذیرفته؟ هدر پیام دریافت‌شده چه نتیجه‌ای داره؟ با همین تفکیک میشه فهمید قدم بعدی کجاست، بدون اینکه هر بار چند تا رکورد رو بی‌دلیل عوض کنیم.

امیدوارم اگه شما هم می‌خواید برای پروژه خودتون ایمیل سرور راه بندازید، این تجربه به کارتون بیاد. اگه با Stalwart یا چالش‌های ارسال به Gmail و Yahoo تجربه‌ای داشتید، خوشحال میشم بخونمش.