IPv6 "only" DNS64/NAT64 & NAT46/SIIT-DC support?

Your question

I’m in the process of configuring my setup, and if I can configure the firewall for IPv6 only rules, I’ll be as happy as a pig in mud, so the questions:

  1. anybody else testing IPv6 only deployments?

  2. what gotchas do I need to be prepared for?

The assumption would be that the Stalwart instances are all on an IPv6 public range behind a firewall. I’d prefer to have it IPv6-only, but I don’t yet have a DNS64/NAT64 setup, so I’ll be “stuck” today with private (RFC1918) IPs on the stalwart instances that will be NAT44 to typically the firewall’s outside IP, while using NAT46/SIIT-DC to get inbound “native” IPv6 only.

  1. What gotchas in the above do I need to be aware of?

  2. How does Stalwart handle IPv6 vs IPv4 for whitelisting/etc.?

  3. Anything else I need to take into consideration?

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

Here’s what the docs say on each of your questions:


1 & 2 – IPv6-only deployments

Stalwart natively supports IPv6. Listeners can be bound to IPv6-only addresses (e.g. [2001:db8::1]:25) or dual-stack via [::]:25. There are no known IPv6-specific caveats called out in the documentation. The server treats IPv6 and IPv4 addresses uniformly throughout.

3 – NAT44 + NAT46/SIIT-DC topology

The most important gotcha here is client IP visibility. When Stalwart sits behind any form of NAT or proxy, it will see the NAT device’s IP rather than the real client’s IP. This breaks:

  • SPF/DMARC sender authentication (relies on the actual remote IP)
  • Auto-banning and rate-limiting (the NAT IP gets banned instead of the actual bad actor, potentially locking out all users)
  • Accurate logging

The fix is to use the Proxy Protocol (HAProxy protocol) so that the real client IP is forwarded through. Stalwart has first-class support for this on all listeners (SMTP, IMAP, POP3, HTTP). For inbound HTTP specifically, useXForwarded: true on the HTTP listener is an alternative when the proxy sends X-Forwarded-For headers. See the Proxy Protocol docs and the reverse proxy overview.

4 – IP allowlisting/blocklisting

Both BlockedIp and AllowedIp records accept a single IP address or a CIDR range (e.g. 2001:db8::/32), so they work equally well with IPv6 prefixes. AllowedIp entries bypass rate limits and auto-banning; BlockedIp entries reject at connection time. Both can be managed from the WebUI or CLI, and both support an optional expiry (expiresAt). The loopback addresses 127.0.0.1 and ::1 are allowed by default. See the General server settings docs.

5 – Outbound delivery

When Stalwart has multiple local addresses (e.g. both an RFC1918 IPv4 and a public IPv6), the kernel will pick one arbitrarily unless you explicitly configure sourceIps in an MtaConnectionStrategy. You should list exactly which source IPs to use for outbound SMTP, with matching ehloHostname values per address, to ensure correct EHLO advertisement and avoid deliverability issues. See the Connection strategy docs.

One further thing to be aware of: the DNS resolver caches A and AAAA records separately (dnsIpv4 / dnsIpv6 cache entries). If your DNS64 setup synthesizes AAAA records for IPv4-only destinations, Stalwart will use those synthesized addresses transparently – but this depends on your NAT64 infrastructure functioning correctly; Stalwart itself has no built-in DNS64/NAT64 awareness.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

SIIT-DC do encode the IPV4 address using the NAT64 translation scheme, just in reverse. ie. using the well-known prefix 64:ff9b::/96 as defined in RFC6052 for NAT64 translation, 192.168.1.100 gets translated to 64:ff9b::c0a8:164 (See IPv6 NAT64 Translator | Networking Toolbox for a working examples)

Would Stalwart developers consider handling these cases “correctly” for SPF/etc. matching?

Plain IPv6-only is fine; Stalwart binds and receives on IPv6 like any
modern server and AAAA-based SPF (ip6: mechanisms) works the same as ip4.
The specific NAT64 / SIIT-DC case (an IPv4 client arriving at Stalwart as
64:ff9b::) is not decoded back to v4 anywhere. SPF
evaluation in mail-auth will check that v6 source against ip6: mechanisms
only, not the embedded ip4: ones, so senders behind sending-side NAT64
may fail SPF when they shouldn’t, and IP allow-lists / spam-filter rules
that key on the raw remote address will see the v6 representation.

The cleanest workaround if you control the inbound boundary is to put a
PROXY-protocol-aware front-end (HAProxy or similar) between the SIIT-DC
translation and Stalwart, with PROXY protocol enabled on Stalwart’s
listeners. The front-end then forwards the original IPv4 to Stalwart
explicitly via PROXY, and SPF / allow-list rules see the real IPv4.