اگر در Show original جیمیل جلوی DKIM چیزی جز pass می‌بینید، پیام یا امضا نشده یا امضایش با کلید DNS نمی‌خواند. بسته opendkim کنار Postfix پیام‌های خروجی را با کلید خصوصی سرور امضا می‌کند؛ dkim=pass وقتی می‌آید که selector، کلید، رکورد TXT، سوکت و جدول امضا با هم جور باشند.

در یک نگاه: پنج خروجی سرور سالم

  • ✅ کلید ساخته شد: s2026a.private و s2026a.txt
  • ✅ رکورد منتشر شد: TXT روی s2026a._domainkey
  • ✅ کلید سنجیده شد: 1 pass, 0 fail در خط آخر آزمون
  • ✅ پیام امضا شد: هدر DKIM-Signature با d= و s= شما
  • ✅ گیرنده پذیرفت: dkim=pass در خط Authentication-Results

💡 کلید خصوصی امضا می‌کند، DNS می‌سنجد

فرستنده هدرها و بدنه را با کلید خصوصی امضا می‌کند و گیرنده امضا را با کلید عمومی دامنه در DNS می‌سنجد.
✅ اگر Postfix به سوکت فیلتر نرسد، با تنظیم accept پیام بی‌امضا می‌رود و فقط یک warning در لاگ می‌ماند.
✅ بقیه شرط‌های اینباکس در راهنمای ارسال ایمیل از سرور ایران بدون اسپم آمده‌اند.

خروجی‌های Postfix، هدرها و خطاهای لاگ این راهنما از آزمایشی واقعی روی Ubuntu 24.04 و Postfix 3.8.6 با دامنه example.ir آمده‌اند. خروجی آزمون کلید را، چون DNS بیرونی در دسترس نبود، از کد خود ابزار آورده‌ایم. اولین سطری که با خروجی سرورتان فرق کرد، جای گره است.

نمودار مسیر امضای ایمیل از Postfix تا فیلتر امضا و کلید عمومی در DNS، opendkim

امضای DKIM روی هدر ایمیل چه چیزی را ثابت می‌کند

امضای DKIM ثابت می‌کند دارنده کلید خصوصی دامنه d= مسئولیت پیام را پذیرفته و هدرهای امضاشده و بدنه بعد از امضا عوض نشده‌اند. سند RFC 6376 امضا را فقط گره‌زدن دامنه به پیام می‌داند. تصمیم اسپم با گیرنده است.

فیلتر OpenDKIM مثل مهر دفترخانه اسناد رسمی کار می‌کند: دفترخانه پای سند مهر می‌زند و هر کسی آن را با نمونه ثبت‌شده مقایسه می‌کند. مهر اینجا کلید خصوصی سرور است و نمونه ثبت‌شده، کلید عمومی در DNS.

نامی که گیرنده در DNS جست‌وجو می‌کند

گیرنده برای d=example.ir و s=s2026a نام s2026a._domainkey.example.ir را می‌پرسد. پس selector در جدول کلید، نام رکورد TXT و هدر امضا باید یکی باشد.

نصب OpenDKIM و ساخت کلید با selector

روی Ubuntu 24.04 دو بسته لازم است: خود فیلتر و بسته ابزارهای ساخت و آزمون کلید. نسخه نصب‌شده 2.11.0~beta2-9build4 بود و پیکربندی پیش‌فرضش از Debian آمده است.

sudo apt install opendkim opendkim-tools
sudo -u opendkim opendkim-genkey -b 2048 -r -D /etc/dkimkeys -d example.ir -s s2026a
sudo ls -l /etc/dkimkeys

خروجی درست: دو فایل s2026a.private و s2026a.txt با دسترسی -rw------- که مالکشان کاربر فیلتر است.

خروجی خراب: کلیدی که مالکش root مانده برای فیلتر خواندنی نیست. Postfix این خط را نوشت و پیام تا اصلاح مالکیت (جدول بخش آخر) در صف ماند:

milter-reject: END-OF-MESSAGE from localhost[127.0.0.1]: 4.7.1 Service unavailable - try again later

پیش‌فرض -b در Debian ۲۰۴۸ و در نسخه اصلی ۱۰۲۴ بیت است (صفحه راهنمای genkey در Debian)؛ -r کلید را به ایمیل محدود می‌کند و بدون -s نام selector همان default است.

🔸 نام‌گذاری selector و چرخش کلید

سند RFC 6376 تاریخ را یکی از الگوهای نام selector می‌آورد. با s2026a، چرخش یعنی ساختن s2026b، انتشار رکوردش و عوض کردن جدول کلید. رکورد قدیمی را مدتی نگه دارید و بعد با p= خالی، که همین RFC نشانه لغو می‌داند، باطلش کنید.

جدول کلید، جدول امضا و خط Socket

طبق صفحه راهنمای پیکربندی در Debian، با تعریف KeyTable گزینه‌های Domain، Selector و KeyFile نادیده گرفته می‌شوند. هر جدول را جداگانه بسازید:

sudo tee /etc/dkimkeys/key.table <<'EOF'
s2026a._domainkey.example.ir  example.ir:s2026a:/etc/dkimkeys/s2026a.private
EOF
sudo tee /etc/dkimkeys/signing.table <<'EOF'
*@example.ir  s2026a._domainkey.example.ir
EOF

ستون اول KeyTable نامی دلخواه است و ستون دوم دامنه، selector و مسیر کلید را با دونقطه جدا می‌کند. با پیشوند refile، سمت چپ جدول امضا الگویی برای آدرس From است و پیام بدون ردیف جورشده بی‌امضا می‌رود. دامنه دوم کلید و رکورد تازه و یک ردیف در هر جدول می‌خواهد.

بعد در فایل /etc/opendkim.conf خط Socket را با خط اول زیر عوض کنید و سه خط دیگر را اضافه کنید:

Socket         local:/var/spool/postfix/opendkim/opendkim.sock
KeyTable       file:/etc/dkimkeys/key.table
SigningTable   refile:/etc/dkimkeys/signing.table
InternalHosts  127.0.0.1, ::1
sudo install -d -o opendkim -g opendkim -m 750 /var/spool/postfix/opendkim
sudo systemctl restart opendkim
sudo ls -l /var/spool/postfix/opendkim

خروجی درست: سوکتی با دسترسی srwxrwx--- که خط UMask 007 پیکربندی Ubuntu می‌سازد.

⚠️ سوکت پیش‌فرض از داخل chroot دیده نمی‌شود

پیکربندی Ubuntu سوکت را در /run می‌سازد، ولی Postfix فرایندهای smtpd و cleanup را در /var/spool/postfix زندانی (chroot) می‌کند. راهنمای Milter در Postfix می‌گوید مسیر مطلق سوکت برای فرایند chroot‌شده از پوشه صف حساب می‌شود. در آزمایش ما با سوکت موجود در /run، Postfix نوشت connect to Milter service unix:/run/opendkim/opendkim.sock: No such file or directory و پیام بی‌امضا رفت. فایل سر جایش بود؛ Postfix از پشت دیوار chroot نمی‌دیدش. برای سوکت TCP، فیلتر inet:8891@localhost و Postfix inet:localhost:8891 می‌نویسد.

امضای DKIM روی هدر ایمیل چه چیزی را ثابت می‌کند

رکورد DNS را منتشر و با opendkim-testkey آزمون کنید

محتوای s2026a.txt را به‌صورت رکورد TXT روی نام s2026a._domainkey منتشر کنید تا ابزار آزمون کلید آن را از DNS بگیرد و با کلید خصوصی مقایسه کند. فایل ساخته‌شده (کلید کوتاه شده):

s2026a._domainkey  IN  TXT  ( "v=DKIM1; h=sha256; k=rsa; s=email; "
    "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqAVInRW3hJjt..."
    "...JiJNmw9wIDAQAB" )  ; ----- DKIM key s2026a for example.ir

برچسب h=sha256 را Debian می‌گذارد و s=email از پرچم -r می‌آید. فیلد نام در پنل s2026a._domainkey است؛ کار با رکورد متنی در راهنمای تنظیمات DNS و مدیریت رکوردها آمده است.

💡 رشته‌های جدا در رکورد TXT

ابزار genkey کلید را در تکه‌های ۲۵۰ کاراکتری داخل چند جفت گیومه می‌نویسد و طبق RFC 6376 گیرنده آن‌ها را بی‌فاصله به هم می‌چسباند. RFC 8301 هم می‌گوید کلید RSA خیلی بلندتر از ۱۰۲۴ بیت در یک رشته ۲۵۶ بایتی جا نمی‌شود؛ رکورد ما ۴۲۹ کاراکتر بود. برای پنل تک‌فیلدی مقدار یکپارچه را این‌طور بسازید:
sudo sed -n 's/.*"\(.*\)".*/\1/p' /etc/dkimkeys/s2026a.txt | tr -d '\n'

آزمون کلید با همان کاربر فیلتر

اول با dig +short TXT s2026a._domainkey.example.ir ببینید رکورد دیده می‌شود؛ خروجی خالی یعنی هنوز نه؛ DNS عجله ندارد. با KeyTable در پیکربندی و بدون d و s، ابزار آزمون همه کلیدهای جدول را می‌سنجد:

sudo -u opendkim opendkim-testkey -vvv

خروجی مورد انتظار (سه خط آخر، از کد منبع ابزار؛ DNS بیرونی در دسترس نبود):

opendkim-testkey: key s2026a._domainkey.example.ir: OK
opendkim-testkey: key s2026a._domainkey.example.ir not secure
opendkim-testkey: 1 key checked; 1 pass, 0 fail

خروجی خراب (پیام‌های خطای همان کد): record not found یا keys do not match در خط کلید، و 0 pass, 1 fail در خط آخر. علت هر دو در جدول عیب‌یابی آمده است.

اتصال به Postfix و خواندن نتیجه در هدر جیمیل

اتصال به Postfix و خواندن نتیجه در هدر جیمیل

سرویس Postfix پیام‌های SMTP را از مسیر smtpd_milters و پیام‌های فرمان sendmail (مثل تابع mail در PHP) را از non_smtpd_milters به OpenDKIM می‌دهد. برای پیام sendmail، cleanup اتصالی از 127.0.0.1 می‌سازد که InternalHosts پیش‌فرض امضایش می‌کند.

postconf -F smtp/inet/chroot cleanup/unix/chroot
sudo usermod -aG opendkim postfix
sudo postconf -e 'smtpd_milters = unix:/opendkim/opendkim.sock' \
  'non_smtpd_milters = $smtpd_milters' 'milter_default_action = accept'
sudo systemctl restart opendkim postfix

خروجی درست: دستور اول روی Ubuntu 24.04 دو خط smtp/inet/chroot = y و cleanup/unix/chroot = y چاپ کرد؛ برای همین مسیر سوکت بدون پیشوند /var/spool/postfix آمده و با n باید کامل نوشته شود. طبق README بسته Debian، شاید لازم باشد کاربر postfix عضو گروه فیلتر شود.

خروجی خراب: بدون عضویت گروهی، Postfix جلوی همان سوکت Permission denied نوشت. پیش‌فرض milter_default_action مقدار tempfail است؛ با accept پیام بی‌امضا می‌رود، پس /var/log/mail.log را برای connect to Milter service زیر نظر بگیرید.

پیام تست را به جیمیل خودتان بفرستید؛ پورت 25 خروجی باید باز باشد:

printf 'From: noreply@example.ir\nTo: you@gmail.com\nSubject: DKIM test\n\nTest.\n' \
  | sendmail -f noreply@example.ir you@gmail.com

خواندن یک هدر واقعی، خط به خط

این هدر را همان Postfix از راه سوکت chroot امضا کرد. خط Authentication-Results را همان فیلتر در نقش تأییدکننده، با کلید عمومی و نام میزبان mail.example.ir، نوشت (b= کوتاه شده):

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.ir; s=s2026a;
	t=1793740674; bh=eO1OH8foLWTI6MKeNhM6rN4LIOqkc2vpJmQ7ZdGuSSA=;
	h=From:To:Subject:Date:From;
	b=Ab9cqnQJrf2atVogqmM7wmzkJFruukOYWUrLiYMLqLZyc/ApWD0q2ZVtCRQ+1lGdT ...

Authentication-Results: mail.example.ir;
	dkim=pass (2048-bit key) header.d=example.ir header.i=@example.ir
	header.a=rsa-sha256 header.s=s2026a header.b=Ab9cqnQJ
  1. برچسب d= دامنه مسئول، s= رکورد کلید و c=relaxed/simple پیش‌فرض Ubuntu است.
  2. در h= نام From دو بار آمده؛ خط OversignHeaders آن را یک بار بیشتر امضا می‌کند تا طبق RFC 6376 گیرنده From افزوده‌شده بعد از امضا را تشخیص دهد.
  3. مقدار bh= درهم‌سازی بدنه است؛ با تغییر یک کلمه بدنه، نتیجه dkim=fail reason="signature verification failed" شد.
  4. مقدار b= خود امضاست؛ header.b هشت کاراکتر اول آن را تکرار می‌کند و header.i=@example.ir مقدار پیش‌فرض i= است که RFC 6376 از d= می‌سازد.

