Using MTA-STS to enhance email transport security and privacy

Overview

SMTP is broken by design. It comes from a time when communication partners trusted each other and the NSA was intercepting facsimiles and phone calls instead of internet traffic.

To enhance privacy, in 2002 RFC 3208 was added to the SMTP protocol. Unfortunately STARTTLS is only optional, it is not allowed to only accept encrypted connections.

The RFC states: A publicly-referenced SMTP server MUST NOT require use of the
STARTTLS extension in order to deliver mail locally
.

That is really problematic because it leaves SMTP vulnerable to MITM (Man-In-The-Middle) attacks as the connection is first established in clear text and using the STARTTLS command to establish an encrypted session. In that time window, an attacker is able to enforce a clear text communication by suppressing the STARTTLS command. DNS cache poisoning to hijack mail transfer by fake MX records is also an attack vector.

What to do to solve the problem? Well, DANE is a good solution but it requires DNSSEC to work as expected. Unfortunately DNSSEC is not available for all domains, so SMTP MTA Strict Transport Securityβ€œ (RFC 8461) was introduced.

It works similar like HTTP Strict Transport Security (HSTS). It works as “Trust on First Use” or also known as “TOFU”. The policy is announced by a DNS record (which will be cached) and will be retrieved by HTTPS.

MTA-STS is less secure than DANE, but it is a huge step forward.

Who is using it?

There are already some large scale mail providers that make use of MTA-STS. Here are a few of them:

  • Google mail
  • mail.de
  • Yahoo
  • GMX

All of them are using the testing mode in the policy.

Announce your policy

The first step is to create the DNS records needed. A TXT record is used to announce the policy and a A (or AAAA) record for the hosting of the policy. For this example I use my test domain ldelouw.ch. The data is STSv1 where v1 stand for the protocol number, where one is the first and only version at the moment. The ID is used to identify changes in the policy. It is good practice to use a timestamp in Zulu time format.

If you are using IPA for your DNS management, its very easy:

[root@ipa1 ~]# ipa dnsrecord-add ldelouw.ch _mta-sts --txt-rec="v=STSv1;id=$(date -u +'%Y%m%d%H%M%S')Z;"
  Record name: _mta-sts
  TXT record: v=STSv1;id=20181216095025Z
[root@ipa1 ~]#

Create a _smtp._tls record to announce where error reports are sent to. This is usually the postmaster of the domain. This is optional but recommended.

[root@ipa1 ~]# ipa dnsrecord-add ldelouw.ch _smtp._tls --txt-rec="v=TLSRPTv1;rua=mailto:postmaster@example.com"
  Record name: _smtp._tls
  TXT record: v=TLSRPTv1;rua=mailto:postmaster@example.com
[root@ipa1 ~]#

Be aware for the A/AAAA record(s) there is no _ (underscore) needed.

[root@ipa1 ~]# ipa dnsrecord-add ldelouw.ch mta-sts --aaaa-rec="2a01:4f8:141:14ce::9"
  Record name: mta-sts
  AAAA record: 2a01:4f8:141:14ce::9
[root@ipa1 ~]# 

The second step is to create a web server instance for https://mta-sts.<your-domain>:443. The x509 certificate must be valid (make sense, right? πŸ˜‰ ) otherwise it will not work. Free certificates can be created using Letsencrypt.org. It can also be a SAN (Subject Alternative Name). If you have a web mailer software installed on your mail server it can be reused.

In the web server instance you need to create a file containing your MTA-STS policy. The file contains the protocol version (STSv1), the mode, a list of your mail exchange servers and the maximum age caching your policy. The mode first should be testing so see if it working properly.

mkdir -p /var/www/html/mta-sts.ldelouw.ch/.well-known

cat > /var/www/html/mta-sts.ldelouw.ch/.well-known/mta-sts.txt << EOF
version: STSv1
mode: testing
mx: your-smtp-server.example.com
mx: secondary-smtp.example.com
max_age: 86400
EOF

Testing your Policy

There are two web based tests available.

  • https://aykevl.nl/apps/mta-sts/
    Does not work with IPv6 only setups, it caches DNS requests which is bad when you do some testing and need to correct DNS entries.
  • https://www.hardenize.com You need to register to be able to run multiple test. Checks a lot of related configuration parameters as well.

Fetch policies for Postfix on your SMTP Server

Postfix itself does not support MTA-STS. You need a little helper for that: postfix-mta-sts-resolver.

