AWSで独自ドメインメール受信 〜 postfix編|アドカレ2013 : CFn #19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

よく訓練されたアップル信者、都元です。アドベントカレンダー2013「AWS CloudFormationビッグバンテンプレート」、本日は19日目です。

昨日は都元さんのCassandra編でした。って俺じゃーーんwww まさかの連投www

AWSでメール受信

e-mail。インターネットの初期からある通信手段で、Webと並んで最も存在感を発揮しているインターネット機能の一つであります。AWSにおいて、メールの「送信」についてはAmazon Simple Email Serivice (SES)というサービスによる強力なサポートがありますが、メールの「受信」となると実は結構頭を抱えてしまう人は多いのではないでしょうか。

もちろんAWSにはRoute 53というDNSサービスがありますので、こちらにMXレコードを記述することで、メールの到達先を示すことはできます。ただその到達先となるサーバはEC2で構成し、そこにSMTPサーバをセットアップしておくことになるでしょう。しかし、AWSにおける可用性の考え方で示した通り、AWS上のシステムはAZ障害レベルに耐えられる構成、つまりMulti-AZの構成を取るべきとされています。複数のデータセンタ(AZ)にまたがって、単一障害点(SPOF)を作らないアーキテクチャを構成しなければならないのです。

基本的に、SMTPサーバは受信したメールデータをローカルファイルシステムに書き込むため、完全なMulti-AZ体制を組むことが難しいのが現実です。従って今回は「メールボックス(≒ストレージ)自体はAWSに置かない」という方針を取りました。通常受信したメールはそのサーバが保持し、mailコマンドなりPOP3/IMAPなりで読み出されることを期待しますが、今回の構成ではそれを「別のメールアドレスに転送する」ということで解決します。

ぶっちゃけ、いまさら「生のメールボックス」を新規に作る必要性があるケースは少ないでしょう。みんな各々、個人のメールボックスは既にもっているでしょうから、そこに転送できれば充分なケースがほとんどかと思います。

今回の構成

postfix

というわけでこれが今回の構成です。Postfix1サーバにはpostfixadminという管理ツールがインストールされており、このWeb UI経由でメールの転送先登録等を行えます。

まず正常系。Senderはどこかのメールサーバ *1を使ってメールを送ります。メールを受け取ったメールサーバは、宛先ドメインのMXレコードを確認し、そこにメールを配送します。通常、high-priorityに設定されたPostfix1サーバにメールが届きます。Postfix1は届いたメールを設定に従って別のメールアドレスに転送します。

次に異常系。Postfix1に障害が発生したとします。その場合、low-priorityに設定されたPostfix2サーバがメールを受け取ります。このPostfix2サーバは届いたメールPostfix1に配送しようと試みます。が、Postfix1には障害が起きています。Postfix2は配送の試行を繰り返しながらPostfix1の復旧を待ちます。ここで運良くPostfix1が復旧すれば、正常系と同じようにメールが転送されます。しかし、長期間(設定上は5日間)Postfix1が復旧しなかった場合、メールは配送に失敗し、Senderにエラーが返る、という帰結になります。

このアーキテクチャは「Postfix1が障害を起こしている間、メールのロストはしないが、メールの流れがせき止められてしまう」という状況が発生するため、完全な可用性は確保できませんが、そこそこ充分な実用レベルには達しているのではないかと思っています。

デモ

いきなり起動したいところですが、少々の事前準備が必要です。Route 53において、Hosted zoneを作成し、ゾーンの委譲設定 *2を完了させておいてください。ここではexample.comというゾーンを作成したものとして話を進めます。

というわけでこれを起動してみましょう。

postfixのデモを起動

パラメータは以下のとおりです。

  • HostedZone: 上で作成したhosted zone名(example.com)
  • DomainName: メールアドレスとして使うドメイン名(example.com や hoge.example.com等)
  • KeyName: ECのキーペア名。EC2へのSSH接続用。
  • PostfixAdminSetupPass: postfixadminのセットアップパスワード。任意。
  • Sub-Stackを利用するため「I acknowledge that this template may create IAM resources」にチェックを入れてください。

あとはデフォルトのままでContinueボタン連打で構いません。

スタック作成の所要時間はだいたい10分強ってとこでしょうか。スタックが完成(CREATE_COMPLETE)したら、出来上がったStackのOutputsにあるURLにアクセスしてみてください。この例ではhttp://mail.example.com/postfixadmin/setup.phpとなっているはずです。

