b6c8291c image

Email Strict Transport Security with MTA-STS


Email. Insecure by design. SMTP was designed in an era of high trust and low understanding of the shenanigans that would later arise on the Internet. In particular, encryption, let alone email strict transport security, was not something baked in from the start. Let’s fix that!

After waves of spam and snooping and phishing and ransomware etc., many band-aids were added over the years. DNS-based realtime blackhole lists (DNSRBL), STARTTLS, authentication, DANE, … so many. Did you know some email providers don’t even allow encrypting your mail in transit?

After spending yesterday double and triple checking our DMARC, DKIM, SPF (due to some suspected delivery problems), I decided to enable MTA-STS. Why not, one more standard can’t hurt right? If you want to understand some of the issues this is addressing, this article from The Register is a good spot to start.

In a nutshell, in MTA-STS, you add 2 more DNS records (because its a general purpose database of key/value pairs, right?), and then add a new web site (which must be proper TLS), with a single file (/.well-known/mta-sts.txt). And boom. You now have Strict Transport Security (STS) on your Mail Transfer Agent (MTA). As you know we are all-in on the TLS, placing our domain(s) on the HSTS preload list. Encryption or GTFO.

Now, it might seem like a pain to bring up a new web site just for this. But, hold my beer, we got this. Cuz cloud native and Kubernetes. Engage your peril-sensitive sunglasses, here come some YAML. You can skip to the GitHub if you want to use it and start enjoying email strict transport security today.

---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: server
   namespace: mta-sts
 spec:
   selector:
     matchLabels:
       app: nginx
   replicas: 1
   template:
     metadata:
       labels:
         app: nginx
     spec:
       containers:
       - name: nginx
         image: nginx:1.17.5
         ports:
         - containerPort: 80
           name: http
           protocol: TCP
         volumeMounts:
           - name: config
             mountPath: /usr/share/nginx/html/.well-known
       volumes:
         - name: config
           configMap:
             name: config
---
apiVersion: extensions/v1beta1
 kind: Ingress
 metadata:
   annotations:
     certmanager.k8s.io/cluster-issuer: letsencrypt-prod
     kubernetes.io/ingress.class: nginx-transparent
     ingress.kubernetes.io/ssl-redirect: "true"
   name: ingress
   namespace: mta-sts
 spec:
   rules:
 host: mta-sts.agilicus.com http:   paths: backend:
   serviceName: service
   servicePort: http
 path: /
 host: mta-sts.agilicus.ca http:   paths: backend:
   serviceName: service
   servicePort: http
 path: /
 tls:
 hosts: mta-sts.agilicus.com
 mta-sts.agilicus.ca
 secretName: mta-sts-tls 
---
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: config
   namespace: mta-sts
 data:
   mta-sts.txt: |
     version: STSv1
     mode: testing
     mx: aspmx.l.google.com
     mx: alt1.aspmx.l.google.com
     mx: alt2.aspmx.l.google.com
     mx: alt3.aspmx.l.google.com
     mx: alt4.aspmx.l.google.com
     max_age: 604800
---
 apiVersion: v1
 kind: Service
 metadata:
   name: service
   namespace: mta-sts
 spec:
   ports:
 name: http
 port: 80
 protocol: TCP
 targetPort: http
 selector:
 app: nginx
 type: ClusterIP