The software is in an early state, it lacks a bit of documentation. That probably will improve over time. The software itself works nicely, I do however don't have any experience on a large scale.

Unfortunately the stock Python version of RHEL7 is too outdated, but it will work with Python 3.6 which is available in the Software Collections.

yum -y install rh-python36-python-pip.noarch

Install postfix-mta-sts-resolver using PIP

/opt/rh/rh-python36/root/bin/pip install postfix-mta-sts-resolver

Create a user for the MTA-STS daemon:

useradd -c "Daemon for MTA-STS policy checks" mta-sts -s /sbin/nologin

Lets install a Systemd unit file:

cat > /etc/postfix-mta-sts.service << EOF
[Unit]
Description=Postfix MTA STS daemon
After=syslog.target network.target 

[Service]
Type=simple
User=mta-sts
Group=mta-sts
# This is the ExecStart path for RHEL7 using python 36 from the Software collections.
# You may use a different python interpreter on other distributions
ExecStart=/opt/rh/rh-python36/root/bin/mta-sts-daemon

[Install]
WantedBy=multi-user.target
EOF

Enable the service on system startup:

systemctl enable postfix-mta-sts.service

There is a some configuration needed for the MTA-STS daemon itself.

cat > /etc/postfix/mta-sts-daemon.yml << EOF
host: 127.0.0.1
port: 8461
cache:
  type: internal
  options:
    cache_size: 10000
default_zone:
  strict_testing: true
  timeout: 4
zones:
  myzone:
    strict_testing: false
    timeout: 4
EOF

I'm not sure what the configuration statement strict_testing means. Without setting it to true, it will not work when the policy is set to testing. Looks like it overrides the policy from testing to enforcing, handle with care!

Configuring Postfix

The last step is to let postfix know about MTA-STS. This is done with the configuration statement smtp_tls_policy_maps.

smtp_tls_policy_maps = socketmap:inet:127.0.0.1:8461:postfix

Also ensure that smtp_tls_CAfile is set to make use of your global CA-cert bundle, otherwise sending emails to TLS enabled servers will completely fail since STARTTLS is not opportunistic anymore!

smtp_tls_CAfile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem

Nice to have: Increase the TLS log level to see if it is working:

smtp_tls_loglevel = 1

Testing your setup

mail:~# postmap -q ldelouw.ch socketmap:inet:127.0.0.1:8461:postfix

It should return something like:

secure match=mail.delouw.ch

Now send an email to a domain using MTA-STS and verify the Posfix log.

Dec 16 13:32:24 mail postfix/smtp[3583]: Verified TLS connection established to gmail-smtp-in.l.google.com[2a00:1450:400c:c0c::1a]:25: TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)

It states Verified not just Trusted πŸ™‚

Conclusion

MTA-STS is a way that enhances security without DNSSEC. It is still in its early stage, IETF just released the first version of RFC 8461 in September 2018.

The critical point is the MTA-STS lookup to be done by the MTA. There is not much choice of software that can be used to achieve the goal of an MTA-STS capable MTA. I only made some tests with the most poplar MTA, Postfix, solutions for others like Sendmail and Exim may, or may not exist.

Another problem is the MTA-STS agent. It is a possible new attack vector for the bad guys, input sanitation for policies is key.

At the end of the day, lets give it a try and enable our MTAs out there.

Have fun πŸ™‚

Using Unbound for recursive DNS lookup

Some organizations decide to use its internal authoritative DNS servers as recursive DNS because of easiness and reverse lookup of internal RFC 1918 networks works out of the box. That should be avoided for (at least) two reasons:

  • Cache poisoning can cause security nightmares
  • Authoritative answers are never cached and can cause a high load on the DNS servers.

Cache poisoning is a problem that can lead to severe problems, as more and more information is stored in DNS. Examples:

  • SSHFP entries for SSH fingerprint of servers
  • SRV entries for LDAP and Kerberos server autodetection

If an attacker can manipulate those kind of entries it can potentially be abused for redirecting users to fake authentication services.

There are some protective measures to avoid this kind of problems:

  • The usage of a separate recursive DNS infrastructure
  • Setting up DNSSEC and sign your DNS zones
  • The use of TLS for LDAP queries

This article is about how to set up recursive DNS servers, DNSSEC will be covered in a follow-up article.

Turning off recursion in authoritative DNS servers

In the option section of the bind DNS configuration make sure you have the following line in /etc/named.conf:

allow-recursion { none; };

If you are using a different DNS server software, check the vendor manual. After a restart, check if it is working as expected.

