Vì sao OpenDKIM chạy nhưng không ký thư là lỗi rất khó chịu
OpenDKIM chạy nhưng thư gửi ra vẫn không có chữ ký DKIM là một lỗi khá “âm thầm” trên Debian/Ubuntu. Dịch vụ báo active (running), log không thấy lỗi nghiêm trọng, DNS TXT cũng đã khai báo, nhưng khi kiểm tra header thư thì hoàn toàn không có dòng DKIM-Signature.
Vấn đề này dễ làm bạn mất nhiều giờ vì mọi thứ nhìn qua đều đúng. Đặc biệt với server chạy Postfix, nhiều tên miền, hoặc hệ thống mail tự quản cho MMO, affiliate, transactional mail, lỗi DKIM không ký có thể làm giảm độ tin cậy của thư mà bạn không nhận ra ngay.
Pain point 1: gửi mail vẫn đi, nên tưởng là ổn
DKIM không giống lỗi SMTP bị reject ngay. Nếu OpenDKIM không ký, Postfix vẫn có thể gửi thư bình thường.
Bạn chỉ phát hiện khi:
- Gmail hiển thị
dkim=none. - Mail vào spam nhiều hơn.
- Công cụ kiểm tra báo thiếu chữ ký.
- DMARC fail vì không có DKIM alignment.
- Header thư không có dòng
DKIM-Signature.
Điều khó chịu là người nhận vẫn thấy thư đến. Vì vậy lỗi có thể tồn tại nhiều ngày, thậm chí nhiều tháng.
Pain point 2: tài liệu gốc và gói Debian/Ubuntu không giống nhau
Rất nhiều hướng dẫn OpenDKIM trên mạng bảo bạn cấu hình KeyTable, SigningTable, ExternalIgnoreList, InternalHosts trong /etc/opendkim.conf.
Nhưng trên Debian/Ubuntu, tùy phiên bản và cách đóng gói, file thực sự được service đọc có thể khác với bạn nghĩ. Có trường hợp bạn sửa đúng cú pháp nhưng sửa nhầm file, hoặc thêm dòng vào file không được include.
Kết quả là OpenDKIM vẫn chạy, không báo lỗi rõ ràng, nhưng rule ký thư không bao giờ được áp dụng.
Nếu bạn đang dựng mail server từ đầu, nên đọc thêm phần nền tảng về DNS và bản ghi mail tại hướng dẫn DNS cơ bản và các ghi chú trong cụm Email trên VSIS.

