Sendmail で DomainKeys による送信元情報を提供する

SERVER


DomainKeys による成りすまし防止について

DomainKeys とは

DomainKeys という送信元認証のための仕組みが Yahoo や Google によって推奨されているようです。

これは、電子署名を使用して、そのメールのドメインが送信に使用する送信メールサーバーを主張することで、そのメールが成りすましメールではないということを、受信側で検証することができるようにする仕組みです。

 

これを利用するには、自分が管轄しているドメインの送信用 SMTP サーバーと DNS サーバーとの両方で対応が必要になります。

まず、送信用 SMTP サーバーでは、そこから送られる電子メールに、そのメールの内容を元に電子署名を作成し、それをメールヘッダー内に付与します。また、DNS サーバー側では、その電子メールで使用しているドメインに、公開鍵の情報を TXT レコードで登録しておきます。

こうすることで、DomainKeys の電子署名が付与された電子メールを受け取った側が、その送信元 (From または Sender) で使用されているドメインを MX レコードで調べて、そのドメインに TXT レコードで登録されている公開鍵を使用して、付与されていた電子署名が正しいかどうかを確認できます。

こうすることで、自分が正しい経路で出したメールが、成りすましされていないということを、相手に判断してもらうことができるようになります。

DomainKeys に感じる疑問

ただ、とても疑問に思うところがあるのですが、平成 22 年 11 月 15 日現在、電子メールが受け取った側が DomainKeys でそのメールの正当性を検証するためには、そのメールに付与されている "DomainKey-Signature" ヘッダーに記されているセレクタ情報を使って DNS 検索しないといけないところです。

つまり、送信元メールサーバーで付けられた "DomainKey-Signature" ヘッダーがなければ、そのメールのドメインに DomainKeys が採用されているかを判断できなくなります。

 

DomainKeys が採用された正しいメールサーバーから送信すれば、電子メールにも "DomainKeys-Signature" が付与されるので、そこのセレクタ情報から DNS クエリで公開鍵を取得して、それでその電子メールの正当性を判断することができるでしょう。

ただ、それ以外の DomainKeys を採用していないメールサーバーから、送信元を偽装されて送信された場合には、"DomainKeys-Signature" ヘッダーがないため、当然セレクタもなく、公開鍵を探すことはありません。

これでは、その電子メールが成りすましを行っているかどうかを "成りすましされていない" か "わからない" までしか判断できずどまりです。

 

肝心なのは、"成りすましされている" ことを検出することなのではないのでしょうか。

そを実現するために、"DomainKeys-Signature" ヘッダーに記載されたセレクタなど使用せずに、From ヘッダーから MX ドメインを検索して、それに "あらかじめ定められた" セレクタを追加して TXT レコードをクエリして、公開鍵を取得するという仕様ではダメなのでしょうか。

これが仕様上許されるのならば、"DomainKeys-Signature" が付与されていなくても、一定のルールで DNS クエリーを行うことで、そのドメインが本来 DomainKeys を採用しているかどうかを判断することができるようになると思います。

それで "DomainKeys-Signature" が付与されていれば、DNS から取得した公開鍵でその情報を検証すれば良いわけですし、もし "DomainKeys-Signature" が付与されていないとすれば、それは不正な経路から送信されたメール(すなわち、偽装されている可能性が高い)という判定ができるようになるような気がします。

 

また、DomainKeys では電子署名によって本文の改ざんも判定することができるようになっているようなのですけど、これについても少し疑問を感じます。

こちらについても、電子メールに "DomainKeys-Signature" ヘッダーの付与があってはじめて機能することもあり、その電子メールに本来そのヘッダーが付与されているかどうかを、ヘッダーがなくても自主的に判断できるようになっていなければ、"DomainKeys-Signature" がない限りは、その内容の正当性については判断できなくなってしまいます。

改ざん防止は電子署名ですから、暗号化はされていないはずで、メールの送信経路で改ざんついでに "DomainKeys-Signature" ヘッダーも一緒にとってしまえば、それで改ざんされたことが判断できなくなってしまうような気がしてなりません。

 

そんな DomainKeys ですけど、"そのドメインが DomainKeys を採用しているかどうか" を自主的に判定できるようになれば、かなりの部分でそのメールの正当性を判断できるようになる気がするんですけどね。

