Skip to content

About

check e-mails on a remote IMAP server for spam locally and sort spam e-mails into Junk folder on the IMAP server

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

11 Commits

Folders and files

Repository files navigation

IMAP local spamcheck

Introduction

This project exits to fight spam e-mails. My e-mail provider does a horrible job keeping my inbox clean, and I only have imap and web-access to it, so I cannot install software via an ssh-shell. But I have my own little linux server, so I decided to fight spam myself.

What you need

  1. IMAP access to an e-mail account
  2. a server running linux, to which you have ssh access and root access (sudo), a raspberry will do. It doesn't need to be reachable from the outside, a server in your intranet will do.

Basic idea

This project provides IMAP-based spam filtering for mail accounts where you typically don't have server access (IMAP only) and the built-in spam filter simply isn't good enough.

Unlike server-side software localspamcheck is not involved in mail delivery or mail keeping, it runs as a mail-user-agent on a completely different machine to where your mailbox lives. So it is a tool to tidy your IMAP mailbox without having to temper with it.

The localspamcheck-software of this project continuously watches 2 folders from an e-mail account on the IMAP-server, INBOX and Junk, via IMAP IDLE, classifies new mail using a local rspamd instance, and automatically learns from manual corrections you make in your MUA (e.g. Thunderbird) — move a message from INBOX to Junk by hand and it gets trained as spam; move a message from Junk back to INBOX and it gets trained as ham.

The localspamcheck-software running on your own localspamcheck-server can be used on as many independent e-mail accounts on as many independent IMAP servers as you like, it keeps rspamd spam/ham databases separate.

Why this exists (my motivation)

  • My mail provider only offers webmail/IMAP, no SSH access to the mail server.
  • The server-side spam filter runs without a learning component (e.g. SpamAssassin without Bayes training) and delivers mediocre results.
  • A self-hosted satellite mail server (Postfix/Dovecot and an SMTP relay in front) with SpamAssassin and ClamAV worked, but turned out to be more fragile than it should be — relayhost configurations tend to break whenever the provider changes something, so I wanted a simpler solution that doesn't break anything should it crash or become unavailable.
  • isbg (IMAP Spam Begone) does more or less the right thing, but seems abandoned and has no IMAP IDLE support — cron/polling only.

This project is deliberately not part of the delivery chain: it's a plain IMAP client, just like a MUA. No SMTP, no relay, no MTA integration — which makes it structurally more robust against provider quirks, simply because it never touches mail delivery at all.

Architecture

                  ┌───────────────────────────────────────────────────┐
                  │  your own Linux host:                             │
                  │  imapfilter to intreact with imap-mailboxes       │
                  │  rspamd + Redis/Valkey (Bayes stats)              │
                  │                                                   │
Provider A: IMAP ─┼─> watch-inbox@account1 ==> rspamc check for spam ─┼─> IMAP move to junk
                                                    + learn ham       │
                  │   watch-junk@account1  ==> rspamc to lern spam    │
                  │   SQLite DB (to keep track of scanned mails)      │
                  │                                                   │
Provider B: IMAP ─┼─> watch-inbox@account2 ==> rspamc ────────────────┼─> IMAP move to junk
                  │   watch-junk@account2  ==> rspamc                 │
                  │   SQLite DB                                       │
                  └───────────────────────────────────────────────────┘

Each mail account runs two independent, long-lived processes (systemd template instances):

  • watch-inbox — idles on INBOX. New mail is sent to rspamd for classification; anything classified as spam is moved to Junk. If a message reappears that the local database previously marked as spam, the user evidently moved it back → it gets trained as ham.
  • watch-junk — idles on Junk. If a message shows up there that was not moved in by our own watch-inbox process, the user must have filed it manually → it gets trained as spam.

The "do we already know this message, and as what" logic is keyed on the Message-ID header, stored in a small per-account SQLite database — not on the IMAP UID, since IMAP always assigns a new UID when a message is moved between folders.

Features

  • IMAP-IDLE based, no cron-polling delay
  • Local rspamd instance as classifier (Bayes, fuzzy network, SPF/DKIM/DMARC — all usable without any MTA integration)
  • Learns automatically from manual INBOX <==> Junk moves in your MUA
  • Multiple accounts across different providers in parallel, cleanly isolated via systemd template units
  • e-mails classified as ham stay in your INBOX, so other e-mail programmes work as before
  • e-mails classified as spam are moved to your Junk folder
  • a bulk-training script is provided for initial training of the Bayes bootstrap using collection of existing ham/spam e-mails (eg in existing arbitrary (sub)folders of your mail account)
  • simple configuraiton with a very short config-file
  • nothing needs to be compiled, everything runs from stock software
  • a few library files with helper functions must be written (provided) and put to the right folders

