朝から昼寝

整理しておきたいIT関連の小ネタ

PostfixとOpenDKIMの設定(図解)

概要

電子メールの送信元ドメインが詐称されていないかを検証する送信ドメイン認証(Sender Domain Authentication)。
本記事は、Postfixが動作するメールサーバにおいてOpenDKIMによりDKIMの検証と署名追加を行う手順をまとめたものです。
SPFDKIM、DMARC、ARC、BIMIといった送信ドメイン認証を俯瞰的にまとめた記事はこちらです。
主に、RHEL8、CentOS8の環境を対象としていますが、方式的にはどのシステムでも同様です。

本記事の目的

  • Postfixが動作するメールサーバにおいてOpenDKIMを実装する。


目次

基本

構成

Postfix+OpenDKIMの構成です。
OpenARCについては、別記事で説明予定です。

図に示します。

Postfix+OpenDKIM
Postfix+OpenDKIM
testnap1.jpが自ドメイン、testnap2.jpが外部のドメインであるものとします。

本記事では、Postfixよりメールストア寄り、あるいはクライアント寄りのコンポーネントDKIM以外の送信ドメイン認証関連の機能については省略します。
具体的には、以下についての詳細は記載していません。

OpenDKIMのインストール

RHEL系のOS標準パッケージにOpenDKIMは含まれておらず、EPEL経由でインストール可能です。
本番環境に適用する際には、サポート等に関しOS標準パッケージでないという点は把握しておく必要性があります。

# dnf install epel-release
# dnf install opendkim
# dnf install opendkim-tools

"opendkim-tools"は、DKIMのコア機能を実装する上で必須コンポーネントではありませんが、opendkim-genkeyコマンドを使用できた方が便利なのでインストールしておきます。

パッケージのインストールにより、opendkimユーザが作成されます(手動での作成不要)。

# id opendkim
uid=972(opendkim) gid=972(opendkim) groups=972(opendkim),12(mail)

OpenDKIM用の鍵の生成

OpenDKIMに含まれるopendkim-genkeyコマンドにてDKIM署名に用いる鍵を生成します。
OpenSSL等で鍵ペアから生成することもできます。

opendkim-genkeyコマンドは以下をまとめて生成してくれます。

  • DKIM署名に用いるための秘密鍵が格納されたファイル
    (セレクタ名).private
  • DNSに登録するDKIMレコードを定義する際の設定内容(ゾーンファイルに記述すべき設定内容)が格納されたファイル
    (セレクタ名).txt
    ※公開鍵はこのDKIMレコードに含まれます
# opendkim-genkey -D /etc/opendkim/keys/ -d testnap1.jp -s 202209 -b 2048

以下のパラメタを指定しています。

  • -D ファイルの出力先を指定します。
  • -d DKIM署名対象のドメイン名を指定します。
    今回の例では、"testnap1.jp"です。
  • -s セレクタ名を指定します。
    省略時は"default"というセレクタ名になります。将来的な鍵の管理や更新において、新旧の鍵を並行して扱いたい場合には、"YYYYMMDD"等、一意に識別できるものを指定すると良いでしょう。これは公開するDNSレコードのサブドメイン部分の文字列にもなります。
  • -b 鍵長のビット数を指定します。
    省略時のデフォルトは1024bitです。詳細は後述しますが、今回の例では、できるだけ大きな鍵長とするため2048bitを指定しています。

今回の例では、opendkim-genkeyコマンド実行により、202209.privateと202209.txtが生成されます。

opendkimが秘密鍵ファイルを使用できるよう、所有権を変更しておきます。

# chown opendkim:opendkim 202209.private

DNSへのDKIMレコード登録

生成された202209.txtの中身をDNSに登録します。
設定対象のDNSドメインは、testnap1.jpです。
レコードの登録手順は、使用しているDNSサーバの運用に従いましょう。

202209.txtの中身は以下のようになっているはずです。

202209._domainkey     IN      TXT     ( "v=DKIM1; k=rsa; "
  "p=xxx(長い文字列)xxx"
  "xxx(長い文字列)xxx" )  ; ----- DKIM key 202209 for testnap1.jp

