Automatic DNS management does not generate SMTP TLSA records for configured mailExchangers

Issue Description

When automatic DNS management is enabled with tlsa: true, Stalwart should generate TLSA records for every hostname configured as a mail exchanger. All MX hostnames are covered by the certificate used by Stalwart.

Expected Behavior

Domain: mail.example.org

Mail exchangers:
10 node1.example.org
10 node2.example.org

Stalwart correctly generates:

mail.example.org. IN MX 10 node1.example.org.
mail.example.org. IN MX 10 node2.example.org.

The generated DNS zone to also contain SMTP DANE records such as:

_25._tcp.node1.example.org. IN TLSA 3 1 1
_25._tcp.node2.example.org. IN TLSA 3 1 1

and the corresponding trust-anchor TLSA records if applicable.

Actual Behavior

Stalwart generates the MX records correctly, but does not generate any _25._tcp TLSA records for the configured mail exchangers.

The generated zone contains:

mail.example.org. IN MX 10 node1.example.org.
mail.example.org. IN MX 10 node2.example.org.

and TLSA records for other Stalwart services:

_443._tcp.mail.example.org. IN TLSA 3 1 1
_993._tcp.mail.example.org. IN TLSA 3 1 1
_995._tcp.mail.example.org. IN TLSA 3 1 1
_465._tcp.mail.example.org. IN TLSA 3 1 1

but these records are missing:

_25._tcp.node1.example.org.
_25._tcp.node2.example.org.

This means automatic DNS management publishes MX hosts for inbound SMTP but does not publish the DANE records required for those same MX destinations.

Reproduction Steps

  1. Configure the server default hostname/domain as: mail.example.org

  2. Configure multiple mail exchangers:

    • node1.example.org priority 10
    • node2.example.org priority 10
  3. Configure automatic certificate management with a certificate containing:

    • *.example.org
    • *.mail.example.org
  4. The wildcard certificate therefore covers both:

    • node1.example.org
    • node2.example.org
  5. Configure automatic DNS management:

    {
      "@type": "Automatic",
      "origin": "example.org",
      "publishRecords": {
        "autoConfig": true,
        "autoConfigLegacy": true,
        "autoDiscover": true,
        "caa": true,
        "dkim": true,
        "dmarc": true,
        "mtaSts": true,
        "mx": true,
        "spf": true,
        "srv": true,
        "tlsRpt": true,
        "tlsa": true
      }
    }
    
  6. Run/refresh the DNS Management task.

  7. Fetch dnsZoneFile using x:Domain/get.

  8. Observe that MX records are generated:

    mail.example.org. IN MX 10 node1.example.org.
    mail.example.org. IN MX 10 node2.example.org.
    

    but no SMTP TLSA records are generated for either MX hostname.

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

PostgreSQL

Blob Storage

S3-compatible

Search Engine

PostgreSQL

Directory Backend

Internal

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

What you are seeing is expected behaviour rather than a bug, though I agree it is not obvious and the server gives you no hint about why the records are missing.

DNS records are generated per domain object, and a record is only generated for hostnames that live inside that domain’s own namespace. In your setup the domain object is mail.example.org, so the check is “does this hostname end in .mail.example.org, or is it mail.example.org itself”. Your service hostnames fall back to the server name, which is mail.example.org, so they pass and you get the TLSA records on ports 443, 993, 995 and 465. Your mail exchangers are node1.example.org and node2.example.org, which sit one level up in example.org, so they do not pass and no _25._tcp records are produced for them. The MX records themselves are unaffected because an MX target is allowed to point anywhere, so there is nothing to scope there.

You might reasonably ask why the origin: "example.org" setting does not widen this. It tells Stalwart which provider zone to write this domain’s records into, and it does mean Stalwart has write access to example.org. What it does not do is make the mail.example.org object responsible for names owned by example.org. Keeping the two separate matters for a few reasons: in a multi-tenant setup it would let one tenant’s domain publish records at names belonging to another tenant’s domain, several domains sharing the same origin would each publish the same RRSet and overwrite each other on every task run, and TLSA in particular is a record where publishing the wrong value is considerably worse than publishing none at all, since a mismatched certificate association will break inbound delivery from every DANE validating sender. The same scoping rule is applied when requesting certificates, which is why it is consistent across the board.

The fix on your side is to add example.org as a domain in Stalwart and enable automatic DNS management on it with publishRecords limited to tlsa (leave mx, spf, dmarc and the rest disabled if you do not want the apex touched). The mail exchanger list is server wide, not per domain, so once a domain object owns example.org it will pick up node1.example.org and node2.example.org, confirm they are covered by your *.example.org certificate, and publish:

_25._tcp.node1.example.org. IN TLSA 3 1 1 ...
_25._tcp.node1.example.org. IN TLSA 2 1 1 ...
_25._tcp.node2.example.org. IN TLSA 3 1 1 ...
_25._tcp.node2.example.org. IN TLSA 2 1 1 ...

into the same example.org zone you are already writing to, including the trust anchor records you were expecting.