enforcing multiple per-mailbox quotas for shared mailboxes
Jost-Philip Matysik
jost-philip at matysik.it
Mon May 20 23:05:41 EEST 2019
Hi!
I'm trying to get quotas for shared mailboxes set up on my server. It's
not working, and I fail to understand why. Documentation for setup of
this complexity is rather scarce on the web, and the discussions I found
either don't directly apply or terminate with "I got it working" but
no explanation.
Can someone please help?
The setup is rather lengthy and complicated, so I'll try to give a
summary first for easier understanding.
All help is appreciated! Please respond if you need more info!
Thanks!
Best regards,
Jost
Basic setup (where am I coming from?):
======================================
- This is a Debian 9 system with a more recent version of dovecot
installed from https://repo.dovecot.org/ce-2.3-latest/debian/stretch
- users have individual mailboxes with individual quotas -- this works great!
- users can share mailboxes with other users through ACLs. To achieve
this, there exists a wildcarded namespace with
prefix="ZZZ_Freigaben/%%u" --this also works great!
==> up to here it's pretty much following the examples from the Wiki to
the letter.
Configuration goal (what do I want to achieve?):
================================================
- attach quotas to mailboxes, not logins
- when copying/moving mails across shared mailboxes during imap
sessions, enforce quota based on target mailbox, not logged-in user
doing the copying.
To clarify:
==> so if Bob has a quota of 500MB, all messages in Bob's mailbox
should count against (and only against!) Bob's quota, regardless of who
put them there.
==> if Alice has access to her own mailbox (directly), as well as Bob's
mailbox and Dave's mailbox (through sharing), she should see 3
individual quotas when logging in: her own quota (200MB) for everything
in her mailbox, bob's quota (300MB) for everything in Bob's mailbox, and
Dave's quota (10GB) for everything in Dave's mailbox. These 3 quotas
should be completely independant and neither block, nor override each
other.
Setup Idea (how I tried to get there):
======================================
- "quota=..." "quota_rule" and "quota_rule2" always refer to the user's
own mailbox (with an additional rule for Trash). Everyone has those,
so these are loaded statically from dovecot config file.
- "quota_rule" is overwritten from userdb with the user's individual
mailbox quota (the more I like you, the more space you get...)
- since the number of different additional quotas required per user
depends on how many mailboxes are shared with that user, individual
"quota2=...", "quota3=...", "quotaN=..." fields are dynamically
generated by the MySQL backend and loaded from userdb upon login.
- consequently, for each "quotaN=..." entry, a corresponding
"quotaN_rule=*:storage=XXX" is generated and returned from userdb
(substitute N=1,2,3,4,... accordingly)
Observations:
=============
1. enforcing quota for the user's personal mailbox works as expected,
both through IMAP and when delivering incoming mail
2. overriding "quota_rule=..." from userdb for the user's personal
mailbox works great. Individual quota is recognized and enforced both
through IMAP and when delivering incoming mail.
3. dynamically loading "quota2=...", "quota3=..." etc. from userdb
doesn't seem to work at all! I can see them being added as extra
fields in the logs upon user login, but the quota-plugin seems to
completely ignore them. They are not enforces, and tools like
'doveadm quota' list the userdb fields in the debug messages, but do
not interpret them in any way. They do not throw errors either, the
additional quota roots are just silently ignored.
4. the end result is that in an IMAP session the logged-in user's quota
is enforced for their individual mailbox, but as soon as they write
to someone else's mailbox (move a mail
to /ZZZFreigaben/bob/Some-Subfolder), no quota is enforced at all!
Additional debugging done:
==========================
if I hardcode a "quota2=" and/or "quota3=" in the config file, I can
observe the following:
- If I hardcode "quota2=count:some_name:ns=ZZZ_Freigaben/", dovecot and
doveadm will recognize the additional quota root. But since the folder
"ZZZ_Freigaben/" on its own isn't a mailbox (it's just a path
CONTAINING mailboxes), the quota is neither displayed in clients, nor
enforced. It has no real-word effect, other than 'doveadm quota'
showing an additional line.
- If I hardcode "quota2=count:other_name:ns=ZZZ_Freigaben/postmaster/"
in the config file (with that path being the real IMAP path to a
shared mailbox), dovecot will complain in the log saying
Error: quota: Unknown namespace: ZZZ_Freigaben/postmaster/
dovecot will start and work for the most part, but again completely
ignore settings for the additional quota.
- additionally, trying to override "quota2_rule=..." from userdb doesn't
work as it does with "quota_rule=..."! If I have both
"quota_rule=..." and "quota2_rule=..." in the config file as well as
returned from userdb (with different values), dovecot will use
"quota_rule" from userdb, but "quota2_rule" from config file. This
behavior seems inconsistent!
- I tried both maildir and count backends (seperately or mixed). Both
show the same behavior.
- I tried appending or removing an empty "ns=" to the first "quota=..."
entry (the wiki shows some examples with it, some without). Makes no
difference...
Configuration Dumps below (slightly redacted to remove complete email
addresses or IPs)
==========================
output of dovecot -n:
# 2.3.6 (7eab80676): /etc/dovecot/dovecot.conf
# Pigeonhole version 0.5.6 (92dc263a)
# OS: Linux 4.9.0-9-amd64 x86_64 Debian 9.9
# Hostname: [censored]
auth_failure_delay = 10 secs
auth_mechanisms = plain login
default_vsz_limit = 512 M
dict {
acl = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
}
listen = *
lmtp_rcpt_check_quota = yes
login_greeting = Mail system ready.
mail_gid = vmail
mail_location = maildir:~/Maildir
mail_plugins = " quota acl"
mail_uid = vmail
managesieve_notify_capability = mailto
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
namespace Freigaben {
list = children
location = maildir:%%h/Maildir:INDEXPVT=%h/Maildir/Freigaben/%%u
prefix = ZZZ_Freigaben/%%u/
separator = /
subscriptions = no
type = shared
}
namespace inbox {
inbox = yes
location =
mailbox Drafts {
auto = subscribe
special_use = \Drafts
}
mailbox Junk {
auto = subscribe
autoexpunge = 61 days
special_use = \Junk
}
mailbox Sent {
auto = subscribe
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
mailbox Trash {
auto = subscribe
autoexpunge = 61 days
special_use = \Trash
}
prefix =
separator = /
subscriptions = yes
type = private
}
passdb {
args = /etc/dovecot/dovecot-sql.conf.username
driver = sql
}
plugin {
acl = vfile:/etc/dovecot/dovecot-acl
acl_defaults_from_inbox = yes
acl_shared_dict = proxy::acl
imapsieve_mailbox1_before = file:/etc/dovecot/sieve/learn-spam.sieve
imapsieve_mailbox1_causes = COPY
imapsieve_mailbox1_name = Junk
imapsieve_mailbox2_before = file:/etc/dovecot/sieve/learn-ham.sieve
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_from = Junk
imapsieve_mailbox2_name = *
quota = count:User quota:ns=
quota_grace = 1%%
quota_rule = *:storage=5G
quota_rule2 = Trash:storage=+100M
quota_status_nouser = DUNNO
quota_status_overquota = 552 5.2.2 Mailbox is full
quota_status_success = DUNNO
quota_vsizes = yes
sieve = file:~/sieve;active=~/.dovecot.sieve
sieve_after = /etc/dovecot/sieve-after
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
sieve_max_script_size = 10M
sieve_pipe_bin_dir = /etc/dovecot/sieve
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_quota_max_storage = 50M
}
postmaster_address = postmaster at matysik-ingenieurwesen.de
protocols = " imap lmtp sieve"
service auth-worker {
user = vmail
}
service auth {
unix_listener /var/spool/postfix/private/auth {
group = postfix
mode = 0660
user = postfix
}
unix_listener auth-userdb {
mode = 0666
}
}
service dict {
unix_listener dict {
group = vmail
mode = 0660
user = dovecot
}
}
service imap-login {
inet_listener imap {
port = 0
}
inet_listener imaps {
address = [censored]
port = 993
ssl = yes
}
}
service imap {
process_limit = 1024
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
group = postfix
mode = 0600
user = postfix
}
}
service managesieve-login {
inet_listener sieve {
address = [censored]
port = 4190
}
service_count = 1
}
service managesieve {
process_limit = 64
}
ssl_cert =
</etc/letsencrypt/live/fullchain.pem
ssl_cipher_list =
ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
ssl_dh = # hidden, use -P to show it
ssl_key = # hidden, use -P to show it
ssl_min_protocol = TLSv1.2
ssl_options = no_compression
ssl_prefer_server_ciphers = yes
# comment: since imap logins differ from email-addresses I have 2
# userdb queries. One will return fields for %u='login', the other will
# return fields for %u='email at domain.tld'.
# no %u will ever make BOTH userdbs return rows (they are mutually
exclusive)
userdb {
args = /etc/dovecot/dovecot-sql.conf.email
driver = sql
}
userdb {
args = /etc/dovecot/dovecot-sql.conf.username
driver = sql
}
protocol lmtp {
mail_plugins = " quota acl sieve"
}
protocol imap {
mail_plugins = " quota acl imap_sieve imap_quota imap_acl"
}
example userdb output:
=======================
root at server:/# doveadm -Dv user jost
Debug: Loading modules from directory: /usr/lib/dovecot/modules
Debug: Module loaded: /usr/lib/dovecot/modules/lib01_acl_plugin.so
Debug: Module loaded: /usr/lib/dovecot/modules/lib10_quota_plugin.so
Debug: Loading modules from directory: /usr/lib/dovecot/modules/doveadm
Debug: Module loaded: /usr/lib/dovecot/modules/doveadm/lib10_doveadm_acl_plugin.so
Debug: Skipping module doveadm_expire_plugin, because dlopen() failed: /usr/lib/dovecot/modules/doveadm/lib10_doveadm_expire_plugin.so: undefined symbol: expire_set_deinit (this is usually intentional, so just ignore this message)
Debug: Module loaded: /usr/lib/dovecot/modules/doveadm/lib10_doveadm_quota_plugin.so
Debug: Module loaded: /usr/lib/dovecot/modules/doveadm/lib10_doveadm_sieve_plugin.so
Debug: Skipping module doveadm_fts_lucene_plugin, because dlopen() failed: /usr/lib/dovecot/modules/doveadm/lib20_doveadm_fts_lucene_plugin.so: undefined symbol: lucene_index_iter_deinit (this is usually intentional, so just ignore this message)
Debug: Skipping module doveadm_fts_plugin, because dlopen() failed: /usr/lib/dovecot/modules/doveadm/lib20_doveadm_fts_plugin.so: undefined symbol: fts_user_get_language_list (this is usually intentional, so just ignore this message)
Debug: Skipping module doveadm_mail_crypt_plugin, because dlopen() failed: /usr/lib/dovecot/modules/doveadm/libdoveadm_mail_crypt_plugin.so: undefined symbol: mail_crypt_box_get_pvt_digests (this is usually intentional, so just ignore this message)
field valuedoveadm(jost)<25290><>: Debug: auth USER input: jost home=/var/vmail/matysik.it/jost-philip uid=5000 gid=5000 acl_groups=matysik,admin quota_rule=*:storage=10G quota_rule2 = Trash:storage=+100M quota2 = count:shared quota for maschinen:ns=ZZZ_Freigaben/maschinen/ quota2_rule = *:storage=500M quota3 = count:shared quota for matysik:ns=ZZZ_Freigaben/matysik/ quota3_rule = *:storage=10G quota4 = count:shared quota for postmaster:ns=ZZZ_Freigaben/postmaster/ quota4_rule = *:storage=1G
uid 5000
gid 5000
home /var/vmail/matysik.it/jost-philip
mail maildir:~/Maildir
acl_groups matysik,admin
quota_rule *:storage=10G
quota_rule2 Trash:storage=+100M
quota2 count:shared quota for maschinen:ns=ZZZ_Freigaben/maschinen/
quota2_rule *:storage=500M
quota3 count:shared quota for matysik:ns=ZZZ_Freigaben/matysik/
quota3_rule *:storage=10G
quota4 count:shared quota for postmaster:ns=ZZZ_Freigaben/postmaster/
quota4_rule *:storage=1G
More information about the dovecot
mailing list