[luc@bond ~]$ dig www.example.com @ipa2.delouw.ch

; <<>> DiG 9.10.4-P6-RedHat-9.10.4-4.P6.fc25 <<>> www.example.com @ipa2.delouw.ch
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 58272
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.example.com.               IN      A

;; Query time: 0 msec
;; SERVER: 192.168.100.106#53(192.168.100.106)
;; WHEN: Sat Apr 15 12:10:15 CEST 2017
;; MSG SIZE  rcvd: 44

[luc@bond ~]$ 

Using Unbound as recursive DNS

Unbound is very secure, lightweight and high performance DNS server for validating, recursion, and caching of queries. Its astonishing how easy it is to configure Unbound.

Installation on RHEL7, Fedora and probably other Linux and BSD distributions is easy:

recursor1:~# yum -y install unbound

For this example, all configuration is made in /etc/unbound/unbound.conf
First you must define on which IPs Unbound should listen. The default is localhost only.

interface: 0.0.0.0
interface: ::0

The next default that needs to be changed is the access control. Default to refuse all but localhost. In this example you will allow access from two of your RFC 1918 subnets and the RFC 3849 IPv6 range.

access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.0/8 allow
access-control: 192.168.1.0/24 allow
access-control: 192.168.2.0/24 allow
access-control: ::1 allow
access-control: 2001:DB8::/32 allow

Forward PTR queries to your RFC 1918 zones

Unbound has a nice default setting: It ignores any queries to RFC 1918 PTR queries to avoid sending queries to the blackhole servers.

In this example, we need to change the behavior to allow queries for our internal networks 192.168.1.0 and 192.168.2.0.

local-zone: "1.168.192.in-addr.arpa." transparent
local-zone: "2.168.192.in-addr.arpa." transparent

Next up: Forward this queries to our internal DNS server infrastructure (i.e IPA or MS-DNS or simply bind)

forward-zone:
        name: "1.168.192.in-addr.arpa."
        forward-host: ipa1.example.com
        forward-host: ipa2.example.com
        forward-host: ipa3.example.com

forward-zone:
        name: "2.168.192.in-addr.arpa."
        forward-host: ipa1.example.com
        forward-host: ipa2.example.com
        forward-host: ipa3.example.com

This will forward queries at random to DNS servers ipa1,ipa2 and ipa3.example.com. Add more servers as needed.

The final step is to (re)configure your clients to use the newly set up recursive DNS servers.

Have fun πŸ™‚

Providing SRV and TXT records for Kerberos and LDAP with dnsmasq

What if you have an application such as OVirt/RHEV-M that relies on DNS services records and you dont have the possibility to add them to the DNS servers because the DNS admins do not like to do its job?

Fake them! DNSMasq is your friend πŸ™‚ Install dnsmasq on the server in question and configure /etc/resolv.conf to query first dnsmask on localhost.

yum -y install dnsmasq
chkconfig dnsmasq on

Assuming your subdomain is called example.com and your ldap and kerberos providers are ipa1.example.com and ipa2.example.com, configure dnsmasq as following:

cat << EOF >> /etc/dnsmasq.conf
srv-host =_kerberos._udp.example.com,ipa1.example.com,88
srv-host =_kerberos._udp.example.com,ipa2.example.com,88
srv-host =_kerberos._tcp.example.com,ipa1.example.com,88
srv-host =_kerberos._tcp.example.com,ipa2.example.com,88
srv-host =_kerberos-master._tcp.example.com,ipa1.example.com,88
srv-host =_kerberos-master._tcp.example.com,ipa2.example.com,88
srv-host =_kerberos-master._udp.example.com,ipa1.example.com,88
srv-host =_kerberos-master._udp.example.com,ipa2.example.com,88
srv-host =_kpasswd._tcp.example.com,ipa1.example.com,88
srv-host =_kpasswd._tcp.example.com,ipa2.example.com,88
srv-host =_kpasswd._udp.example.com,ipa1.example.com,88
srv-host =_kpasswd._udp.example.com,ipa2.example.com,88
srv-host =_ldap._tcp.example.com,ipa1.example.com,389
srv-host =_ldap._tcp.example.com,ipa2.example.com,389
txt-record=_kerberos.example.com,"EXAMPLE.COM"
EOF

Add the follwing line to /etc/resolv.conf and make sure 127.0.0.1 is the first DNS server to be queried.

nameserver 127.0.0.1

Start dnsmasq and have fun πŸ™‚

service dnsmask start