|new 0.0.2||Oct 11, 2020|
|0.0.1||Aug 31, 2020|
#98 in Email
This software is in the initial development stage. Feedback from early adopters is welcome, but note that this is under construction and actively being worked on. Production use is not yet recommended.
SPF Milter is a milter application that verifies email senders using the Sender Policy Framework (SPF) protocol. It can be integrated with milter-capable MTAs to check and enforce authorisation published as SPF policy in the DNS.
SPF Milter closely adheres to the rules and recommendations of the SPF
specification, RFC 7208. By design, SPF verification proceeds in the
recommended manner: The first, optional step is to verify a client’s HELO
identity (ie, the domain name given with the SMTP
HELO command). If this step
is not done or is not conclusive, then the client’s MAIL FROM identity (ie,
the reverse-path or envelope sender given with the SMTP
Within the constraints of the specification, SPF Milter exposes highly flexible configuration options. Configuration toggles and settings for the verification procedure, result handling, SMTP reply, header fields, and more, support a broad range of use cases. SPF Milter is capable of in-flight configuration reloading, so that no restart is necessary for configuration changes.
In terms of implementation, SPF Milter is essentially a configuration interface to an SPF verifier integrated in the milter protocol. The SPF implementation is provided by the viaspf library, and the DNS implementation is provided by the domain library. We believe these to be a solid foundation for SPF milter software.
(not yet published)
SPF Milter is a Rust project. It can be built and/or installed using Cargo in the usual way. For example, use the following command to install the latest version published on crates.io:
cargo install --locked spf-milter
As a milter, SPF Milter requires the libmilter C library to be available. For
example, on Debian and Ubuntu one needs to install the
If your distribution does not provide pkg-config metadata for libmilter, try
using the provided
milter.pc file: Run any Cargo command with the directory
milter.pc added to the pkg-config path:
PKG_CONFIG_PATH=/path/to/dir cargo build
The domain library requires the OpenSSL library and development files. On Debian
and Ubuntu, install the package
The minimum supported Rust version is 1.45.0.
Once installed, SPF Milter can be invoked on the command-line as
This program reads configuration parameters from the default configuration file
/etc/spf-milter.conf. At a minimum, the mandatory
socket parameter must be
specified in that file. See the included
spf-milter.conf for a sample
spf-milter causes SPF Milter to read the configuration and start the
milter. Send a termination signal to the process or press Control-C to shut the
To set up SPF Milter as a system service, try using the provided
spf-milter.service systemd service. Install this file in
/etc/systemd/system, then start and enable the service.
SPF Milter logs status messages to syslog. By default, warnings and errors are logged, as well as the verification result for each verified identity.
SPF Milter is configured primarily by setting configuration parameters in the
/etc/spf-milter.conf. Parameters come with sensible default
settings, and SPF Milter can be used right away by specifying just the mandatory
The included man page spf-milter.conf(5) currently serves as the reference
documentation. (Tip: You can view the man page without installing by giving the
Configuration can be reloaded during operation by sending the signal SIGUSR1 to the milter process. Refer to the man page for details.
Before integrating SPF Milter in your mail server setup, consult at least the documentation for the parameters presented in brief below.
socket parameter controls where SPF Milter opens a listening
socket for the connection from the MTA. This parameter accepts a socket
specification of the form
inet:port@host for a TCP socket, or
a UNIX domain socket. The socket specification is passed to the milter library
The main configurable facet of the verification procedure is whether the HELO
identity is verified or not: this is controlled with the boolean parameter
verify_helo. Thus there are two possible verification procedures:
- MAIL FROM only: verify MAIL FROM identity and treat result as final outcome
- HELO and MAIL FROM: verify HELO identity; if the result is definitive, treat it as the final outcome; if it is not definitive then verify MAIL FROM identity and treat that result as final outcome
Which HELO identity results are definitive can be controlled with
Negative authorisation results and error results may be rejected at the SMTP
level. The selection of results can be controlled with
When a definitive result has been reached, and the result is one to be rejected then it will be rejected with a transient or permanent SMTP error reply.
The SMTP reply code and reply text, and the enhanced status code can be
specified for each SPF result. For example, for the fail result, use
The type of header field to be added can be configured using the
parameter. The values
Authentication-Results each select
the header field of the same name. The default
Received-SPF header records SPF
verification parameters in full detail, whereas the
header only records the result and sender domain. Compare:
Received-SPF: pass (mail.gluet.ch: domain of email@example.com has authorized host 126.96.36.199) receiver=mail.gluet.ch; client-ip=188.8.131.52; helo=mxout1-he-de.apache.org; envelope-from="firstname.lastname@example.org"; identity=mailfrom; mechanism="include:_spf.apache.org"
Authentication-Results: mail.gluet.ch; spf=pass smtp.mailfrom=spamassassin.apache.org
To make the above information more practical, this section presents two common example use cases: ‘Standard’ SPF, and SPF as part of DMARC.
The standard, RFC-compliant SPF verification use case is well covered by SPF
Milter’s default configuration settings. Therefore, just setting the mandatory
socket parameter and leaving all other parameters at their default is enough
to configure a standard SPF verification use case.
socket = inet:3000@localhost
Let us do a brief walkthrough of the default behaviour. SPF Milter will first
verify the HELO identity (
verify_helo = yes). If the HELO identity cannot be
evaluated to one of the definitive authorisation results – pass or fail –,
next the MAIL FROM identity is verified. Which HELO verification result is
treated as definitive is configurable through the parameter
The final result from either identity will then be enforced as suggested in RFC
7208. That is, the results fail, temperror, and permerror will be rejected
with an appropriate permanent or transient SMTP error reply, and for all other
results a header line recording the result is added to the message. The
specifics of rejection at the SMTP level can be adjusted using the parameter
reject_results and a number of parameters for each SPF result.
By default, a header field of type
Received-SPF is used. The parameter for
configuring the header field type is named
softfail as a failing result. A very strict setup may treat
softfail with the same severity as a
fail result. To implement this, the
softfail result needs to be added to the definitive HELO results and also to
the results that get rejected.
definitive_helo_results = pass, fail, softfail reject_results = fail, softfail, temperror, permerror
Enable both header fields. SPF Milter supports both of the specified header fields, and one might want to have them both added to messages. The configuration is straightforward. Note that in the RFC there is cautionary advice that care must be taken to ensure that both headers convey the same result; of course, SPF Milter ensures that already.
header = Received-SPF, Authentication-Results
SPF may also be used as part of a DMARC verification setup. Domain-based Message Authentication, Reporting, and Conformance (DMARC) is specified in RFC 7489.
socket = inet:3000@localhost verify_helo = no reject_results = header = Authentication-Results
DMARC will use an SPF result as an input for its own validation procedure. As shown, a few adjustments are necessary for a minimal DMARC setup.
First, DMARC does not use HELO identity verification, therefore this step should
be turned off by disabling
verify_helo. Only the MAIL FROM identity is
Second, one might want to delegate authorisation to DMARC, and not do any
rejection during SPF verification. Though depending on requirements other
approaches may make sense. The above configuration disables rejection with
reject_results = (the empty set), thus always letting the milter add a header
line to messages and ultimately delegate the authorisation decision to a
subsequent DMARC component.
The final parameter
header changes the header type from the default
Authentication-Results, since this is the header field that
DMARC relies on.
Reject failing HELO identity early. When SPF is set up for DMARC, it is
still possible to enable HELO identity verification. In this variant, we set up
HELO identity verification, deferring all HELO results except the result
We then reject this result, but only for the HELO identity. The resulting
behaviour is just like above, with the difference that a HELO identity which
fail is rejected right away, before the usual
Authentication-Results header field is added.
verify_helo = yes definitive_helo_results = fail reject_helo_results = fail reject_results =
Copyright © 2020 David Bürgin
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.