Setting up Dovecot with Fetchmail and SpamAssassin

Dovecot is a really cool imap server. If you, like me, have a lot of mail accounts around the internet, a bit of gmail there, some hotmail over here, and want to collect everything on your nice local home server, this is the howto to follow.

Fetchmail

First of all we need to configure Fetchmail to fetch our mail. Fetchmail can't handle multiple IMAP accounts with IDLE with a single Fetchmail running, so we need to run multiple Fetchmail instances, one for each IMAP account we want to monitor.
To do this we use this script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/bin/bash

# searches for subdirectories of $HOME/.fetchmail, and considers each as a
# directory to run fetcmail from. The config should be stored as "fetchmailrc"
# within this directory; without the leading ".". Networks can be started and
# stopped independently, or all together. See the --help option for detail on
# invoking the script.

FETCHMAILROOT=~/.fetchmail

cd "$FETCHMAILROOT"

ACTION="start"

while [ "$1" ]; do
ARG="$1"
case "$ARG" in
-k|--kill) ACTION="kill" ;;
-l|--list) ACTION="list" ;;
-h|--help) ACTION="help" ;;
--) shift; break ;;
-*) echo "Unknown argument $ARG"; exit 1 ;;
*) break ;;
esac
shift
done

WHICH="$1"

function find_fetchmail_homes() {
# Find directories immediately off FETCHMAILROOT
for ENT in *; do
if [ -d "$ENT" ]; then
echo "$ENT"
fi
done
}

case "$ACTION" in
help)
echo "$0: [options..] [network]"
echo "Options are:"
echo " -h, --help: This text"
echo " -k, --kill: Stop network"
echo " -l, --list: List networks, running or not"
echo "If a network is named, the action applies only to that network"
exit 0
;;
start)
for ENT in `find_fetchmail_homes`; do
if [ "$WHICH" -a "$ENT" != "$WHICH" ]; then continue; fi
export FETCHMAILHOME=`pwd`/$ENT

fetchmail $EXTRAOPTS #$ENT
done
;;
list)
for ENT in `find_fetchmail_homes`; do
if [ "$WHICH" -a "$ENT" != "$WHICH" ]; then continue; fi
export FETCHMAILHOME=`pwd`/$ENT
FETCHMAILPID=$FETCHMAILHOME/fetchmail.pid

echo "Config $ENT in $FETCHMAILHOME"
if [ -r $FETCHMAILPID ]; then
echo " running as PID `cat $FETCHMAILPID`"
else
echo " not running"
fi
done
;;
kill)
for ENT in `find_fetchmail_homes`; do
if [ "$WHICH" -a "$ENT" != "$WHICH" ]; then continue; fi
export FETCHMAILHOME=`pwd`/$ENT
FETCHMAILPID=$FETCHMAILHOME/fetchmail.pid

echo "Config $ENT in $FETCHMAILHOME"
if [ -r $FETCHMAILPID ]; then
fetchmail -q #$ENT
fi
done
;;
*)
echo "Ahh; unknown action $ACTION"
exit 1
;;
esac

We call it fetchmultimail.sh and save it somewhere, maybe in our ~/bin/fetchmultimail.sh.

The script is configured with fetchmailrc files, one for every account:

1
2
3
4
5
6
7
8
9
10
$ tree .fetchmail
.fetchmail
├── email1@gmail.com.INBOX@gmail.com
│   └── fetchmailrc
├── email2@outlook.com@imap-mail.outlook.com
│   └── fetchmailrc
└── email3@outlook.com@imap-mail.outlook.com
└── fetchmailrc

3 directories, 3 files

The fetchmailrc configuration file in those directories is a standard fetchmailrc file, and looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
set postmaster "postmaster"
set bouncemail
set no spambounce
set properties ""
set daemon 300
set invisible
set syslog


poll imap-mail.outlook.com protocol IMAP port 993
user 'email2@outlook.com' there with ssl with password 'verysecureandsecretpassword' folder 'INBOX' idle ssl
mda "{ echo X-Mailbox: email2@outlook.com; cat; } | spamc -s 5000000 | /usr/lib/dovecot/deliver -d s2"

So fetchmail fetches our mail from the IMAP server, and then delivers it to the mda. In this case, we

  • add a header to the mail, so we can sort it to the correct folder later, with { echo X-Mailbox: email2@outlook.com; cat; }
  • check for spam with SpamAssassin with spamc -s 5000000
  • and then send it to Dovecot with /usr/lib/dovecot/deliver -d s2, where s2 is the user we are delivering the mail to.

To run this script on startup the easy way, we can use crontab:

1
2
3
$ crontab -l
@reboot /home/s2/bin/fetchmultimail.sh >/dev/null
$

This runs fetchmultimal.sh after every server startup.

Dovecot

On the Dovecot side we now receive the mail, but we want to sort it to the correct folder using some custom rules. To do this, we can use Sieve.

So first of all, we install and configure Sieve for Dovecot. To do this follow the official guide, or one specific for your Linux distribution.

Once installed and configured Sieve, we can create a Sieve config file with the rules we want to have. A quick example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
require "fileinto";

#webcam alerts
if address :is ["From"] "webcamalerts@home.in.tern.al" {
fileinto "webcamalerts";
stop;
}

#spam
if header "X-Spam-Flag" "YES" {
fileinto "Junk";
stop;
}

#email1@gmail.com
if header "X-Mailbox" "email1@gmail.com" {
fileinto "email1@gmail_com";
stop;
}

#email2@outlook.com
if header "X-Mailbox" "email2@outlook.com" {
fileinto "email2@outlook.com";
stop;
}

#email3@outlook.com
if header "X-Mailbox" "email3@outlook.com" {
fileinto "email3@outlook.com";
stop;
}


fileinto "INBOX";

As you can see, we use the X-Mailbox header we set with Fetchmail to sort our mail. We could use the To header too, but if someone sends you an email where you are in the Bcc or the Cc filed, the To header field would be empty, so we use the custom X-Mailbox header we set by ourselves, so we are sure the mail arrived to that account, and gets put in the correct folder.

The end

So that was all. I skipped uninteresting stuff like how to install SpamAssassin and stuff like that, because it's Linux distribution dependent, and is very easy.

Hope the notes above helped someone!

avatar

Simons stuff