Postfix で、Gmail にメールをリレーする

はじめに (2009年9月24日加筆)

多くの方に参照頂いているようですが、私もプロトコルを分かっている訳ではなく、また、情報が古びているところも目だってきました。以下、少しだけ新しい情報を加筆しておきます。書き直しの時間がとれず、どーもすみません。

  • Cyrus SASL については、/usr/pkgsrc/security/cy2-login をインストールすれば良いようです。
  • Postfix: NetBSD 5.0.1 付属の Postfix でも、SASL は入っていないような感じです。pkgsrc から SASL 付きの Postfix を入れたほうが良いようです。

この辺(↓)も、参考にしてください。

以下、当時の原文

昨日の夜、ようやく動き始めました。2日ほどハマってしもた。以下は備忘録ですが、同じようなことをされる方の参考になれば幸いです。

背景 (興味ない人は飛ばして!)

GoogleGmail サービスを使っている方は多いと思います。また、私のように「おうちサーバー」を運用されているかたも多いかと思います。最近の spam *1 の跋扈、OP25B の跳梁*2を見ると、インターネットプロバイダ、メールプロバイダ等の信用されているメールサーバーを経由してメールを送るのが現実解になってきたように思えます。いずれにしても、From: 行の内容を証明できるような…。
話を戻します。LinuxNetBSD *3から Gmail 経由でメールを送りたい人は多いと思いますので、以下に設定方法のメモを残しておくことにしました。スカっと動いてしまった人にとってはなんてことはないのでしょうが、悩んでいる方も多いかと思います。私は 2日間も悩んでしまいました。各所に英語で書かれた有用なドキュメントはありますが、現状の最新情報を鑑みると必ずしも正確でない部分、落とし穴になりそうな部分もあるので、参考にして頂けると幸いです。また、各種エラーメッセージから原因を追及するセクションも設けようと思います。

Postfix のインストール (NetBSD 3.x 限定)

