Eric Radman : a Journal

OpenSMTPD Mail Filtering

The title of this post should be "Spam Filtering on OpenSMTPD", but I didn't want to use the word "spam" on my home page. Running an e-mail server is a challenging and low-reward task, so we need to keep this as simple as we possibly can. These days is I classify mail using SpamAssasin, which will require three packages


spamassassin is the classification engine, spampd connects to the spamassassin daemon and forwards the mail to an MTA, and Procmail writes the messages in the mbox format.

SpamAssassin & SpamPD

Filtering mail with OpenSMTPd is most easily accomplished by running a SpamAssassin proxy that we can forward mail to. The SpamPD relay will add X-Spam- headers to each message and forwarded them back to localhost on the port that we specify

# rc.conf.local
spampd_flags="--port=10025 --relayhost= --tagall --log-rules-hit --maxsize=2048"

These SpamPD options only need to match the MTA configuration. Setting --maxsize allows messages with largish attachments to be processed. --tagall and --log-rules-hit are not required, but useful for diagnostics.

After routing through spampd each message will contain headers with the evaluation. This is spam that did not reach the configured threshold

X-Spam-Level: ***
X-Spam-Status: No, score=3.0 required=4.0 tests=BAD_CREDIT,DKIM_SIGNED,

And this is a message that was not spam

X-Spam-Status: No, score=-2.2 required=4.0 tests=BAYES_00,DKIM_SIGNED,
        RCVD_IN_MSPIKE_H2,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.1


Now that each message is annotated with a score we can optionally route it using Procmail, which can be configured per user with $HOME/.procmailrc or per site with /etc/procmailrc. The following rules will cause suspect mail to be delivered to an mbox named "spam"

* ^X-Spam-Flag: YES

OpenSMTPD Routing

The following adds routing rules to forward incoming mail to the mail proxy for evaluation

# smtpd.conf
listen on localhost port 25
listen on egress port 25
listen on localhost port 10028 tag DKIM
listen on localhost port 10026 tag SPAMD

table aliases db:/etc/mail/aliases.db

accept tagged SPAMD from any for domain "" deliver to mda "procmail -f -"
accept from any for domain "" relay via smtp://
accept for local alias <aliases> deliver to mbox

accept tagged DKIM for any relay
accept from local for any relay via smtp://

Incoming mail on port 25 is not tagged, which matches the rule relay via smtp:// After SpamAssassin grades the mail, SpamPD relays it back to OpenSMTPD on port 10026 where it matches the tag SPAMD and is handed over to procmail for delivery.

Failure Conditions for SpamPD

1. If spampd corrupts it's own bayes database, stop the daemon, and remove everything under /var/spampd/.spamassassin/

2. If spampd encounters trouble creating lockfiles, raise open-files-cur in /etc/login.conf

3. SpamPD uses a lot of RAM, you will need to provision nearly 500MB for this task alone

$ ps -U _spampd -o rss | awk '{sum += $1} END {print "RSS for _spamd", sum/1024 "MB"}'
RSS for _spamd 329.785MB

4. If spamassassin is not yet running spamd may panic and begin pegging the server CPU. Start spamd late in the boot process