Tags: #OpenBSD #security #Dovecot #Sieve #ManageSieve #quota
OpenBSD: Dovecot with Sieve, ManageSieve and Quota
The installation and configuration of Sieve, ManageSieve and Quota support in Dovecot in OpenBSD…
Introduction
Sieve is a scripting language for filtering e-mail messages. It was originally developed by the CMU Cyrus Project and is currently specified in RFC5228. The Sieve filtering rules can be created by a either a graphical user interface (GUI)-based editor or using a text editor. The process of transferring the scripts to the (mail) server depends on its software.
Therefore, ManageSieve protocol was defined in RFC5804 in order to allow users to securely manage their script rules on a remote server (and also alert them to syntactically flawed scripts), where they are usually stored in a .sieve
file in user’s home directory.
ManageSieve in Dovecot
A minimal configuration is already present in Dovecot’s configuration. However, the sieve_deprecated
section can be commented as follows:
$ vi /etc/dovecot/conf.d/20-managesieve.conf
protocols = $protocols sieve
service managesieve-login {
inet_listener sieve {
port = 4190
}
#inet_listener sieve_deprecated {
# port = 2000
#}
}
protocol sieve {
}
Sieve in Dovecot
Sieve support in Dovecot is not installed by default. The Pigeonhole project provides Sieve support as a plugin to Dovecot’s LDA and can be installed as follows:
$ pkg_add dovecot-pigeonhole
quirks-2.367 signed on 2017-10-03T11:21:28Z
dovecot-pigeonhole-0.4.20v0: ok
Note: Nowadays it is recommended to use LMTP instead of LDA. The main difference is that the LDA is a short-running process, started as a binary from command line, while LMTP is a long-running process started by Dovecot’s master process.
There are two Dovecot’s plugins implementing the Sieve processes:
Sieve
, which needs to be used for LMTP (or LDA) andIMAPSieve
, which only applies to IMAP (and not to LDA/LMTP).
All user defined Sieve scripts, that are managed by ManageSieve, will be stored in the virtual user’s home directory, i.e. in:
$ cd /var/vmail/domain.tld/USERNAME/sieve
Just one sieve script can be active per user and is automatically sym-linked to:
$ ls -lA /var/vmail/domain.tld/USERNAME/.dovecot.sieve
ManageSieve ensures that the existing .dovecot.sieve
file does not get overwritten. After a new sieve script is activated, the old one is backed up and moved to the sieve sub-folder.
External programs can be also used to filter or pipe (process) messages using executable scripts.
As previously noted, the LMTP is preferred and thus, enabled by default. It can be verified by:
$ grep ^protocols /etc/dovecot/dovecot.conf
protocols = imap lmtp
The Sieve
plugin needs to be enabled as follows:
$ vi /etc/dovecot/conf.d/20-lmtp.conf
protocol lmtp {
mail_plugins = $mail_plugins sieve
}
And the IMAPSieve
plugin as follows:
$ vi /etc/dovecot/conf.d/20-imap.conf
protocol imap {
mail_plugins = $mail_plugins imap_sieve
}
The default configuration of the Sieve
plugin is sufficient and looks as follows:
$ grep -vE "^[ \t]*$|^.*#" /etc/dovecot/conf.d/90-sieve.conf
plugin {
sieve = file:~/sieve;active=~/.dovecot.sieve
}
However, the IMAPSieve
plugin needs a bit of polishing (can be applied as is):
$ vi /etc/dovecot/conf.d/90-sieve.conf
plugin {
...
# Location of scripts which are uploaded through ManageSieve
sieve = file:~/sieve;active=~/.dovecot.sieve
...
### MY SETUP (ALMOST AT THE END OF THE FILE)
# Sieve plugin
# Global sieve scripts to run before and after processing ALL incoming mail
sieve_before = /etc/dovecot/sieve-before.d
sieve_after = /etc/dovecot/sieve-after.d
# Maximum size of a single sieve script per user
sieve_quota_max_storage = 50M
# IMAP Sieve plugin
sieve_plugins = sieve_imapsieve sieve_extprograms
# From elsewhere to Junk folder
imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY
imapsieve_mailbox1_before = file:/etc/dovecot/sieve/report-spam.sieve
# From Junk folder to elsewhere
imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Junk
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_before = file:/etc/dovecot/sieve/report-ham.sieve
sieve_global = /etc/dovecot/sieve
sieve_pipe_bin_dir = /etc/dovecot/sieve
sieve_global_extensions = +vnd.dovecot.pipe
}
Dovecot’s configuration can be verified by:
$ doveconf -n | head -n 1
# 2.2.32 (dfbe293d4): /etc/dovecot/dovecot.conf
and the new settings traditionally applied by:
$ rcctl restart dovecot
Sieve can be configured to automatically move e-mails considered as “spam” (or “ham”) using the IMAPSieve
plugin.
First, a Sieve directory needs to be created:
$ mkdir -p /etc/dovecot/sieve/
“Spam”-considered messages will be reported using the following Sieve script:
$ vi /etc/dovecot/sieve/report-spam.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
if environment :matches "imap.email" "*" {
set "email" "${1}";
}
pipe :copy "train-spam.sh" [ "${email}" ];
“Ham”-considered messages will be reported using the following Sieve script:
$ vi /etc/dovecot/sieve/report-ham.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
if environment :matches "imap.mailbox" "*" {
set "mailbox" "${1}";
}
if string "${mailbox}" "Trash" {
stop;
}
if environment :matches "imap.email" "*" {
set "email" "${1}";
}
pipe :copy "train-ham.sh" [ "${email}" ];
Since Dovecot doesn’t have write permission to the directory, the scripts needs to be compiled manually using the Pigeonhole’s sievec
script compiler as follows:
$ cd /etc/dovecot/sieve/
$ sievec report-spam.sieve
$ sievec report-ham.sieve
The previously defined Sieve scripts execute appropriate shell scripts. Considering that the e-mail content filter is performed by Rspamd and ClamAV, they can be created as follows:
$ echo "exec /usr/local/bin/rspamc -h localhost:11332 learn_spam" > /etc/dovecot/sieve/train-spam.sh
$ echo "exec /usr/local/bin/rspamc -h localhost:11332 learn_ham" > /etc/dovecot/sieve/train-ham.sh
$ chmod +x /etc/dovecot/sieve/train-{spam,ham}.sh
Beside users’ scripts, Sieve is able to utilise globally defined scripts. For this purpose, the following directories need to be created:
$ mkdir -p /etc/dovecot/{sieve-before.d,sieve-after.d}
In order to make Dovecot to automatically move e-mails tagged as spam by the Rspamd to the users’ Junk
folder (based on the X-Spam-Status
header), the the following global filter needs to be defined:
$ vi /etc/dovecot/sieve-before.d/10-rspamd.sieve
require ["fileinto"];
if header :is "X-Spam-Status" "Yes" {
fileinto "Junk";
stop;
}
Again, this script needs to be manually compiled:
$ cd /etc/dovecot/sieve-before.d
$ sievec 10-rspamd.sieve
Finally, the new settings can be applied by:
$ rcctl reload dovecot
Dovecot is now configured to retrain the Rspamd’s spam filter according the Junk folder operation. In other words, when users put messages into (or out of) the Junk folder, the rspamc
command action is triggered and logged into:
$ tail -f /var/log/rspamd/rspamd.log
The Sieve interface should be accessible via the TCP port 4190 and its availability can be verified using the telnet
command as follows (STARTTLS will be used if available, the session needs to be ended by <Ctrl>
+]
sequence):
$ telnet server.domain.tld 4190
Trying EGRESS_IP...
Connected to server.domain.tld.
Escape character is '^]'.
"IMPLEMENTATION" "Dovecot 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 imapsieve vnd.dovecot.imapsieve"
"NOTIFY" "mailto"
"SASL" "PLAIN"
"STARTTLS"
"VERSION" "1.0"
OK "Dovecot ready."
^]
telnet> quit
Connection closed.
Sieve Usage in Thunderbird
Mozilla Thunderbird (TB) does not come with Sieve support. Thomas Schmid created the Sieve extension. Once downloaded, it needs to be installed manually via TB’s menu:
Tools -> Add-ons -> "cog icon" on top -> Install Add-on From File...
The extension needs to be activated on a selected e-mail account as follows:
- via TB’s menu:
Edit -> Account Settings
(orTools -> Sieve Filter Settings
) - left column:
Sieve Settings
- right column:
Yes, manage Sieve scripts for this account.
The usage is straightforward via TB’s menu: Tools -> Sieve Message Filters
.
Considering (an example) that it is required to:
- move e-mails for
rua-dmarc@domain.tld
intoINBOX.rua-dmarc
folder, - more e-mails for
postmaster@domain.tld
intoINBOX.postmaster
and - keep the rest in
INBOX
,
a new Sieve script can be created as follows:
- right column:
New
Enter the name for your new Sieve script
: e.g. “mysieve”- and the new Sieve script can be created as follows:
require ["copy", "fileinto", "variables"];
if address :is "to" "rua-dmarc@domain.tld" {
fileinto "INBOX.rua-dmarc";
stop;
} elsif address :is "to" "postmaster@domain.tld" {
fileinto "INBOX.postmaster";
stop;
} elsif address :domain :is "from" "filtered-domain.tld" {
# forward message to another mailbox and keep the message in INBOX
redirect :copy "forwarding-mailbox@forwarding-domain.tld";
keep;
stop;
} else {
# the rest goes into INBOX ("implicit keep" by default, but this is explicit)
keep;
}
- confirmation:
Save
Close
In order to activate the previously created Sieve script, the following action needs to be performed:
- right column:
Activate
- the grey icon goes green
Verification can be done on the server by checking that the automatic symbolic link (symlink) appeared in the virtual user’s home directory:
$ ls -lA /var/vmail/domain.tld/USERNAME/ | grep .dovecot.sieve
lrwx------ 1 vmail vmail 19 Sep 23 12:34 .dovecot.sieve -> sieve/aliases.sieve
Note: Since the developer has not published the extension officialy in Mozilla Add-ons, it needs to be updated manually.
Vacation aka Out-of-Office
Due to the Pigeonhole Sieve, the vacation extension is available by default, but can be verified as follows:
$ doveconf -a | grep vacation
managesieve_sieve_capability = 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 imapsieve vnd.dovecot.imapsieve
In order to configure the extension, the original Sieve script needs to be updated with the following:
require ["variables", "vacation"];
# store old Subject line so it can be used in vacation message
if header :matches "Subject" "*" {
set "subjwas" ": ${1}";
}
vacation
:days 1
:subject "Out of office reply${subjwas}"
:addresses ["j.doe@domain.tld", "john.doe@domain.tld"]
"Dear Sender,
I'm out of office, please contact Joan Doe instead.
Best regards
John Doe";
The default behaviour is sending one reply per day and in order to disable the extension, the previously defined rules need to be removed or commented.
Quotas in Dovecot
Quota root is a concept from IMAP4 QUOTA extension, which was defined in RFC2087. In theory, there could exist different quota roots, e.g. “user quota” and “domain quota” roots.
The quota
plugin is installed in Dovecot by default, and it can be enabled as follows:
$ vi /etc/dovecot/conf.d/10-mail.conf
mail_plugins = $mail_plugins quota
The quota_imap
plugin can be enabled as follows:
$ vi /etc/dovecot/conf.d/20-imap.conf
protocol imap {
mail_plugins = $mail_plugins imap_quota
# or
mail_plugins = $mail_plugins imap_sieve imap_quota
}
Considering that Dovecot is set up according to OpenBSD: Dovecot, the quota limits are defined within the mailQuota
attribute in the ou=people,dc=domain,dc=tld
subtree. In order to load this parameter by Dovecot, the LDAP search string (user_attrs
) needs to be updated as follows:
$ vi /etc/dovecot/dovecot-ldap.conf.ext
### MY SETUP
hosts = server.domain.tld
tls = yes
auth_bind = yes
ldap_version = 3
dn = uid=dovecot,ou=services,dc=domain,dc=tld
dnpass = DOVECOT-PASSWORD
base = ou=people,dc=domain,dc=tld
user_filter = (&(objectClass=PostfixBookMailAccount)(uid=%n))
user_attrs = uid=user,mailStorageDirectory=home=/var/vmail/%$,mailQuota=quota_rule=*:bytes=%$
pass_filter = (&(objectClass=PostfixBookMailAccount)(uid=%n))
pass_attrs = uid=user
# http://wiki.dovecot.org/Authentication/PasswordSchemes
#default_pass_scheme = CRYPT
default_pass_scheme = SSHA # used to store passwords in LDAP
Let’s assume the following example:
- user has 512MB quota defined in LDAP (a 256MB fallback quota is applied when not in LDAP),
- when saving messages to
Trash
mailbox, users get an additional 100MB quota, which allowes them to “move to Trash” while deleting messages to get under their quota, - any messages in the
SPAM
folder are ignored and would not count against the quota.
In order to implement the previous example, the following needs to be done:
$ vi /etc/dovecot/conf.d/90-quota.conf
### MY SETUP AT THE END OF THE FILE
plugin {
quota = maildir:User quota
quota_rule = *:storage=256M
quota_rule2 = Trash:storage=+100M
quota_rule3 = SPAM:ignore
}
Alternatively: Instead of hard size over quota limit, percents can be used. Percents are relative to the default rule and the “%
” character needs to be written twice to escape it. An example configuration of an additional 10% space can be found as follows:
$ vi /etc/dovecot/conf.d/90-quota.conf
### MY SETUP AT THE END OF THE FILE
plugin {
quota = maildir:User quota
quota_rule = *:storage=256MB
# 10% of quota
quota_rule2 = Trash:storage=+10%%
quota_rule3 = Spam:ignore
}
Quota warnings can be configured in order to inform users that they exceeded a specific limit. Also, a “reverse warning”, which starts with “-
” character, can be configured when a quota drops below a specific value (e.g. user no longer over quota):
$ vi /etc/dovecot/conf.d/90-quota.conf
plugin {
quota_warning = storage=95%% quota-warning 95 %u
quota_warning2 = storage=80%% quota-warning 80 %u
quota_warning3 = -storage=100%% quota-warning below 100 %u # user is no longer over quota
}
service quota-warning {
executable = script /usr/local/bin/quota-warning.sh
user = vmail # an unprivileged user to execute the quota warnings
unix_listener quota-warning {
user = vmail
mode = 0666
}
}
The previous configuration defines the /usr/local/bin/quota-warning.sh
script within the executable
parameter. This shell script needs to be created manually and it can look like the following:
$ vi /usr/local/bin/quota-warning.sh
#!/bin/sh
PERCENT=$1
USER=$2
cat << EOF | /usr/local/libexec/dovecot/dovecot-lda -d $USER -o "plugin/quota=maildir:User quota:noenforcing"
From: postmaster@domain.tld
Subject: Quota Warning
Your mailbox is now $PERCENT% full.
EOF
The script needs to be executable, hence:
$ chmod +x /usr/local/bin/quota-warning.sh
And finally, Dovecot requires a restart to apply the quota settings:
$ rcctl restart dovecot
Verification of the quota setup can be performed by checking the current quota of a particular user using the doveadm
command as follows:
# doveadm -f tab quota get -u user1@domain.tld
Quota name Type Value Limit %
User quota STORAGE 51 524287 0
User quota MESSAGE 42 - 0
Should it be necessary, the quota limit can be manually recalculated for a particular user:
$ doveadm quota recalc -u user1@domain.tld