در جیمیل از منوی More کنار Reply گزینه Show original را بزنید. خط Authentication-Results جیمیل شکل دیگری دارد؛ فقط dkim=pass و دامنه و selector خودتان را مقایسه کنید.

جدول عیب‌یابی: نشانه، علت و اصلاح

نشانه در لاگ یا خروجیعلتاصلاح
No such file or directoryسوکت بیرون از chrootخط Socket با مسیر /var/spool/postfix/opendkim
Permission deniedکاربر postfix عضو گروه فیلتر نیستافزودن با usermod -aG opendkim postfix و راه‌اندازی دوباره Postfix
4.7.1 Service unavailableفیلتر کلید را نمی‌خوانداجرای chown opendkim:opendkim /etc/dkimkeys/s2026a.*
record not foundنام رکورد اشتباه یا منتشرنشدهنام selector._domainkey را با dig بسنجید
keys do not matchرکورد مال کلید دیگری استمحتوای s2026a.txt فعلی را منتشر کنید

هدر خام را که باز کردید، معمولاً این پرسش‌ها پیش می‌آیند.

رکورد DKIM چیست و کجای DNS ثبت می‌شود؟

رکوردی از نوع TXT است که کلید عمومی دامنه را نگه می‌دارد؛ نامش از selector، بخش _domainkey و دامنه ساخته و در پنل DNS ثبت می‌شود.

سند RFC 8301 حداقل را ۱۰۲۴ بیت و توصیه را ۲۰۴۸ بیت گذاشته است؛ راهنمای فرستندگان جیمیل هم همین را می‌گوید. هزینه ۲۰۴۸ بیت، رکورد چندرشته‌ای است.

پاسخ DNS با DNSSEC امضا نشده است و این خط جلوی قبولی کلید را نمی‌گیرد.

هر دامنه کلید و رکورد خودش و یک ردیف در هر دو جدول می‌خواهد؛ تنظیم Postfix عوض نمی‌شود.

با تنظیم accept این راهنما پیام بی‌امضا تحویل می‌شود و warning در لاگ می‌ماند؛ با پیش‌فرض tempfail پیام برگشت موقت می‌خورد و دوباره ارسال می‌شود.

بله، اگر با فرمان sendmail یا SMTP همین سرور بروند و From با جدول امضا جور شود.

نه به‌تنهایی؛ گیرنده PTR، SPF، DMARC و سابقه IP را هم می‌سنجد.

جمع‌بندی؛ امضای ایمیل با OpenDKIM روی Postfix هر جا امضا خراب شود، یکی از این سه بخش ایراد دارد. بخش اول کلید است: selector یکسان در جدول کلید، نام رکورد TXT و هدر امضا، و فایل خصوصی که مالکش کاربر فیلتر باشد. بخش دوم DNS است: رکوردی که آزمون کلید با «1 pass» قبولش کند. بخش سوم سوکت است: فایلی داخل /var/spool/postfix که Postfix در chroot ببیند و گروهش اجازه باز کردنش را بدهد. این سه را با نمونه‌ها و جدول عیب‌یابی بسنجید و ایمیل تست را به جیمیل بفرستید. با دیدن dkim=pass نوبت بقیه شرط‌های اینباکس است.

🚀 امضا تنها شرط اینباکس نیست

رکوردهای PTR و DMARC، پورت 25 و بلک‌لیست هر کدام جدا از امضا سنجیده می‌شوند و در چک‌لیست تحویل ایمیل آمده‌اند.

در هدر آخرین ایمیلی که سرورتان فرستاده، جلوی dkim چه نوشته شده است؟ همان کلمه را با خط خطای لاگ بیاورید و دامنه را مثل این راهنما example.ir کنید.

منابع

وب داده
وب داده

جدید ترین مطالب آموزشی و کاربردی را در اینجا بخوانید !
ما با بهره‌گیری از دانش روز و تجربه متخصصان حوزه فناوری، مجموعه‌ای از آموزش‌های کاربردی و مقالات تخصصی را گردآوری کرده‌ایم که هر یک، پاسخی دقیق به پرسش‌های شماست. ؛ از مفاهیم پایه تا پیچیده‌ترین تکنیک‌های حرفه‌ای. اینجا، دانش با زبانی ساده اما عمیق در اختیار شما قرار می‌گیرد.

مقاله‌ها: 263
پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *