Features
PhoenixDKIM is a DKIM signing and verifying milter built around modern
cryptography, safe defaults, and verifiable builds. The highlights below are
the features that matter most when you run it.
Cryptography |
Key management |
Safe defaults |
Observability |
Extensible |
Reproducible builds
- OpenSSL 3 (and LibreSSL) — all cryptography uses the
modern EVP high-level API.
- Ed25519 — signing and verification per RFC 8463,
alongside RSA.
- Dual signing — sign a single message with both Ed25519
and RSA in one pass, so you can adopt Ed25519 while RSA verifiers catch
up.
- Many key backends — flat files, LMDB, Redis, an
http:/https: service, and HashiCorp Vault
(vault:), all interchangeable.
- Zero-downtime key rotation — a Vault secret can list
several currently-valid selectors, and PhoenixDKIM signs with all of them at
once (old + new, RSA + Ed25519) across the overlap window, so a rollover needs
no flag day. The secret layout matches Rspamd's, so the same store works in
both.
- No weak RSA-SHA1 — an RSA-SHA1 signature is never treated
as valid (reported
dkim=neutral, never dkim=pass)
per RFC 8301, with On-WeakAlgorithm choosing only the message
disposition.
- 2048-bit minimum RSA keys — a deliberate choice; RFC 8301
permits 1024.
- Hardened by default — compiled with modern protections
(
_FORTIFY_SOURCE, stack-protector and stack-clash protection, a
non-executable stack, and Intel CET where the hardware supports it).
- DNSSEC-aware key verification — a passing signature whose
key record is not DNSSEC-protected can be downgraded or rejected
(
UnprotectedKey), a control most DKIM implementations don't
expose. It works with the stock validating resolver via the reply's AD bit, so
libunbound is not required. A missing AD bit is treated as
ambiguous rather than "insecure": before penalising a signature,
PhoenixDKIM runs a DNSSECProbe (modelled on
Postfix's dnssec_probe) to
confirm the resolver actually validates, logs the disposition it applies, and
suppresses the penalty when validation can't be confirmed — so a
non-validating resolver never silently fails every message.
- Built-in metrics — a Prometheus
text-file exporter and a StatsD
UDP pusher, both dependency-free and off until you configure them. Counts of
messages, signatures (by result and algorithm), verifications (by RFC 8601
result), and DNS queries, plus a DNS-latency histogram. An OpenTelemetry
Collector can ingest either, so the numbers reach Grafana, Datadog, and the
rest without a native exporter in the daemon.
- Per-message summary line — one structured
key=value log entry per message (action, result, domain,
algorithm), the human-readable companion to the counters and a natural feed
for log-based tooling.
- No new attack surface — metrics are scraped from a file
(via the Prometheus node_exporter text-file collector) or pushed over UDP; the
milter opens no metrics listener of its own.
- Lua 5.4 scripting — drive signing and verifying policy
from Lua, updated from the older Lua 5.1.
- Runs where you do — UNIX-like systems; tested by the
author on Linux and BSD.
- The source tarball and the Debian package build bit-for-bit identical,
so anyone can rebuild from source and confirm the published binaries
match.