Triệu chứng: dịch vụ active, log sạch, thư vẫn không có chữ ký
Triệu chứng phổ biến nhất là systemctl báo OpenDKIM đang chạy:
systemctl status opendkim
Bạn thấy kiểu như:
Active: active (running)
Postfix cũng không báo lỗi khi gửi thư:
journalctl -u postfix -n 100 --no-pager
OpenDKIM có thể cũng không báo lỗi đáng chú ý:
journalctl -u opendkim -n 100 --no-pager
Nhưng khi gửi thư thử đến Gmail, mở phần “Show original”, kết quả lại là:
DKIM: 'NONE'
Hoặc trong header hoàn toàn không có:
DKIM-Signature:
Nếu dùng dòng lệnh để xem mail raw, bạn cũng không thấy chữ ký DKIM được chèn vào phần đầu thư.
Một số dấu hiệu khác:
opendkim-testkeybáo key hợp lệ, nhưng thư vẫn không ký.- DNS TXT đã đúng selector.
- Private key có tồn tại và quyền đọc có vẻ ổn.
- Socket giữa Postfix và OpenDKIM đã khai báo.
- Không có lỗi
permission denied. - Không có lỗi
key not found. - Không có lỗi
no signing table match.
Trường hợp nguy hiểm nhất là log quá sạch. Bạn tưởng OpenDKIM đã xử lý thư, nhưng thật ra milter chưa được gọi, hoặc được gọi nhưng không tìm thấy rule ký phù hợp.
Bạn nên kiểm tra cả hai phía:
- Postfix có gọi milter OpenDKIM không.
- OpenDKIM có nạp đúng cấu hình ký không.
Với Postfix, xem nhanh:
postconf -n | grep -E 'milter|smtpd_milters|non_smtpd_milters'
Ví dụ cấu hình thường gặp:
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
milter_default_action = accept
Nếu bạn dùng socket dạng Unix, có thể là:
smtpd_milters = unix:/run/opendkim/opendkim.sock
non_smtpd_milters = unix:/run/opendkim/opendkim.sock
Điểm cần nhớ: DKIM ký thư ở luồng gửi ra. Nếu bạn chỉ gửi bằng sendmail nội bộ mà thiếu non_smtpd_milters, thư có thể không đi qua OpenDKIM.
Nguyên nhân: gói Debian không đọc KeyTable như tài liệu gốc
OpenDKIM bản upstream thường được hướng dẫn cấu hình trực tiếp trong /etc/opendkim.conf. Bạn sẽ thấy các dòng như:
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList /etc/opendkim/TrustedHosts
InternalHosts /etc/opendkim/TrustedHosts
Vấn đề là trên Debian/Ubuntu, package có thể dùng file tham số riêng, unit systemd riêng, hoặc mặc định khác với tài liệu gốc. Bạn không nên mặc định rằng cứ sửa /etc/opendkim.conf là chắc chắn có hiệu lực.
Một số hệ thống dùng thêm:
/etc/default/opendkim
Trong file này có thể khai báo:
SOCKET=inet:8891@localhost
Hoặc có dòng trỏ đến file cấu hình:
DAEMON_OPTS="-x /etc/opendkim.conf"
Trên vài bản cài, OpenDKIM có thể chạy với option -x chỉ định file config. Nếu option này trỏ sang file khác, mọi sửa đổi của bạn trong file còn lại sẽ bị bỏ qua.
Bạn cần phân biệt rõ:
| Thành phần | Vai trò | Lỗi hay gặp |
|---|---|---|
/etc/opendkim.conf | File cấu hình chính thường thấy | Sửa file này nhưng service không đọc |
/etc/default/opendkim | Biến môi trường và tham số daemon trên Debian/Ubuntu | Socket hoặc config path khác với Postfix |
KeyTable | Ánh xạ selector/domain sang private key | Đúng cú pháp nhưng không được include |
SigningTable | Quy định email/domain nào dùng key nào | Sai pattern, thư không match |
TrustedHosts | Host/IP được phép ký | Thiếu 127.0.0.1 hoặc hostname server |
| Postfix milter | Cầu nối Postfix sang OpenDKIM | Khai báo sai socket hoặc thiếu non_smtpd_milters |
Điểm gây nhầm nhiều nhất là KeyTable không tự có hiệu lực chỉ vì file tồn tại. Nó chỉ được dùng khi file cấu hình đang chạy có khai báo KeyTable.
Tương tự, SigningTable không được đọc nếu bạn chỉ tạo file mà không khai báo trong config chính.
Với Debian/Ubuntu, cách xử lý an toàn là không đoán. Hãy đọc đúng process đang chạy.
Đọc đúng tệp cấu hình nào đang có hiệu lực
Bước đầu tiên là xem OpenDKIM đang chạy bằng lệnh nào:
ps aux | grep '[o]pendkim'
Bạn có thể thấy dạng:
opendkim -x /etc/opendkim.conf
Hoặc chỉ thấy daemon chạy không rõ option. Khi đó tiếp tục xem systemd unit:
systemctl cat opendkim
Lệnh này cho biết service đang dùng file unit nào và có override không.
Bạn cần chú ý các dòng:
EnvironmentFile=-/etc/default/opendkim
ExecStart=/usr/sbin/opendkim ...
Nếu ExecStart có dùng biến môi trường, hãy mở file:
cat /etc/default/opendkim
Tìm các dòng liên quan:
SOCKET=
DAEMON_OPTS=
Nếu có:
DAEMON_OPTS="-x /some/other/file.conf"
thì file /some/other/file.conf mới là nơi bạn cần kiểm tra.
Bạn cũng có thể dùng strace để xác nhận OpenDKIM đang mở file nào, nhưng thường không cần. Cách nhẹ hơn là kiểm tra cấu hình qua log khi restart.
systemctl restart opendkim
journalctl -u opendkim -n 100 --no-pager
Nếu sửa file cấu hình và cố tình thêm lỗi cú pháp nhỏ, service phải báo lỗi. Nếu không báo gì, rất có thể bạn đang sửa nhầm file.
Không nên giữ lỗi cú pháp trên server thật. Cách này chỉ dùng rất nhanh để xác nhận, rồi hoàn nguyên ngay.
Mẫu cấu hình nên kiểm tra
Một cấu hình cơ bản cho nhiều tên miền thường có:
Syslog yes
UMask 002
Mode sv
Canonicalization relaxed/simple
SubDomains no
OversignHeaders From
Socket inet:8891@localhost
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList /etc/opendkim/TrustedHosts
InternalHosts /etc/opendkim/TrustedHosts
Nếu bạn ký cả thư từ ứng dụng chạy trên localhost, TrustedHosts nên có:
127.0.0.1
localhost
::1
Có thể thêm hostname server:
mail.example.com
Và IP private nếu app gửi qua mạng nội bộ:
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
Không nên thêm bừa 0.0.0.0/0. Làm vậy có thể khiến server ký cho nguồn không mong muốn nếu SMTP relay bị cấu hình hở.
Kiểm tra Postfix có dùng đúng socket không
Nếu OpenDKIM nghe ở:
Socket inet:8891@localhost
Postfix nên có:
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
Kiểm tra bằng:
postconf -n | grep milter
Sau khi sửa:
systemctl restart opendkim
systemctl reload postfix
Nếu dùng Unix socket, cần chú ý quyền thư mục. Postfix chạy trong môi trường chroot ở một số cấu hình có thể không thấy socket ngoài chroot.
Với người mới, socket TCP localhost thường dễ kiểm tra hơn:
ss -lntp | grep 8891
Nếu thấy OpenDKIM đang listen trên 127.0.0.1:8891, Postfix có thể kết nối được nếu khai báo đúng.
Tài liệu chính thức của OpenDKIM có thể xem tại OpenDKIM Project và thông tin chuẩn DKIM nằm trong RFC 6376.
Kiểm tra bằng opendkim-testkey và thư thử
Sau khi biết đúng file cấu hình, bạn cần kiểm tra theo hai lớp:
- Key và DNS có khớp không.
- Mail thật có được ký không.
opendkim-testkey chỉ kiểm tra key/DNS, không chứng minh Postfix đang ký thư. Đây là điểm nhiều người nhầm.
Giả sử bạn có domain example.com, selector default, private key:
/etc/opendkim/keys/example.com/default.private
File KeyTable:
default._domainkey.example.com example.com:default:/etc/opendkim/keys/example.com/default.private
File SigningTable:
*@example.com default._domainkey.example.com
Kiểm tra key:
opendkim-testkey -d example.com -s default -k /etc/opendkim/keys/example.com/default.private -vvv
Nếu DNS đúng, bạn thường thấy không có lỗi nghiêm trọng. Một số cảnh báo về key không secure có thể liên quan DNSSEC, không phải lúc nào cũng chặn ký.
Bạn cũng có thể kiểm tra bản ghi DNS:
dig TXT default._domainkey.example.com +short
Kết quả phải chứa public key dạng:
"v=DKIM1; k=rsa; p=..."
Nếu bạn mới thêm DNS, hãy đợi TTL hoặc kiểm tra từ resolver công cộng:
dig @8.8.8.8 TXT default._domainkey.example.com +short
dig @1.1.1.1 TXT default._domainkey.example.com +short
Gửi thư thử đúng cách
Sau khi restart dịch vụ:
systemctl restart opendkim
systemctl reload postfix
Gửi một thư thử từ đúng domain:
echo "DKIM test" | mail -s "DKIM test" [email protected]
Hoặc dùng sendmail:
sendmail -v [email protected]
Nội dung mẫu:
From: [email protected]
To: [email protected]
Subject: DKIM test
Hello
.
Nếu gửi bằng sendmail nội bộ, nhớ rằng Postfix cần có:
non_smtpd_milters = inet:localhost:8891
Nếu thiếu dòng này, thư từ local submission có thể không được OpenDKIM ký dù thư gửi qua SMTP port 25 lại có ký.
Khi nhận thư ở Gmail, chọn “Show original”. Bạn muốn thấy:
DKIM: PASS
Trong header raw, tìm:
DKIM-Signature:
Nếu có chữ ký nhưng Gmail báo fail, vấn đề thường là DNS public key, canonicalization, hoặc thư bị sửa sau khi ký. Nếu không có chữ ký, quay lại kiểm tra milter, SigningTable, TrustedHosts.
Đọc log có trọng tâm
Mở log theo thời gian thực:
journalctl -u opendkim -f
Ở cửa sổ khác, gửi thư thử. Nếu OpenDKIM không có log gì khi thư đi ra, nhiều khả năng Postfix chưa gọi milter.
Khi đó xem log Postfix:
journalctl -u postfix -f
Nếu Postfix báo không kết nối được milter, kiểm tra socket. Nếu Postfix không nhắc gì, kiểm tra postconf -n.
Một số hệ thống ghi log vào:
/var/log/mail.log
Xem bằng:
tail -f /var/log/mail.log
Nếu bạn vận hành nhiều domain, nên ghi chú lại từng selector, DNS, đường dẫn private key. Bạn có thể tham khảo thêm checklist vận hành mail tại tài liệu cấu hình email server và phần kiểm tra bản ghi DNS cho email.
Cách bố trí khoá cho nhiều tên miền trên một máy
Với một máy gửi mail cho nhiều domain, đừng dùng một file key lẫn lộn. Nên bố trí theo thư mục từng tên miền để dễ kiểm tra và xoay key.
Ví dụ:
/etc/opendkim/
├── KeyTable
├── SigningTable
├── TrustedHosts
└── keys/
├── example.com/
│ ├── default.private
│ └── default.txt
└── example.net/
├── default.private
└── default.txt
Tạo thư mục:
mkdir -p /etc/opendkim/keys/example.com
mkdir -p /etc/opendkim/keys/example.net
Tạo key cho từng domain:
opendkim-genkey -b 2048 -d example.com -s default -D /etc/opendkim/keys/example.com
opendkim-genkey -b 2048 -d example.net -s default -D /etc/opendkim/keys/example.net
Đặt quyền:
chown -R opendkim:opendkim /etc/opendkim
chmod 750 /etc/opendkim/keys
chmod 750 /etc/opendkim/keys/example.com
chmod 750 /etc/opendkim/keys/example.net
chmod 640 /etc/opendkim/keys/example.com/default.private
chmod 640 /etc/opendkim/keys/example.net/default.private
File KeyTable:
default._domainkey.example.com example.com:default:/etc/opendkim/keys/example.com/default.private
default._domainkey.example.net example.net:default:/etc/opendkim/keys/example.net/default.private
File SigningTable:
*@example.com default._domainkey.example.com
*@example.net default._domainkey.example.net
Nếu dùng subdomain gửi mail như news.example.com, bạn có hai lựa chọn.
Cách 1 là ký bằng domain gốc:
*@news.example.com default._domainkey.example.com
Cách 2 là tạo key riêng cho subdomain:
default._domainkey.news.example.com news.example.com:default:/etc/opendkim/keys/news.example.com/default.private
Tùy nhu cầu DMARC alignment, bạn chọn cách phù hợp. Với đa số hệ thống nhỏ, ký bằng domain gốc dễ quản lý hơn nếu From vẫn align đúng.
Nên dùng selector như thế nào
Selector là phần đứng trước ._domainkey. Ví dụ selector default sẽ tạo DNS:
default._domainkey.example.com
Bạn có thể dùng selector theo năm hoặc mục đích:
| Selector | Khi nên dùng | Ưu điểm |
|---|---|---|
default | Server nhỏ, ít thay đổi | Dễ nhớ, dễ cấu hình |
mail2025 | Muốn xoay key định kỳ | Dễ giữ key cũ trong DNS |
mta1 | Nhiều máy gửi khác nhau | Tách key theo từng MTA |
news | Luồng newsletter riêng | Dễ cô lập rủi ro |
Nếu server gửi nhiều loại thư, bạn vẫn có thể dùng một selector cho một domain. Điều quan trọng hơn là private key an toàn và DNS đúng.
Khi xoay key, không xóa bản ghi cũ quá sớm. Hãy chờ các thư đã gửi trước đó hết vòng đời kiểm tra, thường vài ngày là an toàn hơn.
Sai lầm phổ biến
Chỉ kiểm tra systemctl status rồi kết luận DKIM ổn
active (running) chỉ nói daemon còn sống. Nó không nói OpenDKIM đã ký bất kỳ thư nào.
Bạn luôn cần kiểm tra header thư thật. Dòng quan trọng là:
DKIM-Signature:
Và kết quả xác thực ở phía nhận:
dkim=pass
Tạo KeyTable nhưng quên khai báo trong config chính
File KeyTable không tự được đọc. Config chính phải có:
KeyTable /etc/opendkim/KeyTable
Tương tự với:
SigningTable refile:/etc/opendkim/SigningTable
Nếu thiếu SigningTable, OpenDKIM không biết email nào cần ký bằng key nào.
Sai pattern trong SigningTable
Dòng này chỉ match đúng một địa chỉ:
[email protected] default._domainkey.example.com
Nếu bạn muốn ký mọi user trong domain, dùng:
*@example.com default._domainkey.example.com
Với refile:, pattern linh hoạt hơn. Nhưng nếu bỏ refile: trong config, cách match có thể không như bạn nghĩ.
Thiếu non_smtpd_milters
Nhiều app gửi mail bằng local sendmail hoặc qua queue nội bộ. Nếu Postfix chỉ có smtpd_milters, luồng này có thể không được ký.
Nên khai báo cả hai:
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
Postfix và OpenDKIM lệch socket
OpenDKIM nghe TCP nhưng Postfix lại gọi Unix socket, hoặc ngược lại.
Ví dụ OpenDKIM:
Socket inet:8891@localhost
Nhưng Postfix:
smtpd_milters = unix:/run/opendkim/opendkim.sock
Hai bên phải cùng kiểu socket. Kiểm tra bằng ss, postconf -n, và systemctl cat opendkim.
Quyền private key không đúng
Nếu OpenDKIM không đọc được private key, thường log sẽ có lỗi. Nhưng trong môi trường log không rõ, bạn vẫn nên kiểm tra quyền.
Lệnh hữu ích:
namei -l /etc/opendkim/keys/example.com/default.private
Lệnh này cho thấy quyền của từng thư mục cha. Không chỉ file key, thư mục chứa key cũng phải cho user opendkim đi qua.
Quên restart đúng dịch vụ
Sau khi sửa OpenDKIM:
systemctl restart opendkim
Sau khi sửa Postfix:
systemctl reload postfix
Nếu chỉ sửa file rồi gửi thử ngay, bạn có thể đang test cấu hình cũ.
Câu hỏi thường gặp
Vì sao opendkim-testkey pass nhưng thư vẫn không có DKIM?
Vì opendkim-testkey chỉ kiểm tra private key và DNS public key. Nó không kiểm tra Postfix có gọi OpenDKIM hay không.
Bạn cần kiểm tra thêm smtpd_milters, non_smtpd_milters, socket, SigningTable, và header của thư thật.
OpenDKIM chạy nhưng không ký thư từ PHP hoặc app web là do đâu?
Thường do app gửi qua local mailer, đi vào luồng non_smtpd. Nếu Postfix thiếu non_smtpd_milters, thư đó sẽ không được ký.
Thêm cấu hình:
non_smtpd_milters = inet:localhost:8891
Sau đó reload Postfix.
Có cần một key DKIM riêng cho mỗi tên miền không?
Nên có. Mỗi domain nên có private key và DNS TXT riêng để dễ quản lý, thu hồi, xoay key.
Không nên dùng một key cho nhiều domain trừ khi bạn hiểu rõ rủi ro vận hành và alignment.
Dùng selector default có an toàn không?
Selector default không tự làm hệ thống kém an toàn. Bảo mật nằm ở độ dài key, quyền private key, và cách bạn xoay key.
Tuy nhiên với hệ thống lớn, selector như mail2025 hoặc mta1 giúp xoay key và quản lý nhiều máy dễ hơn.
Có cần DKIM nếu đã có SPF?
Có. SPF kiểm tra server gửi có được phép gửi cho domain hay không, còn DKIM ký nội dung thư bằng khóa mật mã.
DMARC có thể pass nhờ SPF hoặc DKIM, nhưng DKIM thường bền hơn khi mail bị forward. Bạn nên cấu hình đủ SPF, DKIM và DMARC.
Thư có DKIM-Signature nhưng Gmail báo fail thì xử lý thế nào?
Khi đã có chữ ký nhưng fail, lỗi không còn là “không ký” nữa. Hãy kiểm tra DNS TXT, selector, domain trong chữ ký, và xem thư có bị gateway hoặc phần mềm nào sửa nội dung sau khi ký không.
Bạn cũng nên gửi thư tối giản chỉ có text để loại trừ lỗi do footer, antivirus, hoặc relay trung gian.
Key takeaways
- OpenDKIM
active (running)không đảm bảo thư đã được ký DKIM. - Trên Debian/Ubuntu, cần xác nhận service đang đọc đúng file cấu hình nào.
KeyTablevàSigningTablechỉ có hiệu lực khi được khai báo trong config chính.opendkim-testkeypass chỉ chứng minh key và DNS khớp, không chứng minh mail flow đã ký.- Nếu gửi từ local app, đừng quên
non_smtpd_milterstrong Postfix. - Với nhiều domain, nên tách key theo từng thư mục và từng bản ghi DNS.
- Header thư thật là bằng chứng cuối cùng: phải có
DKIM-Signaturevà phía nhận báodkim=pass.
Tóm tắt nhanh
- Lỗi “OpenDKIM chạy nhưng không ký” thường do sửa nhầm file config hoặc Postfix không gọi đúng milter.
- Dùng
ps aux,systemctl cat opendkim, và/etc/default/opendkimđể biết file nào có hiệu lực. - Kiểm tra Postfix bằng
postconf -n | grep milter. - Kiểm tra DNS và key bằng
opendkim-testkeyvàdig TXT. - Gửi thư thử rồi xem header raw, không chỉ nhìn trạng thái service.
- Với nhiều tên miền, dùng
KeyTable,SigningTable,TrustedHostsrõ ràng. - Luôn restart
opendkimvà reloadpostfixsau khi sửa cấu hình.



