AWS SES+Lambda で作る、ドメインまるっとメール転送

僕は各種サービスに登録するメールアドレスを基本的に Gmail(旧 Google Apps) のエイリアスですべて変えているのですが、困ったことにエイリアスマッピング指定に使う + がはじかれるサービスがとにかく多くて困っています。

f:id:iseebi:20160505123607p:plain
▲正しく入力しているのに「正しく入力してください」と出て、そのたびにイライラしてしまいます。

また、登録上は通るけど、実際に使おうとすると不具合が起こるサービスもあります。*1

いままでは、その度に Google Apps の管理コンソールに行ってエイリアスを登録してきたのですが、アカウントに登録できるエイリアスの上限に行ってしまい、グループを作ってそこにエイリアスを追加していく羽目にもなっています。

スマートフォン上で登録したいタイミングも結構出てきたりして、そろそろ実用上耐えられないなと思っていたところ、AWS の SES が送信だけでなく受信もでき、Lambda でメールを処理させることができるということを最近知りました。

今回やりたかったのは、アカウント名を無視して、サブドメイン部分をアカウントとして転送する、つまりこういう形にしたかったのです。

any@hoge.mail.example.com → hoge@example.com

AWS Lambda SES Email Forwarder というものもあり、簡単に設定できそうだったので試してみました。

設定方法

今回は、us-west-2 (オレゴン) リージョンに設定してみました。

SES の Verified Domains にドメインを登録

SES の Verified Domains のページを開き、Verify a New Domain をクリックして、「受け取るメールのドメイン」と「メールを転送するときのドメイン」を定義します。MX レコードの設定は「受け取るメールのドメイン」だけで OK です。

ゾーン管理に Route53 を使っていれば、ボタン押していくだけで設定が完了しました。

Lambda に Function を登録する

AWS Lambda SES Email Forwarder を今回の目的に合うように改造しました。以下の gist をダウンロードします。

gist.github.com

defaultConfig の部分を、以下の通り変更します。

  • fromEmail: 転送時に使う、送信元のメールアドレス。Verified Domain のものである必要があります。
  • emailBucket: この後作成する S3 バケットの名前を指定します。
  • emailKeyPrefix: オプショナル。必要がなければ空文字列にしておきます。
  • forwardSuffix: 転送するメールドメインサフィックス
  • forwardToDomain: 転送先のメールアドレスのドメイン

これを、Lambda に Function として登録します。

  • 新規作成時の Blueprint はスキップします。
  • Configure function の画面では、以下のように設定します。
    • Name: SesForwarder
    • Runtime: Node.js 4.3
    • Lambda function code に先ほど用意した index.js の中身をペーストします。
    • Lambda function handler and role
      • Handler は index.handler のままで OK
      • Role は Create New Role で basic execution role を指定し、LambdaSesForwarder として作成しておきます。
    • Advanced settings のメモリは 128MB で十分ですが、Timeout は 10 秒ほどに伸ばしておきます。

SES に受信ルールを追加する

SES の Email Receiving にルールを追加します。Rule set がまだなければ作成し、Create New Rule で作成に入ります。

Step:1 の Recipients では、「受け取るメールのドメイン」を入力します。

Step:2 の Actions では、まず S3 を追加します。

  • バケット名の指定のところで Create New で適当なバケットを作成します。
  • Object key prefix は、index.js の emailKeyPrefix と設定値を合わせます。
  • Encrypt, SNS ともになしとします。

次に、Lambda を追加します。

  • Lambda Function を先ほど作成した「SesForwarder」とします。
  • Invocation type は Event のまま、SNS はなしとします。

Step.3 の Rule detail では Rule の名前を定義してください。Enabled と Enable spam and virus scanning のチェックを入れておきます。

ルールを確定する際に、SES に lambda:InvokeFunction の権限をつけるか聞かれる場合があり、その際は同意してください。

IAM のロールに S3 バケットへのアクセス権限をつける

Lambda に Function を登録する際に、IAM に定義されたロールに、SES 受信ルール追加時に作成したバケットへのアクセス権限を付与します。

IAM のロール一覧で LambdaSesForwarder を開き、Inline Policy に次のポリシーを追加します。(##BUCKETNAME## の部分を置き換えてください) *2

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::##BUCKETNAME##"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::##BUCKETNAME##/*"
            ]
        }
    ]
}

設定後

設定すると、From が fromEmail の値に置き換わってしまいますが、Reply-To に元のメールアドレスが入ります。

料金はぱっと見大丈夫そうだったのですが、実際どれくらいかかるかまだよくわからないので、今月は数サービスだけ移行させて様子を見てみようと思います。

2017/05/02(Thr) 追記

メールのヘッダの : の後にスペースがない (From: iseebi@example.comではなくFrom:iseebi@example.comみたいになっている) ケースの場合、うまく転送されていなかったようなので、/^From: ... から /^From: ?... のような正規表現に変更してみました。

*1:FM802 RADIPASS などは、アカウントの登録は + が通るが、実際のアンケートフォームが + を通さないのでいちいち変更しないといけない。

*2:ちょっと権限与えすぎな気もする…今後改善したい。