上記のような方策を取ることができれば、これまでできなかったメール送信元の偽装を、そのドメインの運用者側で抑制できる可能性が見えてくるだけに、現在の DomainKeys の仕様はとても残念な気がしてなりません。

これまでの迷惑メール対策のほとんどに言えることですが、なんでこう、不正防止にあまり効果の期待できない対策ばかりが目立つような気がするのでしょう。

 

Sendmail に DomainKeys を設定する

DomainKeys は、メールの成りすましや改ざんについて期待するほど大きな効果が得られないような気もしますが、少なくとも自分が正当な経路で出したメールについては正当性を主張することはできそうですし、DomainKeys に対応していない相手へのメール送信にも悪影響はなさそうのので、とりあえずの試しも兼ねて、送信メールサーバーへ DomainKeys を設定してみたいと思います。

今回インストールする環境は CentOS 5.5 (x86_64) 上の Sendmail 8.13.8 に対して行ってみようと思います。

必要なプログラム

Sendmail で DomainKeys を実現するためには "dk-milter" というプラグインを組み込む必要があるようです。

平成 22 年 11 月 25 日現在、"dk-milter" は http://sourceforge.net/projects/dk-milter/ からソースコードをダウンロードして、インストールを行う必要があるようです。今のところ yum では入手できないようでした。

dk-milter のインストール条件を確認する

また、dk-milter 1.0.2 を使用するにあたっては、Sendmail のバージョンが 8.13.0 以上であり、かつ Sendmail が "MILTER" に対応している必要があるとのことでした。

インストールされている Sendmail がその条件を満たしているかを確認するには、コマンドラインから次のコマンドを実行します。

sendmail -d0.10

このようにすると、Sendmail のバージョン番号と "Compiled with:" の情報が画面に出力されます。この "Compiled with:" の中に "MILTER" というテキストがあれば、その Sendmail は "MILTER" に対応していることになります。

今回の CentOS 5.5 では、Sendmail のバージョンが "8.13.8" であり、かつ "MILTER" に対応しているようでした。

もし Sendmail のバージョンが 8.13 未満であればアップデートの必要性がありますし、もし "MILTER" に対応していない場合には、Sendmail の再コンパイルを行うなどして対応させる必要があるそうです。

sendmail-devel のインストール

"dk-milter" をインストールするには "libmilter.a" というライブラリが必要になるようでした。

CentOS 5.5 の場合、標準ではこのライブラリがインストールされていないようでした。このライブラリは "sendmail-devel" パッケージの一部として用意されているようでしたので、次のようにして "libmilter.a" のインストールを行います。

yum install sendmail-devel

これで CentOS 5.5 (x86_64) の環境では "/usr/lib64/libmilter.a" にインストールされるようでした。

dk-milter のインストール

それでは "dk-milter" のインストールを行います。

たとえば "/usr/local/src" ディレクトリーなどで、次のようにして "dk-milter" をダウンロードします。

平成 22 年 11 月 25 日現在、"dk-milter" の 1.0.2 が http://sourceforge.net/projects/dk-milter/ で公開されていましたので、以下ではその URL を指定しています。

cd /usr/local/src

wget "http://downloads.sourceforge.net/project/dk-milter/DomainKeys Milter/1.0.2/dk-milter-1.0.2.tar.gz?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fdk-milter%2F&ts=1290686911&use_mirror=jaist"

"dk-milter-1.0.2.tar.gz" のダウンロードが完了したら、次のようにして展開し、展開されたディレクトリへ移動します。

tar xvzf dk-milter-1.0.2.tar.gz

 

cd dk-milter-1.0.2

そうしたら、次のようにして "dk-milter" のコンパイルを行います。

./Build

正常にコンパイルができたらインストール作業に入るのですが、今回の CentOS 5.5 (x86_64) の環境では、その前にマニュアルのインストール先のパスを調整しておく必要があるようでした。

"./Build" を実行したときに自動的に生成された "obj.Linux...." で始まるディレクトリの "dk-filter/Makefile" と "libar/Makefile" を、vi 等のテキストエディタで編集します。

