Setting up MTA-STS using AWS S3, CloudFront and AWS Certificate Manager

If you use AWS then you can easily use it to host an MTA-STS policy using S3 buckets, CloudFront and Certificate Manager.  This tutorial will explain how to do that, and I hope you find it useful.

Disclaimer: This advice is provided as-is without any warranty (express or implied).  Ensure that you fully understand the implications of what you are doing and the impact this could have on the deliverability of email to your domain.  Remember also that using cloud resources comes at a cost, and that cost will be paid by you.  The costs aren't expected to be high, but you should monitor those as you would when consuming any cloud service.

What is MTA-STS?

MTA-STS, or Mail Transfer Agent Strict Transport Security to give its full name, is a protocol where an email server that wants to email you will look to see if you have specified an MTA-STS policy.  In this policy you stipulate which mail servers handle email for your domain.  If the sender finds a policy it will only communicate with the email servers you've specified, and that communication will be done over TLS v1.2 or greater.

An advantage of MTA-STS is that an attacker in the middle of the sender and recipient cannot force the sender to downgrade the security to send over plain text.  Note that MTA-STS doesn't make email a bullet proof, 100% secure message sending mechanism though and there are still other risks.

Microsoft Azure customer and looking for Azure instructions?

If you don't use AWS, and instead use Microsoft Azure, I've written a separate tutorial about setting the equivalent thing up in Azure using Static Web Apps.

Prerequisites

Your email server must allow TLS inbound connections for SMTP, using TLS version 1.2.  If you're unsure of the configuration then it's best to check before proceeding.

As we're using AWS you'll need an AWS account plus permissions to create resources.

Finally you'll need to have DNS control, or be authorised to request someone makes changes on your behalf.

I'm going to assume you have some familiarity with the AWS portal and how to navigate it.

Create an S3 bucket for your policy

Our MTA-STS policy is going to be stored in an S3 bucket.  S3 is Amazon's storage tool allowing you to not only store files but also configure versioning and data retention.  You can also select if the file should be accessible to the whole world or only authenticated persons.

Having logged in to AWS' portal, search for S3 in the search box at the top.  Then click the orange create bucket button.

After clicking to create your bucket you'll be shown the general configuration screen.  Specify a bucket name (mta-sts in my examlpe) and AWS Region

When selecting a region it is important to consider governance and policy requirements based on the type of data your storing.  In this case we're sharing a publicly accessible policy that contains no personally identifiable data.  As we'll also be using CloudFront, a global content delivery network, the policy file will be stored globally anyway.  Later on we'll opt not to encrypt the bucket, for much the same reason.

Further down the page you'll see a section about object ownership.  Select the radio button for ACLs enabled and set object ownership to bucket owner preferred.

Next we'll configure who can access the bucket.  Counter-intuitively this will be set to block all public access because our anonymous visitors (email servers) will be accessing the bucket via CloudFront - more on that further down this tutorial.

I recommend enabling bucket versioning.  Although this does come with an additional cost it's worth remembering that MTA-STS policy files are tiny (108 bytes is not unusual) so the costs will be low.

Optionally, tag your bucket with something meaningful.  Set the default encryption to disable and then click create bucket.

Create .well-known and upload your MTA-STS policy - mta-sts.txt

From your list of S3 buckets, click the one you just created (mta-sts in my example above).  You'll see the bucket's contents under a heading of objects.  Click the create folder button from just under the heading.

The create folder page appears.  Specify a folder name of .well-known (including the full stop) and disable server-side encryption.

You'll need to create your policy file, called mta-sts.txt.  I won't go into detail explaining how to create this policy, as that's outside the scope of this tutorial, but it will look something like the below code fragment.  The NCSC has a good article on MTA-STS which has some pointers.  An example is below.

version: STSv1
mode: testing
mx: your-domain-here.org.uk
max_age: 86400

Note that the above policy is in testing mode.  While the policy is set like this there's no impact to email flow into your mail server.

Once you've created your policy, upload it to the .well-known folder.  While looking at your bucket contents, click the .well-known folder to open it.  Then click the orange upload button.  You'll be shown a form where you can drag and drop the policy file.

You'll see a summary of the upload.  Check the file destination is .well-known and then click the orange upload button in the bottom right.

(optional) Create an S3 bucket for your CloudFront logs

