Issue Description
Outbound mail gets rejected by strict recipients (Exim with verify = sender/callout) with a generic error, even though
SPF/DKIM/DMARC/PTR are all correctly configured:
SMTP RCPT TO rejected ... code = 550, details = "Verification failed for <[email protected]>
No Such User Here
Sender verify failed"
This happens because the recipient’s MTA connects back to our own inbound port 25 to verify the sender address exists before
accepting the message — and that callback was itself getting rejected on our end (see root cause below), which the recipient’s Exim
reports back as a generic “Sender verify failed”.
Environment
-
stalwartlabs/stalwart:v0.16.17 (also reproduced on v0.16), Docker Compose
-
nginxin front of Stalwart as a stream proxy adding PROXY protocol, per the documented pattern
(NGINX | Stalwart):stream {
server {
listen 25;
proxy_pass stalwart:10025;
proxy_protocol on;
}
} -
smtplistener bound to both[::]:25and[::]:10025(public port + internal port nginx forwards to)
Expected Behavior
Port 25 (the public-facing inbound SMTP listener, forwarded internally to port 10025 via nginx’s PROXY protocol) should accept MAIL FROM/RCPT TO without requiring authentication, since inbound MX traffic from arbitrary external mail servers is never
authenticated. The AUTH Stage rule local_port != 25 was configured with this intent.
Actual Behavior
Because all real external traffic actually arrives with local_port = 10025 (not 25) due to the PROXY protocol forward, the
exclusion condition never matches, and Stalwart incorrectly demands authentication:
503 5.5.1 You must authenticate first.
503 5.5.1 MAIL is required first.
This causes external MTAs doing sender-verify callouts (e.g. Exim with verify = sender/callout) to fail their probe against us,
and they in turn reject our outbound mail with a generic 550 ... Sender verify failed / No Such User Here.
Reproduction Steps
-
Deploy Stalwart behind an nginx stream proxy adding PROXY protocol, per the documented pattern
(NGINX | Stalwart):stream {
server {
listen 25;
proxy_pass stalwart:10025;
proxy_protocol on;
}
} -
Bind the
smtplistener to both[::]:25and[::]:10025. -
Set Settings → MTA → Session → AUTH Stage → Require Authentication to
local_port != 25. -
From any host, run:
printf ‘EHLO test\r\nMAIL FROM:<>\r\nRCPT TO:[email protected]\r\nQUIT\r\n’ | nc mail.example.com 25
-
Observe
503 5.5.1 You must authenticate first.instead of the expected250 OK.
Relevant Log Output
SMTP RCPT TO rejected (delivery.rcpt-to-rejected) queueId = …, queueName = “remote”, from = “[email protected]”, to =
[“[email protected]”], hostname = “target-domain.com”, code = 550, details = “Verification failed for [email protected]
No Such User Here
Sender verify failed”, elapsed = 20040ms
— direct local test against our own port 25 (before fix) —
220 mail.example.com Stalwart ESMTP at your service
250-mail.example.com you had me at EHLO
…
250 8BITMIME
503 5.5.1 You must authenticate first.
503 5.5.1 MAIL is required first.
221 2.0.0 Bye.
— after changing the rule to local_port != 25 && local_port != 10025 —
250 2.1.0 OK
250 2.1.5 OK
Stalwart Version:
v0.16.17
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
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