postfixadminによる設定

postfixadmin-1

まずはこの画面で管理ユーザを登録します。Setup passwordは上記で決めた任意のパスワード。ドメイン管理者の欄は、上で指定したドメインにおけるメールアドレス。admin辺りが良いと思います。そしてそのadminユーザのパスワードを入力。

postfixadmin-2

管理ユーザ登録ができたら、URLのsetup.phpを除去してhttp://mail.example.com/postfixadminにアクセスすると、ログイン画面となります。先ほどのadmin@example.comとそのパスワードを入力してログインします。

postfixadmin-3

ログインしたらまずは「ドメインの登録」をします。postfix的には1サーバで複数のドメインを管理できるわけですが、今回の構成ではexample.comのみを登録します。メニューの「ドメイン一覧」から「新しいドメイン」を選択します。

postfixadmin-4

ドメインとしてexample.comを入力し、他はデフォルト空欄のままsubmitします。

postfixadmin-5

続いて転送アドレスの追加です。メニューの「アドレス一覧」から「転送先の追加」を選択します。

postfixadmin-6

そして、独自ドメインのメールアドレスと、それに対応する転送先を入力し、submitします。

postfixadmin-7

以上で設定は完了です。

メールを送ってみる

postfix-1

普通に送るだけです。その時のPostfix1のログはこんな感じ。正直細かいことはよくわかりませんが、foobar@example.com宛のメールがきちんと転送されているようです。実際、メールが届きました。