BINDを使用していれば、ゾーンファイルにこの内容をコピーします。
何らかGUI等で設定する場合は、"202209._domainkey"をレコードを公開するパス/サブドメイン(.testnap1.jpが省略された記法)、"TXT"をレコード種別、「"v=DKIM1; ~ xxx"」をレコードの値として指定すると良いでしょう。
なお、opendkim-genkeyコマンドにより作成されたtxtファイルは、DNSのTXTレコードの文字列1つあたりが255byte以内でないといけないという制限に収まるよう文字列"xxx"を複数に分割されているので便利です。

DNSへの登録が完了したら、DNS検索してみます。
DNS参照可能な環境で、digコマンド等でDNS検索しましょう。

$ dig -t txt 202209._domainkey.testnap1.jp
…
;; ANSWER SECTION:
202209._domainkey.testnap1.jp. 86400 IN TXT    "v=DKIM1; k=rsa; " "p=xxx(…)xxx" "xxx(…)xxx"
…

ANSWER SECTIONに登録したTXTレコードが表示されればDNSレコードの登録自体は問題ありません。

登録したDKIMレコードの検証(テスト)

次に、opendkim-testkeyコマンドにて、登録したDKIMレコードとローカルにある秘密鍵の整合性をチェックします。

# opendkim-testkey -vvv -d testnap1.jp -s 202209 -k /etc/opendkim/keys/202209.private
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: key loaded from 202209.private
opendkim-testkey: checking key '202209._domainkey.testnap1.jp'
opendkim-testkey: key not secure
opendkim-testkey: key OK

以下のパラメタを指定しています。

  • -vvv 結果を詳細表示するよう指定します。
  • -d DKIMの検証を行うドメイン名を指定します。先ほどDKIMレコードを登録したDNSドメイン名です。
  • -s セレクタ名を指定します。
  • -k ローカルにある秘密鍵のパスを指定します。

上記のように"key OK"と表示されれば、公開されたDKIMレコードに対してローカルの秘密鍵の整合性に問題が無いことを確認できます。

DKIMレコードが見つからない場合等は、"opendkim-testkey: key missing"といったエラーになります。

なお、"key not secure"というメッセージは、OpenDKIMによるDNSSEC検証が成功しなかった場合に表示されるようです。これはDKIMと直接関係は無いという意味では無視して良いですが、対処する場合にはDNSサーバ側の設定(必要時)とOpenDKIM側の設定(おそらくTrustAnchorFileあたり)が必要そうです(参考URL1参考URL2参考URL3)。

opendkim.conf等の設定

鍵の準備が終わったので、次にOpenDKIM自体の設定をします。

/etc/opendkim.confを以下のように修正します。

今回の例では、将来的に複数の鍵を管理しやすいよう、KeyTableSigningTableを使用します。

