ollosolucions
New Pleskian
- Server operating system version
- AlmaLinux 8.10 x86_64
- Plesk version and microupdate number
- 18.0.82.1
Hello,
I am investigating an issue where incoming emails from Zoho Campaigns consistently end up in the Spam folder on a Plesk server.
After checking DNS, verifying the signatures locally, and capturing the Postfix–milter communication, I traced a whitespace change to the processing performed by psa-pc-remote or one of the mail handlers it invokes.
Has anyone encountered this behavior, or is there a known bug or supported fix?
Incoming mail follows this pipeline:
On the VPS, both signatures fail:
SPF passes for the Zoho envelope sender domain, but it is not aligned with the visible From domain. As a result, the DKIM failure also causes DMARC to fail:
Dovecot subsequently records:
In the original message investigated, Amavis allowed the message through a whitelist, but the subsequent Plesk DMARC handler still caused delivery to Spam.
A separate recipient copy of the same campaign, delivered directly from Zoho to Microsoft 365 without passing through this VPS, passed both DKIM signatures and DMARC. This was a different recipient-specific message, not an identical copy of the VPS message.
Both signatures validate when the header is changed, in memory only, to:
The only difference is the single space after the semicolon, immediately before the folded line break.
No body changes, DNS changes, replacement signatures, or other header changes are required.
The relevant connection contained exactly one SMTP MAIL transaction.
After reconstructing the TCP streams and parsing the binary milter frames, the Content-Type value passed from Postfix to the milter was:
The space is present at this point.
The milter then returned:
This deletes the existing header.
Later in the same response stream, it returned:
With this value:
The space has disappeared.
The milter deleted and re-added multiple headers during this transaction. It also issued a body-replacement operation, although that operation alone does not establish that the body content changed.
The \n sequences shown above are the line breaks observed inside the milter header values. The stored-message comparison uses reconstructed SMTP \r\n line endings.
For the same test Message-ID on those two ports, Content-Type already lacked the space when entering Amavis and retained the same format on output.
Therefore, Amavis was not the component removing that space in that test.
The later milter capture located the change more precisely: Postfix supplied the space to psa-pc-remote, and the milter requested that the header be re-added without it.
The incoming SMTP milter is:
The listener on that port was confirmed to be psa-pc-remote.
The Plesk handler list includes the incoming DKIM verification handler followed by the DMARC handler in the before-local queue. No obvious custom header-rewriting handler was found.
For the historical message examined, restoring that one space is sufficient for both DKIM signatures to validate.
I have not yet identified whether the normalization occurs in the milter’s message reconstruction code or in a specific Plesk mail handler. I have also not established when this behavior started, so I cannot attribute it to a particular Plesk update.
No mail configuration changes, service restarts, or handler disabling have been performed during the investigation.
Packet captures are available privately, but they may contain sensitive email data and will not be posted publicly.
Thank you.
I am investigating an issue where incoming emails from Zoho Campaigns consistently end up in the Spam folder on a Plesk server.
After checking DNS, verifying the signatures locally, and capturing the Postfix–milter communication, I traced a whitespace change to the processing performed by psa-pc-remote or one of the mail handlers it invokes.
Has anyone encountered this behavior, or is there a known bug or supported fix?
Environment
Code:
OS: AlmaLinux 8.10 x86_64
Plesk Obsidian: 18.0.82.1
plesk-mail-pc-driver:
18.0-2.redhat.8+p18.0.82.0+t261015.0936.x86_64
psa-mail-driver-common:
18.0-2.redhat.8+p18.0.82.0+t261015.0936.x86_64
Postfix:
3.5.28-2.redhat.8+p18.0.82.0+t261013.0859.x86_64
sendmail-milter:
8.15.2-34.el8.x86_64
Incoming mail follows this pipeline:
Code:
External SMTP sender
↓
Postfix / Plesk milter (psa-pc-remote, TCP 12768)
↓
Amavis (TCP 10024)
↓
Postfix reinjection (TCP 10025)
↓
Plesk DKIM and DMARC delivery handlers
↓
Dovecot
Symptoms
Affected messages contain two DKIM signatures: one for the sender’s domain and one for a Zoho domain. Both use:
Code:
a=rsa-sha256
c=simple/simple
On the VPS, both signatures fail:
DKIM verification (...) failed: signature verification failedSPF passes for the Zoho envelope sender domain, but it is not aligned with the visible From domain. As a result, the DKIM failure also causes DMARC to fail:
Code:
align_dkim=fail
align_spf=fail
spfres=pass
dkimres=fail
dmarccheck=DMARC_POLICY_QUARANTINE
Dovecot subsequently records:
stored mail into mailbox 'INBOX.Spam'In the original message investigated, Amavis allowed the message through a whitelist, but the subsequent Plesk DMARC handler still caused delivery to Spam.
A separate recipient copy of the same campaign, delivered directly from Zoho to Microsoft 365 without passing through this VPS, passed both DKIM signatures and DMARC. This was a different recipient-specific message, not an identical copy of the VPS message.
Local cryptographic verification
For an affected message stored on the VPS:- Both calculated DKIM body hashes match the corresponding bh= values.
- The sender-domain DKIM TXT record matches between the local resolver, a public resolver, and both authoritative DNS servers. The Zoho DKIM record also matches between the local and public resolvers.
- Both signatures fail when verifying the stored message.
- Restoring exactly one space in the folded Content-Type header makes both original signatures pass using Mail:
KIM.
b'Content-Type: multipart/alternative;\r\n\tboundary="..."'Both signatures validate when the header is changed, in memory only, to:
b'Content-Type: multipart/alternative; \r\n\tboundary="..."'The only difference is the single space after the semicolon, immediately before the folded line break.
No body changes, DNS changes, replacement signatures, or other header changes are required.
Packet capture: the space is present when Postfix calls the milter
I captured the loopback TCP conversation between Postfix and psa-pc-remote on port 12768 during a new Zoho Campaigns test delivery.The relevant connection contained exactly one SMTP MAIL transaction.
After reconstructing the TCP streams and parsing the binary milter frames, the Content-Type value passed from Postfix to the milter was:
b'multipart/alternative; \n\tboundary="..."'The space is present at this point.
The milter then returned:
Code:
Command: SMFIR_CHGHEADER ('m')
Header: Content-Type
Index: 1
Value: empty
This deletes the existing header.
Later in the same response stream, it returned:
Code:
Command: SMFIR_ADDHEADER ('h')
Header: Content-Type
With this value:
b'multipart/alternative;\n\tboundary="..."'The space has disappeared.
The milter deleted and re-added multiple headers during this transaction. It also issued a body-replacement operation, although that operation alone does not establish that the body content changed.
The \n sequences shown above are the line breaks observed inside the milter header values. The stored-message comparison uses reconstructed SMTP \r\n line endings.
Amavis input/output comparison
In a preceding test delivery, I also captured the message on:
Code:
TCP 10024: Postfix → Amavis
TCP 10025: Amavis → Postfix
For the same test Message-ID on those two ports, Content-Type already lacked the space when entering Amavis and retained the same format on output.
Therefore, Amavis was not the component removing that space in that test.
The later milter capture located the change more precisely: Postfix supplied the space to psa-pc-remote, and the milter requested that the header be re-added without it.
Relevant configuration checks
The following global settings are empty:
Code:
header_checks =
body_checks =
milter_header_checks =
smtp_header_checks =
smtp_mime_header_checks =
smtp_nested_header_checks =
smtp_body_checks =
canonical_maps =
sender_canonical_maps =
smtp_generic_maps =
The incoming SMTP milter is:
smtpd_milters = , inet:127.0.0.1:12768The listener on that port was confirmed to be psa-pc-remote.
The Plesk handler list includes the incoming DKIM verification handler followed by the DMARC handler in the before-local queue. No obvious custom header-rewriting handler was found.
Current conclusion
The captures show that the space is removed during processing by psa-pc-remote and/or a mail handler it invokes.For the historical message examined, restoring that one space is sufficient for both DKIM signatures to validate.
I have not yet identified whether the normalization occurs in the milter’s message reconstruction code or in a specific Plesk mail handler. I have also not established when this behavior started, so I cannot attribute it to a particular Plesk update.
No mail configuration changes, service restarts, or handler disabling have been performed during the investigation.
Questions
- Is this whitespace normalization a known issue in psa-pc-remote or the Plesk mail-handler framework?
- Is there an existing bug ID, supported hotfix, or release that addresses it?
- Is there a supported way to preserve the original whitespace in signed headers without disabling the Plesk milter or globally weakening SPF, DKIM, or DMARC checks?
- If further diagnostics are needed, which targeted logs or traces would help identify the responsible internal component?
Packet captures are available privately, but they may contain sensitive email data and will not be posted publicly.
Thank you.