Requirements

  • A Linux host with root access (tested on bare Debian 13/trixie) (not the mail server itself — pure IMAP client operation)
  • Packages: imapfilter, sqlite3, rspamd, curl, gnupg
  • IMAP credentials for the accounts to be watched
  • A Junk folder per account (name may differ by provider, existing folder can be used)

Installation

Detailed step-by-step guide: docs/installation.md, which serves as as extended readme.

Quick overview, minimal steps:

# install packages
apt install imapfilter sqlite3 rspamd redis-server git

# clone the repository
git clone https://github.com/Sulla00/imaplocalspamcheck.git
cd imaplocalspamcheck

# put project files into right places
cp usr/local/lib/imapfilter/* /usr/local/lib/imapfilter/
cp etc/systemd/*.service      /etc/systemd/system/
cp etc/rspamd/local.d/*       /etc/rspamd/local.d/

# set up an account
cp etc/imapfilter/accounts/a.b-at-c.d /etc/imapfilter/accounts/a.b-at-c.d
$EDITOR /etc/imapfilter/accounts/a.b-at-c.d   # fill in credentials, folder-definitions

systemctl daemon-reload
systemctl restart rspamd
systemctl enable --now imapfilter-inbox@a.b-at-c.d.service
systemctl enable --now imapfilter-junk@a.b-at-c.d.service

Project structure

localspamcheck/
├─ README.md
├─ LICENSE
├─ usr/
│  └─ local/
│     └─ lib/
│        └─ imapfilter
│           ├─ common.lua          # shared Lua functions (rspamc, SQLite)
│           ├─ watch-inbox.lua     # classifies new emails as ham or spam and manually moved mails from junk as ham
│           ├─ watch-junk.lua      # classifies manually placed e-mails as spam
│           └─ train-ham.sh        # bulk training from any IMAP folder
├─ etc/
│  └─ imapfilter/
│     └─ accounts/
│        └─ a.b-at-c.d             # IMAP account config file, fill with your credentials and folder definitions
├─ etc/
│  └─ systemd/
│     ├─ imapfilter-inbox@.service # service file to call imapfilter with script watch-inbox.lua
│     └─ imapfilter-junk@.service  # service file to call imapfilter with script watch-junk.lua
├─ etc/
│  └─ rspamd/
│     └─ local.d/
│        ├─ worker-normal.inc      # config for rspamd
│        ├─ worker-controller.inc  # config for rspamd
│        ├─ redis.conf             # config for rspamd
│        └─ classifier-bayes.conf  # config for rspamd
└─ docs/
   └─ installation.md              # detailed setup guide and extended readme

Known limitations

  • Double notification: IMAPlocalspamcheck is an IMAP-client (MUA) and not a mail-transfer-agent (MTA), i.e. it doesn't interfere with mail delivery but watches the inbox for new e-mails being delivered using imap-idle. Other active MUAs (like opened desktop e-mail clients) using imap-idle as well will be notified of the new message simultaneously. As a consequence, when imaplocalspamcheck moves a message to junk all other active MUAs will still notify the user of the new message even though the message is already gone from the inbox (and put into the junk folder). That's the trade-off for not needing server access and not interfering with mail delivery.
  • No access to the real SMTP envelope: The local rspamd receives messages after (not during) delivery via IMAP. Thus it can't use the same information as server-side software (eg Received: header for SPF/IP reputation checks). Thus, IMAPlocalspamcheck will be a wee bit weaker than software with proper MTA integration, but that's the trade-off for not needing server access.
  • Message-ID dependency: The (rare) messages without a Message-ID header can't be deduplicated or reliably trained.
  • Provider-dependent IMAP IDLE stability: Some providers drop IDLE connections earlier than the IMAP standard suggests (RFC guidance is a 30-minute client-side refresh). The watchers catch dropped connections and reconnect automatically — with some providers this still happens occasionally, but it's harmless since no data is lost (the SQLite DB keeps track of processed e-mails).
  • No multi-tenant security model: The setup assumes a single trusted administrator on the local server who will be able to read all plain-text passwords from all configured e-mail accounts.
  • No package or installer script is provided. You need to copy the files and deal with file permissions manually, but its not difficult.

License

GPL-2.0, see LICENSE.

Disclaimer

This project was coded with the help of Claude-AI, as I myself would - by far - not have been able to write the requird lua files and libraries. But I checked all code suggestions of Claude-AI.

Contributing

Issues and pull requests welcome — experience reports from other mail providers (IDLE timeout behavior, folder naming conventions) are especially useful, since this project has only been tested against a limited number of providers so far.

About

check e-mails on a remote IMAP server for spam locally and sort spam e-mails into Junk folder on the IMAP server

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages