OpenDKIM chạy nhưng không ký thư — lỗi âm thầm trên Debian/Ubuntu

Tài liệu » Email » OpenDKIM chạy nhưng không ký thư — lỗi âm thầm trên Debian/Ubuntu

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.

OpenDKIM chạy nhưng không ký thư - lỗi âm thầm trên Debian/Ubuntu

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-testkey bá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ầnVai tròLỗi hay gặp
/etc/opendkim.confFile cấu hình chính thường thấySửa file này nhưng service không đọc
/etc/default/opendkimBiến môi trường và tham số daemon trên Debian/UbuntuSocket 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
SigningTableQuy định email/domain nào dùng key nàoSai pattern, thư không match
TrustedHostsHost/IP được phép kýThiếu 127.0.0.1 hoặc hostname server
Postfix milterCầu nối Postfix sang OpenDKIMKhai 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:

SelectorKhi nên dùngƯu điểm
defaultServer nhỏ, ít thay đổiDễ nhớ, dễ cấu hình
mail2025Muốn xoay key định kỳDễ giữ key cũ trong DNS
mta1Nhiều máy gửi khác nhauTách key theo từng MTA
newsLuồng newsletter riêngDễ 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?

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.
  • KeyTableSigningTable chỉ có hiệu lực khi được khai báo trong config chính.
  • opendkim-testkey pass 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_milters trong 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-Signature và phía nhận báo dkim=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-testkeydig 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, TrustedHosts rõ ràng.
  • Luôn restart opendkim và reload postfix sau khi sửa cấu hình.
Lên đầu trang