#Mode v ←コメントアウト
Mode  sv ←追加
…
#KeyFile      /etc/opendkim/keys/default.private ←コメントアウト
…
KeyTable      /etc/opendkim/KeyTable ←追加
…
SigningTable  refile:/etc/opendkim/SigningTable
InternalHosts refile:/etc/opendkim/TrustedHosts
ExternalIgnoreList    refile:/etc/opendkim/TrustedHosts
  • Mode OpenDKIMの動作モードを指定します。

    • 検証と署名の両方を行う場合はsvを指定します(s:signer、v:verifier)
    • "v"を指定した場合、その他に以下(a)~(c)のいずれかのパターンにてパラメタ設定が必要です。今回の例は(b)のパターンで設定します。
      (a) Domain, KeyFile, Selector (KeyTable, SigningTableは設定しない)
      (b) KeyTable, SigningTable (Domain, KeyFile, Selectorは設定しない)
      (c) KeyTable, SetupPolicyScript (Domain, KeyFile, Selectorは設定しない)

  • KeyFileKeyTable DKIM署名を追加する際の鍵を設定するファイルを指定します。

    • 1つの鍵のみを使用する場合は、KeyTableでなくKeyFileで問題ありません。複数の鍵を扱う場合はKeyTableを使用します。
    • 鍵の更新時など、新旧両方の鍵を一時的にでも同時に扱いたい場合には、KeyTableの方が柔軟に対応できます。
    • KeyTableでは、opendkim.confとは別のファイル等に設定を定義します(内容は後述)。
      • refile:は、ワイルドカードを含む正規表現を使用可能なファイルを使用したい場合に指定します。refile:を記載しない場合、デフォルトのfile:と同等の扱いになります。
      • この指定方法の詳細は、READMEの"DATA SETS"という箇所に説明があります。

  • SigningTable DKIM署名追加に関する定義を指定します。

    • KeyTable使用時は必須です(もしくはSigningTableでなくSetupPolicyScriptを使用)。
    • 指定方法の書式は前述のKeyTableにある"DATA SETS"と同じです。

  • InternalHosts ここで指定したホストから配送されたメールに対し、OpenDKIMはDKIM署名を追加します。

    • 指定方法の書式は前述のKeyTableにある"DATA SETS"と同じです。

  • ExternalIgnoreList ここで指定したホストから配送されたメールに対し、OpenDKIMは検証時に何もしません(正当なDKIM署名が無くても無視)。

    • opendkim.confのサンプルの設定例では、InternalHostsと同じTrustedHostsというファイルを参照するようになっています。
    • InternalHostsExternalIgnoreListが同じTrustedHostsというファイルを参照するということは、TrustedHostsで指定したホストから配送されたメールに対し、OpenDKIMはDKIM検証せずDKIM署名追加のみ行うことになります。
    • 指定方法の書式は前述のKeyTableにある"DATA SETS"と同じです。

  • (その他)SoftwareHeader OpenDKIMがDKIM検証時にメールに追加するDKIM-Filterヘッダに記載される内容を指定します。

    • デフォルトでは、以下のように製品名、バージョン、サーバ名、ジョブIDが記載されます。
      DKIM-Filter: OpenDKIM Filter v2.11.0 mail.testnap1.jp XXXXXXXXXXX

/etc/opendkim/KeyTableを以下のように修正します。

…
202209._domainkey.testnap1.jp testnap1.jp:202209:/etc/opendkim/keys/202209.private ←追記
  • DKIM署名内容に関する設定を記載します。
  • 左側のフィールド"202209._domainkey.testnap1.jp"は、キー名を指定します。
    • キー名は後述のSigningTableから参照される任意の文字列です。今回はDKIMレコードのパスと同じ文字列にしておきます。
  • 右側のフィールド"testnap1.jp:202209:/etc/opendkim/keys/202209.private"は、DKIM署名時に使用される各設定を指定します。
    • コロン区切り(:)で、ドメイン名(DKIM-Signatureヘッダのdタグに反映される)、セレクタ名(同ヘッダのsタグに反映される)、秘密鍵のパス、の順に記載します。
    • ドメイン名を%にすると、そのメールの送信元ドメインに置換されます(マニュアルに"apparent domain of the sender"senderと記載はありますがおそらくヘッダFromの方のドメイン?)。秘密鍵を指定する文字列でも%を使用可能です。
  • 複数の鍵(複数のDKIM署名)を管理する場合、同様の行を複数記載します。

/etc/opendkim/SigningTableを以下のように修正します。

…
*@testnap1.jp 202209._domainkey.testnap1.jp ←追記
  • ドメインごとのDKIM署名設定を記載します。
    • マルチドメイン運用の場合、この設定を複数行定義します。
  • From:ヘッダーごとにどのDKIM署名を追加するかを定義します(エンベロープFromやSenderではありません)。
  • 複数行定義されている場合、ファーストマッチで動作します。

/etc/opendkim/TrustedHostsは、デフォルトのまま以下の内容です。

