Issue Description
Outbound delivery to port.venice.it fails at the MX lookup stage with a DNSSEC validation error, even though the zone is legitimately unsigned (insecure) and validates correctly with standard tools.
The failure persists with DANE disabled globally, so the validation seems to be applied to the MX lookup itself.
The bounce received by the sender reads:
(failed to lookup 'port.venice.it': DNS resolution error: DNS error: DNSSEC validation failed)
Other .it second-level domains are delivered without problems.
- Stalwart version: v0.16.23
- Deployment: Docker
- DNS resolver: Cloudflare DNS with TLS (also reproduced with the system resolver)
- EDNS enabled, Try TCP on Error enabled
Workaround
A static relay route for port.venice.it pointing to port-venice-it.mail.protection.outlook.com:25 (STARTTLS) bypasses the MX lookup and delivery works.
Expected Behavior
The MX lookup should succeed with an insecure (unsigned) result, and the message should be delivered to port-venice-it.mail.protection.outlook.com.
Actual Behavior
Outbound delivery to port.venice.it fails at the MX lookup stage with a DNSSEC validation error, even though the zone is legitimately unsigned (insecure) and validates correctly with standard tools.
Reproduction Steps
- Configure any resolver type (e.g. Cloudflare with TLS).
- Send a message to any address at
port.venice.it. - Delivery fails with
delivery.mx-lookup-failed/DNSSEC validation failed.
Relevant Log Output
INFO MX record lookup failed (delivery.mx-lookup-failed) queueName = "remote", from = "[email protected]", to = ["[email protected]"],
domain = "port.venice.it", causedBy = DNS error (mail-auth.dns-error) { details = "DNS error: DNSSEC validation failed" }, elapsed = 168ms
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
Additional Context
Validation with standard tools
BIND’s delv (full validation from the root) correctly reports the answer as insecure, both from the host and from a container on the same Docker network querying Cloudflare:
$ delv @1.1.1.1 port.venice.it MX
; unsigned answer
port.venice.it. 3600 IN MX 0 port-venice-it.mail.protection.outlook.com.
Google Public DNS (validating) returns NOERROR without the ad flag, as expected for an insecure zone.
DNS structure (likely root cause)
venice.it is not a separate zone: port.venice.it is delegated directly from the .it registry to its authoritative servers (ns033.name.cloud.ibm.com, ns176.name.cloud.ibm.com).
The DS query for port.venice.it is answered by the .it servers with an NSEC3 closest encloser proof where:
- the closest encloser is the TLD apex
ititself; - the next closer name (
venice.it) is covered by an NSEC3 record with the opt-out flag set.
$ dig +dnssec DS port.venice.it @1.1.1.1
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
;; AUTHORITY SECTION:
it. 3600 IN SOA dns.nic.it. hostmaster.nic.it. 2026092414 10800 900 604800 3600
it. 3600 IN RRSIG SOA 13 1 3600 20261024110329 20260924110329 53706 it. ...
6EQIAO6UF3HPKVP4A8704T8QV4NV63RL.it. 3600 IN NSEC3 1 1 0 - 6ERRV534H8JG5VFJ7B5IG4SMVMTKVGOA NS DS RRSIG
6EQIAO6UF3HPKVP4A8704T8QV4NV63RL.it. 3600 IN RRSIG NSEC3 13 2 3600 20261024110329 20260924110329 53706 it. ...
RS1N3N7M54PDEM5EUNV9NPKH3B6CGPJC.it. 3600 IN NSEC3 1 1 0 - RS1PG43S35R5V1KF1V10FT5SP409IUBV NS SOA RRSIG DNSKEY NSEC3PARAM
RS1N3N7M54PDEM5EUNV9NPKH3B6CGPJC.it. 3600 IN RRSIG NSEC3 13 2 3600 20261024110329 20260924110329 53706 it. ...
Per RFC 5155 (section 8.6), this is a valid proof of an insecure delegation. My guess is that the validator used by Stalwart (hickory-dns) does not handle the case where the closest encloser is two labels above the queried name within an opt-out span, and returns “bogus” instead of “insecure”.
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