
به اشتراک بگذارید

به اشتراک بگذارید
اگر در Show original جیمیل جلوی DKIM چیزی جز pass میبینید، پیام یا امضا نشده یا امضایش با کلید DNS نمیخواند. بسته opendkim کنار Postfix پیامهای خروجی را با کلید خصوصی سرور امضا میکند؛ dkim=pass وقتی میآید که selector، کلید، رکورد TXT، سوکت و جدول امضا با هم جور باشند.
در یک نگاه: پنج خروجی سرور سالم
s2026a.private و s2026a.txts2026a._domainkey1 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 بیرونی در دسترس نبود، از کد خود ابزار آوردهایم. اولین سطری که با خروجی سرورتان فرق کرد، جای گره است.
📌 از کلید تا dkim=pass
◀️ امضای DKIM روی هدر ایمیل چه چیزی را ثابت میکند
◀️ نصب فیلتر امضا و ساخت کلید با selector
◀️ رکورد DNS را منتشر و با ابزار آزمون کلید بسنجید
◀️ اتصال به Postfix و خواندن نتیجه در هدر جیمیل

امضای DKIM ثابت میکند دارنده کلید خصوصی دامنه d= مسئولیت پیام را پذیرفته و هدرهای امضاشده و بدنه بعد از امضا عوض نشدهاند. سند RFC 6376 امضا را فقط گرهزدن دامنه به پیام میداند. تصمیم اسپم با گیرنده است.
فیلتر OpenDKIM مثل مهر دفترخانه اسناد رسمی کار میکند: دفترخانه پای سند مهر میزند و هر کسی آن را با نمونه ثبتشده مقایسه میکند. مهر اینجا کلید خصوصی سرور است و نمونه ثبتشده، کلید عمومی در DNS.
گیرنده برای d=example.ir و s=s2026a نام s2026a._domainkey.example.ir را میپرسد. پس selector در جدول کلید، نام رکورد TXT و هدر امضا باید یکی باشد.
روی 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 نشانه لغو میداند، باطلش کنید.
طبق صفحه راهنمای پیکربندی در 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 مینویسد.

محتوای 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 پیامهای 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
d= دامنه مسئول، s= رکورد کلید و c=relaxed/simple پیشفرض Ubuntu است.h= نام From دو بار آمده؛ خط OversignHeaders آن را یک بار بیشتر امضا میکند تا طبق RFC 6376 گیرنده From افزودهشده بعد از امضا را تشخیص دهد.bh= درهمسازی بدنه است؛ با تغییر یک کلمه بدنه، نتیجه dkim=fail reason="signature verification failed" شد.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 فعلی را منتشر کنید |
هدر خام را که باز کردید، معمولاً این پرسشها پیش میآیند.
رکوردی از نوع 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 کنید.
منابع