Helm chart for Kubernetes

If you run a Kubernetes cluster, the mta-sts Helm chart serves your MTA-STS policy from your own infrastructure. DMARC Examiner maintains it, it is open source (MIT license) and it works on any plan, Free included.

If you do not want to maintain anything, use Hosted MTA-STS.

What it does

  • Several domains. One release serves mta-sts.<domain> for every domain in the list, each with its own policy.
  • Automatic id. The id of the _mta-sts record comes from the policy content: it changes only when the policy changes.
  • DNS records as code. With external-dns, it creates the _mta-sts and _smtp._tls (TLS-RPT) TXT records. Without external-dns, it shows you the records to create.
  • TLS. It annotates the Ingress for cert-manager (ClusterIssuer or Issuer).
  • Secure by default. Non-root nginx, read-only file system and no capabilities.
  • Validation. It rejects invalid modes, a max_age out of range, malformed MX hosts and malformed TLS-RPT addresses before it installs anything.
  • helm test. It downloads every policy through the Service and checks that an unknown host gets a 404.

Requirements

  • Kubernetes 1.25 or later and Helm 3.8 or later.
  • An Ingress controller reachable from the internet on port 443.
  • A publicly trusted certificate for every mta-sts.<domain>. cert-manager with Let's Encrypt works.
  • mta-sts.<domain> must resolve to your Ingress.
  • Optional: external-dns with the crd source, to manage the TXT records.

Install

helm install mta-sts oci://ghcr.io/dmarc-examiner/charts/mta-sts \
  --namespace mta-sts --create-namespace -f values.yaml

One domain on Google Workspace

Put your DMARC Examiner reporting address in tlsRpt.rua. You find it in the DNS Records tab of the domain, under TLS-RPT record.

domains:
  - name: example.com

policy:
  mode: testing
  mx:
    - aspmx.l.google.com
    - "*.aspmx.l.google.com"

tlsRpt:
  rua:
    - mailto:<your-reporting-address>

ingress:
  className: nginx
  certManager:
    clusterIssuer: letsencrypt

On Microsoft 365, the MX is *.mail.protection.outlook.com.

Several domains with different policies

Each domain can override mode, maxAge, mx and tlsRptRua. Anything it does not override comes from policy and tlsRpt.

domains:
  - name: example.com
  - name: example.org
    mode: enforce
    mx:
      - mx1.example.org
      - mx2.example.org
  - name: legacy.example.net
    mode: none

policy:
  mode: testing
  mx:
    - mx.example.com

In tlsRpt.rua, {domain} is replaced by the name of each domain.

DNS records with external-dns

externalDns:
  enabled: true
  labels:
    dns: public

The chart creates one DNSEndpoint per domain with these records:

Name Type Value
_mta-sts.example.com TXT v=STSv1; id=<20 hex characters>
_smtp._tls.example.com TXT v=TLSRPTv1; rua=mailto:<your-reporting-address>

external-dns needs --source=crd and, if you set labels, the matching --label-filter. The A or CNAME record of mta-sts.<domain> comes from the Ingress, as usual.

Without external-dns, helm install and helm upgrade show the exact records to create in the release notes.

The policy id

The chart sets id = first 20 hex characters of sha256(policy file). A helm upgrade that does not touch the policy keeps the id. If it changes the policy, the id changes on its own. With external-dns the TXT record follows; without it, copy the new value from the release notes.

Rollout and withdrawal

Step Values
Start mode: testing and tlsRpt.rua
Switch to enforce mode: enforce, after one or two weeks of TLS reports without failures
Change MX Add the new MX hosts to mx before you change the DNS
Withdraw mode: none for a whole maxAge; then helm uninstall

The reasons for each step are in What is MTA-STS.

Check

helm test mta-sts -n mta-sts
curl -s https://mta-sts.example.com/.well-known/mta-sts.txt
dig +short TXT _mta-sts.example.com

In DMARC Examiner, the MTA-STS card of the domain shows the published mode and the TLS tab shows the reports from senders.

The full list of values is in the chart README.