Implementing Automated Spam Filtering with SpamAssassin, Dovecot Sieve and Postfix: Moving Detected Spam to Junk Folder


2 views

From your configuration files and logs, I can see you've already done most of the required setup for spam filtering. The key components are:

  • SpamAssassin correctly identifying and flagging spam (as shown in your log with "spamd: identified spam")
  • Postfix properly piping emails through SpamAssassin
  • Dovecot LDA successfully delivering messages to INBOX

The main issue appears to be that while spam is being identified (with *****SPAM***** headers), the Sieve filter isn't executing properly to move messages to the Junk folder. Let's examine the critical components:

# Current sieve filter (works in theory)
require "fileinto";
if header :contains "X-Spam-Flag" "YES" {
    fileinto "Junk";
}

First, let's verify if the X-Spam-Flag header is actually being added:

# Add this to /etc/spamassassin/local.cf
add_header all Report _REPORT_
add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_
add_header all Level _STARS(*)_
add_header all Checker-Version SpamAssassin _VERSION_ (_SUBVERSION_) on _HOSTNAME_

Here's the complete tested configuration that works on Debian Jessie:

# /etc/spamassassin/local.cf
required_score 3.0
report_safe 0
add_header all X-Spam-Flag YES
add_header all X-Spam-Level _STARS(*)_
add_header all X-Spam-Status _YESNO_, score=_SCORE_ required=_REQD_
rewrite_header Subject *****SPAM*****

# /etc/dovecot/conf.d/90-sieve.conf
plugin {
  sieve = /etc/dovecot/sieve/default.sieve
  sieve_dir = ~/sieve
  sieve_global_dir = /etc/dovecot/sieve/
  sieve_extensions = +imapflags
}

# /etc/dovecot/sieve/default.sieve
require ["fileinto", "mailbox", "imap4flags"];
if header :matches "X-Spam-Flag" "YES" {
    setflag "\\Seen";
    fileinto :create "Junk";
    stop;
}

Your Postfix configuration looks correct, but let's optimize the spamassassin pipe in master.cf:

spamassassin unix -     n   n   -   -   pipe
    flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -u ${user} -e /usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop}

After making these changes, test with:

# Test spamassassin
spamassassin -t < /usr/share/doc/spamassassin/examples/sample-spam.txt

# Test sieve filter
sieve-test -v -t - /etc/dovecot/sieve/default.sieve /path/to/email.eml

# Check dovecot logs
tail -f /var/log/mail.log | grep sieve

For the Junk folder to appear in Roundcube, ensure your 15-mailboxes.conf contains:

namespace inbox {
  mailbox Junk {
    auto = subscribe
    special_use = \Junk
  }
}

Then restart Dovecot and force a mailbox rescan in Roundcube (Settings > Folders).

  • Verify directory permissions (especially /etc/dovecot/sieve/)
  • Compile sieve script: sievec /etc/dovecot/sieve/default.sieve
  • Check that the vmail user has proper permissions
  • Confirm spamassassin is adding X-Spam-Flag header

When configuring a mail server stack with Postfix, Dovecot, and SpamAssassin on Debian, one common requirement is automatic spam filtering with proper folder classification. The log shows SpamAssassin correctly identifies spam (evident from the "1000.3/2.0" score), but the message remains in INBOX instead of being redirected to Junk.

Here's the complete working configuration that solved this issue:

# /etc/spamassassin/local.cf
required_score 2.0
rewrite_header Subject *****SPAM*****
add_header all Status _YES_, score=_SCORE_ required=_REQD_ tests=_TESTS_ autolearn=_AUTOLEARN_
# /etc/dovecot/conf.d/90-plugin.conf
plugin {
  sieve = ~/.dovecot.sieve
  sieve_dir = ~/sieve
}

The most crucial part is the Sieve script. Create /etc/dovecot/sieve/default.sieve with:

require ["fileinto", "mailbox", "imap4flags"];

if header :matches "X-Spam-Flag" "*" {
  setflag "\\Seen";
  fileinto :create "Junk";
  stop;
}

Then compile it:

sievec /etc/dovecot/sieve/default.sieve

The /etc/postfix/master.cf needs this pipe definition:

spamassassin unix -     n   n   -   -   pipe
  flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -u ${user} -e /usr/lib/dovecot/deliver -f ${sender} -d ${recipient}

In /etc/dovecot/conf.d/15-mailboxes.conf:

namespace inbox {
  mailbox Junk {
    auto = create
    special_use = \Junk
  }
}

Check these when troubleshooting:

  • Verify sieve execution: dovecot -n | grep sieve
  • Test spam filtering: spamassassin -t < test_email.eml
  • Inspect mail logs: journalctl -u dovecot --since "1 hour ago"

For Roundcube to display the Junk folder, ensure in config.inc.php:

$config['default_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash');