Получение электронной почты PAWS SES с помощью S3 и функции lambda для пересылки на внешнюю электронную почту (Gmail)

#python #amazon-web-services #amazon-s3 #aws-lambda #amazon-ses

#Python #Amazon-веб-сервисы #Amazon-s3 #aws-lambda #Amazon-ses

Вопрос:

Я хочу отправить электронное письмо, полученное на мой сайт и сохраненное в S3, во внешнюю службу электронной почты в моем случае Gmail, поэтому вот шаги, которые я сделал:

  1. Создано ведро S3 с этой политикой:
 {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowSESPuts",
            "Effect": "Allow",
            "Principal": {
                "Service": "ses.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::<bucketName>/*",
            "Condition": {
                "StringEquals": {
                    "aws:Referer": "<awsAccountId>"
                }
            }
        }
    ]
}
 
  1. Создан пользователь IAM с этой политикой:
 {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:CreateLogGroup",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "ses:SendRawEmail"
            ],
            "Resource": [
                "arn:aws:s3:::<bucketName>/*",
                "arn:aws:ses:<region>:<awsAccountId>:identity/*"
            ]
        }
    ]
}
 
  1. Создал эту функцию Lambda в Python 3.7 и установил значение тайм-аута равным 30 секундам:
 import json

# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# This file is licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License. A copy of the
# License is located at
#
# http://aws.amazon.com/apache2.0/
#
# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
# OF ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

import os
import boto3
import email
import re
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

MailS3Bucket = 'name-of-the-bucket'
MailS3Prefix = 'prefix of the send rule (folder where I am saving emails in s3)'
MailSender = 'Verified Email in SES emails'
MailRecipient = 'Verified Email in SES emails too (This is the email I want to send emails to)'
Region = 'my region'

region = os.environ['Region']

def get_message_from_s3(message_id):

    incoming_email_bucket = os.environ['MailS3Bucket']
    incoming_email_prefix = os.environ['MailS3Prefix']

    if incoming_email_prefix:
        object_path = (incoming_email_prefix   "/"   message_id)
    else:
        object_path = message_id

    object_http_path = (f"http://s3.console.aws.amazon.com/s3/object/{incoming_email_bucket}/{object_path}?region={region}")

    # Create a new S3 client.
    client_s3 = boto3.client("s3")

    # Get the email object from the S3 bucket.
    object_s3 = client_s3.get_object(Bucket=incoming_email_bucket, Key=object_path)
    # Read the content of the message.
    file = object_s3['Body'].read()

    file_dict = {
        "file": file,
        "path": object_http_path
    }

    return file_dict

def create_message(file_dict):

    sender = os.environ['MailSender']
    recipient = os.environ['MailRecipient']

    separator = ";"

    # Parse the email body.
    mailobject = email.message_from_string(file_dict['file'].decode('utf-8'))

    # Create a new subject line.
    subject_original = mailobject['Subject']
    subject = "FW: "   subject_original

    # The body text of the email.
    body_text = ("The attached message was received from "
                separator.join(mailobject.get_all('From'))
                ". This message is archived at "   file_dict['path'])

    # The file name to use for the attached message. Uses regex to remove all
    # non-alphanumeric characters, and appends a file extension.
    filename = re.sub('[^0-9a-zA-Z] ', '_', subject_original)   ".eml"

    # Create a MIME container.
    msg = MIMEMultipart()
    # Create a MIME text part.
    text_part = MIMEText(body_text, _subtype="html")
    # Attach the text part to the MIME message.
    msg.attach(text_part)

    # Add subject, from and to lines.
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = recipient

    # Create a new MIME object.
    att = MIMEApplication(file_dict["file"], filename)
    att.add_header("Content-Disposition", 'attachment', filename=filename)

    # Attach the file object to the message.
    msg.attach(att)

    message = {
        "Source": sender,
        "Destinations": recipient,
        "Data": msg.as_string()
    }

    return message

def send_email(message):
    aws_region = os.environ['Region']

