Let’s do Dovecot slowly and properly – Part 4: Filtering with Sieve

Sieve is a filtering plugin for Dovecot’s LMTP service. Filtering applies general rules to incoming mail: Move this here, mark this read, flag this as spam, etc. Simple enough but there is plenty potential for misundertanding, so let me break it down.

Dovecot, much like Postfix, is a master process that runs various services that listens for different inputs. Mainly it runs an IMAP service on port 143 (and encrypted on 993) In my last post on Dovecot I also made it run an LMTP service that took over postman duties from Postfix, i.e. saving mail to the correct folder. That service can be augmented by the addition of plugins, like Sieve. The plugin then takes scripts to tell it how to work.

That’s a lot of layers but because Sieve is just a plugin to an existing service, it takes very little to set it up. After the arduous process of setting up Rspamd, it’s worth spelling out that Sieve does not require a new daemon, processes, users or anything like that. It’s just an installation package and a configuration twist. At least until managesieve comes into play but that is optional.

Installation and configuration

Using Debian 10 as my base, all I need to add to my existing Dovecot setup is to apt-get install a single package, dovecot-sieve. After that I add the configuration file 20-lmtp.conf (seeing as I haven’t already got one because LMTP has just been running on defaults):

protocol lmtp {
  mail_plugins = $mail_plugins sieve
}

Which just activates the plugin. The LMTP service is presumed to be set up and running from last post.

Sieve script

There aren’t many configuration options with regards to Sieve – how you use it is in the scripts that you make. Those that I have uncovered deal with the scripts as files rather than their contents: Where are they placed, how many can you have, how much disk space they are allowed to take up, etc. My recommendation: Go with the defaults and everything will work unless you are a script superuser.

I can easily picture a situation where a lot of work has gone into crafting scripts so it’s obviously important to know where they are for backup and moving. By default the script location is in a directory under the user’s mail home. As detailed in the first Dovecot post that is set by these parameters:

mail_home = /var/vmail/%d/%n
mail_location = maildir:~/maildir

Left to the defaults and those settings, all scripts will be taken from the file /var/vmail/%d/%n/.dovecot.sieve. Where %d is replaced with the domain name and %n is replaced with the username without domain name. If the email itself is located somewhere sensible and backuppable, so are the scripts.

So what does a script look like? Sieve script is fairly readable. This is the (entire) contents of my .dovecot.sieve file and I doubt I need to explain much about what it does:

require ["fileinto"];

if address :is "from" "my@othermailaccount.com"
{
    fileinto "test";
}

An important thing, especially if you plan on copy-pasting other people’s scripts is the require line. This refers to extensions that extend Sieve with new conditions or actions. Yes, fileinto is an extension to a plugin to a service to a process to a mail server. Hey, apparently it works. Some like fileinto appear to be shipped with Sieve while others may require separate installation. Information is somewhat sparse. Basic stuff like moving around or forwarding based on senders, subjects and recipients should work out of the box. I found a decent introduction to the language but I’m really going to skip forward to ManageSieve that allows me to construct filters using a GUI.

Aliases and filtering

Before I move on to that topic, I want to remind the reader of a relevant postfix setting that I covered earlier: virtual_alias_maps. Virtual alias maps work just like aliases in a local setup: from the outside it looks like a normal email address, but internally it gets redirected to another account. It’s perfect for secondary and throwaway accounts – and for filtering. Filtering something based on the recipient – iamnotasmurfandiresenttheimplication@example.com – is often a lot easier than the sender. Before trying complicated, multiconditional filtering rules, maybe just use an alias when signing up to something?

ManageSieve

I rarely mind doing anything from the command line but I can see the advantage to adding filtering rules straight from the mail client – because I usually think of the need for rules when I’m parsing mail at the client, not while on SSH to the server. ManageSieve allows me to create scripts in a nice UI in my client of choice – webmail, Thunderbird, Evolution etc. – and leave it the client to upload them to the service. It also allows me not to actually bother learning the Sieve script language in detail which is handy because I’ll only rarely need to add scripts.

ManageSieve is not another plugin but a separate service. Luckily it runs under Dovecot similar to LMTP. If any and all email clients need access to managing the Sieve scripts, it does require opening up a port straight to the internet. And because Sieve comes with the power to move mail around, auto-delete and auto-forward, it is a huge security risk. And it does feel a bit daunting to open it up in this way.

