Skip to main content

Filtering Google Groups Spam with Sieve

·530 words·3 mins
Table of Contents

Lately, more and more spam mails have been showing up in my inbox disguised as legitimate business inquiries (“Inquiry”, “Request for quote”, “Anfrage zu Ihrem Produkt”) and sent through a Google Group. Technically they come from googlegroups.com infrastructure, with valid SPF and DKIM signed by Google, so reputation-based filters tend to let them through. The giveaway sits in the headers: List-Id and List-Unsubscribe point at googlegroups.com, and there is an X-Google-Group-Id header. A real customer inquiry sent to a business address never carries those - which is exactly what the Sieve filter below targets.

Heads-up: the filter blocks every mail that travels through Google Groups. If you rely on a Google Group as a mailing list, don’t deploy it as-is - you won’t get any of those messages anymore. But honestly, who still uses Google Groups these days?

Examples and paths refer to Uberspace . The Sieve syntax itself (RFC 5228 ) is standardised and works on any mail server with Sieve support; only the paths and the name of the spam folder may differ.

Requirements
#

  • Uberspace account with an email address set up
  • SSH access to the Uberspace
  • Sieve is enabled by default on Uberspace via Pigeonhole

Creating the Filter
#

Create a new Sieve file on the Uberspace, for example at ~/users/<mailbox>/sieve/block_googlegroups.sieve (replace <mailbox> with the mailbox name, e.g. info):

require ["fileinto"];

# Block Google Groups / Google Mailinglists
if anyof (
  header :contains "List-Id" "googlegroups.com",
  header :contains "List-Unsubscribe" "googlegroups.com",
  exists "X-Google-Group-Id"
) {
  fileinto "Spam";
  stop;
}

The filter checks three headers typical for Google Groups messages and moves matches into the Spam folder. stop makes sure no further Sieve rules run afterwards.

To activate the filter, include it in the mailbox’s active Sieve script. On Uberspace that is usually ~/users/<mailbox>/.dovecot.sieve.

Testing the Filter
#

Before going live, run a dry test with sieve-test and a sample message.

Place a test mail at ~/tmp/testmail.eml:

From: spam@example.com
To: you@example.org
Subject: Test
List-Id: <bhh.example.com>
List-Unsubscribe: <https://groups.google.com/...>
X-Google-Group-Id: 408114700878

Testmail

Then run sieve-test with the envelope set:

ENVELOPE_SENDER="spammer@example.com"
ENVELOPE_RECIPIENT="info@example.org"
TEST_MAILBOX="info"
EMAIL_FILE="$HOME/tmp/testmail.eml"

sieve-test -D -t - \
  -f "$ENVELOPE_SENDER" \
  -a "$ENVELOPE_RECIPIENT" \
  -l "$HOME/users/$TEST_MAILBOX" \
  "$HOME/users/$TEST_MAILBOX/.dovecot.sieve" \
  "$EMAIL_FILE"

If the filter matches, the output contains:

9: fileinto action
9:   store message in mailbox `Spam'
...
Performed actions:

* store message in folder: Spam

Implicit keep:

  (none)

Info: final result: success

store message in folder: Spam together with a missing Implicit keep confirms that the message is filed only into Spam.

Verifying the Spam Folder Name
#

The folder targeted by fileinto has to match the IMAP folder name exactly. On Uberspace it is Spam by default. IMAP can confirm that:

openssl s_client -crlf -connect xyz.uberspace.de:993

After the TLS handshake:

a1 LOGIN "info@example.org" "PASSWORD"
a2 LIST "" "*"
* LIST (\HasNoChildren \UnMarked \Junk) "." Spam
* LIST (\HasNoChildren \UnMarked \Sent) "." Sent
* LIST (\HasNoChildren \UnMarked \Trash) "." Trash

The \Junk flag marks the spam folder. On other servers it might be Junk, INBOX.Spam or similar; adjust the fileinto line accordingly.

Acknowledgments
#


Found this helpful?
Consider supporting via: