はじめに
メールの送信履歴をたまたま見たら、以外にメールを送信していないなと気づいた。
確かにメールを普段使っているかというと使っていない。
普段のコミュニケーションを何でとるかといったら、Slack。
エビデンスを残す必要があるようなものは、BacklogやRedmineなどでチケット単位でコミュニケーションを取ることが大半。
では、どんな時にメールを使っているかというと、ほぼ、サービスへの登録の本人確認。
でも、このサービスへの登録が本当に重要。
ただ、サービスへの無料体験などに申し込み、体験期間が過ぎたけど、まだやりたいなといった場合、既に登録されているメールでは、再度申し込むことが出来ないことは多々。
そんな時どうしますか?
gmailであればメールアドレスのユーザ部分の任意の箇所にドット( .
)を追加することが可能で、そのドットのタイミングを変えて利用している人も多いと思います。
ただ、それだと限界もあります。
また新しいメールを取得するにしても、今は電話番号で本人確認があったりとちょっと面倒。
例えば、ドメインを取得すれば好きなだけユーザを作成することが出来ますが、メールサーバをAWSで運用するとなると、EC2インスタンスを立ち上げる必要があり、そこまでコストをかけて、、、という思いもあります。
対象読者
- サービス登録用やテスト用など、メールアドレスがほしい。
- 特にメールを送信する訳ではなく、受信出来ればいい。
- メールサーバを運用するつもりはない。
という方の参考になれば。
どうしたか
- ドメインは取得した。Route53で運用。.com であれば年$12
- メールはSESで受信。SESのルールで自ドメイン宛のアドレスをS3へ保存すると同時に、SNSに通知。
- SNSのサブスクリプションでLambdaを起動。起動したLambdaでは、通知からS3に保存したメール本文を取得し、SESで指定したメールアドレスに転送。
サービス設定
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を指定します。
SES
Identity Management > Domains
Route53等でドメインのホストゾーンが登録されていることを前提としています。
こちらでメールのドメインを登録すると、登録する過程でMXレコードや、DKIM用のレコードを作成されます。
Identity Management > Email Addresses
SESから送信先として許可するメールアドレスを登録します。
Email Receiving > Rule Sets
受信したメールをどう処理するかの設定です。
S3へ配置し、SNSへ通知をするように設定します。
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:GetObject
、ses: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のテンプレートを起こしています。手順の通りデプロイして貰えれば、すぐに使えるとは思います。
参考までに。