One halfway house solution would be to run a webmail client on the same machine/docker network and just allow local connections. However, ManageSieve follows whatever authentication procedure is already in place. That means that options like ssl=yes and disable_plaintext_auth=yes apply here, too. Seeing as I trust in the security of that setup, I guess there is no reason not to trust it with ensuring Sieve as well.

As with LMTP all I need to do in order to run ManageSieve is to install a single package (dovecot-managesieved on Debian/Ubuntu) and add the service. As with LMTP I prefer to do this in the 10-master.conf file by adding a service section:

service managesieve-login {
  inet_listener sieve {
    port = 4190
  }
  # Number of connections to handle before starting a new process. Typically
# the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0
  # is faster. <doc/wiki/LoginProcess.txt>
  service_count = 1
}

Other guides mention the need to add sieve to a list of protocols. I didn’t have a protocol setting in my setup but doveconf showed me that it was auto-generated:

$ doveconf | grep protocols
protocols = " imap lmtp"

After adding the service section for managesieve-login and restarting, Dovecot had added sieve as a protocol all by itself.

If I do not enable any kind of encryption in my client setup for Sieve, it appears that it (the client) simply does not attempt any kind of authentication and is rejected. Here’s what the dovecot logs say:

Aug 20 14:17:49 managesieve-login: Info: Disconnected (no auth attempts in 0 secs): user=<>, rip=10.0.4.1, lip=10.0.4.3, sessio
n=<kGjNG06tdMMKAAQB>

Telnet’ting into port 4190 and it appears that STARTTLS is what is expected:

Escape character is '^]'.
"IMPLEMENTATION" "Dovecot (Debian) Pigeonhole"
"SIEVE" "fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"
"NOTIFY" "mailto"
"SASL" ""
"STARTTLS"
"VERSION" "1.0"
OK "Dovecot (Debian) ready."

And switching to STARTTLS in the client does indeed succeed and I’m now allowed to upload scripts. Seeing as I don’t use STARTTLS for IMAPS but straight SSL/TLS on port 993, this is somewhat surprising. Setting the client to use SSL/TLS, i.e. a connection that assumes encryption staightaway without first establishing an unencrypted channel, gives me “SSL error” in the client and a similar rejection message in the logs to that produced by no authentication. STARTTLS it is, then.

One little niggle abot script storage. In the section above I stuck with the simple, default one-script-file-per-user setup and fed it a single test script. Now, I have not tested this but I believe that ManageSieve itself is perfectly happy to pick this up and carry on with it. The client, I used, the webmail project Rainloop, however, see things differently. The test script does not appear in my list of user scripts when I go into the Rainloop scripts/filters section. But Rainloop was clearly aware of it because it has moved it to the newly created sieve folder in my user’s maildir and replaced the .dovecot.sieve file with a symlink to it’s own script file, containing the new Rainloop created test scripts, in the same folder. This is neither a complaint nor necessarily a general issue. I simply bring it up to point out that this is an area that is less standardized than other aspects of email, so be aware that not everything may be compaible with everything else. The Rainloop script file itself, while generated by way of the Web UI, is perfectly readable and editable as ordinary sieve script.

I have skipped testing so far but the all important question is of course: Does it work? Yes, it does. Here is a quick password reset email from Blizzard that runs into a rule that moves it to an IMAP folder (created in advance, of course) named “stest”.

Aug 20 14:44:33 lmtp(____@brokkr.net)<213><OTPoEDFwPl/VAAAAz/Yw1w>: Debug: sieve: Executing script from `______/.dovecot.sieve'                                                                                                  
Aug 20 14:44:33 lmtp(____@brokkr.net)<213><OTPoEDFwPl/VAAAAz/Yw1w>: Debug: Mailbox stest: Mailbox opened because: lib-lda deliv
ery                                                                                                                            
Aug 20 14:44:33 lmtp(____@brokkr.net)<213><OTPoEDFwPl/VAAAAz/Yw1w>: Info: sieve: msgid=<227172648.10847.1597927461633@ams1b-eu-
bn-ac-nexus-notification-prod-blue-00.ams1b.cloud.blizzard.net>: stored mail into mailbox 'stest'

Leave a Reply