MMM dd HH:mm:ss ip-10-0-0-XX postfix/smtpd[19306]: connect from mail-xxx-xxxx.example.net[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-0-XX postfix/smtpd[19306]: 26534207EF: client=mail-xxx-xxxx.example.net[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-0-XX postfix/cleanup[19315]: 26534207EF: message-id=<xxx@mail.example.net>
MMM dd HH:mm:ss ip-10-0-0-XX postfix/qmgr[2296]: 26534207EF: from=<xxx@example.net>, size=4069, nrcpt=1 (queue active)
MMM dd HH:mm:ss ip-10-0-0-XX postfix/smtp[19318]: connect to xxx.example.net[2607:f8b0:4002:c01::1b]:25: Network is unreachable
MMM dd HH:mm:ss ip-10-0-0-XX postfix/smtpd[19306]: disconnect from mail-xxx-xxxx.example.net[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-0-XX postfix/smtp[19318]: 26534207EF: to=<miyamoto.daisuke@classmethod.jp>, orig_to=<foobar@example.com>, relay=xxx.example.net[xx.xx.xx.xx]:25, delay=2, delays=0.91/0.01/0.5/0.61, dsn=2.0.0, status=sent (250 2.0.0 OK 1386138803 zk5si24848154pac.61 - gsmtp)
MMM dd HH:mm:ss ip-10-0-0-XX postfix/qmgr[2296]: 26534207EF: removed

Postfix1障害時にメールを送ってみる

postfix-2

このようにPostfix1を止め、その上でまたメールを普通に送ってみます。Postfix2のログはこんなかんじ。受信したメールをPostfix1に配信しようとしますが、タイムアウトしてしまいます。

MMM dd HH:mm:ss ip-10-0-1-XX postfix/smtpd[2166]: connect from mail-xxx-xxxx.example.net[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-1-XX postfix/smtpd[2166]: 8E28B201F8: client=mail-xxx-xxxx.example.net[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-1-XX postfix/cleanup[2170]: 8E28B201F8: message-id=<xxx@mail.example.net>
MMM dd HH:mm:ss ip-10-0-1-XX postfix/qmgr[2165]: 8E28B201F8: from=<xxx@example.net>, size=6253, nrcpt=1 (queue active)
MMM dd HH:mm:ss ip-10-0-1-XX postfix/smtpd[2166]: disconnect from mail-xxx-xxxx.example.net[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-1-XX postfix/smtp[2171]: connect to mail.example.com[xx.xx.xx.xx]:25: Connection timed out
MMM dd HH:mm:ss ip-10-0-1-XX postfix/smtp[2171]: 8E28B201F8: to=<foobar@example.com>, relay=none, delay=31, delays=0.96/0.02/30/0, dsn=4.4.1, status=deferred (connect to mail.example.com[xx.xx.xx.xx]:25: Connection timed out)

このような試行を定期的(デフォルトで1000秒ごと)に何度か繰り返します。

MMM dd HH:mm:ss ip-10-0-1-XX postfix/qmgr[2165]: 8E28B201F8: from=<xxx@example.net>, size=6253, nrcpt=1 (queue active)
MMM dd HH:mm:ss ip-10-0-1-XX postfix/smtp[2204]: connect to mail.example.com[xx.xx.xx.xx]:25: Connection timed out
MMM dd HH:mm:ss ip-10-0-1-XX postfix/smtp[2204]: 8E28B201F8: to=<foobar@example.com>, relay=none, delay=525, delays=494/0.03/30/0, dsn=4.4.1, status=deferred (connect to mail.example.com[xx.xx.xx.xx]:25: Connection timed out)

このまま5日待つ実験をする必要もあるかとは思いますが、今回は省略で。ここでPostfix1を復活させます。すると次回試行時に配信が成功し、その結果、Postfix1からメール転送が行われ、メールボックスにメールが届くことを確認できました。

MMM dd HH:mm:ss ip-10-0-0-41 postfix/smtpd[1585]: connect from xxx.example.org[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-0-41 postfix/smtpd[1585]: B540B207CA: client=xxx.example.org[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-0-41 postfix/cleanup[1594]: B540B207CA: message-id=<xxx@mail.example.net>
MMM dd HH:mm:ss ip-10-0-0-41 postfix/qmgr[1508]: B540B207CA: from=<xxx@example.net>, size=6527, nrcpt=1 (queue active)
MMM dd HH:mm:ss ip-10-0-0-41 postfix/smtpd[1585]: disconnect from xxx.example.org[xx.xx.xx.xx]
MMM dd HH:mm:ss ip-10-0-0-41 postfix/smtp[1597]: connect to xxx.example.net[2607:f8b0:4002:c01::1b]:25: Network is unreachable
MMM dd HH:mm:ss ip-10-0-0-41 postfix/smtp[1597]: B540B207CA: to=<miyamoto.daisuke@classmethod.jp>, orig_to=<foobar@example.com>, relay=xxx.example.net[xx.xx.xx.xx]:25, delay=1.3, delays=0.05/0.02/0.46/0.73, dsn=2.0.0, status=sent (250 2.0.0 OK 1386140453 zk5si24920746pac.3 - gsmtp)
MMM dd HH:mm:ss ip-10-0-0-41 postfix/qmgr[1508]: B540B207CA: removed

本番用テンプレート

VPC環境の構築もCloudFormationにまかせてしまいたい場合、上記デモスタックでも充分実用に耐えます。が、既存システムで既にあるVPCにメールサーバを構築したい場合は、下記のリンクから本番用テンプレートをご利用ください。デモはパラメータを少なくして素早くお試しができることに主眼を置いており、本番用はパラメータ多めでカスタムがしやすいことに主眼を置いています。

postfixを起動

パラメータは以下のとおりです。

  • VpcId: 構築対象のVPC IDを指定
  • VpcCidrBlock: 上記VPC全体のCIDRブロックを指定
  • PrimarySubnetId: Postfix1(稼動系)を配置するサブネットIDを指定
  • SecondarySubnetId: Postfix2(待機系)を配置するサブネットIDを指定
  • InstanceType: Postfix1 及び Postfix2 のインスタンスタイプ
  • MaintenanceFrom: SSH及びpostfixadminにアクセスできるアクセス元IPアドレスのCIDRブロックを指定
  • HostedZone: 上で作成したhosted zone名(example.com)
  • DomainName: メールアドレスとして使うドメイン名(example.com や hoge.example.com等)
  • KeyName: ECのキーペア名。EC2へのSSH接続用。
  • PostfixAdminSetupPass: postfixadminのセットアップパスワード。任意。
  • IAM Roleを利用するため「I acknowledge that this template may create IAM resources」にチェックを入れてください。

例によってGitHubに公開済みです

このテンプレートは例によってGitHubに公開済みです。要望・改善案等ございましたら、issue発行なりpull requestなり、ご自由にどうぞ!

さて、明日はどんなテンプレートが飛び出すか、お楽しみに! 俺じゃないよ!!!www

脚注

  1. 検証にはgmailを使いました。
  2. AWSから出力された4つのネームサーバを、親ゾーンのNSレコードに登録。