MANROOT=/usr/share/man/man

変更箇所はその中の "MANROOT" の定義で、内容としては上記のように "/usr/man/man" となっているのを "/usr/share/man/man" に変更する形になります。

また、何故か "dk-filter/Makefile" の MAN8 と MAN8MAN の値が "${MANROOT}/usr/man/man8" と "${MANROOTMAN}/usr/man/man8" になっていたので、それぞれを "${MANROOT}8" と "${MANROOTMAN}8" に変更しておきました。s

 

編集したら次のようにして、インストールを行います。

./Build install

これで、"dk-filter" が "/usr/bin" にインストールされました。

dk-filter を起動する

"dk-filter" のインストールが終わったら、それの起動を行います。

 

そのためにまず、DomainKeys で使用する秘密鍵を作成します。

openssl md5 /var/log/* > /tmp/seed.txt

このようにするといくつかエラーメッセージが表示されることがありますが、その内容が "system library:fread:Is a directory:bss_file.c:198" や "BIO routines:FILE_READ:system lib:bss_file.c:199:" といった内容なら無視して大丈夫です。

続いて、次のコマンドを実行します。

openssl genrsa -rand /tmp/seed.txt -out /etc/pki/tls/private/dk-filter.key 1024

これで、秘密鍵が "etc/pki/tls/private/dk-filter.key" に生成されました。

秘密鍵ができたら、それを用いて公開鍵を生成します。

openssl rsa -in /etc/pki/tls/private/dk-filter.key -out /etc/pki/tls/certs/dk-filter.pem -pubout -outform PEM

このようにすることで、公開鍵 "etc/pki/tls/certs/dk-filter.pem" が生成されました。

 

 

続いて、"dk-filter" の起動スクリプトを作成します。

起動スクリプトの内容としては、次のリンク "dk-filter 起動スクリプト" に記したような内容で大丈夫だと思います。このような内容の起動スクリプトを "/etc/rc.d/init.d/dk-filter" として保存します。

起動スクリプトを保存したら、次のようにして、起動スクリプトに実行権限を与えておきます。

chmod +x /etc/rc.d/init.d/dk-filter

起動スクリプトを作成したら、それを CentOS 環境に登録しておきます。

chkconfig --add dk-filter

これで service コマンドを利用して制御できるようになりました。

 

続いて、上記の起動スクリプトで読み込むようにした設定ファイル "/etc/sysconfig/dk-filter" を、次のような内容で新規に作成します。

SOCKETSPEC=inet:18892@localhost

DOMAINLIST=domain1,xx domain2.xx

PRIVATEKEY=/etc/pki/tls/private/dk-filter.key

SELECTOR=key

OPTIONS=

この設定ファイルの内容は、今回独自に作成した起動スクリプト用のものですので注意してください。

なお、今回の独自の設定ファイルの内容としては、次のようになっています。

SOCKETSPEC dk-filter の -p オプションに指定する値です。が待機するネットワークソケットを指定します。
DOMAINLIST dk-filter の -d オプションに指定する値です。DomainKeys を採用するドメインをカンマ区切りのリストで指定します。
PRIVATEKEY dk-filter の -s オプションに指定する値です。署名の際に使用される秘密鍵です。
SELECTOR dk-filter の -S オプションに指定する値です。DomainKeys で使用するセレクタを指定します。
OPTIONS dk-filter の引数として使用するテキストです。上記を除く何らかのオプションが必要な場合に使用します。

ここでは、待ち受けポートとして 18892 を使用し、セレクタには "key" を設定しています。秘密鍵は、先ほど作成した "/etc/pki/tls/private/dk-filter.key" を使用するように指定してあります。

 

設定ファイルの準備ができたら、次のようにして dk-filter を起動させておきます。

service dk-filter start

正しく起動することが確認できたら、次のようにして、CentOS 再起動時にも自動的に dk-filter が起動するようにしておきます。

chkconfig dk-filter on

 

DNS に公開鍵を登録する

DomainKeys を機能させるためには、DNS サーバーに DomainKeys の公開鍵を設定する必要があります。

例えば、今回 "@domain1.xx" というドメインの電子メールアドレスに対して DomainKeys を採用するとした場合、その DOMAIN1.XX ドメインの DNS サーバーに、公開鍵の内容を記した TXT レコードを追加しておく必要があります。

DNS への公開鍵の登録は、DNS サーバーによって設定方法は違いますが、例えば BIND であれば、DOMAIN1.XX のゾーンファイルに次のような TXT レコードを登録すれば良い感じになります。

key._domainkey    IN    TXT "g=; k=rsa; p=公開鍵;"

ここでは、"key._domainkey.domain1.xx" という TXT レコードを登録していますが、この先頭の "key" の部分は、先ほど "dk-filter" で設定したセレクタと同じものを指定します。

 

"公開鍵" のところには、先ほど作成した公開鍵 "/etc/pki/tls/certs/dk-filter.pem" ファイル内のテキストを指定します。

具体的には、公開鍵ファイルの中の "-----BEGIN PUBLIC KEY-----" と "-----END PUBLIC KEY-----" との間に挟まれた行に書かれている文字列となります。画面上では複数行に分かれているかもしれませんが、これを 1 行で指定する必要があるので注意しましょう。

 

その他、"g=" には暗号強度を、"k=" にはキーの種類を、"n=" としてメモを、指定することができるようになっていました。

このような TXT レコードを登録して DNS で取得できるようにすれば、DNS 側での準備は完了となります。

Sendmail に dk-filter を登録する

Sendmail で dk-filter を利用するには、設定ファイル "/etc/mail/sendmail.mc" を調整します。

INPUT_MAIL_FILTER(`dk-filter', `S=inet:18892@localhost')dnl

編集時には、文字列をくくる引用符の最初が [ ' ] ではなく [ ` ] であるところに注意します。

また、このような dk-filter の設定についての記載が既存の設定ファイル内になければ、末尾に追記しておけば大丈夫です。

 

設定ファイルを編集したら、次のようにして Sendmail の設定を有効化します。

cd /etc/mail

 

make sendmail.cf

make restart

これで、Sendmail の Submission ポート (587) から送信されるメールについて、DomainKeys の署名が "DomainKey-Signature" ヘッダーに付与されて送信されるようになりました。

25 番ポートでの送信の場合は DomainKeys の署名が付与されないようでしたので、リレーサーバーなどで、どこかからのメールは無条件でリレーするような SMTP (25) サーバーとして運用されている場合には、ローカルからのメール送信設定を行う でも示したようなやり方を使うなどして、リレー元の SMTP サーバーが Submission (587) を使ってメール送信するようにする必要がありそうです。

 

DomainKeys の動作を確認する

DomainKeys を自分のドメインの SMTP サーバーと DNS サーバーとに設定したら、それが正しく動作しているかのチェックを行います。

チェックには、平成 22 年 11 月 26 日現在、DomainKeys に対応している Yahoo! メール を使用するのが簡単です。

 

Yahoo! メールのメールアカウントを取得したら、そこへ DomainKeys を設定したドメインからメールを送信します。

DomainKeys が正しく設定できていれば、Yahoo! メールでそれを受信したときに、"From:" のところに "DomainKeys は、このメールが domain1.xx から送信されたことを確認しました。" というメッセージが表示されます。

もし設定がうまくいっていない場合には、ここには何も表示されません。

 

右下の辺りから "詳細ヘッダー" をクリックすると、DomainKeys の詳細を確認することが可能です。

ここで "Authentication-Results:" という内容を見て、ここの最後の方に "domainkeys=pass (ok)" とあれば、送信元メールに DomainKeys の署名が付与されていて、それが正しく検証できたことを意味しています。

もしここが "domainkeys=fail (bad key)" となっていた場合には、DNS の公開鍵が間違っているか、dk-filter のセレクタと DNS レコードとの間にずれがあるなどの間違いが考えられます。"domainkeys=neutral (no sig)" となっていた場合には、DomainKeys の署名がメールに付与されていなかったことになるので、dk-filter がそもそも正しく動作していないなどの間違いが考えられます。

 

詳細ヘッダーの "DomainKey-Signature" ヘッダーを確認しても、意図した DomainKeys 署名が付与されているかを確認することができるので、このような感じで DomainKeys が正しく設定されているかを確かめて行くと良いと思います。