…
127.0.0.1
localhost
…
  • 前述のInternalHostsExternalIgnoreListの対象となるホストを指定するファイルです。
    • デフォルトで、localhost(自サーバ)が扱うメールはDKIMの署名対象になります。
  • 他に、OpenDKIMがDKIM検証せずDKIM署名追加するメールを送信してよいメールサーバ(自ドメインの信頼できるメールサーバ等)があれば、そのメールサーバのIPアドレスを記載します。
    • 他のメールサーバを追加した際の動作は未確認です(自サーバのPostfixSMTP経由でメールを受け取った際の接続元IPアドレスをOpenDKIMが判定するのか、他のメールサーバがこのOpenDKIMをMilterとして直接指定し、そのMilter経由の接続元IPアドレスをOpenDKIMが判定するのかのか、具体的には未確認)。
    • CIDR表記も可能です。

OpenDKIMのサービス有効化、起動

OpenDKIMのサービスの自動起動を有効化し、起動します。

# systemctl status opendkim
# systemctl enable opendkim
# systemctl start opendkim
# systemctl status opendkim

(参考)/etc/sysconfig/opendkim

Postfixの設定

PostfixがOpenDKIMをMilterとして使用するよう設定します。
既にDKIM対応以外のPostfixの設定は完了しているものとします。

Postfixのmain.cfを以下のように修正します。

smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:localhost:8891
#milter_default_action = tempfail
  • OpenDKIMはデフォルトでlocalhostの8891/tcpを使用します。
  • smtpd_miltersは、Postfixのsmtpd(25/tcp)が受け取るメールに対するMilterを指定します。
  • non_smtpd_miltersは、sendmailコマンドによリ送信されるメールや、postsuper -rによりキューに入ったメールに対するMilterを指定します。
  • milter_default_actionは、Milter(ここではOpenDKIM)が使用不可であった場合の振る舞いを指定します。デフォルトはtempfailです(試していませんがおそらく400番台応答)。accceptを指定すると、OpenDKIMが使用不可だった場合、そのMilter設定が無い場合と同等に処理されます。上記の例では、いったんデフォルト(tempfail)としています。
  • 複数のMilterがある場合は、カンマ区切りで順に指定します。

動作確認

実際にメールを送信し、届いたメールのヘッダやログからDKIMの動作を確認してみます。
いくつかテストケースを例示します。

No. 送信元ドメイン 送信先ドメイン 期待動作
1 @testnap2.jp @testnap1.jp メールサーバ(mail.testnap1.jp)にてDKIM検証
2 @testnap1.jp(MUA or sendmailコマンド) @testnap2.jp メールサーバ(mail.testnap1.jp)にてDKIM署名追加
3 @testnap1.jp(MUA or sendmailコマンド) @testnap1.jp メールサーバ(mail.testnap1.jp)にてDKIM署名追加し送信、その後、受信時にDKIM検証

No.1その1

No.1は、2パターンあります。testnap2.jpから受信したメールにDKIM署名がある場合と、無い場合です。
まず(No.1その1)、DKIM署名があるメールを受信した場合は、メールヘッダに以下のように記録されます。

Received: …
        by mail.testnap1.jp xxxx ←自ドメインのReceivedヘッダの下にDKIM関連のヘッダあり
DKIM-Filter: OpenDKIM Filter v2.11.0 mail.testnap1.jp XXXXXXXXXXX 
Authentication-Results: mail.testnap1.jp;
    dkim=pass (2048-bit key, unprotected) header.d=testnap2.jp header.i=xxxx@testnap2.jp header.a=rsa-sha256 header.s=xxxxxx header.b=xxxxxx
Received: …

この例では、受信したメールのDKIMの署名対象ドメインが"testnap2.jp"であり、そのDKIM署名の検証に成功した(dkim=pass)という記録が読み取れます。
DKIM検証に失敗すると、"dkim=temperror"等が記録されます。

maillogには以下のようなログが記録されます。

xxx opendkim[56742]: XXXXXXXXXXX: [x.x.x.x] [x.x.x.x] not internal
xxx opendkim[56742]: XXXXXXXXXXX: not authenticated
xxx opendkim[56742]: XXXXXXXXXXX: DKIM verification successful ←検証成功