最初に、Postfix のインストールからです。私が NetBSD ユーザーなので、その説明しかできません。以降の節は OS に関わらず一緒だと思いますので、他 OS の方は自分でインストールして次節に進んでください。ポイントは、

  • Postfix のインストールには、TLS と SASL オプションを有効にしておく必要がある。(これは、Gmail の要求です*4。) このため、Postfix 2.2 以降が必要です。
  • TLS を有効にするには、OpenSSL をインストールしておく必要がある。(バージョンは、0.9.7i なら確実です。が、これを読まれたときにセキュリティホールが見つかっていたら、最新版をお試しください。なお、NetBSD では OpenSSL は初めからインストールされていますが、後で問題が起きた際の要因切り分けの時に、0.9.7i の openssl コマンド(/usr/pkg/bin/openssl)が必要になるかも知れないので、pkgsrc からインストールしておくことをお勧めします。ただし、Postfix のビルドでは使いません。)
  • SASL を有効にするには、Cyrus SASL (http://asg.web.cmu.edu/sasl/) をインストールしておく必要がある。(この際、後でハマらないためには、--enable-login オプションを付けて configure しておくとベターだが、必要ないかも知れない。後記: Postfix を正しく設定すれば、これは不要でした。必要があります。結果として、SASL 認証は AUTH LOGIN になります。原因は不明です。Cyrus SASL と Gmail のサーバー間で、AUTH PLAIN の相性が悪いのかも知れません。)

という感じです。
さて NetBSD で pkgsrc からビルドする場合ですが、pkgsrc が /usr/pkgsrc にあると仮定します。まず、SASL と OpenSSL をインストールします。

# cd /usr/pkgsrc/security/cyrus-sasl
# make clean install
# cd /usr/pkgsrc/security/openssl
# make clean install

続いて、Postfix ですが、これには Makefile の修正が必要です。/usr/pkgsrc/mail/postfix/Makefile の先頭に、PKG_OPTIONS.postfix = sasl tls と追加してから make します。

# cd /usr/pkgsrc/mail/postfix
# vi Makefile (お好きなエディタで、PKG_OPTIONS.postfix = sasl tls を追加)
# make clean install

TLS の動作確認 (興味ある人のみ)

OpenSSL のインストールが完了した時点で、GmailSMTP サーバーと TLS の接続が張れるか確認してみます。興味なければ飛ばして構いませんが、何事もモノゴトの仕組を知らないと気がすまない人、後でうまく動かなかったときの要因切り分け技術を習得する上で、試しておくとベターです。
なお、NetBSD 3.0 標準の openssl コマンドではエラーが出ましたので、pkgsrc からインストールした 0.9.7i 版を使用しました。

$ /usr/pkg/bin/openssl s_client -starttls smtp -connect smtp.gmail.com:587

ここで、submission ポート 587 を使っていますが、プロバイダで OP25B が課せられてなければポート 25 でも良いでしょう。
次のような結果が出ると思います。

CONNECTED(00000003)                            
depth=0 /C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
verify error:num=20:unable to get local issuer certificate
(略)
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDYzCCAsygAwIBAgIQGammhRRmnz+EbNWqDxllyTANBgkqhkiG9w0BAQUFADCB
(略)
---
New, TLSv1/SSLv3, Cipher is DES-CBC3-SHA
Server public key is 1024 bit
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DES-CBC3-SHA
(略)
---
250-mx.google.com at your service, [nnn.nnn.nnn.nnn]
250-SIZE 20971520
250-8BITMIME
250-STARTTLS
250 ENHANCEDSTATUSCODES

いくつかエラー、警告が出ています*5が、最後に

250-STARTTLS
250 ENHANCEDSTATUSCODES

が表示されていれば、TLS のセッションは張れています。エラーについてですが、前半の verify error はクライアント(つまり、おうちサーバー)が出しているもので、Gmail SMTP サーバーからの TLS 証明書を認証できなかった、というものです。openssl コマンドにルート証明書等を与えていないので、証明書の正当性が確認できなかった訳です。今のところこれは問題にはなりません*6
末尾のほうで出ている unable to verify the first certificate ですが、これは未調査です。ご存知のかた、教えてください。
なお、上記のような長いメッセージが出ず、

9398:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:
/home/builds/ab/netbsd-3-0-RELEASE/src/crypto/dist/openssl/ssl/s23_clnt.c:475:

などのようになった場合、OpenSSL の新しいバージョン(0.9.7i 以降?)を試してください。蛇足ながら openssl コマンドのバージョンは、

$ /usr/pkg/bin/openssl
OpenSSL> version
OpenSSL 0.9.7i 14 Oct 2005
OpenSSL> ^D
$ 

のように確認できます。
さて。せっかく TLS の接続が張れたので、ちょっと SMTP サーバーと会話してみましょう。下記、斜体で書かれた部分は私が入力した要求です。

250-STARTTLS
250 ENHANCEDSTATUSCODES
EHLO foobar.net
250-mx.google.com at your service, [nnn.nnn.nnn.nnn]
250-SIZE 20971520
250-8BITMIME
250-AUTH LOGIN PLAIN
250 ENHANCEDSTATUSCODES
MAIL FROM:
530 5.5.1 Authentication Required xxxxxxxxxx
QUIT
DONE

上記ボールド体部分を見ると、SASL 認証が必要であることが分かり、認証方法としては login と plaintext が使えることも分かります。試しに認証を無視してセッションを続行しようとすると、認証拒絶ではねられています。
最後に、念を入れて plaintext 認証が通るかどうか手動で確認しておきましょう。そうすれば、後で問題が出たときの原因切り分けに役立ち(?)ます。なお、プロトコルの詳細は RFC4616 あたりを参考にしてください。
ここでは仮に Gmail のアカウントを、ユーザー ID: hogehoge@gmail.com、パスワード: puipui としておきます。

$ perl -MMIME::Base64 -e 'print encode_base64("hogehoge\0hogehoge\0puipui");'
aG9nZWhvZ2UAaG9nZWhvZ2UAcHVpcHVp

(ここで、\0 の 2文字目は 1バイトコードの「ゼロ」です。) 再度 TLS セッションを張ります。

250-STARTTLS
250 ENHANCEDSTATUSCODES
EHLO foobar.net
250-mx.google.com at your service, [nnn.nnn.nnn.nnn]
250-SIZE 20971520
250-8BITMIME
250-AUTH LOGIN PLAIN
250 ENHANCEDSTATUSCODES
AUTH PLAIN aG9nZWhvZ2UAaG9nZWhvZ2UAcHVpcHVp
235 2.7.0 Accepted
QUIT
DONE

無事に accept されましたね。

Postfix の設定 (TLS 編)

さて、ここから Postfix の設定です。NetBSD で pkg の Postfix を使う場合、設定ファイルは /usr/pkg/etc/postfix に置かれます。
まず、main.cf に TLS の設定を追加します。ついでに、基本的に必要な定義も以下に示します。標準で提供される main.cf に下記を追加します。

# myhostname には、おうちサーバーホストの FQDN を書きます。
myhostname = myhost.hogehoge.net

# mydomain には、おうちサーバーのインターネットドメイン名を書きます。
mydomain = hogehoge.net

# myorigin には、このように書いておきます。ユーザー foo さんがメールを
# 出すと、From: が foo@hogehoge.net になります。
myorigin = $mydomain

# GmailSMTP ホストを記述します。ここでは submission ポート 587 を
# 指定しておきます。
relayhost = [smtp.gmail.com]:587

# TLS を使う旨、指示します。
smtp_use_tls = yes

><

Postfix の設定 (SASL 編)

次に、TLS 上で SASL を有効にします。また main.cf を編集し、以下を追加します。

# SASL 認証を使う旨、指示します。
smtp_sasl_auth_enable = yes

# SASL 認証のためのパスワードファイルを指定します。sasl_passwd ファイルに
# ついては後述します。
smtp_sasl_password_maps = hash:/usr/pkg/etc/postfix/sasl_passwd

# SASL で plaintext を使えるよう指定します。(Postfix のデフォルトでは、
# plaintext 認証しないようです。なお、Cyrus SASL で --enable-login
# してある場合、デフォルトの noplaintext, noanonymous でも、LOGIN SASL で
# 認証されますが、もしかすると RFC になっていないっぽいので、plaintext
# 認証にしておきましょう。将来の Gmail SMTP サーバーでは、この辺変わるかも
# 知れませんが。)
smtp_sasl_tls_security_options = noanonymous

次に、SASL 認証パスワードファイル sasl_passwd を作成します。場所は、main.cf で指定した /usr/pkg/etc/postfix/sasl_passwdです。ここでは Gmail のアカウントを、ユーザー ID: hogehoge@gmail.com、パスワード: puipui としておきます。

[smtp.gmail.com]:587    hogehoge@gmail.com:puipui

注意点としては、例えば以下のように記述することはできるようですが*7、[ ] を付けた場合は、正しくポートも指定しないといけません。私は、これでハマりました。

smtp.gmail.com          hogehoge@gmail.com:puipui

最後に、sasl_passwd ファイルのハッシュファイルを作ります。

# cd /usr/pkg/etc/postfix
# /usr/pkg/sbin/postmap sasl_passwd

動作確認、あるいはテスト

さて。ここでメールが正しく届くかどうかテストします。テストには、Postfix に付いてくる sendmail コマンドが便利です。コマンドを入力すると標準入力から入力待ちになるので、To: 行と Subject: 行を入力します。ヘッダが終わったら空行の後、本文に入ります。最後に 1バイトコードでピリオドだけを入力して改行すると、プロンプトに戻ります。

$ /usr/pkg/sbin/sendmail -f 差出人アドレス 宛先アドレス
To: 宛先アドレス
Subject: test mail

本文
.
$

通常は差出人アドレスに hogehoge@gmail.com を指定しますが、違う場合、GmailSMTP サーバー上で適切なアドレスに書き換えられます。これでメールが届けば OK です。試しにログファイル(NetBSD では、/var/log/maillog)の末尾を見ると、次のような行が書かれているはずです。

Dec  7 17:06:25 myhost postfix/smtp[28095]: xxxxxxxxxxx:
                to=, relay=smtp.gmail.com[nnn.nnn.nnn.nnn]:587,
                delay=4.3, delays=0.23/0.09/2.5/1.4, dsn=2.0.0,
                status=sent (250 2.0.0 OK xxxxxxxxxx xxxxxxxxxx)
Dec  7 17:06:25 myhost postfix/qmgr[2378]: xxxxxxxxxxx: removed

sent の代わりに、deferred とか bounced とか書かれていたら、配送(リレー)に失敗しています。

ヘッダー From について

Gmail では、たとえば hogehoge@gmail.com というアドレス(アカウント)で登録していても、これとは異なるアドレスをヘッダーの From: として指定することができます。(ただしそのためには、自分がそのアドレスでメールを受け取れることを証明しなくてはいけません。) この辺は Gmail 一般の話題なので、詳しくは Gmail のヘルプを参照して頂くとして、ここでは同様の機能を SMTP リレーで実現できることを記述しておきます。
結論から言うと、Postfix から出て行くメールの From: が、Gmail 設定から「アカウントの設定」にて、「他のメールアドレスを追加」として登録されていれば OK です。それだけです。例えば、他のメールアドレスとして fugafuga@mydomain.net がある場合、上記の sendmail コマンドで

$ /usr/pkg/sbin/sendmail -f fugafuga@mydomain.net 宛先アドレス

としてテストしてみましょう。届いたメールの From: 行が意図通りになっていれば正常です。Gmail のアドレスに書き換えられてしまった場合、Gmail の設定が間違っているか、メールアドレスの指定が間違っている可能性が高いです。

Google独自ドメインホスティング

ご存知の方もあると思いますが、最近、GoogleGmail と同様のサービスを独自インターネットドメインで提供するサービスを始めています。

これを使って、Postfix から独自ドメインのメールを運用(おうちサーバーから配送)することも可能です。変更点は以下だけです。独自ドメイン mydomain.net を運用し、ユーザー名が myname@mydomain.net である場合、sasl_passwd に次のように記述をします。あ、postmap も忘れずに。

smtp.gmail.com  myname@mydomain.net:puipui

もちろん、puipui はパスワードです。

こんなエラーが出たら

メールがうまく配送(リレー)されないときは、Postfix のログ(/var/log/maillog など)の末尾近くを確認します。

status=bounced (host smtp.gmail.com[nnn.nnn.nnn.nnn] said: 530 5.7.0
Must issue a STARTTLS command first xxxxxxxx (in reply to MAIL FROM command))

この場合は、TLS が正しく動作していません。Postfix は、TLS 付きでビルドされていますか? main.cf に、smtp_use_tls = yes は記述されていますか?

status=bounced (host smtp.gmail.com[nnn.nnn.nnn.nnn] said: 530 5.5.1
Authentication Required xxxxxxxx (in reply to MAIL FROM command))

この場合、SASL 認証がうまくいっていません。Postfix は SASL 付きでビルドされていますか? main.cf に、smtp_sasl_auth_enable = yes は記述されていますか? あるいは、sasl_passwd ファイルの内容は正しいですか? main.cf で relayhost = [smtp.gmail.com]:587 と記述されている場合、sasl_passwd でも、左項は [smtp.gmail.com]:587 のように記述しないといけません。また、postmap は忘れていませんか?

fatal: specify a password table via the `smtp_sasl_password_maps'
configuration parameter

エラーの文句そのままですね。SASL は定義されていますが、パスワードファイルが指定されていないようです。smtp_sasl_password_maps の記述はありますか? 綴りの間違いはないですか?*8

status=deferred (SASL authentication failed; cannot authenticate to server
smtp.gmail.com[nnn.nnn.nnn.nnn]: no mechanism available)

SASL は動作していますが、サーバーとクライアントの間で、合意できる SASL 認証手法が見つからなかったようです。main.cf に、smtp_sasl_tls_security_options = noanonymous は記述されていますか?

status=bounced (SASL authentication failed;
server smtp.gmail.com[nnn.nnn.nnn.nnn] said:
535 5.7.1 Credentials Rejected xxxxxxxx)

sasl_passwd に記述した Gmail のアカウント名(ID 名)、あるいはパスワードは正しいですか? また、postmap は忘れていませんか?

それでも分からなかったら

main.cf 中で以下のような設定をすると、SMTP サーバーとの対話を詳しくログに出力してくれます。ログ中に SASL 認証用のパスワードも出力されるので、ログのファイルパーミッションに注意してください。

debug_peer_list = smtp.gmail.com

デバッグが終わったら、このオプションを外しておきましょう。

Gmail SMTP サーバーの証明書を正しく検証する

今までは、Gmail SMTP サーバーが渡してくる TLS 証明書を検証しないで接続していましたが、たとえば DNS の情報が偽造されるなどして、もしかすると接続先の SMTP サーバーが本物の Gmail サーバーでないかも知れません! これを防ぐには、Gmail SMTP サーバーが持っている秘密鍵の証明書を、ルート証明書で正しく検証する必要があります。
そのためには、まず、Gmail SMTP サーバーが持っている証明書から、ルート CA を調べます。上記の openssl s_client セッションから、Thawte Premium Server CA がルート CA だということが分かります。ルート CA のインターネットドメインは thawte.com ですので、ここからルート証明書をダウンロードします。調べると、この証明書は http://www.thawte.com/roots/ からダウンロードできることが分かりました。
ダウンロードしたファイルから、Thawte Premium Server CA を探します。PEM 形式のファイルがあれば、そのまま使えます*9。そうでない場合、

$ openssl x509 -inform der -in 'Thawte Server Roots/ThawtePremiumServerCA.cer'
-outform pem -out ThawtePremiumServerCA.pem

のようにして変換できます。この ThawtePremiumServerCA.pem を /usr/pkg/etc/postfix に置き、main.cf を修正します。

# ルート CA の証明書を指定します。
smtp_tls_CAfile = /usr/pkg/etc/postfix/ThawtePremiumServerCA.pem

# 最初は、ログレベルを上げておきます。うまく動作することが分かったら、
# デフォルトの 0 に戻して良いでしょう。
smtp_tls_loglevel = 1

# TLS が有効でかつ、サーバーの TLS 証明書を検証できたときだけ、
# SMTP サーバーに接続するようにします。
smtp_tls_security_level = secure

この辺の詳細は、以下の参考ドキュメントを当たってください。

おまけ

おうちサーバー内部から、自ドメイン宛のメールを送るとき、Gmail に投げずにローカルに配送する方法をメモしておきます。これは、独自ドメインGoogleホスティングする際に欲しくなる動作です。つまり、外部から届く myname@hogehoge.net 宛のメールは Google ホスティングSMTP サーバーで受けたいけど、おうちサーバー内部で root@hogehoge.net 宛に送るメールを、いちいち GoogleSMTP サーバーにリレーしたくない、というケースです。
一つの簡単な方法は、Postfix の main.cf で、mydestination を書き換えるというものです。デフォルトは

mydestination = $myhostname, localhost.$mydomain, localhost

になっていると思いますが、これを

mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain

に変更すれば、root@hogehoge.net 宛のメールはローカルに配送されるようになります。

参考ドキュメント

以上、備忘録としてまとめさせて頂きましたが、下記のドキュメントが非常に役に立ちました。この場を借りて、お礼申し上げます。

><

*1:迷惑メール。

*2:是非の議論は、ここでは置いておきます。

*3:FreeBSD, OpenBSD も可。

*4:普通に SMTP でセッションを張ると「STARTTLS してね」と言われること、また、TLS を張って EHLO すると、250-AUTH LOGIN PLAIN と言ってくるので分かりますね。

*5:設定に依ってはエラーが出ないかも知れません。

*6:Postfix の設定を「厳密側」に倒すと、これは問題になってくる。

*7:[ ] を外すと、DNS で MX レコードを引いてくるようだ。

*8:このような場合、postconf コマンドで main.cf の記述をダンプして確認するのも手です。

*9:ここでは、Thawte Server Roots/ThawtePremiumServerCA.txt というファイルがそうです。