Sendmail/Postfix Milters in Python
by Jim Niemira
Stuart D. Gathman
|Mascot by students of Christian Hafner||
See the FAQ | Download now |
Sendmail introduced a new API beginning with version 8.10 - libmilter. Sendmail 8.12 officially released libmilter. Version 8.12 seems to be more robust, and includes new privilege separation features to enhance security. Even better, sendmail 8.13 supports socket maps, which makes pysrs much more efficient and secure. Sendmail 8.14 finally supports modifying MAIL FROM via the milter API, and a data callback allowing spam to be rejected before beginning the DATA phase (even after accepting some recipients).
You may be required to register your user nickname (nick) and identify with that nick. Otherwise, you may not be able to join or be heard on the IRC channel. There is a page describing how to register your nick at freenode.net.
SESlibrary, a sendmail socketmap daemon implementing SRS, and (Real Soon Now) an srsmilter daemon implementing SRS, now that sendmail-8.14 supports CHGFROM and this is supported in pymilter-0.9.
spfmodule, a well tested implementation of the of the SPF protocol, which is useful for detecting email forgery.
gossiplibrary and server daemon for the GOSSiP protocol, which exchanges reputation of qualified domains. (Qualified in the milter package means that example.com:PASS tracks a different reputation than example.com:NEUTRAL.)
DNSlibrary for python DNS lookups. It is much smaller and lighter than the more capable (and bigger) dnspython library. Low level lookups are needed to find SPF and MX records for instance.
At the lowest level, the
milter module provides a thin wrapper around the
sendmail libmilter API. This API lets you register callbacks for
a number of events in the
process of sendmail receiving a message via SMTP.
These events include the initial connection from a MTA,
the envelope sender and recipients, the top level mail headers, and
the message body. There are options to mangle all of these components
of the message as it passes through the milter.
At the next level, the
Milter module (note the case difference)
provides a Python friendly object oriented wrapper for the low level API. To
use the Milter module, an application registers a 'factory' to create an object
for each connection from a MTA to sendmail. These connection objects
must provide methods corresponding to the libmilter callback events.
Each event method returns a code to tell sendmail whether to proceed with processing the message. This is a big advantage of milters over other mail filtering systems. Unwanted mail can be stopped in its tracks at the earliest possible point.
Milter.Milter class provides default implementations for event
do nothing, and also provides wrappers for the libmilter methods to mutate
mime module provides a wrapper for the Python email package that
fixes some bugs, and simplifies modifying selected parts of a MIME message.
Finally, the bms.py application is both a sample of how to use the Milter and spf modules, and the beginnings of a general purpose SPAM filtering, wiretapping, SPF checking, and Win32 virus protecting milter. It can make use of the pysrs package when available for SRS/SES checking and the pydspam package for Bayesian content filtering. SPF checking requires pydns. Configuration documentation is currently included as comments in the sample config file for the bms.py milter. See also the HOWTO and Milter Log Message Tags.
Python milter is under GPL. The authors can probably be convinced to change this to LGPL if needed.
The Python milter package includes a sample milter that replaces dangerous
attachments with a warning message, discards mail addressed to
MAILER-DAEMON, and demonstrates several SPAM abatement strategies.
The MimeMessage class to do this used to be based on the
multifile standard python packages.
As of milter version 0.6.0, it is based on the email standard
python packages, which were derived from the
The MimeMessage class patches several bugs in the email package,
and provides some backward compatibility.
The "defang" function of the sample milter was inspired by MIMEDefang, a Perl milter with flexible attachment processing options. The latest version of MIMEDefang uses an apache style process pool to avoid reloading the Perl interpreter for each message. This makes it fast enough for production without using Perl threading.
mailchecker is a Python project to provide flexible attachment processing for mail. I will be looking at plugging mailchecker into a milter.
TMDA is a Python project to require confirmation the first time someone tries to send to your mailbox. This would be a nice feature to have in a milter.
There is also a Milter community website where milter software and gory details of the API are discussed.
For example, the HTML parsing feature to remove scripts from HTML attachments is rather CPU intensive in pure python. Using the C replacement for sgmllib greatly speeds things up.
[wiretap]section. This is not by user, however.
To Bcc a message, call
self.add_recipient(rcpt) in envfrom after
determining whether you want to copy (e.g. whether the sender is local). For
def envfrom(... ... if len(t) == 2: self.rejectvirus = t in reject_virus_from if t in wiretap_users.get(t,()): self.add_recipient(wiretap_dest) if t == 'mydomain.com': self.add_recipient('<copy-%s>' % t) ...
To make this a generic feature requires thinking about how the configuration would look. Feel free to make specific suggestions about config file entries. Be sure to handle both Bcc and file copies, and designating what mail should be copied. How should "outgoing" be defined? Implementing it is easy once the configuration is designed.