SES is not supported in your AWS region? Here’s what you can do

Laurent Jalbert Simard
poka-techblog
Published in
5 min readJan 15, 2018
This is our relay, delivering an email into your mailbox.

Last summer we faced a problem. A customer needed his data to transit solely on Canadian soil. Despite the fact AWS offers a great range of services in its Canadian region, Amazon Simple Email Service (SES) isn’t one of them. It’s actually one the least available service region-wise with a total of 3 supported regions worldwide.

In-house or outsource?

After a few hours of research, I was baffled that nothing came up when looking for a reliable mail service with servers in Canada. I quickly understood that we would have to host the mail relay ourselves.

Requirements

Our requirements were basics. To replicate what SES offered us, we needed a highly available SMTP relay to send email notifications to our customers. These emails needed to be signed using DKIM to avoid getting flagged as spoofed. We needed a way to handle bounces and complaints so our public IP could stay trustworthy. Finally, since SES is a managed service we wanted our custom solution to be provisioned automatically and be maintenance-free.

What’s out there?

There are of course softwares like Exim, Postfix and Sendmail that have been the gold standard for 30+ years. Most of them seemed overkill, with huge configuration files to achieve even the simplest use case. Furthermore, one of the goals was to handle bounces and complaints the same way SES did so I needed some level of extensibility. Unfortunately, for most of them it meant altering the source code rebuilding the whole thing.

Haraka

As I was reading pros and cons for each of them, I stumbled upon Haraka, a modern and flexible SMTP server running on Node.js. It was love at first sight. Haraka has a modular plugin architecture that allows you to only install and configure the bare minimum. You can also extend the functionalities by writing very simple JavaScript plugins.

Let’s dive into Haraka

I’ve setup a project on GitHub with a Dockerfile, all the related configurations (about 8 lines in total), and the javascript plugins so you can follow along. Of course, you can fork it to customize it for your needs. Here’s the whole file structure needed to run our SMTP relay. Yep, pretty simple.

docker-haraka/
├── app/
│ ├── config/
│ │ ├── loglevel
│ │ ├── plugins
│ │ ├── relay.ini
│ │ └── smtp.ini
│ ├── plugins/
│ │ ├── publish_ses_error_on_bounces.js
│ │ └── route53_dkim_record.js
│ └── package.json
├── docker-entrypoint.sh
└── Dockerfile

Configuration files are dead simple

Let’s have a look file by file:

loglevel is a one liner file that contains the log level (ex: WARN).

plugins contains the list of plugins that need to be loaded by Haraka, that includes the official ones and the custom ones. Since we want a really basic relay that mimics SES, we only need 4 of them (there’s 70+ available if you need to extend the functionalities).

;Haraka official plugins
relay
dkim_sign
;Custom plugins
route53_dkim_record
publish_ses_error_on_bounces

Inrelay.ini we usually handle security rules using ACLs or whitelisted domains. However, considering that this will be a private relay and that AWS Security Groups will act as a firewall, we can leave this opened as follows:

[relay]
all=true

smtp.ini was created only to specify configurations required for the route53_dkim_record

nodes=1
graceful_shutdown=true

Custom plugins to replicate SES functionalities

One of the beloved feature of SES is that it can configure automatically your Route53 records to provide DKIM support. This was also one of my initial requirements so I’ve created a straightforward plugin called route53_dkim_record to replicate this feature.

Another well appreciated feature of SES also part of my requirements is the ability to push bounces and complaints to an SNS topic for further processing. The end goal is to make sure you don’t send an email to someone who flagged your address as spam. And since we’re using the same notification structure as SES, we can process them in the same way for all our regions. Once again an elementary plugin (publish_ses_error_on_bounces) made up of a few JavaScript lines did the trick.

Next steps

There’s a lot that could be said on how to provision this service, but it would be a bit out of scope. I’ll just give you basic directions on how we achieved it.

SPF and DMARC

In order to avoid SPAM filters there are a couple of DNS records you need to setup. DMARC is not dynamic so creating the record is straightforward. SPF on the other hand, you have to know from which external IP the email will come from. If you’re in a VPC environment and you are leveraging NAT gateways, this will be the elastic IP of said NAT. If you have assigned public IPs to each of your instances then it will be this one you’ll need to pass to your SPF record. Either way the process can be automated using CloudFormation or the API.

Run it as a service using ECS

I’ve made our Haraka container image available on DockerHub so you cam pull the image directly from there. Any container orchestrator would do the trick but ECS is what we are currently using at Poka. Make sure you define the proper environment variables in the task definition. Don’t forget to allow your service to make changes to your Route53 record sets for the DKIM plugin in the IAM role. The service also needs the “Publish” SNS permission for the bounces and complaints plugin to alert you.

Achieve HA using Network Load Balancers

If you can’t risk that an email doesn’t get sent, you’ll want your SMTP relay to be highly available. The new Network Load Balancers from AWS will handle the TCP balancing for you. It’s compatible with ECS so you’ll be able to balance the traffic between 2 or more containers.

Testing your email relay

There’s a really simple command-line tool called SWAKS (Swiss Army Knife for SMTP) that can help you test your SMTP relay. Just write your email and the hostname of your Haraka relay and execute swaks.

swaks --to your@mailbox.com --server haraka-test.poka.io --port 2525

The next step is to inspect the headers of the email you received to make sure DKIM, SPF and DMARC were properly configured. If everything looks ok, you’re done, you’ve officially replaced AWS SES with a custom email relay that can run anywhere in the world!

Got questions/suggestions about this post? Write a comment below and I’ll do my best to answer it.

--

--