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
Cryptography
- 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.
Key management
- 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.
Safe defaults
- No weak RSA-SHA1 — an RSA-SHA1 signature is never treated
as valid (reported
dkim=neutral, neverdkim=pass) per RFC 8301, withOn-WeakAlgorithmchoosing 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 aDNSSECProbe(modelled on Postfix'sdnssec_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.
Observability
- 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=valuelog 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.
Extensible
- 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.
Reproducible builds
- The source tarball and the Debian package build bit-for-bit identical, so anyone can rebuild from source and confirm the published binaries match.