Post Syndicated from Zip Zieper original https://aws.amazon.com/blogs/messaging-and-targeting/leverage-iam-roles-for-email-sending-via-ses-from-ec2-and-eliminate-a-common-credential-risk/
Sending automated transactional emails, such as account verifications and password resets, is a common requirement for web applications hosted on Amazon EC2 instances. Amazon SES provides multiple interfaces for sending emails, including SMTP, API, and the SES console itself. The type of SES credential you use with Amazon SES depends on the method through which you are sending the emails.
In this blog post, we describe how to leverage IAM roles for EC2 instances to securely send emails via the Amazon SES API, without the need to embed IAM credentials directly in the application code, link to a shared credentials file, or manage IAM credentials within the EC2 instance. By adopting the approach outlined in this blog, you can enhance security by eliminating the risk of credential exposure and simplify credential management for your web applications.
Solution Overview
Below we provide step-by-step instructions to configure an IAM role with SES permissions to use on your EC2 instance. This allows the EC2 hosted web application to securely send emails via Amazon SES without storing or managing IAM credentials within the EC2 instance. We present an option for running EC2 and SES in the same AWS account, as well as an option to accommodate running EC2 and SES in different AWS accounts. Both options offer a way to enhance security and simplify credential management.
Either option begins with creating an IAM role with SES permissions. Next, the IAM role is attached to your EC2 instance, providing it with the necessary permissions for SES without needing to embed IAM credentials in your application code or on a file in the EC2 instance. In option 2, we’ll add cross-account permissions that allow the code on the EC2 instance in account “A” to send email via the SES API in account “B”. We also provide a sample Python script that demonstrates how to send an email from your EC2 instance using the attached IAM role.
Option 1 – SES and EC2 are in a single AWS account
In a typical scenario where an EC2 instance is operating in the same AWS account as SES, the process of using an IAM role to send emails via SES is straightforward. In the steps below, you’ll configure and attach an IAM role to the EC2 instance. You’ll then update a sample Python script to use the permissions provided by the attached IAM role to send emails via SES. This direct access simplifies the SES sending process, as no explicit credential management is required in the code, nor do you need to include a shared credentials file on the EC2 instance.
Prerequisites – single AWS account for EC2 and SES
- A single AWS account in a region that supports SES
- Verified domain or email identity in Amazon SES.
- Make note of a verified sending email address here: ___________
- EC2 instance (Linux) in running state
- If you don’t have a EC2 instance create one (Linux)
- Administrative Access to Amazon SES, IAM and EC2 consoles.
- Access to a recipient email address to receive test emails from the python script.
- Make note of a SES verified recipient email address to send test emails here: ___________
Step 1 – Create IAM Role for EC2 instance with SES Permissions
To start, create an IAM role that grants the necessary permissions to send emails using Amazon SES by following these steps:
- Sign in to the AWS Management Console and open the IAM console.
- In the navigation pane, choose “Roles,” and then choose “Create role.”
- Choose the trusted entity type as “AWS service” and select “EC2” as the service that will use this role, then click ‘Next’
- Search for and select the “AmazonSESFullAccess” policy from the list (or create a custom policy with the necessary SES permissions), then click ‘Next’.
- Provide a name for your role (e.g.,
EC2_SES_SendEmail_Role
). - Click “Create role“.
Step 2 – Attach the IAM Role to EC2 instance.
Next, attach the IAM role to your EC2 instance:
- Open the EC2 Management Console.
- In the navigation pane, choose “Instances,” and select the running EC2 instance to which you want to attach the IAM role.
- With the instance selected, choose “Actions,” then “Security,” and “Modify IAM role.“
- Choose the IAM role you created (
EC2_SES_SendEmail_Role
) from the drop-down menu and click “Update IAM role.”
Step 3 – Create a sample python script that sends emails from the EC2 instance with the attached role.
- Now that your EC2 instance is configured with the necessary permissions, you can set up an example Python script to send emails via Amazon SES using the IAM Role. Here, we’re using the AWS SDK for Python (Boto3), a powerful and versatile library to interact with the SES API endpoint. Before running the example script, ensure that Python, pip (the package installer for Python), and the Boto3 library are installed on your EC2 instance:
-
- Run the ‘
python3 –version
‘ command to check if Python is installed on your EC2 instance. If Python is installed, the version will be displayed, otherwise you’ll receive a ‘command not found’ or similar error message.- If python is not installed, run the command ‘
sudo yum install python3 -y
‘
- If python is not installed, run the command ‘
- Run the ‘
pip3 --version
‘ command to check if pip is installed on your EC2 instance. If pip3 is installed, is installed, the version will be displayed, otherwise you’ll receive a ‘command not found’ or similar error message.- If pip3 is not installed, run the command ‘
sudo yum install python3-pip
‘
- If pip3 is not installed, run the command ‘
- Install the Boto3 Library which allows Python scripts to interact with AWS services including SES. Run the command ‘
pip3 install boto3
‘ to install (or update) Boto3 using pip.
- Run the ‘
- Save the code below as a Python file named ‘
sesemail.py
‘ on your EC2 instance. - Edit
'sesemail.py
‘ and replace the placeholder values of SENDER, RECIPIENT, and AWS_REGION with your values (see prerequisites). Do not modify any “” marks.
[copy]
import boto3
from botocore.exceptions import ClientError
SENDER = "[email protected]"
RECIPIENT = "[email protected]"
#CONFIGURATION_SET = "ConfigSet"
AWS_REGION = "us-west-2"
SUBJECT = "Amazon SES Test Email (SDK for Python) using IAM Role"
BODY_TEXT = ("Amazon SES Test (Python)\r\n"
"This email was sent with Amazon SES using the "
"AWS SDK for Python (Boto)."
)
BODY_HTML = """<html>
<head></head>
<body>
<h1>Amazon SES Test (SDK for Python) using IAM Role</h1>
<p>This email was sent with
<a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the
<a href='https://aws.amazon.com/sdk-for-python/'>
AWS SDK for Python (Boto)</a>.</p>
</body>
</html>
"""
CHARSET = "UTF-8"
client = boto3.client('ses',region_name=AWS_REGION)
try:
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
)
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
- Run ‘
python3 sesmail.py
‘ to execute the Python script. - When ‘
python3 sesmail.py
‘ runs successfully, an email is sent to theRECIPIENT
(check the inbox), and the command line will display the sent Message ID.
Option 2 – SES and EC2 are in different AWS accounts
In some scenarios, your EC2 instance might operate in a different AWS account than SES. Let’s call the EC2 AWS account “A” and SES AWS account “B”. Because the AWS resources in account A don’t automatically have permission to access AWS resources account B, we need some way to allow the code on EC2 to assume a role in the SES Account using the AWS Security Token Service (STS). This involves a method that generates temporary credentials that include an access key, secret access key, and session token, which are only valid for a limited time.
In the steps below, you’ll configure and attach an IAM role to the EC2 instance in account “A” such that it can run an example Python script. This Python script can use the permissions provided by the attached IAM role to send emails via SES in account “B”. This approach leverages cross-account access and simplifies sending email from the EC2 in account A via SES in account B. As with Option 1, no explicit credential management is required in the code running on EC2, nor do you need to include a shared credentials file on the Ec2 instance.
Prerequisites – different AWS accounts for EC2 and SES (use cross-account access)
- An AWS account “A” with:
- EC2 instance (Linux) in running state. (If you don’t have a EC2 instance, create one using Amazon Linux)
- Administrative Access to Amazon IAM and EC2 consoles.
- Make note of your “A” AWS account ID here: ________________
- An AWS account “B” with:
- Verified domain (or email identity for testing only) in Amazon SES
- Make note of a verified sending email address here: ___________
- Administrative Access to Amazon SES and IAM consoles.
- Make note of your “B” AWS account ID here: ________________
- In the steps below, you will create a “
SES_Role_for_account_A
” role.- Make note of the ARN of the “
SES_Role_for_account_A
” role here: ___________
- Make note of the ARN of the “
- Access to a recipient email address to receive test emails from the python script.
- Make note of a SES verified recipient email address to send test emails here: ___________
- Verified domain (or email identity for testing only) in Amazon SES
Step 1 – Create IAM Role in the SES “B” account
- Sign in to the SES “B” account via the AWS Management Console and open the IAM console.
- In the navigation pane, choose “Roles,” and then choose “Create role“.
- Choose the trusted entity type as ‘AWS account’ and select ‘Another AWS account’.
- Add the AWS account ID where your EC2 instance resides (AWS account “A” in the prerequisites) and click ‘Next’.
- Search for and select the “
AmazonSESFullAccess
” policy or create a custom policy with the necessary SES permissions, then click ‘Next’. - Provide a name for your role (e.g., ‘
SES_Role_for_account_A'
). - Click “Create role“.
- Copy the arn for the new
SES_Role_for_account_A
(you’ll need the arn in the next step).
Step 2 – Create a IAM policy in the EC2 “A” account that allows this role to assume the SES_Role_for_account_A role you just created in the SES “B” Account.
- Sign in to the EC2 “A” account via the AWS Management Console and open the IAM console.
- In the navigation pane, choose “Policies,” and then choose “Create Policy”.
- Choose the service as ‘EC2’ and select policy editor as JSON.
- Copy the policy below, and in the policy editor, replace the Resource with the arn of the
SES_Role_for_account_A
in the SES account “B” (you created this in step 1).
[copy, paste into policy editor & replace the arn with SES_Role_for_account_A
]
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::<SES_Account_ID>:role/<Role_Name>"
}
]
}
- Click ‘Next’ and provide a name for your role (e.g.,
EC2_Policy_for_account_B
). - Click ‘Create the Policy’
Step 3 – Create an IAM role in the EC2 “A” account, and attach the previously created IAM policy (EC2_Policy_for_account_B
) to it.
- In the EC2 “A” account IAM console navigation pane, choose “Roles,” and then choose “Create role.”
- Choose the trusted entity type as “AWS service” and select “EC2” as the service, then click ‘Next’.
- Filter by type “customer managed”, search for (
EC2_Policy_for_account_B
) and select that policy and ‘Next’ (note – if you are using AWS Session Manger to remotely connect to your EC2 instance, you may need to add the “AmazonSSMManagedInstanceCore
” policy to the role).
- Provide a name for your role (e.g.,
EC2_SES_in_account_B_role
). - Click “Create role“.
Step 4 – Attach the IAM Role (EC2_SES_in_account_B_role
) to the EC2 instance in AWS account “A”.
- Open the EC2 Management Console in AWS account “A”
- In the navigation pane, choose “Instances,” and select the instance to which you want to attach the
EC2_SES_in_account_B_role
IAM role. - With the instance selected, choose “Actions,” then “Security,” and “Modify IAM role.”
- Choose the IAM role you created (
EC2_SES_in_account_B_role
) from the drop-down menu. - Click “Update IAM role.”
Step 5 – Create a sample python script that sends emails via SES in AWS account “B” from the EC2 instance in AWS account “A” using the EC2 attached role.
- Now that your EC2 instance is configured with the necessary permissions, you can set up an example Python script to send emails via Amazon SES in AWS Account “B” using the IAM Role on EC2 in AWS Account “A”. We’ll use the AWS SDK for Python (Boto3), a powerful and versatile library to interact with the SES API endpoint. Before running the example script, ensure that Python, pip (the package installer for Python), and the Boto3 library are installed on your EC2 instance:
-
- Run the ‘
python3 –version
‘ command to check if Python is installed on your EC2 instance. If Python is installed, the version will be displayed, otherwise you’ll receive a ‘command not found’ or similar error message.- If python is not installed, run the command ‘
sudo yum install python3 -y
‘
- If python is not installed, run the command ‘
- Run the ‘
pip3 --version
‘ command to check if pip is installed on your EC2 instance. If pip3 is installed, is installed, the version will be displayed, otherwise you’ll receive a ‘command not found’ or similar error message.- If pip3 is not installed, run the command ‘
sudo yum install python3-pip
‘
- If pip3 is not installed, run the command ‘
- Install the Boto3 Library which allows Python scripts to interact with AWS services including SES. Run the command ‘
pip3 install boto3
‘ to install (or update) Boto3 using pip.
- Run the ‘
- Save the code below as a Python file named
cross_sesemail.py
on your EC2 instance.
4b. Editcross_sesemail.py
and replace the placeholder values of theROLE_ARN
with ARN of theSES_Role_for_account_A
you created in SES Account “B” (see prerequisites), SENDER, RECIPIENT, and AWS_REGION with your values (see prerequisites). Do not modify any “” marks.
[copy, edit & replace the ROLE_ARN]
import boto3
from botocore.exceptions import ClientError
# Replace with your role ARN in SES Account
ROLE_ARN = "arn:aws:iam::<Account_ID>:role/<Role_Name>"
# Create an STS client
sts_client = boto3.client('sts')
# Assume the role
assumed_role = sts_client.assume_role(
RoleArn=ROLE_ARN,
RoleSessionName="SESSession"
)
# Extract the temporary credentials
credentials = assumed_role['Credentials']
# Create an SES client using the assumed role credentials
ses_client = boto3.client(
'ses',
region_name='us-west-2',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken']
)
# Email parameters
SENDER = "[email protected]"
RECIPIENT = "[email protected]"
SUBJECT = "Amazon SES Test (SDK for Python) using cross-account IAM Role"
BODY_TEXT = ("Amazon SES Test (Python)\r\n"
"This email was sent with Amazon SES using the "
"AWS SDK for Python (Boto) using IAM Role."
)
BODY_HTML = """<html>
<head></head>
<body>
<h1>Amazon SES Test (SDK for Python) using IAM Role</h1>
<p>This email was sent with
<a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the
<a href='https://aws.amazon.com/sdk-for-python/'>
AWS SDK for Python (Boto)</a> using IAM Role.</p>
</body>
</html>
"""
CHARSET = "UTF-8"
# Send the email
try:
response = ses_client.send_email(
Destination={
'ToAddresses': [RECIPIENT],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
)
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
- Run the python script
python3 cross_sesemail.py
. When the email is sent successfully, the command line output will display the message ID of the sent email, and the recipient will receive an email.
Conclusion:
By implementing IAM roles for EC2 instances with SES permissions, you can securely send emails via the SES APIs from your web applications without the need to store or manage IAM credentials within the EC2 instance or application code. This approach not only enhances security by eliminating the risk of credential exposure, but also simplifies the management of credentials. With the step-by-step guide provided in this blog post, you can easily configure IAM roles for your EC2 instances and start sending emails via the Amazon SES API in a secure and efficient manner, regardless of whether your EC2 and SES resources reside in the same or different AWS accounts.
Next Steps:
- Sign up for the AWS Free Tier and try out Amazon SES with IAM roles for EC2 instances as demonstrated in this blog post.
- Consult the AWS documentation on IAM Roles for Amazon EC2 and Amazon SES for more detailed instructions and best practices.
- Join the AWS Community Forums to ask questions, share experiences, and learn from other AWS users who have implemented similar solutions for secure email sending from their web applications.
Maintain an Engaged Subscriber List by Removing Inactive Recipients
As an email marketer, you must operate under the assumption that if a subscriber is not opening your emails, or is no longer engaging with the calls-to-action in your emails, they are no longer interested in the content that you are sending. Subscribers who fall into this category should be periodically removed from your mailing lists to help ensure your subscriber lists are healthy and engaged. Increase campaign success and deliverability by periodically reviewing and updating your subscription lists with this two-pronged approach:
Track Subscriber Engagement with Amazon SES
Amazon SES provides methods to monitor your sending activity using events, metrics, and statistics. These monitoring methods can be used to measure the rates at which your customers engage with the emails you send. For example, you can identify your overall open and click through rates by utilizing SES’ event publishing when using custom email domains that you associate with configuration sets as discussed in the SES documentation.
Figure 3: Serverless Architecture to Analyze Amazon SES events
To track your email sending activities at a granular level, refer to the AWS blog post, Analyzing Amazon SES event data with AWS Analytics Services.