VacationResponse bypasses dkimSignDomain and signs with the hostname domain

Issue Description

On a server hosting multiple domains, JMAP VacationResponse auto-replies are DKIM-signed with the domain of the server hostname instead of the domain in the From: header, breaking DMARC alignment. Server hostname is mx.example.net; the account sends as [email protected], a second hosted domain with p=reject. Active, published DKIM signatures exist for both domains.

dkimSignDomain on SenderAuth is at its default: is_local_domain(sender_domain) && !is_empty(authenticated_as) → sender_domain, else false.

Expected Behavior

A vacation response has no authenticated session, so authenticated_as is empty and the expression evaluates to false — the message should not be signed at all. If it is signed, the signature should use the From: domain (example.com), for which valid signatures are configured.

Actual Behavior

The message is signed regardless, and with the hostname domain:

From: [email protected]
Message-ID: [random]@localhost
DKIM-Signature: v=1; a=ed25519-sha256; s=v1-ed25519-[hostname-selector]; d=example.net
DKIM-Signature: v=1; a=rsa-sha256;     s=v1-rsa-[hostname-selector];     d=example.net

Regular submissions from the same account are signed correctly with d=example.com and carry Message-ID: [random]@example.com — verified for both SMTP submission and JMAP. So this code path appears to bypass dkimSignDomain entirely rather than honouring it, and to build the response without the account’s sending identity in scope.

Receivers report dkim=pass [email protected] alongside header.from=example.com, i.e. DKIM is not aligned. DMARC only passes because the envelope sender is aligned and SPF holds; on any forwarding path the auto-reply is rejected.

Reproduction Steps

  1. Host two domains on one server; the hostname belongs to the first domain.
  2. Create and publish DKIM signatures for both domains.
  3. Leave dkimSignDomain at its default.
  4. Enable a JMAP VacationResponse on an account sending as the second domain.
  5. Send a message to that account from an external provider and inspect the auto-reply headers.

Reproduced against two independent receiving providers.

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

These auto-replies also use the account address as envelope sender instead of an empty one (RFC 3834 §5), which has already triggered an NDR from a remote system. Worth fixing together — correcting only the envelope sender would move SPF evaluation to the HELO identity, which is equally unaligned, and every auto-reply from a p=reject domain would then be rejected outright instead of only on forwarding paths.

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

v0.16.21 will have its own dkimSignDomain expression for the untrusted sieve interpreter. In the meantime you can use if_: "source == 'autogenerated' && is_local_domain(sender_domain)", then_: "sender_domain", else_: "system('domain')" in dkimSignDomain.

On 0.16.19 the suggested workaround is rejected on save, both as an additional condition and as the only one:

“Error parsing ‘if’ expression in condition #N: Variable “source” not allowed in this context”

Is source meant to be available in the SenderAuth dkimSignDomain context on this version, or does the workaround only apply once the dedicated Sieve expression lands in 0.16.21?

In that case wait for v0.16.21 which will include a dkimSignDomain in SieveUserInterpreter.

Thanks for the quick confirmation — I’ll wait for 0.16.21 then.