Trading Fish The blog of Hector Castro

Sending E-Mail via Amazon SES over SMTP with IAM Roles

TL;DR: As of the date this post was published, sending e-mail via Amazon Simple E-mail Service (SES) over SMTP with IAM role credentials does not seem to work.


Earlier this week, I set out to wire up a Django application with Amazon SES for sending e-mail. Because the application is going to live in Amazon Elastic Compute Cloud (EC2), I decided to make use of IAM roles to provide the application with the credentials it needs to authenticate with SES. Unfortunately, the SMTP endpoint does not seem to accept the IAM role credentials.

IAM Roles

IAM roles are an elegant way to setup an EC2 instance for API access to other Amazon Web Services. All API requests must be signed with an access key and secret key, so it is usually up to you to populate the EC2 instance with the proper credentials. However, if you make use of IAM roles, an automatically rotated set of keys is provided to the instance via its metadata service:

$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/s3access
{
  "Code" : "Success",
  "LastUpdated" : "2012-04-26T16:39:16Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "AKIAIOSFODNN7EXAMPLE",
  "SecretAccessKey" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  "Token" : "token",
  "Expiration" : "2012-04-27T22:39:16Z"
}

Deriving SES SMTP Credentials

Once your application retrieves a set of keys from the metadata service, SecretAccessKey needs to go through a little bit of a transformation before it can be used with the SES SMTP endpoint. Amazon’s pseudocode for the transformation algorithm follows:

key = AWS Secret Access Key;
message = "SendRawEmail";
versionInBytes = 0x02;
signatureInBytes = HmacSha256(message, key);
signatureAndVer = Concatenate(versionInBytes, signatureInBytes);
smtpPassword = Base64(signatureAndVer);

And for good measure, a translation of that into Python (for use with Django):

SES_SMTP_CONVERSION_HMAC_MESSAGE = 'SendRawEmail'
SES_SMTP_CONVERSION_VERSION = '\x02'

def hash_smtp_pass_from_secret_key(key):
    h = hmac.new(key.encode('utf-8'),
                 SES_SMTP_CONVERSION_HMAC_MESSAGE,
                 digestmod=hashlib.sha256)
    return base64.b64encode("{0}{1}".format(SES_SMTP_CONVERSION_VERSION,
                                            h.digest()))

(Credit: Charles Lavery, Steve Lamb)

Authentication Credentials Invalid

After launching an EC2 instance associated with an IAM role that allows ses:SendEmail, pulling credentials via the metadata service, and transforming the provided SecretAccessKey, you’ll notice that the SMTP endpoint still returns 535 Authentication Credentials Invalid.

I tried several approaches to make things work, but always without success. I even compiled the Java implementation of the transformation algorithm provided by AWS to compare inputs and outputs. Alas, I simply don’t think IAM role credentials work with the SES SMTP endpoint.