1
0

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 5 years have passed since last update.

Amazon SESのアクションにLambdaを登録して独自ドメインメールをGmailとかに転送する

Last updated at Posted at 2019-06-19

Gmail + AWSで独自ドメインのメールを受信する

 こちらの記事とやっていることはほぼ同じで、Lambda関数をS3のほうではなくてSESのアクションに設定しているだけです。
SESのeventに入っているmessageIdがobject keyになるように変更したり、環境変数を使ってみたりしてますが基本コピペです。

import os
import boto3
import email
import re
import logging

s3_client = boto3.client('s3')
ses_client = boto3.client('ses')

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ORIGINAL_ADDRESS = os.environ.get('originalAddress', '')
FORWARD_ADDRESS = os.environ.get('forwardAdresses', '')
S3_BUCKET_NAME = os.environ.get('s3BucketName', '')
SES_REGION = 'us-east-1'

def parse_mail(raw_message):
    from_name = 'No Name'

    pattern = re.compile(r'^From:\s*(.+?)$', re.MULTILINE)
    m = re.search(pattern, raw_message)
    if m:
        from_name = m.group(1)[:-1]
        
    replaced_message = raw_message.replace(ORIGINAL_ADDRESS, FORWARD_ADDRESS)
    replaced_message = re.sub(r'^From:\s*.+?$', r'From: "%s" <%s>' % (from_name, ORIGINAL_ADDRESS), replaced_message, flags=re.MULTILINE)
    replaced_message = re.sub(r'^Return-Path:\s*.+?$', r'Return-Path: "%s" <%s>' % (from_name, ORIGINAL_ADDRESS), replaced_message, flags=re.MULTILINE)
    replaced_message = re.sub(r'^Sender:\s*.+?$', r'Sender: "%s" <%s>' % (from_name, ORIGINAL_ADDRESS), replaced_message, flags=re.MULTILINE)

    return replaced_message

def send_mail(message):
    ses = boto3.client('ses', region_name=SES_REGION)

    ses.send_raw_email(
        Source = FORWARD_ADDRESS,
        Destinations=[
            FORWARD_ADDRESS
        ],
        RawMessage={
            'Data': message
        }
    )

def lambda_handler(event, context):
    try:
        s3_object = s3_client.get_object(
            Bucket = S3_BUCKET_NAME,
            Key = event['Records'][0]['ses']['mail']['messageId'])

        raw_message = s3_object["Body"].read().decode('utf-8')
        message = parse_mail(raw_message)
        send_mail(message)

        logger.info('SUCCESS')
    except Exception:
        logger.critical(
            'Execution failed. Exception encountered',
            exc_info=True)
    
    logger.info('FAILED')

SES > Rule Sets > View Active Rule set > [rule name] でactionを追加したら完成です。
cap.png

2019年6月28日追記

Amazonアソシエイトのユーザー追加確認メールなどDKIM-Signatureが2つ入っていたりするものが転送できない問題があった。
githubにもっとシンプルなものがあったのでこちらを使ったほうがよいと思う。

message_from_fileがStreamingBodyをうまく扱えなかったので、以下のようにmessage_from_stringに変更した。

from email import message_from_string
import json
import logging
import os
import re

import boto3
from botocore.exceptions import ClientError

FORWARD_MAPPING = {
    'xxxxxxx@example.com': ['xxxxxxxx@gmail.com'],
}

VERIFIED_FROM_EMAIL = os.environ['VERIFIED_FROM_EMAIL']  # An email that is verified by SES to use as From address.
SES_INCOMING_BUCKET = os.environ['SES_INCOMING_BUCKET']  # S3 bucket where SES stores incoming emails.

s3 = boto3.client('s3')
ses = boto3.client('ses')

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


def lambda_handler(event, context):
    record = event['Records'][0]
    assert record['eventSource'] == 'aws:ses'

    o = s3.get_object(Bucket=SES_INCOMING_BUCKET, Key=record['ses']['mail']['messageId'])
    raw_mail = o['Body'].read().decode('utf-8')

    msg = message_from_string(raw_mail)

    del msg['DKIM-Signature']
    del msg['Sender']
    del msg['Return-Path']

    original_from = msg['From']
    del msg['From']

    msg['From'] = re.sub(r'\<.+?\>', '', original_from).strip() + ' <{}>'.format(VERIFIED_FROM_EMAIL)

    if not msg['Reply-To']: msg['Reply-To'] = original_from
    msg['Return-Path'] = VERIFIED_FROM_EMAIL
    msg_string = msg.as_string()

    for recipient in record['ses']['receipt']['recipients']:
        forwards = FORWARD_MAPPING.get(recipient, [])
        if not forwards:
            logger.warning('Recipent <{}> is not found in forwarding map. Skipping recipient.'.format(recipient))
            continue
        #end if

        for address in forwards:
            try:
                o = ses.send_raw_email(Destinations=[address], RawMessage=dict(Data=msg_string))
                logger.info('Forwarded email for <{}> to <{}>. SendRawEmail response={}'.format(recipient, address, json.dumps(o)))
            except ClientError as e: logger.error('Client error while forwarding email for <{}> to <{}>: {}'.format(recipient, address, e))
1
0
0

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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?