Logs can be useful for troubleshooting, but bear in mind you will be paying to store them so this step is optional.  First, create a new S3 bucket like you did before, but this time without versioning enabled.  You'll want to pick the AWS region that's most appropriate to you, and ensure ACLs are enabled with object ownership set to bucket owner preferred.  Public access to the bucket should be blocked.  Tag as you wish (I used the same tag for my policy bucket and my logs bucket).

Given you'll be paying to store the logs it's worth considering how long you want to keep those for.  You can add a retention policy to the S3 bucket to purge the logs after a certain number of days, keeping your costs down.  That's outside the scope of this tutorial though.

Request a certificate with AWS Certificate Manager

At the top right of the AWS console there is a drop down menu that allows you to specify what region you're creating assets in.  When it comes to creating certificates these need to be in the US East (N. Virginia region in order to be seen by CloudFront.  Click the drop down menu and switch to US East (N. Virigina) now.

Next, from the search box at the top of the AWS console, search for ACM (AWS Certificate Manager).  If this is your first time here, you’ll see the introduction page, rather than a list of your certificates:

Click on the orange request a certificate button to be taken to the form.  You need to select the radio button for request a public certificate and then click next.

Under the domain names heading, enter your domain name for MTA-STS in the fully-qualified domain name box.  In my case this would be mta-sts.jonco-it.co.uk.  For simplicity, you do not want to add another name to the certificate (although you could do so if you wanted to set up multiple domains for MTA-STS).

Scroll down to select validation method and choose DNS validation - recommended.  Then scroll down and optionally add tags as we've done previously.  Then click the request button.

You will be taken to a list of certificates.  If this is your first certificate, the list of certificates may appear blank at first.  Click the refresh button to see your request:

After refreshing your certificate should show as pending validation.  Click on your certificate ID to see more details.  To validate your right to get this certificate you'll need to create a CNAME record in DNS.  ACM tells you the CNAME name and value, so go and create those in DNS now (working with DNS is outside the scope of this guide).  Once your DNS record has been created go back to your list of certificates.  It may take some time (up to 24 hours) for your certificate to pass validation as DNS has to propagate, although in my experience this took at most 15 minutes.

After validation has completed, ACM will show a green tick next to the certificate and you can proceed to the next step - CloudFront.

Set up CloudFront to serve your policy

CloudFront is a global content delivery network (CDN) that is going to allow us to publish our MTA-STS policy file.  You don't need to worry about changing the AWS region as the CDN is global, so go ahead and search for CloudFront in the search box at the top of the console and click on the link for CloudFront Global Content Delivery Network.

Our policy will be published via what AWS call a distribution.  If this is your first distribution, click the big, orange, Create a CloudFront Distribution button:

Choose your origin domain from the drop down list, which will be the S3 bucket created earlier (mta-sts in my case).  We don’t want an origin path, so leave that blank.  You can change the name for the origin if desired, but it is not necessary:

Under S3 bucket access choose Yes use OAI (bucket can restrict access to only CloudFront) as our bucket doesn’t allow public access.  Also click the radio button for Yes, update the bucket policy:

Then click Create new OAI.  Leave the name as-is (unless you need to change it) and click the orange create button:

Next there are options for Origin Shield.  This can be left as no.

Under default cache behaviour we're going to say yes to compress objects automatically.  Then adjust the cache behaviour so access is HTTPS only (MTA-STS has to be served over HTTPS) with GET and HEAD the only allowed HTTP methods.  Do not restrict viewer access:

Leave Cache key and origin requests at the defaults.  Also skip over the Funtion associations section.  Scroll down to Settings.   For Price class set to use all edge locations (best performance) and leave Amazon WAF web ACL blank:

Next set the Alternate domain name (CNAME) to your desired mta-sts.DOMAIN-HERE.co.uk value (so mta-sts.jonco-it.co.uk for me).  Then scroll down to custom SSL certificate.  From the drop down list select the certificate you got issued earlier.

Not seeing the certificate?
Either you didn't create your certificate in the US East (N. Virigina) region, or your certificate has not yet been issued.  Head to ACM in another browser tab to check.

Leave the security policy at the recommended level:

