What is MTA-STS (MTA Strict Transport Security) about?
MTA-STS basically enforces TLS for your mail communication, similar to HTTP Strict Transport Security (HSTS) for HTTP/HTTPS traffic. By telling the sender that TLS has to be used one can reduce / stop Man-in-the-Middle (MITM) attacks. A probably better explanation is found in the abstract of the RFC:
SMTP MTA Strict Transport Security (MTA-STS) is a mechanism enabling mail service providers (SPs) to declare their ability to receive Transport Layer Security (TLS) secure SMTP connections and to specify whether sending SMTP servers should refuse to deliver to MX hosts that do not offer TLS with a trusted server certificate.
SMTP MTA Strict Transport Security (MTA-STS). RFC 8461. https://www.rfc-editor.org/rfc/rfc8461.html
If DNSSEC is not implementable for you, DANE won’t be possible and MTA-STS might be a good alternative. Without DNSSEC on the other hand, MTA-STS is prone to Downgrade-attacks using DNS-Spoofing, though.
What is TLSRPT (TLS Reporting) about?
TLSRPT adds reporting to SMTP TLS connections, so that TLS errors and information like which DANE/MTA-STS policy were used are reported in a specific format to a given address. The RFC states
A number of protocols exist for establishing encrypted channels between SMTP [..]. These protocols can fail due to misconfiguration or active attack, leading to undelivered messages or delivery over unencrypted or unauthenticated channels. This document describes a reporting mechanism and format by which sending systems can share statistics and specific information about potential failures with recipient domains. [..]
SMTP MTA TLS Reporting (TLSRPT). RFC 8460. https://www.rfc-editor.org/rfc/rfc8460.html
Requirements
- SSL Certificate (Unlike DANE it can’t be self-signed.)
- Webserver to publish the policy
- The ability to add DNS records
- Minimum TLS version in your MTA needs to be 1.2
Setting up the DNS records
If you send E-Mail from subdomains, the MTA STS DNS record has to be added for that subdomain as well. Basically you will need to add MTA-STS DNS Records for every domain you do send mails from. RFC 8461 states that [..] for mail sent to “user@mail.example.com”, the policy can be fetched only from “mail.example.com”, not “example.com”. So while the policy can be the same; you will need to add DNS records to all your domains.
_mta-sts TXT record
First of all, you need to create the _mta-sts DNS Record. It consists of two fields separated by a semicolon. v= for Version (currently only STSv1 is supported) and id= for the identifier which helps senders to check if the policy was changed – similar to the serial of your domain in DNS. So whenever you update the policy later, you need to increase the id within the policy file.
Using dynamic dns updates with Bind 9.16 it is as simple as just typing:
root@ns3:~# nsupdate -l
> zone jeanbruenn.info
> update add _mta-sts.jeanbruenn.info. 3600 IN TXT "v=STSv1; id=202107300200Z"
> send
Since this would mean that I will need to update the ID whenever the policy changes for various domains, I prefer to use CNAMEs. My mailing domain is no-uce.de so instead of the above (delete the record like this: update delete _mta-sts.jeanbruenn.info IN TXT) I do use the following:
root@ns3:~# nsupdate -l
> zone no-uce.de
> update add _mta-sts.no-uce.de. 3600 IN TXT "v=STSv1; id=202107300200Z"
> send
> # for every domain point it to the mailservers domain
> zone jeanbruenn.info
> update add _mta-sts.jeanbruenn.info. 3600 IN CNAME _mta-sts.no-uce.de
> send
mta-sts A/AAAA record
Another DNS record is required, because we need to make the policy available through HTTPS. I want to store the policy only on one system. The IP of it is 84.200.7.154. Hence:
root@ns3:~# nsupdate -l
> zone no-uce.de
> update add mta-sts.no-uce.de 3600 IN A 84.200.7.154
> send
Above I used CNAMEs to point the _mta-sts.* records to _mta-sts.no-uce.de. I will do the same here:
root@ns3:~# nsupdate -l
> zone jeanbruenn.info
> update add mta-sts.jeanbruenn.info. 3600 CNAME mta-sts.no-uce.de
> send
_smtp._tls TXT record
To get the TLS reports (and especially to tell systems where to send those reports to) you need to add another record. The RFC does not state if CNAMEs should be followed; I’m using them here to simplify the configuration in case I will change the mail address those reports are sent to in the future.
root@ns3:~# nsupdate -l
> zone no-uce.de
> update add _smtp._tls.no-uce.de 3600 IN TXT "v=TLSRPTv1; rua=mailto:himself@jeanbruenn.info"
> zone jeanbruenn.info
> update add _smtp._tls.jeanbruenn.info 3600 CNAME _smtp._tls.no-uce.de
> send
Setting up the webserver for the policy
If you take a look at the SNI and policy delegation parts of the RFC you will notice, that in such a case you will need your webserver to provide the policy by the name we created above. So the policy needs to be available both through mta-sts.no-uce.de and mta-sts.jeanbruenn.info. The SSL certificate will hence need to contain mta-sts.no-uce.de and mta-sts.jeanbruenn.info
Webserver configuration / NGINX
You might want/need to set it up without SSL to obtain the Letsencrypt certificates first. However, it is important, that the server { } block listens on all mta-sts names (see server_name).
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/mta-sts.no-uce.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mta-sts.no-uce.de/privkey.pem;
root /var/www/mta-sts;
index index.html index.htm index.nginx-debian.html;
server_name mta-sts.no-uce.de
mta-sts.jeanbruenn.info;
location / {
try_files $uri $uri/ =404;
}
}
Letsencrypt
I’m creating one certificate for all my domains.
root@mail:~# certbot certonly -d mta-sts.no-uce.de -d mta-sts.jeanbruenn.info
In /etc/letsencrypt/live/* you can find it. You may create individual certificates and individual server { } blocks in your nginx configuration.
The policy
A simple textfile sets the policy:
root@mail:/var/www/mta-sts/.well-known# cat mta-sts.txt
version: STSv1
mode: enforce
max_age: 2419200
mx: mail.no-uce.de
The policy consists of a few key value pairs. These are version (currently only STSv1 supported), mode (one of testing, enforce and none), max_age and one or more mx keys.
mode=testing can be used together with TLSRPT to get information about failures with TLS. mode=none can be used to disable MTA-STS. mode=enforcing is obvious I believe. I’m using 28 days for max_age – the age is expressed in seconds.
The second half of the MTA-STS implementation
All of the above is just one part of implementing MTA-STS. The second half is using something which verifies MTA-STS when someone sends mail to us. At a first glance using the postfix-mta-sts-resolver in Postfix (which does exactly that) looks promising. However, one of the Github Issues states that using the MTA-STS resolver (https://github.com/Snawoot/postfix-mta-sts-resolver) overrides DANE. So if you run DANE and MTA-STS just like I do, you should keep that in mind.
Install python3-pip and the postfix-mta-sts-resolver by issuing:
apt-get install python3-pip
python3 -m pip install postfix-mta-sts-resolver
Now edit your /etc/postfix/main.cf and add:
smtp_tls_policy_maps = socketmap:inet:127.0.0.1:8461:postfix
My /etc/mta-sts-daemon.yml looks like this:
root@mail:/etc/systemd/system# cat /etc/mta-sts-daemon.yml
host: 127.0.0.1
port: 8461
reuse_port: true
shutdown_timeout: 20
cache:
type: internal
options:
cache_size: 10000
proactive_policy_fetching:
enabled: true
default_zone:
strict_testing: false
timeout: 4
I used the systemd scripts of the contrib/ folder of postfix-mta-sts-resolver. You have to modify the path to the executable.
Does it work?
A test to verify that MTA-STS and TLSRPT are set up correctly is e.g.: https://aykevl.nl/apps/mta-sts/. For my sites no-uce.de and jeanbruenn.info it states:
A report I received by google while I was testing and playing around with MTA-STS and TLSRPT:
{
"organization-name":"Google Inc.",
"date-range":
{
"start-datetime":"2021-07-29T00:00:00Z",
"end-datetime":"2021-07-29T23:59:59Z"
},
"contact-info":"smtp-tls-reporting@google.com",
"report-id":"2021-07-29T00:00:00Z_no-uce.de",
"policies":[
{
"policy":
{
"policy-type":"no-policy-found",
"policy-domain":"no-uce.de"
},
"summary":
{
"total-successful-session-count":7,
"total-failure-session-count":0
}
}]
}
Who uses MTA-STS?
A simple script I fastly wrote down (yes yes, there are most likely better ones! :^)) like this:
while read -r domain; do
echo $domain;
mtasts=$(dig TXT _mta-sts.$domain +short);
if [ ! -z "$mtasts" ]; then
echo $mtasts;
curl -s "https://mta-sts.$domain/.well-known/mta-sts.txt";
fi
dig TXT _smtp._tls.$domain +short;
echo "";
done< <(grep -E "qmgr.*from=<" /var/log/mail.info | sed -r 's_.*from=<(.*)@(.*)>,? .*_\2_' | sort -u | grep -v queue)
I can see that:
- facebook (testing)
- gmail (enforce)
- hotmail (testing)
and a few others use MTA-STS as well as TLSRPT. I’ll follow this and probably update this list in a while.
A few notes / things to keep in mind
- MTA-STS consists of two parts. Telling receivers how to handle our mails is the easy and first part. Checking how to handle mails from senders and acting appropriate is the second and difficult part.
- Due to the requirement of using a webserver to publish the MTA-STS policy it might add more attack vectors.
- DANE is more secure than MTA-STS and hence the preferred technique. Which does not mean you may not mix both techniques. However, using the currently available postfix-mta-sts-resolver might override DANE and hence result in less security.
See also
- SMTP MTA Strict Transport Security (MTA-STS). RFC 8461. https://www.rfc-editor.org/rfc/rfc8461.html
- SMTP MTA TLS Reporting (TLSRPT). RFC 8460. https://www.rfc-editor.org/rfc/rfc8460.html
- Increase email security with MTA-STS and TLS reporting. Google Workspace Admin Help. https://support.google.com/a/answer/9261504?hl=en
- MTA-STS validator. https://aykevl.nl/apps/mta-sts/
- Github postfix-mta-sts-resolver. https://github.com/Snawoot/postfix-mta-sts-resolver
Hi Jean,
I’m interested in the third part 😉 – which is sadly missing:
The implementation of TLS reporting with Postfix.
It seems to be not easy possible, if at all…
Hey Christoph,
give me a few days I’ll check. I’m also curious about that one. If I find something for that I’ll let you know 🙂
Jean