DomainKeysは、Yahooが提唱している送信者認証の技術です。
送信側は秘密鍵でメールに署名し、受信側はDNSで入手した公開鍵で
署名を検証する事によって、送信者の確認を行います。
今回はPostfix用のDomainKeysフィルタであるdkfilterを使用して、
PostfixでDomainKeysの署名および検証を行ってみます。
今回使用したサーバのOSはFreeBSD 5.4-STABLEです。
PostfixやPerlが動作するならば、他のOSでも問題ないと思います。
今回使用したPostfixのバージョンは2.2.5です。
標準的なdkfilterの設定ではBefore Queue Content Filterを使用するので、
Postfixのバージョンはこの機能に対応した2.1以降である方がいいでしょう。
Before Queue Content Filterの代わりにAfter Queue Content Filterを
使うのならばPostfix-2.0でも構わないと思いますが、この場合、状況によっては
問題が起きる可能性があります。
dkfilterはPerlでかかれたスクリプトで、使用するためには以下のPerlの モジュールが必要になります。
モジュールの依存関係で、以下のモジュールも必要になるかもしれません。
今回はFreeBSDのportsを使用して、以下のportをインストールしました。
CPANシェルを使用して、自動でインストールする事もできます。 その時は以下のようにコマンドを実行すればいいでしょう。
# perl -MCPAN -e shell cpan> install Crypt::OpenSSL::RSA cpan> install Mail::Address cpan> install MIME::Base64 cpan> install Net::DNS cpan> install Test::More
dkfilterの配布サイトからdkfilterをダウンロードします。
2005年8月27日時点での最新版はVersion 0.8です。
アーカイブを展開し、インストールを実施します。
今回は、/usr/local/dkfilterにインストールする事にしました。
% tar zxvf dkfilter.tgz % cd dkfilter-0.8 % ./configure --prefix=/usr/local/dkfilter % su # make install
dkfilter用のユーザおよびグループを作成します。
今回は、両方ともdkfilterにしました。
# vi /etc/group グループdkfilterを追加する。 # vipw ユーザdkfilterを追加する。
配布パッケージにはOS起動時の自動起動用スクリプト(init-script.sh)が付属していますが、
これはSuSE用に書かれた物のようで、FreeBSDではそのままでは使用できません。
今回は、とりあえずこのようなスクリプトを作成して、
/usr/local/etc/rc.dにコピーしました。
#!/bin/sh DKFILTER_DIR=/usr/local/dkfilter KEYFILE=$DKFILTER_DIR/keys/selector1.key SELECTOR=selector1 USERNAME=dkfilter if [ -x /usr/local/sbin/postconf ]; then POSTCONF=/usr/local/sbin/postconf elif [ -x /usr/sbin/postconf ]; then POSTCONF=/usr/sbin/postconf else echo "postconf not found!!" exit 1 fi HOSTNAME=`$POSTCONF -h myhostname` DOMAINNAME=`$POSTCONF -h mydomain` INBOUND_PROG="$DKFILTER_DIR/bin/dkfilter.in" OUTBOUND_PROG="$DKFILTER_DIR/bin/dkfilter.out" INBOUND_ARGS="--hostname=$HOSTNAME 127.0.0.1:10025 127.0.0.1:10026" OUTBOUND_ARGS="--keyfile=$KEYFILE --selector=$SELECTOR --domain=$DOMAINNAME --method=nofws --headers 127.0.0.1:10027 127.0.0.1:10028" case "$1" in start) if [ -x $INBOUND_PROG ]; then echo "Starting dkfilter-inbound." su $USERNAME -c \ "$INBOUND_PROG $INBOUND_ARGS > /dev/null 2>&1 &" fi if [ -x $OUTBOUND_PROG ]; then echo "Starting dkfilter-outbound." su $USERNAME -c \ "$OUTBOUND_PROG $OUTBOUND_ARGS > /dev/null 2>&1 &" fi ;; stop) echo "Stopping dkfilter." killall -u dkfilter perl ;; restart) $0 stop $0 start ;; *) echo "Usage: $0 [ start | stop | restart ]" exit 1 ;; esac
dkfilterには受信(署名検証)用と送信(メール署名)用の二つのフィルタがあります。 受信用フィルタは、smtpポート(TCP:25)経由でのメールに、送信用フィルタは submissionポート(TCP:587)経由でのメール、およびメールサーバからsendmail コマンドを使用して送信したメールに対して適用する事にします。
まず、受信用フィルタの設定を行う事にします。
受信用フィルタはPostfixのBefore Qeueu Content Filterとして動作します。
Postfixからdkfilterへはポート10025、dkfilterからPostfixへはポート10026を使う事にしました。
まず、試験的にコマンドラインからdkfilterを起動してみます。
# /usr/local/dkfilter/bin/dkfilter.in --hostname=[自ホスト名] 127.0.0.1:10025 127.0.0.1:10026 &
次に、Postfix側の設定を行います。
受信メールをdkfilterに渡すために、master.cfのsmtpサービスの設定を以下のように変更します。
smtp inet n - n - - smtpd -o smtpd_proxy_filter=127.0.0.1:10025 -o smtpd_client_connection_count_limit=10
dkfilterから戻されるメールを受け取る為に、master.cfに以下の設定を追加します。
localhost:10026 inet n - n - - smtpd -o smtpd_authorized_xforward_hosts=127.0.0.0/8 -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o smtpd_data_restrictions= -o mynetworks=127.0.0.0/8 -o receive_override_options=no_unknown_recipient_checks -o smtpd_use_tls=no # TLS使用時のみ(なくても大丈夫なはずですが、念のため)
master.cfの修正を反映します。
# postfix reload
これで、受信(署名検証)用フィルタの設定が出来ました。
設定が出来ましたので、テストを行います。
まず、通常のメール配送に問題がないかを確認する為に、自分のアドレス宛に
メールを送信してみます。
送信方法は、普通にメールソフトを使ってもいいですし、以下の例のように
telnetコマンドを使用してもいいでしょう。
ただし、sendmailコマンドやmailコマンド(大抵は内部でsendmailコマンドを呼ぶ)
等は使用できません。(smtpd経由ではないので意味がありません)
% telnet 127.0.0.1 25 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. 220 mail.example.org ESMTP Postfix HELO localhost 250 mail.example.org MAIL FROM:<user@example.org> 250 Ok RCPT TO:<user@example.org> 250 Ok DATA 354 End data with <CR><LF>.<CR><LF> Subject: test test mail. . 250 Ok: queued as 7BB371733A QUIT 221 Bye Connection closed by foreign host.
メールが問題なく送信できましたら、届いたメールを確認します。
Return-Path: <user@example.org> X-Original-To: user@example.org Delivered-To: user@example.org Received: from mail.example.org (localhost [127.0.0.1]) by mail.example.org (Postfix) with ESMTP id 7BB371733A for <user@example.org>; Tue, 30 Aug 2005 17:41:58 +0900 (JST) Authentication-Results: mail.example.org; domainkey=neutral (unable to determine sender domain) Received: from localhost (localhost [127.0.0.1]) by mail.example.org (Postfix) with SMTP for <user@example.org>; Tue, 30 Aug 2005 17:41:52 +0900 (JST) Subject: test Message-Id: <20050830084158.7BB371733A@mail.example.org> Date: Tue, 30 Aug 2005 17:41:58 +0900 (JST) From: user@example.org To: undisclosed-recipients:; test mail.
受信用フィルタが正常に動作していると、Authentication-Results:というヘッダが 付加されます。 このテストメールはDomainKeysでの署名がされていないため、domainkey=neutral (unable to determine sender domain)という結果が記録されています。
次に、署名検証が正常に行われるかを確認します。
手軽な方法としては、Gmailや
Yahoo!Mail等のDomainKeysに対応した
メールサービスを利用するというのがあります。
これらのサービスのアカウントを持っている場合は、そこから自分宛にメールを
送信します。
もしアカウントを持っていなくても、Yahoo!Mailならば無料で取得できますし、
Gmailは利用者からの招待(invite)があればアカウントが取得できますので、
身近に利用者がいないか探してみましょう。
メールが送信できたら、届いたメールのヘッダを確認します。
Return-Path: <domainkeys.test@gmail.com> X-Original-To: user@example.org Delivered-To: user@example.org Received: from mail.example.org (localhost [127.0.0.1]) by mail.example.org (Postfix) with ESMTP id AD19C28426 for <user@example.org>; Fri, 2 Sep 2005 13:58:07 +0900 (JST) Authentication-Results: mail.example.org from=domainkeys.test@gmail.com; domainkey=pass Received: from zproxy.gmail.com (zproxy.gmail.com [64.233.162.197]) by mail.example.org (Postfix) with ESMTP for <user@example.org>; Fri, 2 Sep 2005 13:58:06 +0900 (JST) Received: by zproxy.gmail.com with SMTP id j2so221327nzf for <user@example.org>; Thu, 01 Sep 2005 21:57:34 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=received:message-id:date:from:to:subject:mime-version:content-type:content-transfer-encoding:content-disposition; b=DEIXKymTYiq2[〜略〜]I8NJI1uicxjU= Received: by 10.36.221.76 with SMTP id t76mr1787938nzg; Thu, 01 Sep 2005 21:57:34 -0700 (PDT) Received: by 10.36.119.14 with HTTP; Thu, 1 Sep 2005 21:57:34 -0700 (PDT) Message-ID: <923fa33c05090121572d0845e6@mail.gmail.com> Date: Fri, 2 Sep 2005 13:57:34 +0900 From: DomainKeys Test <domainkeys.test@gmail.com> To: user@example.org Subject: test Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-2022-JP Content-Transfer-Encoding: 7bit Content-Disposition: inline test
署名が検証され結果が問題ないと、Authentication-Results:ヘッダに domainkey=passと記録されます。
次に、送信用フィルタの設定を行います。
送信用フィルタは、PostfixのAfter Queue Content Filterとして動作します。
Postfixからdkfilterへはポート10027、dkfilterからPostfixへはポート10028を
使う事にしました。
まず、署名するための秘密鍵、およびDNSで公開する公開鍵を生成します。
生成する鍵は、DomainKeysで必ずサポートされているRSA鍵で、鍵長は1024ビットとします
# cd /usr/local/dkfilter # mkdir keys # chown dkfilter:dkfilter keys # chmod 700 keys # cd keys # openssl genrsa -out selector1.key 1024 Generating RSA private key, 1024 bit long modulus ..........................................................................................++++++ .............................++++++ e is 65537 (0x10001) # chown dkfilter:dkfilter selector1.key # chmod 400 selector1.key # openssl rsa -in selector1.key -pubout | grep -v "^--" | tr -d '\012' > pubkey.tmp writing RSA key
生成されたselector1.keyが秘密鍵で、pubkey.tmpが公開鍵になります。 pubkey.tmpは次のDNSへの登録が済んだ後は削除しても構いません。
次に、公開鍵をDNSに登録します。
公開鍵は、"セレクタ名._domainkey.example.org(自ドメイン名がexample.orgの場合)
のTXTレコードとして登録します。
セレクタとは、一つのドメインで複数の鍵を使う時に、どの鍵を使っているか判別
するために付ける名前で、DNSおよびメールヘッダの制限の範囲内で自由に決められます。
例えば、BINDでセレクタ名selector1の公開鍵を登録する場合は、次の設定を
example.orgのゾーンデータに追加します。
selector1._domainkey IN TXT "k=rsa; p=pubkey.tmpの内容; t=y"
k=rsaというのは、公開鍵の種類がRSA鍵である事を表します。
p=というのが公開鍵の本体で、pubkey.tmpの内容をここに記述します
t=yというのは、この公開鍵はテストモードで使用している事を表します。
テストモードの公開鍵での署名検証が失敗しても、メールを拒絶したりしない事に
なっています。
秘密鍵を生成したら、試験的にコマンドラインから送信用フィルタを起動してみます。
# dkfilter.out \ --keyfile=/usr/local/dkfilter/keys/selector1.key \ --selector=selector1 \ --domain=[ドメイン名] \ --method=nofws \ --headers \ 127.0.0.1:10027 127.0.0.1:10028 &
次に、Postfixの設定を行います。 送信用フィルタ関連では、以下の修正をmaster.cfに行います。
まずは、submissionポートに送られて来たメールを送信用フィルタに渡すために、
submissionの設定を以下のように変更します。(submissionがない場合は追加してください)
この例では、SASL認証は必須としています。SASLの設定は
Postfixのぺーじの
SMTP-AUTH対応版インストール記録
などを参考にして適宜行ってください。
submission inet n - n - - smtpd -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_security_options=noplaintext,noanonymous -o broken_sasl_auth_clients=yes -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject -o smtpd_data_restrictions= -o smtpd_etrn_restrictions=reject -o content_filter=dksign:[127.0.0.1]:10027
sendmailコマンドを使用しての送信時にも署名するために、pickupサービスを 以下のように変更します。
pickup fifo n - n 60 1 pickup -o content_filter=dksign:[127.0.0.1]:10027
dkfilterへメールを渡すためのトランスポートであるdksignを定義するため、 以下の設定を追加します。
dksign unix - - n - 10 smtp -o smtp_send_xforward_command=yes -o smtp_use_tls=no # TLS使用時のみ
署名されたメールをdkfilterから受け取るため、以下の設定を追加します。
localhost:10028 inet n - n - 10 smtpd -o smtpd_authorized_xforward_hosts=127.0.0.0/8 -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o smtpd_data_restrictions= -o mynetworks=127.0.0.0/8 -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks -o content_filter= -o smtpd_use_tls=no # TLS使用時のみ
上記のmaster.cfの修正を行ったら、設定を反映する為にreloadを行います。
# postfix reload
これで、送信(署名)用フィルタの設定が出来ました。
送信用フィルタが正常に動作するか確認するため、GmailやYahoo!Mailのアカウントに テストメールを送ってみます。 submissionポートからのメールのみ署名するようにしているので、 メールソフトの送信用ポートを587に変更するのを忘れないでください。
メールが送信できたら、送信先に届いたメールのヘッダを確認します。
Gmailの場合は、"詳細オプション" -> "オリジナルを表示" でヘッダ部分も表示できます。
〜略〜 Received-SPF: pass (gmail.com: best guess record for domain of user@example.org designates xxx.xxx.xxx.xxx as permitted sender) DomainKey-Status: good (test mode) Received: from mail.example.org (localhost [127.0.0.1]) by mail.example.org (Postfix) with ESMTP id 43A4162D01; Sat, 3 Sep 2005 03:41:54 +0900 (JST) DomainKey-Signature: a=rsa-sha1; h=Received:Date:From:To:Subject:Message-Id:X-Mailer:Mime-Version:Content-Type:Content-Transfer-Encoding; b=m31f[〜略〜]a3U=; c=nofws; d=example.org; q=dns; s=selector1 Received: from mail.example.org (localhost [IPv6:::1]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.example.org (Postfix) with ESMTP id 7ADA362D00; Sat, 3 Sep 2005 03:41:51 +0900 (JST) Date: Sat, 03 Sep 2005 03:41:52 +0900 〜略〜
DomainKey-Signature:ヘッダが付加されている事、およびDomainKey-Status:ヘッダに
goodと記録されている事を確認します。
Gmailは古い仕様のDomainKeysに従ったソフトを使っているようで、
Authentication-Results:ヘッダではなくDomainKey-Status:ヘッダに
検証結果が記録されます。
Yahoo!Mailの場合は、"詳細ヘッダ"で全ヘッダの表示ができます。
〜略〜 Authentication-Results: mta44.mail.bbt.yahoo.co.jp from=example.org; domainkeys=pass (ok) X-Originating-IP: [xxx.xxx.xxx.xxx] Return-Path:Received: from xxx.xxx.xxx.xxx (EHLO mail.example.org) (xxx.xxx.xxx.xxx) by mta44.mail.bbt.yahoo.co.jp with SMTP; Sun, 28 Aug 2005 11:02:32 +0900 Received: from mail.example.org (localhost [127.0.0.1]) by mail.example.org (Postfix) with ESMTP id 4B4571729B; Sun, 28 Aug 2005 11:02:32 +0900 (JST) DomainKey-Signature: a=rsa-sha1; h=Received:Date:From:To:Subject:Message-Id:X-Mailer:Mime-Version:Content-Type:Content-Transfer-Encoding; b=Y4mt[〜略〜]Z5k=; c=nofws; d=example.org; q=dns; s=selector1 Received: from mail.example.org (localhost [IPv6:::1]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.example.org (Postfix) with ESMTP id 7FF6017260; Sun, 28 Aug 2005 11:02:31 +0900 (JST) Date: Sun, 28 Aug 2005 11:02:29 +0900 〜略〜
DomainKey-Signature:ヘッダが付加されている事、およびAuthentication-Results: ヘッダにdomainkeys=passと記録されている事を確認します。
DomainKeysを設定、運用しているうちにいくつか問題に出会う事があると思います。
例えば、以下のような問題が考えられます。
この文書はまだ未完成です。 以下のような事についても、そのうちに書きたいと思います。
ご意見、ご感想等が有りましたら、sue-nospam-dkfilter@nospam.postfix.jpまでメールを下さい。
スタイルシート未対応ブラウザ(もしくはスタイルシートを無効にした状態)では、
上記アドレスのspam避け部分も表示されてしまいます。
その場合は、上記アドレスのローカルパートから-nospam-dkfilterを、
ドメイン部からnospam.を削除したアドレス宛にメールを下さい
設定方法に関する質問等は、
Postfix-JP メーリングリスト
の方がいいかも知れません。(私も読んでいますので、出来る限り答えるようにします)