DKIM署名がされたりされなかったりする現象の調査
2024年2月にGoogleが迷惑メール対策を強化してから、メール送信に関してSPFの他にDKIMにも対応をした/し始めているところも多いのではないかと思います。
DKIMはメールヘッダーに電子署名を行い、受信側でメールヘッダーが改ざんされていないこと確認できるようにします。 これにより、受信者はメールがFromヘッダーを書き換えたなりすましメールではなく、正当な送信者から送信されたものであることを確認できます。
メールサーバーにSendmailやPostfixを使っている場合、これらのMTAにはDKIM機能がないため、外部ソフトウェアを使ってDKIM署名を行う必要があります。 SendmailやPostfixではOpenDKIMを使うことが多いと思います。
今回、お客様環境でメール自体は送信されるがDKIM署名がされたりされなかったりする現象が発生していたので調査しました。その時の調査内容をまとめておきます。
環境
- OS: AmazonLinux2/CentOS7
(ちょうど同時期に2件の問い合わせがありました) - MTA: Postfix (v2.10系)
- Milter: OpenDKIM
他、特記事項としては、メール送信時はリレーサーバーを使っており、リレーサーバーの存在を隠すためReceivedヘッダーを削除しています。
エラーの内容
まず、mail.logを確認すると以下のログが出力されていました。
opendkim[5707]: XXXXXXXXXX: can't determine message sender; accepting
エラーメッセージで検索したのですが結局何のエラーなのかわからなかったので、OpenDKIMのソースコードを確認すると、Fromヘッダーがない場合に本エラーになるようです(opendkim/opendkim.c::mlfi_eoh()参照)。
OpenDKIMはFromアドレスから送信者のドメインを取得して、該当ドメインの秘密鍵を使って署名を行います。Fromヘッダーがない場合、送信者ドメインがわからないため署名できずに本エラーとなります。
エラーの解決方法については、例えば、以下のページを見ると、https://stackoverflow.com/questions/20151999/dkim-signing-emails-using-postfix-with-removed-headers
Received:ヘッダーを削除すると同様のエラーが発生し、Received:ヘッダーを削除するのではなく、ヘッダー内のIPアドレスを書き換えるようにすると解決するとのことでした。
今回の環境でもメールをリレーしているため、リレーサーバーを隠すためにReceived:ヘッダーを削除しているため該当します。
しかし、この時点で以下の点で納得ができていませんでした。
- Fromヘッダーがない場合に本エラーになるのに、Receivedヘッダーを削除するとエラーが発生するのはなぜか?
- Fromアドレスは間違いなく設定している(受信メールを確認しても設定されている)。
- 署名できるケースとエラーになるケースがあるのはなぜか?
本当の原因を知るためにもう少し調査してみました。
Receivedヘッダーを削除するとエラーになる理由
結論からいうと、Postfixの既知のバグでした。
発生条件は以下のとおりです。
- Receivedヘッダーを削除している
- Postfixバージョンが~2.11
- 送信メールのFromヘッダーが先頭にある
OpenDKIMとのやりとりはMilter(Mail Filter)という仕組みを使って行われます。 Milterとは元々Sendmailが外部ソフトウェアと連携するための仕組みで、 PostfixでもSendmailを真似してMilterという仕組みを実装しているため、Sendmailと同じようにOpenDKIMを使うことができます。
Milterでは送信メールのメールヘッダーをMilterプログラム(今回はOpenDKIM)に渡しますが、PostfixではSendmailとの互換性のため、Postfix自身が作成したReceivedヘッダーをMilterには渡さないようにしています。
この自分のReceivedヘッダーを無視する処理に問題がありました。自身が作成したReceivedヘッダーは通常先頭に存在するので、無条件に最初のヘッダーをMilter(OpenDKIM)に渡さないようにしているのが原因です。
今回の環境ではリレーサーバーを隠すためにReceivedヘッダーを削除していました。
/etc/postfix/header_checks
/^Received:.*/ IGNORE
Postfixは無条件に最初のヘッダーを無視しているため、本来先頭に存在するはずのReceivedヘッダーを削除すると、Receivedヘッダー削除後の次のヘッダーがMilterに渡されなくなります。このため、送信メールの先頭にFromヘッダーが先頭にあった場合、OpenDKIMにFromヘッダーが渡されず、今回のエラーが発生するわけです。たまたま、Fromヘッダーが先頭になければ問題なくDKIM署名されます。
Fromヘッダーの場所が変わることによって、DKIM署名されたりされなかったりするわけです。
これで、
- 削除したのはReceivedヘッダーなのに、なぜFromヘッダーが無い旨のエラーになるのか
- DKIM署名される場合とされない場合があるのはなぜか
の疑問が解決されました。
この不具合はVer.3以降ではヘッダー種別を見ながら処理するように修正されています。本件は2012年頃から既知のもの(*1)だったようですが、個人的にはOpenDKIMを使い始めたのが最近であったり、Receivedヘッダーを削除することはあまりなかったので、いままで気づきませんでした。
解決策
エラーの原因も納得できたので後は解決策です。
- Receivedヘッダーを削除するのではなく書き換えるようにする
- Postfixを新しくする
てっとり早い対策はReceivedの削除ではなく書き換えにする方法でしょうか。以下のページが参考になります。
https://we.riseup.net/debian/mail#getting-your-postfix-anonymized
PREPENDを使ってダミーのヘッダーを追加して回避する方法もあるようです(私は試していませんが)。
一方、該当するバージョンのPostfixが採用されているLinuxディストリビューションは以下くらいでしょうか。
- AmazonLinux2
- CentOS7 (CentOS7は2024.6.30でEOLになります)
AmazonLinux2もEOLが近くなっていますし、この問題に遭遇することも少なくなっていくでしょう。
参考情報
本件に関する他の参考情報をまとめておきます。
(1) 本不具合の修正
https://github.com/vdukhovni/postfix/ のe4699078c~..e4699078c間の修正が該当します。
(2) https://github.com/vdukhovni/postfix/blob/master/postfix/src/milter/milter8.c
milter8_header()のコメント。
* XXX Sendmail compatibility. Don't expose our first (received) header * to mail filter applications. See also cleanup_milter.c for code to * ensure that header replace requests are relative to the message * content as received, that is, without our own first (received) header, * while header insert requests are relative to the message as delivered, * that is, including our own first (received) header.
Sendmailとの互換のため、最初のReceivedヘッダーをMilterに渡していないことの説明。
(3) https://www.postfix.org/MILTER_README.html
公式ドキュメントによる注意事項。
NOTE for Postfix versions that have a mail_release_date before 20141018: do not use the header_checks(5) IGNORE action to remove Postfix's own Received: message header. This causes problems with mail signing filters. Instead, keep Postfix's own Received: message header and use the header_checks(5) REPLACE action to sanitize information.
mail_release_dateが20141018より前のPostfixについての注意: header_checks(5) IGNOREアクションを使ってPostfix自身のReceivedヘッダーを削除しないでください。メール署名フィルタで問題を引き起こします。代わりに、Postfix自身のReceivedヘッダを残し、header_checks(5) REPLACEアクションを使って情報をサニタイズしてください。
(4) https://github.com/vdukhovni/postfix/blob/master/postfix/HISTORY
ソースコードに添付されているHISTORY情報。20141018の項。
Bugfix (introduced: Postfix 2.3): when a Milter inserted a header ABOVE Postfix's own Received: header, Postfix would expose its own Received: header to Milters (violating protocol) and hide the Milter-inserted header from Milters (wtf). Files: cleanup/cleanup.h, cleanup/cleanup_message.c, cleanup/cleanup_state.c, milter/milter.[hc], milter/milter8.c.
バグ修正(Postfix 2.3で発生):Postfix自身のReceived:ヘッダよりも上にヘッダを挿入した場合、Postfixは自身のReceived:ヘッダをMilterに公開し(プロトコル違反)、Milterが挿入したヘッダをMilterから隠します。
(5) https://bofhskull.wordpress.com/2014/03/25/postfix-opendkim-and-missing-from-header/
最初のヘッダーが無視されることをつきとめて、PREPENDアクションを使ってダミーのヘッダーを挿入する解決方法を提案している。header_checksのWARNアクションを使って調査する方法も参考になる。
(*1) https://serverfault.com/questions/412389/postfix-not-reporting-from-address-to-opendkim
投稿日:2024/05/26 08:59(最終更新:2024/05/28 11:29)