Enable support for HTTP/2 and do not specify a root object (leave it blank.

If you don't want to log access, click the Create distribution button now.  The CloudFront deplopment will start and will take a few minutes to complete and become available.

(optional) Enable CloudFront logging

As mentioned before, this is an optional step.  Remember you will be paying to store logs, so it's worth creating a retention policy on the S3 bucket to only store the logs for a limited period.

While setting up the distribution, under Standard Logging choose on from just underneath the heading.  Next select your logging bucket from the S3 bucket drop down box.  Ensure cookie logging is off.

Click the Create distribution button now.  The CloudFront deplopment will start and will take a few minutes to complete and become available.

Troubleshooting CloudFront: can't add Alternate domain name (CNAME)

If you get a warning that says:

To add an alternate domain name (CNAME) to a CloudFront distribution, you must attach a trusted certificate that validates your authorisation to use the domain name.

Then you didn’t select your certificate correctly.  Go back to the Custom SSL certificate stage and select the certificate issued by ACM.

Create your MTA-STS DNS record

This is the first of two DNS records you'll need to make MTA-STS work (everything up to this point has been the infrastructure to make that possible).

Once the CloudFront distribution has been created you'll be able to see it in your list of distributions.  Click on the one you just created, and on the general tab you'll see the distribution domain name shown (it will start with a random ID and end in cloudfront.net).  If you copy the address from there and add /.well-known/mta-sts.txt to the URL you should see your MTA-STS policy file.

Now create a DNS CNAME record of mta-sts.your-domain-here.org.uk (so mta-sts.jonco-it.co.uk in my case) so that your policy can be accessed.  For the CNAME value use the distribution domain name shown on the CloudFront distribution's general tab.  As before configuring DNS records is outside the scope of this tutorial.

Create your _MTA-STS DNS record

To enable MTA-STS (even in testing mode) you will need a DNS TXT record for _mta-sts.your-domain-here.org.uk (so _mta-sts.jonco-it.co.uk in my case) to show MTA-STS is supported.  Note this DNS record is different to the one made a moment ago - it begins with an underscore.  Any time you update your MTA-STS policy you need to update the id in the record, so it's helpful to use a numerical ID.  For _mta-sts.jonco-it.co.uk my current TXT record says

v=STSv1; id=202112041647

The id value I'm using is simply the date and time that I make the change, so that ID was set on 4th December 2021 at 16:47.

Troubleshooting: I've changed my policy but browsing to the file shows the old one

Cloudfront caches the files on the CDN and it can take some time for your updated policy to be available.  You can force a cache expiry using the invalidate files feature of Cloudfront.  I recommend you consult AWS' documentation for further information.

Test your MTA-STS policy works

There's a neat tool at https://aykevl.nl/apps/mta-sts/ that will give you a report on your MTA-STS status.  Simply enter your mail domain and click Check! and you'll get a short report:

You'll note from the screenshot that MTA-STS claims to be disabled at the moment - that's a result of being in testing mode.  Once we're happy our email server is correctly configured we can change our policy to say mode: enforce.  I leave it to the reader (and system administrator) to choose when it's appropriate to do that.

The observant among you will note the MTA-STS validator screenshot mentions SMTP-TLSRPT which is a reporting mechanism for SMTP connections into your mail server.  It's worth having SMTP TLS reporting configured as compatible senders will generate a report that you'll receive detailing successful and failed TLS connections to your mail server.  This can be a useful indicator as to whether or not you're ready to move to enforce mode.  If connections regularly fail then your mail server configuration may need changing.

What happens when I turn on enforce mode?

Senders that obey MTA-STS will only send you email over encrypted connections.  If your server stopped supporting TLS connections then you would not receive emails from senders that support MTA-STS.  For senders that do not support sending email over a TLS encrypted connection you'd still receive their emails, unencrypted, as you always did.

Further reading

The UK's Nation Cyber Security Centre has an excellent article on MTA-STS and how to use it (also referenced earlier in this post).  Their article helped inform some of my thinking while configuring this for myself.

More details on MTA-STS can be found in RFC8461 which explains the requirements and how the system operates.

Conclusion

In this tutorial we've used S3 buckets to store our MTA-STS policy.  Optionally, a further S3 bucket has been used to store logs from CloudFront, as it's CloudFront that has provided access to our MTA-STS policy via a custom domain name.  We used Amazon Certificate Manager to provision a free certificate for our custom domain name.


Banner image: AWS logo with logos of the various components involved in the MTA-STS setup.