SendGrid Advent Calendarの12日目は、主流なMTA3種類(Sendmail, Postfix, OpenSMTPD)からSMTPでSendGridへリレーする方法の話です。
前書き
ここ数年でちょいちょい「PostfixからSendGridへ移行した」系の記事を見かけるようになり、さくらインターネットでSendGridが使えるようになり、クラウド系のメール配信がより身近になってきたなぁという印象です。
遅延対策としてのリレー先
僕は前職ではメール配信(多くて100万通ぐらい/日)に使われているSendmailの守役をしていたのですが、現職では大量配信はまったく行っていなくて、原則としてサイトを構成するネットワーク内にあるSMTPサーバから配信することにしています。それでも宛先ホストによっては遅延しがちなところもあります。
そこでSendGridです。
設定を変えて観測して、一旦SendGridのSMTPサーバを経由したほうが遅延が少ないようであれば、そのドメイン宛メールはSendGridに丸投げすることにして、なるべくこちら側のMTAに滞留するキューが少なくなるように運用しています。
SMTPでリレーする
今時のアプリケーションは、最初からSendGridなどクラウド系メール配信サービスを使う前提で開発されていればWeb APIを使った配信機能を持っていることでしょう。しかし、「たまに通知系のメールが飛ぶ」「監視系のアラートがたまに出る」ようにメールの流量が少ないケースでは、アプリケーションが動いているホストの/usr/sbin/sendmail
に任せっきりで、遅延が発生してから「メール配信どうしよう」と壁に当たることもあるかと思います。
そんな時はMTAの設定を少し変更してSendGridへリレーするだけで、配信状況が劇的に改善する可能性があります。 この記事は、そんな壁に当たった時に役に立つかもしれない内容です、たぶん。
この記事でやること
いずれのMTAでも、宛先が@gmail.com
か@aol.com
の場合に限り、SendGridへリレーする設定を行います。
- SendGridでSMTP認証用アカウントを作る
- SendmailでSendGridの25番ポートへリレーする
- PostfixでSendGridの587番ポートへリレーする
- OpenSMTPDでSendGridの2525番ポートへリレーする
1. SMTP認証用アカウントの作成
ここから本題です。認証を経てSMTPでリレーするためのリレー用アカウントをSendGridで作っておきます。SendGridにログインした左側の[Settings]→[Credentials]から[Add New Credential]って青いボタンを押し、ユーザ名とパスワードを入力して、□MAIL:のところにチェックを入れて[Create Credential]です。
以下のユーザ名とパスワードを各MTAの設定で使うことにします。
- ユーザ名 = sgac-2016-smtp-auth-1
- パスワード = kijitora-nyan-nyan-22
ちなみに、たとえ例示用パスワードの文字列といえども未知の単語が出てくると「?」と感じる事があると思いますので、軽く言及しておきます。
kijitoraはキジトラで雉虎猫の事を指しています。茶色と黒のシマシマ模様な猫で、街中で野良猫として非常によく見かけるネコです。逆にペットショップで売っているネコの中にキジトラは先ず居ないと言っていいでしょう。この模様は野生型とも言われるもので、イエネコの源流と言われるリビアヤマネコと同じ毛色です。
Two stray cats and cherry blossoms | 猫と桜
この猫を「キジトラ」と表現する人はわりとネコの事をよく知っている人で、そうでもない人は「黒っぽい猫」「シマシマの猫」「ヘビっぽい模様の猫」といった表現をしはります。白猫、黒猫、三毛猫と違ってキジトラの呼び方は猫をよく知っている人かどうかのリトマス試験紙として機能しますね。
なお、黒と灰色のシマシマ模様な猫は「サバトラ=(鯖っぽい模様の虎猫)」と言います。
2. Sendmail
最初はSendmailです。信頼と実績、歴史と伝統のSendmailです。ここ10年かもうちょいを見れば最早MTAとしてSendmailを意図的に選択することは少なくなっていると思いますが、Postfix登場〜隆盛以前の時代はオープンソースのMTA=Sendmailでした。まだSendmailを使っているところもあると思いますし、僕もSendmailが現役で動くホストを(職業的に必要なので)幾つか確保しています。
ちなみに、このSendmailの節は2011年に書いたブログの焼き直しです。今回はMTA3種盛りなので、ササッと進めます。
Sendmailでのリレー動作を確認したのは、つい先日発表されたAWS Lightsailで作ったAmazonLinuxの一番小さいインスタンスです。
/etc/mail/sendmail.cf
authinfo
という認証に使用する機能が/etc/mail/sendmail.cf
に組み込まれている必要があるので、grep
して何も出てこなければ、次のようにsendmail.mc
に1行書いて、sendmail.cf
を作り直します。
% grep authinfo /etc/mail/sendmail.cf ##### $Id: authinfo.m4,v 1.9 2002/06/27 23:23:57 gshapiro Exp $ ##### # authinfo list database: contains info for authentication as client Kauthinfo hash /etc/mail/authinfo ... いろいろ出る
authinfo
が組み込まれていれば↑のような何かが出る、何も出なければ↓のようにFEATURE(`authinfo')dnlを書く
FEATURE(`authinfo')dnl ←この行を追加する FEATURE(`mailertable', `hash -o /etc/mail/mailertable.db')dnl ←最初から入ってることが多いけど無い時はこれも追加で
書いたらmake sendmail.cf
を実行する(sendmail.mc
からsendmail.cf
が生成される)
# cd /etc/mail # make sendmail.cf
/etc/mail/authinfo
authinfo
を組み込んだsendmail.cf
が出来上がったら、SendGridへ認証付きでリレーする定義を/etc/mail/authinfo
に書きます、次のような感じで。
Authinfo:smtp.sendgrid.net "U:sgac-2016-smtp-auth-1" "P:kijitora-nyan-nyan-22" "M:plain"
リレー時の認証情報が生で書いているのでパーミッションは安全に。
# cd /etc/mail # /usr/sbin/makemap hash authinfo.db < authinfo # chown root ./authinfo* # chmod 0600 ./authinfo*
このファイルはsmtp.sendgrid.net
へリレーする時に使う認証情報としてsendmailのプロセスに参照されます。
/etc/mail/mailertable
この設定をしているホストから出る全てのメールをSendGridへリレーすることもできますが、今回の記事は特定の宛先だけSendGridへリレーするので、ここは@gmail.com
と@aol.com
宛メールはSendGridへリレーする設定をします。
特定ドメイン宛のリレーは/etc/mail/mailertable
を使います。次のような書式で書くと良いです。
gmail.com relay:[smtp.sendgrid.net] aol.com relay:[smtp.sendgrid.net]
# cd /etc/mail # /usr/sbin/makemap hash mailertable.db < mailertable # make mailertable.db ←/etc/mail/Makefileがあればこれでも良い
Sendmailの再起動と送信テスト
authinfo
を組み込んだ(sendmail.cf
を変更した)のでSendmailを再起動します。それから送信テストです。
# service sendmail restart Shutting down sm-client: [ OK ] Shutting down sendmail: [ OK ] Starting sendmail: [ OK ] Starting sm-client: [ OK ]
送信テストは/bin/mail
コマンドで。もしmail
コマンドがないRed Hat系Linuxならyum install mailx
してください。
% date | mail -s 'Test from Sendmail via SendGrid' 宛先ローカルパート@gmail.com
# cat /var/log/maillog Dec 12 01:18:38 sgac2016 sendmail[2607]: uBC1Ic4D002607: from=ec2-user, size=246, class=0, nrcpts=1, msgid=<201612120118.uBC1Ic4D002607@sgac2016.nyaan.jp>, relay=root@localhost Dec 12 01:18:38 sgac2016 sendmail[2608]: uBC1Ic0h002608: from=<ec2-user@sgac2016.nyaan.jp>, size=513, class=0, nrcpts=1, msgid=<201612120118.uBC1Ic4D002607@sgac2016.nyaan.jp>, proto=ESMTP, daemon=MTA, relay=localhost [127.0.0.1] Dec 12 01:18:38 sgac2016 sendmail[2607]: uBC1Ic4D002607: to=宛先ローカルパート@gmail.com, ctladdr=ec2-user (500/500), delay=00:00:00, xdelay=00:00:00, mailer=relay, pri=30246, relay=[127.0.0.1] [127.0.0.1], dsn=2.0.0, stat=Sent (uBC1Ic0h002608 Message accepted for delivery) Dec 12 01:18:38 sgac2016 sendmail[2610]: STARTTLS=client, relay=smtp.sendgrid.net., version=TLSv1/SSLv3, verify=FAIL, cipher=AES128-GCM-SHA256, bits=128/128 Dec 12 01:18:39 sgac2016 sendmail[2610]: uBC1Ic0h002608: to=<宛先ローカルパート@gmail.com>, ctladdr=<ec2-user@sgac2016.nyaan.jp> (500/500), delay=00:00:01, xdelay=00:00:01, mailer=relay, pri=120513, relay=smtp.sendgrid.net. [108.168.190.109], dsn=2.0.0, stat=Sent (Ok: queued as X5tJVpcRR9yPrMm9oYisjA)
ちゃんとSendGridのSMTPサーバに流れていますね、relay=smtp.sendgrid.net. [108.168.190.110]って部分を見ると。 そういえば、この記事書くためのテストで思い出しましたが、STARTTLSのところもちゃんと確認したいとこです。
ササッと進めるとか書きましたがSendmailだけでわりと長くなってしまいました、次へ行きます。
3. Postfix
今やSMTPサーバを構築するならPostfix一択(というと言い過ぎなところはありますが実際そんな感じ)でしょう。僕もSendmailしかない時代はSendmailでしたが、PostfixがリリースされてすぐにPostfixに乗り換えました、快適でした。ところが次に行った会社は全てSendmailで、しかもたくさんあったので、結局今に至るまでうちのカミさんと言ってもいいぐらいSendmailと付き合ってます。
Postfixでも同様に、@gmail.com
と@aol.com
宛はSendGridのSMTPサーバsmtp.sendgrid.net
へ認証を経てリレーします。
この節の内容もSendmailと同じAmazonLinuxで動作テストをして書きました。
/etc/postfix/main.cf
Sendmailのmailertable
(宛先ドメインごとにリレーする経路を定義する)に相当するPostfixの機能がtransport_maps
です。この定義が/etc/postfix/main.cf
に書いていなければなりません。それと、リレー時の認証で使用されるファイルの定義、これも同様に記述します。
# grep -E '(transport_maps|sasl)' /etc/postfix/main.cf transport_maps = hash:/etc/postfix/transport ←これが書いていればOK smtp_sasl_auth_enable = yes ←SMTP認証を使う smtp_sasl_password_maps = hash:/etc/postfix/authinfo ←認証で使うアカウント・パスワードの設定 smtp_sasl_security_options = noanonymous smtp_tls_security_level = encrypt
/etc/postfix/authinfo
main.cf
で認証アカウントを定義するファイルは/etc/postfix/authinfo
としたので、以下のようなファイルを/etc/postfix/authinfo
として保存します。ファイル名はmain.cf
で指定したものであればcredentials
でもrelay-accounts
でもなんでも良いです。
[smtp.sendgrid.net]:587 sgac2016-smtp-auth-1:kijitora-nyan-nyan-22
authinfo
に認証情報を書いたらpostmap
コマンドでauthinfo.db
を作って、安全なパーミッションに変更しておきます。
# cd /etc/postfix # /usr/sbin/postmap authinfo # chown root ./authinfo* # chmod 0600 ./authinfo* ←パーミッションは安全に
/etc/postfix/transport
続いて、transport_maps
で定義しているファイル/etc/postfix/transport
に下記のようなSendGridへリレーする設定を書きます。
gmail.com relay:[smtp.sendgrid.net]:587 aol.com relay:[smtp.sendgrid.net]:587
中身はSendmailのmailertable
と同じです。なお、smtp.sendgrid.net
と書く([``]
に入れない)と、smtp.sendgrid.net
のMXレコードを引く動作(Sendmailのmailertableも同じ)になります。実際、このホスト名にはMXレコードはないのでAレコードを引いて正しくリレーされるのですが、[smtp.sendgrid.net]
と書くほうが確実です。
なお、動作テストでは25番ポート(Sendmailでテストした)でも2525番ポート(OpenSMTPDでテストした)でも正しく送信できる事を確認していますが、公式ドキュメント: Postfixでメール送信 - ドキュメント | SendGridにも記述がある通り、587番ポートを使うと良いでしょう。
Postfixの再起動と送信テスト
main.cf
を変更したので、Postfixを再起動してから送信テストをします。
% sudo service postfix restart Shutting down postfix: [ OK ] Starting postfix: [ OK ] % date | mail -s 'Test from Postfix via SendGrid' 宛先ローカルパート@gmail.com % sudo cat /var/log/maillog Dec 12 04:50:43 sgac2016 postfix/qmgr[9677]: D69EC625E9: from=<ec2-user@sgac2016.nyaan.jp>, size=716, nrcpt=1 (queue active) Dec 12 04:50:43 sgac2016 sendmail[9680]: uBC4oh1N009680: to=宛先ローカルパート@gmail.com, ctladdr=ec2-user (500/500), delay=00:00:00, xdelay=00:00:00, mailer=relay, pri=30248, relay=[127.0.0.1] [127.0.0.1], dsn=2.0.0, stat=Sent (Ok: queued as D69EC625E9) Dec 12 04:50:43 sgac2016 postfix/smtpd[9681]: disconnect from localhost[127.0.0.1] Dec 12 04:50:43 sgac2016 postfix/smtp[9685]: certificate verification failed for smtp.sendgrid.net[108.168.190.109]:587: untrusted issuer /C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority Dec 12 04:50:43 sgac2016 postfix/smtp[9685]: D69EC625E9: to=<宛先ローカルパート@gmail.com>, relay=smtp.sendgrid.net[108.168.190.109]:587, delay=0.09, delays=0.05/0.01/0.02/0, dsn=2.0.0, status=sent (250 Ok: queued as guKZiGUJTjKvZcRsOP9OOA)
下から二つ目のログにrelay=smtp.sendgrid.net[108.168.190.109]:587
と書いているので、リレー成功です。
4. OpenSMTPD
*BSDが好きな人やメールサーバに関心のある人は知ってはると思います、OpenBSDプロジェクトから誕生した比較的新しいMTAであるOpenSMTPDでリレーする方法です。
OpenBSDプロジェクトから誕生したプロダクトで最も有名なのはおそらくOpenSSHですね。昔は、SSHプロトコルv1の時代は商用製品しかなかったと記憶していますが、今やSSHプロトコルv2の時代、皆さんが使ってはるSSHはサーバもクライアントもOpenSSHであることやと思います。
OpenSMTPDはセキュアである事(OpenBSDプロジェクトですし)・設定が簡単である事・高い信頼性を特徴とするMTAで、現在の最新版は6.02
です。この記事を書くために用意した環境は、FreeBSD 10.1-RELEASE-p26で動くOpenSMTPD 6.0.2p1(/opt/smtpd
へソースビルドしたやつ)です。
/opt/smtpd/etc/smtpd.conf
SendmailやPostfixと同じ要領で、リレー条件の定義と認証情報を書いたファイルを作ります。OpenSMTPDの設定ファイルには下記のような内容を書きます。
table authinfo file:/opt/smtpd/etc/authinfo accept for domain "gmail.com" relay via "tls+auth://sendgrid@smtp.sendgrid.net:2525" auth <authinfo> accept for domain "aol.com" relay via "tls+auth://sendgrid@smtp.sendgrid.net:2525" auth <authinfo>
書く場所はaliases
の定義近辺で良いでしょう。上記のtls+auth://sendgrid@
の**sendgrid
の部分はラベルと呼ばれる部分で、後述の認証情報テーブル(authinfo
)からOpenSMTPDのプロセスが検索するときのキーとなります。
/opt/smtpd/etc/authinfo
こっちはリレーするときの認証情報です。下記のようにPostfixのそれと同じような形式で書きます。
sendgrid sgac-2016-smtp-auth-1:kijitora-nyan-nyan-22
ファイルを書いたらroot
とOpenSMTPDの特権分離ユーザ(_smtpd
)だけが読めるパーミッションにしておきます。
# cd /opt/smtpd/etc # chown root:_smtpd ./authinfo # chmod 0640 ./authinfo
なお、smtpd.conf
でtable authinfo db:...
という書き方もできる(makemap(8)
でハッシュを作る)のですが、OpenSMTPD 6.0.2p1ではauthinfo.db
がなぜか作れなかったので、man smtpd.conf
の設定例にしたがってfile:...
としています。
OpenSMTPDの再起動と送信テスト
/etc/rc.d/smtpd
なんて便利なものがあればそれでrestart
でOKですが、特に用意していないので、昔ながらの泥臭い再起動でやります。
% sudo /opt/smtpd/sbin/smtpctl stop && /opt/smtpd/sbin/smtpd
再起動をしたら同じようにテストメールを送ってログの確認です。
% date | mail -s 'Test from OpenSMTPD via SendGrid' 宛先ローカルパート@gmail.com % sudo cat /var/log/maillog Dec 12 16:36:27 neko smtpd[88528]: 1e0b8dc91093c292 mta event=connecting address=tls://167.89.125.25:2525 host=o16789125x25.outbound-mail.sendgrid.net Dec 12 16:36:27 neko smtpd[88528]: 1e0b8dc91093c292 mta event=connected Dec 12 16:36:28 neko smtpd[88528]: 1e0b8dc91093c292 mta event=starttls ciphers=version=TLSv1.2, cipher=AES128-GCM-SHA256, bits=128 Dec 12 16:36:29 neko smtpd[88528]: smtp-out: Server certificate verification succeeded on session 1e0b8dc91093c292 Dec 12 16:36:30 neko smtpd[88528]: 1e0b8dc91093c292 mta event=delivery evpid=8c545a4e9a16e5dc from=<cat@neko.nyaan.jp> to=<宛先ローカルパート@gmail.com> rcpt=<-> source=133.242.157.118 relay=167.89.125.25 (o16789125x25.outbound-mail.sendgrid.net) delay=3s result=Ok stat=250 Ok: queued as dfzaGbhcTE2D0abtiIAEFA
長いホスト名.sendgrid.net
へリレーしたって書いているので、リレー成功です、めでたし。
まとめ
SendGridの公式ブログ: Web APIかSMTPリレーか: どうやってメールを送ればいいの? | SendGridブログ では「可能であればWeb APIのご利用をお勧めいたします」と書いています。たしかにAPIのほうが細かい制御ができる・SMTPリレーより高速という利点があります。
とはいえ、サイトで動いているアプリケーションに簡単に手を入れられないケースもあるでしょう。そんな時はこの記事で紹介しているようなSMTPリレーの方が簡単です。もちろん、MTAの設定を変更する方が困難ってケースもあると思いますので、それはコストの少ない方を選ぶとか良しなにってところではありますが。
後書き
現職では大量メール送信は全然扱っていなくて、主にバウンスメールを正しく解析する方面でSMTPに関わっています。
SendGridが米国で登場してすぐに「メール送信の主流になりそう」ってことでアカウントを作っていたのですが、今回12日目の記事を書くための動作確認とJSONで得られるバウンスオブジェクトを解析するコード実装で、SendGridへログインする必要がでてきました。
ところが、最後にログインして1年以上経過していたので、アカウントが無効な状態になっていました。「さて、どうしたもんやろ」と思いながらTwitterに書いたら、日本法人の中の人にツイートを拾っていただいて、助け舟をだしていただきました。
“Your account has been deactivated.”な状態からSendGridのアカウントを復帰させる方法
— ネコ物質 (@azumakuniyuki) November 15, 2016
僕のアカウントは米国で作ったもので、最終的に英語での問い合わせで解決したのですが、それまでの道筋を日本語で教えて頂けてとても助かりました。
いざという時に日本語でどうにかなるというのも嬉しいところですね。