# Create a new SES client.
    client_ses = boto3.client('ses', region)

    # Send the email.
    try:
        #Provide the contents of the email.
        response = client_ses.send_raw_email(
            Source=message['Source'],
            Destinations=[
                message['Destinations']
            ],
            RawMessage={
                'Data':message['Data']
            }
        )

    # Display an error if something goes wrong.
    except ClientError as e:
        output = e.response['Error']['Message']
    else:
        output = "Email sent! Message ID: "   response['MessageId']

    return output

def lambda_handler(event, context):
    # Get the unique ID of the message. This corresponds to the name of the file
    # in S3.
    message_id = event['Records'][0]['ses']['mail']['messageId']
    print(f"Received message ID {message_id}")

    # Retrieve the file from the S3 bucket.
    file_dict = get_message_from_s3(message_id)

    # Create the message.
    message = create_message(file_dict)

    # Send the email and print the result.
    result = send_email(message)
    print(result)

 
  • Создано правило получения электронной почты моего подтвержденного домена с этой информацией: Информация о получении электронной почты

PS:

  • info@my-domain.com это префикс, который я установил в функции lambda, и папка, в которую я сохраняю электронное письмо в s3.
  • email-forwarding-service это фактическое имя функции lambda
  • Конечно, я изменил переменные в политиках S3 и IAM на фактические данные
  • Я установил правило активным, поэтому оно должно работать
  • Это взято из документации AWS
  • Я ничего не знаю в python, все было скопировано и вставлено, кроме переменных, которые я добавил в начале файла python

Результат: я могу сохранять отправленные электронные письма в s3 в их правой папке (info @ …), но функция lambda не работает, поэтому я не получаю электронные письма в своей учетной записи Gmail!!

ОБНОВЛЕНИЕ: Я изменил Лямбду:

 os.environ['MailS3Bucket'] = 'name-of-the-bucket'
os.environ['MailS3Prefix'] = 'prefix of the send rule (folder where I am saving emails in s3)'
os.environ['MailSender'] = 'Verified Email in SES emails'
os.environ['MailRecipient'] = Verified Email in SES emails too (This is the email I want to send emails to)'
os.environ['Region'] = 'my region'
 

Вместо

 MailS3Bucket = 'name-of-the-bucket'
MailS3Prefix = 'prefix of the send rule (folder where I am saving emails in s3)'
MailSender = 'Verified Email in SES emails'
MailRecipient = 'Verified Email in SES emails too (This is the email I want to send emails to)'
Region = 'my region'
 

Итак, теперь у меня возникла эта ошибка в cloud watch:

 [ERROR] ClientError: An error occurred (AccessDenied) when calling the GetObject operation: Access DeniedTraceback (most recent call last):  File "/var/task/lambda_function.py", line 143, in lambda_handler    file_dict = get_message_from_s3(message_id)  File "/var/task/lambda_function.py", line 48, in get_message_from_s3    object_s3 = client_s3.get_object(Bucket=incoming_email_bucket, Key=object_path)  File "/var/runtime/botocore/client.py", line 357, in _api_call    return self._make_api_call(operation_name, kwargs)  File "/var/runtime/botocore/client.py", line 676, in _make_api_call    raise error_class(parsed_response, operation_name)
 

Строка 48 равна: object_s3 = client_s3.get_object(Bucket=incoming_email_bucket, Key=object_path)

Итак, я думаю, что функция lambda не использует созданного пользователя в IAM для доступа к s3, и я не знаю python, чтобы принудительно использовать пользователя IAM; ОДНАКО обратите внимание, что это получение правильного идентификатора электронной почты, сохраненного в s3 в соответствии с журналами cloudwatch

Любая помощь, спасибо и высоко ценится!

Комментарии:

1. Пожалуйста, предоставьте журналы CloudWatch для лямбды.

2. @RobertKoss Все графики мониторинга говорят об этом No data available

3. Было ли что-то не так с конфигурациями, поэтому он не обращается к функции lambda

4. Пожалуйста, сравните мою работу с фактической документацией по AWS, которую я предоставил, возможно, я сделал что-то не так!!

5. Для чего это нужно? region = os.environ[‘Region’]

Ответ №1:

Я смог устранить эту проблему, перейдя к настройкам функции lambda и добавив разрешение доступа к s3 и ses.