No.1その2

次に(No.1その2)、DKIM署名の無いメールを受信した場合は、メールヘッダに以下のように記録されます。

Received: …
        by mail.testnap1.jp xxxx
DKIM-Filter: OpenDKIM Filter v2.11.0 mail.testnap1.jp XXXXXXXXXXX 
Received: …

DKIM-Filterのみが追加されます。

maillogには以下のようなログが出力されます。

xxx opendkim[56742]: XXXXXXXXXXX: [x.x.x.x] [x.x.x.x] not internal
xxx opendkim[56742]: XXXXXXXXXXX: not authenticated
xxx opendkim[56742]: XXXXXXXXXXX: no signature data  ←検証対象のDKIM署名が無い

今回の例の設定では、DKIM署名が無い場合でもPostfixはそのメールを破棄せず配送します。

No.2,3

No.2,3は、mail.testnap1.jpのPostfixからメール送信される際にDKIM署名追加されます。
No.3は、いったん別MTAに配送されてからmail.testnap1.jpのPostfixが再度メールを受信する場合には、DKIM署名検証されます。

$ sendmail -f bar@testnap1.jp foo@testnap2.jp 等により、メール送信します。

DKIM署名追加される際には、メールヘッダに以下のように記録されます。

…
Received:…
        by mail.testnap1.jp xxxx ←自ドメインのReceivedヘッダの下にDKIM関連のヘッダあり
DKIM-Filter: OpenDKIM Filter v2.11.0 mail.testnap1.jp XXXXXXXXXXX 
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=testnap1.jp; ←追加されたDKIM署名
        s=202209; t=xxxxxxxxxx;
        bh=xxxxxxxxxx;
        h=Subject:Date:From:From;
        b==xxxxxxxxxx
           xxxxxxxxxx
Subject: test
…

maillogには以下のようなログが出力されます。

xxx opendkim[7617]: XXXXXXXXXXX: DKIM-Signature field added (s=202209, d=testnap1.jp)

さらに、No.3では、メール受信時にDKIM検証が行われます。内容は"No.1その2"と同様です。

詳細

DKIM署名に使用する鍵の鍵長

2048bitの指定が良いと思われます。

OpenDKIMのREADMEによると、鍵長は1024が推奨されています。ただ、これはおそらく古い記載内容かと思いますので、できるだけ長い鍵長を採用すべきでしょう。

DNSのパケットが512byteを超えるものを扱えない環境があることを考慮すると、2048bitが良さそうです。

以下、参考情報です。

(LARGE KEYSより)
If you wish to use a large key in DNS, there are some limitations of which you should be aware. A TXT record in the DNS consists of a series of strings each of which don't exceed 255 bytes.

(CONFIGURING OPENDKIMより)
BIND servers have a 256 byte limit on serving TXT records, so a 1024 bit RSA key is recommended if using BIND as your primary DNS server. See section on LARGE KEYS.

http://www.opendkim.org/opendkim-README

以下も、参考情報です。

DNSプロバイダの中には登録可能な文字数に制限を設けているところもあるため、このような質問がしばしば聞かれます。しかし、ほとんどのプロバイダは2048ビットの鍵長をサポートしています。サポートしていないDNSプロバイダの中には独自の回避策を用意しているところもありますので、プロバイダに問い合わせて別の解決策を検討してみるのも良いでしょう。 https://sendgrid.kke.co.jp/blog/?p=14551

その他、RFC6376(DKIM)の関連箇所へのリンクも参考までに記載しておきます。

参考URL

  • OpenDKIMは、Postfixのページにて、アドオンとして紹介されています。
    Postfix Add-on Software

  • OpenDKIMのページは以下です。
    OpenDKIM
    マニュアル等はDocumentationから参照できます。

  • OpenDKIMの開発元のThe Trusted Domain Project (TDP) のページは以下です。
    The Trusted Domain Project
    OpenDKIM自体の情報については、上記のopendkim.orgへのリンクになっています。