Subjectmilter: A postfix milter to reject bad words in a subject

Achtung! Dieser Artikel ist älter als ein Jahr. Der Inhalt ist möglicherweise nicht mehr aktuell!

TL;DR: SpamAssassing is an ancient, bloated software. I like go and wrote my own milter.

Background story

I’m running my own mailserver for a couple of years now and hardly received any e-mail spam. Of course it was quickly discovered and spammers tried to relay over it which is of course blocked. A year and a half ago I started to receive a few spam mails, because I posted my e-mail address unprotected on this blog. That was pretty stupid.
Fighting them off was easy and could be done by the postfix’s built in header_checks function. I’ve quickly added the option header_checks = regexp:/etc/postfix/header_checks to my postfix config and created the file for it:

/^Subject:.*Partnership with NordVPN.*/		REJECT Fuck off
/^Subject:.*Scanned image.*/		REJECT Fuck off
/^Subject:.*Bitcoin Welle.*/		REJECT Fuck off
/^Subject:.*Nagelpilz.*/		REJECT Fuck off

This worked flawless except for NordVPN. They were pretty persistent and tried it over and over again. At least for a couple of months. No my inbox is flodded with with e-mail subjects like:

Dozens of e-mails a day! I’ve added them to the file but to my surprise they were not blocked. After inspecting the raw content of such an email I found out that the subject is not in clear text anymore. They looked like this: =?utf-8?B?SV9JbnN0YWNoZWF0IFJlcXVlc3QgYXVzc3RlaGVuZA==?=. They are now RFC 2047 and base64 encoded and after searching the internet to get postfix to decode them I found out that there is no such mechanism built in. All pointed out that you should use SpamAssassin.

After having a look at SpamAssasin I discovered that it’s an ancient, bloated piece of software written in perl. A whole new complexity layer I did not want to run or learn. Postifx and SpamAssassin talk over the milter API so there was my point for attack: Writing my own simple milter application.


I’m a golang fan and I’ve found a maintained milter library for it. I copied the sample code into a project, threw out a whole bunch of unneeded stuff and adapted it until it did what I want: Wait for the subject header, decoded the value to plain text, perform a string compare against some bad words or sentences and return a response to postifx:

The source of bad words or sentences to match against is a simple text file which is read line by line. You can find my project on GitHub (🖇️ 🔐) . Here is an example logoutput from subjectmilter itself:

Feb 12 08:37:06 subjectmilter[101466]: Subject to analyze: "I_InstaFuck-Anforderung ausstehend"
Feb 12 08:37:06 subjectmilter[101466]: Bad string "I_InstaFuck" detected. Fuck off sent!
Feb 12 08:40:37 subjectmilter[101466]: Subject to analyze: "I_Instacheat Request ausstehend"
Feb 12 08:40:37 subjectmilter[101466]: Bad string "I_Instacheat" detected. Fuck off sent!
Feb 12 08:43:47 subjectmilter[101466]: Subject to analyze: "Ich habe die Nachricht über die Beziehung vermisst"
Feb 12 08:43:47 subjectmilter[101466]: Bad string "Ich habe die Nachricht über die Beziehung vermisst" detected. Fuck off sent!
Feb 12 08:52:38 subjectmilter[101466]: Subject to analyze: "I_InstaFuck-Anforderung ausstehend"
Feb 12 08:52:38 subjectmilter[101466]: Bad string "I_InstaFuck" detected. Fuck off sent!

And here is the log output from postfix when the milter rejects a message:

Feb 12 02:44:21 postfix/cleanup[156219]: 0736E6472306: milter-reject: END-OF-MESSAGE from[]: Fuck off; from=<redacted> to=<redacted> proto=ESMTP helo=<>

At this moment I’m spam free again. Hopefully it will stay like this. See the GitHub project for the souce code, installation instructions and systemd service file. Keep in mind that there is no need to run this application as root.

Du hast einen Kommentar, einen Wunsch oder eine Verbesserung? Schreib mir doch eine E-Mail! Die Infos dazu stehen hier.

🖇️ = Link zu anderer Webseite
🔐 = Webseite nutzt HTTPS (verschlüsselter Transportweg)