1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

受信用のメールアドレスが欲しかったのでサーバレスで構築した方法

Posted at

はじめに

メールの送信履歴をたまたま見たら、以外にメールを送信していないなと気づいた。
確かにメールを普段使っているかというと使っていない。
普段のコミュニケーションを何でとるかといったら、Slack
エビデンスを残す必要があるようなものは、BacklogRedmineなどでチケット単位でコミュニケーションを取ることが大半。
では、どんな時にメールを使っているかというと、ほぼ、サービスへの登録の本人確認
でも、このサービスへの登録が本当に重要。
ただ、サービスへの無料体験などに申し込み、体験期間が過ぎたけど、まだやりたいなといった場合、既に登録されているメールでは、再度申し込むことが出来ないことは多々。

そんな時どうしますか?

gmailであればメールアドレスのユーザ部分の任意の箇所にドット( . )を追加することが可能で、そのドットのタイミングを変えて利用している人も多いと思います。
ただ、それだと限界もあります。
また新しいメールを取得するにしても、今は電話番号で本人確認があったりとちょっと面倒。

例えば、ドメインを取得すれば好きなだけユーザを作成することが出来ますが、メールサーバをAWSで運用するとなると、EC2インスタンスを立ち上げる必要があり、そこまでコストをかけて、、、という思いもあります。

対象読者

  • サービス登録用やテスト用など、メールアドレスがほしい。
  • 特にメールを送信する訳ではなく、受信出来ればいい。
  • メールサーバを運用するつもりはない。

という方の参考になれば。

どうしたか

  • ドメインは取得した。Route53で運用。.com であれば年$12
  • メールはSESで受信。SESのルールで自ドメイン宛のアドレスをS3へ保存すると同時に、SNSに通知。
  • SNSのサブスクリプションでLambdaを起動。起動したLambdaでは、通知からS3に保存したメール本文を取得し、SESで指定したメールアドレスに転送。

ses-forwarder-v2.png

サービス設定

S3

S3 bucket policy

バケットポリシーでは、LambdaからS3のオブジェクトを取得できる権限(PutObject)を許可します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ses.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::ses-forwarder-stack-bucket-bks5djn2hcl1/*",
            "Condition": {
                "StringEquals": {
                    "aws:Referer": "123456789012"
                }
            }
        }
    ]
}

SNS

Subscription

トピックは適当に作成してください。サブスクリプションでは、後述するLambdaを指定します。

image.png

SES

Identity Management > Domains

Route53等でドメインのホストゾーンが登録されていることを前提としています。
こちらでメールのドメインを登録すると、登録する過程でMXレコードや、DKIM用のレコードを作成されます。

image.png

Identity Management > Email Addresses

SESから送信先として許可するメールアドレスを登録します。

image.png

Email Receiving > Rule Sets

受信したメールをどう処理するかの設定です。
S3へ配置し、SNSへ通知をするように設定します。

image.png

Lambda

Function code

from email import message_from_file
import json
import boto3
import re
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

DISABLED_MESSAGE_IDS = ['AMAZON_SES_SETUP_NOTIFICATION']

def lambda_handler(event, context):

    logger.info(json.dumps(event))
    logger.debug(event.get('Records')[0].get('Sns').get('Message'))
    message = json.loads(event.get('Records')[0].get('Sns').get('Message'))
    message_id = message.get('mail').get('messageId')
    if message_id in DISABLED_MESSAGE_IDS:
        logger.info('Skip Invalid messageID. (%s)' % message_id)
        return
    notification_type = message.get('notificationType')
    destinations = message.get('mail').get('destination')
    if not destinations:
      destinations = message.get('receipt').get('recipients')
    bucket_name = message.get('receipt').get('action').get('bucketName')
    object_key = message.get('receipt').get('action').get('objectKey')

    mail_from = destinations[0] if not os.environ['MAIL_FROM'] else os.environ['MAIL_FROM']
    forward_to = os.environ['RCPT_TO'].split(",")

    # get s3 object
    s3 = boto3.client('s3')
    response = s3.get_object(
        Bucket = bucket_name,
        Key    = object_key
    )

    # change mail headers
    raw_mail = response['Body']
    msg = message_from_file(raw_mail)

    del msg['DKIM-Signature']
    del msg['To']
    del msg['From']
    del msg['Return-Path']

    msg['To'] = ", ".join(forward_to)
    msg['From'] = mail_from
    msg['Return-Path'] = mail_from

    # send mail
    ses = boto3.client('ses')
    response = ses.send_raw_email(
        Source = mail_from,
        Destinations = forward_to,
        RawMessage = {
            'Data': msg.as_string()
        }
    )
    logger.info(json.dumps(response))
    return response

Execution role

AWSLambdaBasicExecutionRoleのマネージドポリシー以外に、以下のようなs3:GetObjectses:SendRawEmailの実行ポリシーをサービスロールに付与

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:GetObject",
                "ses:SendRawEmail"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}

Environment variables

  • MAIL_FROM : 転送する際の送信元メールアドレス
  • RCPT_TO : 転送する際の宛先メールアドレス

さいごに

これでSESのルールで指定したドメイン宛に適当なユーザ名で送信したメールが、Lambdaの環境変数 RCPT_TO に指定した宛先に転送します。
転送する際の送信元は、Lambdaの環境変数 MAIL_FROM で指定したアドレスになりますが、未設定であれば、元の宛先が送信元になります。

これで、適当なメールアドレスのためにユーザを作成する必要がなく、例えば、アカウントのユーザ登録をしようとした場合に、そのまま適当なユーザ名のメールアドレスを利用することが可能です。

CloudFormationのテンプレートを起こしています。手順の通りデプロイして貰えれば、すぐに使えるとは思います。
参考までに。

1
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?