skip to Main Content

I have configured a shared private namespace in dovecot so that, via ACLs, users can access other users’ mailboxes.

I set up a quota for each user and this also works well. The quota is monitored and new messages are denied if the quota is exceeded.

There is one problem though: apparently dovecot treats shared mailboxes in relation to the current user’s quota, it does not consider (as would be appropriate) the shared mailbox owner’s quota.

I will try to explain better with a couple of examples.

==============

Consider the following case:

  • user1 has a quota of 1MB, totally available
  • user2 has a quota of 10MB, totally available
  • user1 shares the "Sales" mailbox with user2
  • user2 copies a 500KB message to the shared "Sales" mailbox
  • user1’s quota will be correctly deducted by 500KB, thus resulting in 0.5MB available

At this point, however, let’s try another operation:

  • user2 copies an 800KB message to the shared "Sales" mailbox.

The server should return error because this way user1 exceeds the allocated quota of 1MB (500KB + 800KB), but instead the copy operation succeeds.

This happens because at the time of the copy operation, dovecot considers the quota limit of user2 instead of considering the quota limit of user1.

==============

Let us consider another case, performing the same operations but where the user quota limits are reversed (user1 has a larger quota than user2):

  • user1 has a quota of 10MB, totally available
  • user2 has a quota of 1MB, totally available
  • user1 shares the "Sales" mailbox with user2
  • user2 copies a 500KB message to the shared mailbox "Sales"

The operation fails by overquota, because again dovecot considers user2’s quota limit instead of considering user1’s quota limit. Therefore, it is as if user2, with a 1MB limit, tries to write to a "larger" area (10MB).

As a counterevidence, setting user1 and user2 with the same quota limit, the operation succeeds perfectly.

==============

I guess it is a configuration problem with my dovecot.
Can anyone tell me how to solve it?

Below I attach the output of dovecot -n

# 2.3.19.1 (9b53102964): /etc/dovecot/dovecot.conf
# Pigeonhole version 0.5.19 (4eae2f79)
# OS: Linux 6.1.0-16-cloud-arm64 aarch64 Debian 12.4 

auth_master_user_separator = *
auth_mechanisms = PLAIN LOGIN
default_client_limit = 7168
default_process_limit = 1024
deliver_log_format = from=%{from}, envelope_sender=%{from_envelope}, subject=%{subject}, msgid=%m, size=%{size}, delivery_time=%{delivery_time}ms, %$
dict {
  acl = mysql:/etc/dovecot/dovecot-dict-acl-sql.conf.ext
}
first_valid_uid = 2000
last_valid_uid = 2000
listen = * [::]
login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k session=<%{session}>
mail_gid = 2000
mail_location = maildir:%Lh:INDEX=%Lh
mail_plugins = quota mailbox_alias acl mail_log notify
mail_uid = 2000
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 editheader
metric {
  filter = event=imap_command_finished
  metric_name = imap_command
}
namespace {
  inbox = yes
  location = 
  mailbox {
    auto = no
    special_use = Archive
    name = Archive
  }
  mailbox {
    auto = no
    special_use = Archive
    name = Archives
  }
  mailbox {
    auto = no
    special_use = Trash
    name = Deleted Messages
  }
  mailbox {
    auto = subscribe
    special_use = Drafts
    name = Drafts
  }
  mailbox {
    auto = subscribe
    special_use = Junk
    name = Junk
  }
  mailbox {
    auto = no
    special_use = Junk
    name = Junk E-mail
  }
  mailbox {
    auto = subscribe
    special_use = Sent
    name = Sent
  }
  mailbox {
    auto = no
    special_use = Sent
    name = Sent Items
  }
  mailbox {
    auto = no
    special_use = Sent
    name = Sent Messages
  }
  mailbox {
    auto = no
    special_use = Junk
    name = Spam
  }
  mailbox {
    auto = subscribe
    special_use = Trash
    name = Trash
  }
  prefix = 
  separator = /
  type = private
  name = 
}
namespace {
  list = children
  location = maildir:%%Lh:INDEXPVT=%%Lh/Shared/%{auth_user}
  prefix = Shared/%%u/
  separator = /
  subscriptions = yes
  type = shared
  name = 
}
passdb {
  args = /etc/dovecot/dovecot-sql.conf.ext
  driver = sql
}
passdb {
  args = /etc/dovecot/master-users
  driver = passwd-file
  master = yes
  result_success = continue
}
plugin {
  acl = vfile:/etc/dovecot/dovecot-acl
  acl_shared_dict = proxy::acl
  mail_log_events = delete undelete expunge copy mailbox_create mailbox_delete mailbox_rename
  mail_log_fields = uid box msgid size from subject flags
  quota = maildir:User quota
  quota_rule = *:storage=1G
  quota_rule2 = Trash:storage=+100M
  quota_status_nouser = DUNNO
  quota_status_overquota = 552 5.2.2 Mailbox is full
  quota_status_success = DUNNO
  quota_warning = storage=95%% quota-warning 95 %u
  quota_warning2 = storage=80%% quota-warning 80 %u
  sieve = file:/home/vmail/%d/sieve/%n/;active=/home/vmail/%d/sieve/%n/.dovecot.sieve
  sieve_before = /home/vmail/sieve/sievebefore1
  sieve_editheader_forbid_add = X-Verified
  sieve_editheader_forbid_delete = X-Verified X-Seen
  sieve_editheader_max_header_size = 1k
  sieve_extensions = +editheader
  sieve_global_dir = /home/vmail/sieve
  sieve_max_redirects = 30
  sieve_vacation_default_period = 1d
  sieve_vacation_min_period = 0
  sieve_vacation_send_from_recipient = yes
}
protocols = pop3 imap sieve lmtp
service replication-notify-fifo {
  name = aggregator
}
service anvil-auth-penalty {
  name = anvil
}
service auth-worker {
  name = auth-worker
}
service {
  unix_listener {
    group = postfix
    mode = 0666
    user = postfix
    path = /var/spool/postfix/private/auth
  }
  unix_listener {
    group = vmail
    mode = 0660
    user = vmail
    path = auth-userdb
  }
  name = auth
}
service config {
  name = config
}
service dict-async {
  name = dict-async
}
service {
  unix_listener {
    group = vmail
    mode = 0660
    user = vmail
    path = dict
  }
  name = dict
}
service login/proxy-notify {
  name = director
}
service dns-client {
  name = dns-client
}
service doveadm-server {
  name = doveadm
}
service imap-hibernate {
  name = imap-hibernate
}
service imap {
  process_min_avail = 8
  service_count = 0
  vsz_limit = 1 G
  name = imap-login
}
service imap-urlauth {
  name = imap-urlauth-login
}
service imap-urlauth-worker {
  name = imap-urlauth-worker
}
service token-login/imap-urlauth {
  name = imap-urlauth
}
service {
  process_limit = 2048
  name = imap
}
service indexer-worker {
  name = indexer-worker
}
service indexer {
  name = indexer
}
service ipc {
  name = ipc
}
service {
  unix_listener {
    group = postfix
    mode = 0666
    user = postfix
    path = /var/spool/postfix/private/dovecot-lmtp
  }
  name = lmtp
}
service log-errors {
  name = log
}
service {
  inet_listener {
    port = 4190
    name = sieve
  }
  name = managesieve-login
}
service login/sieve {
  name = managesieve
}
service old-stats-mail {
  name = old-stats
}
service pop3 {
  service_count = 0
  name = pop3-login
}
service login/pop3 {
  name = pop3
}
service {
  executable = script /opt/scripts/dovecot/quota-warning.sh
  unix_listener {
    group = vmail
    mode = 0660
    user = vmail
    path = quota-warning
  }
  name = quota-warning
}
service replicator-doveadm {
  name = replicator
}
service login/stats-writer {
  unix_listener {
    group = vmail
    mode = 0660
    user = vmail
    path = stats-reader
  }
  unix_listener {
    group = vmail
    mode = 0660
    user = vmail
    path = stats-writer
  }
  name = stats
}
service submission {
  name = submission-login
}
service login/submission {
  name = submission
}
ssl = required
ssl_cert = </etc/letsencrypt/live/mydomain.com/fullchain.pem
ssl_cipher_list = EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
ssl_dh = # hidden, use -P to show it
ssl_key = # hidden, use -P to show it
ssl_prefer_server_ciphers = yes
userdb {
  args = /etc/dovecot/dovecot-sql.conf.ext
  driver = sql
}
protocol lda {
  lda_mailbox_autocreate = yes
  lda_mailbox_autosubscribe = yes
  mail_plugins = quota mailbox_alias acl mail_log notify sieve
  service replication-notify-fifo {
    name = aggregator
  }
  service anvil-auth-penalty {
    name = anvil
  }
  service auth-worker {
    name = auth-worker
  }
  service auth-client {
    name = auth
  }
  service config {
    name = config
  }
  service dict-async {
    name = dict-async
  }
  service dict {
    name = dict
  }
  service login/proxy-notify {
    name = director
  }
  service dns-client {
    name = dns-client
  }
  service doveadm-server {
    name = doveadm
  }
  service imap-hibernate {
    name = imap-hibernate
  }
  service imap {
    name = imap-login
  }
  service imap-urlauth {
    name = imap-urlauth-login
  }
  service imap-urlauth-worker {
    name = imap-urlauth-worker
  }
  service token-login/imap-urlauth {
    name = imap-urlauth
  }
  service imap-master {
    name = imap
  }
  service indexer-worker {
    name = indexer-worker
  }
  service indexer {
    name = indexer
  }
  service ipc {
    name = ipc
  }
  service lmtp {
    name = lmtp
  }
  service log-errors {
    name = log
  }
  service sieve {
    name = managesieve-login
  }
  service login/sieve {
    name = managesieve
  }
  service old-stats-mail {
    name = old-stats
  }
  service pop3 {
    name = pop3-login
  }
  service login/pop3 {
    name = pop3
  }
  service replicator-doveadm {
    name = replicator
  }
  service login/stats-writer {
    name = stats
  }
  service submission {
    name = submission-login
  }
  service login/submission {
    name = submission
  }
}
protocol lmtp {
  mail_plugins = quota mailbox_alias acl mail_log notify sieve
  postmaster_address = [email protected]
  recipient_delimiter = +
  service replication-notify-fifo {
    name = aggregator
  }
  service anvil-auth-penalty {
    name = anvil
  }
  service auth-worker {
    name = auth-worker
  }
  service auth-client {
    name = auth
  }
  service config {
    name = config
  }
  service dict-async {
    name = dict-async
  }
  service dict {
    name = dict
  }
  service login/proxy-notify {
    name = director
  }
  service dns-client {
    name = dns-client
  }
  service doveadm-server {
    name = doveadm
  }
  service imap-hibernate {
    name = imap-hibernate
  }
  service imap {
    name = imap-login
  }
  service imap-urlauth {
    name = imap-urlauth-login
  }
  service imap-urlauth-worker {
    name = imap-urlauth-worker
  }
  service token-login/imap-urlauth {
    name = imap-urlauth
  }
  service imap-master {
    name = imap
  }
  service indexer-worker {
    name = indexer-worker
  }
  service indexer {
    name = indexer
  }
  service ipc {
    name = ipc
  }
  service lmtp {
    name = lmtp
  }
  service log-errors {
    name = log
  }
  service sieve {
    name = managesieve-login
  }
  service login/sieve {
    name = managesieve
  }
  service old-stats-mail {
    name = old-stats
  }
  service pop3 {
    name = pop3-login
  }
  service login/pop3 {
    name = pop3
  }
  service replicator-doveadm {
    name = replicator
  }
  service login/stats-writer {
    name = stats
  }
  service submission {
    name = submission-login
  }
  service login/submission {
    name = submission
  }
}
protocol imap {
  imap_client_workarounds = tb-extra-mailbox-sep
  mail_max_userip_connections = 256
  mail_plugins = quota mailbox_alias acl mail_log notify imap_quota imap_acl
  service replication-notify-fifo {
    name = aggregator
  }
  service anvil-auth-penalty {
    name = anvil
  }
  service auth-worker {
    name = auth-worker
  }
  service auth-client {
    name = auth
  }
  service config {
    name = config
  }
  service dict-async {
    name = dict-async
  }
  service dict {
    name = dict
  }
  service login/proxy-notify {
    name = director
  }
  service dns-client {
    name = dns-client
  }
  service doveadm-server {
    name = doveadm
  }
  service imap-hibernate {
    name = imap-hibernate
  }
  service imap {
    name = imap-login
  }
  service imap-urlauth {
    name = imap-urlauth-login
  }
  service imap-urlauth-worker {
    name = imap-urlauth-worker
  }
  service token-login/imap-urlauth {
    name = imap-urlauth
  }
  service imap-master {
    name = imap
  }
  service indexer-worker {
    name = indexer-worker
  }
  service indexer {
    name = indexer
  }
  service ipc {
    name = ipc
  }
  service lmtp {
    name = lmtp
  }
  service log-errors {
    name = log
  }
  service sieve {
    name = managesieve-login
  }
  service login/sieve {
    name = managesieve
  }
  service old-stats-mail {
    name = old-stats
  }
  service pop3 {
    name = pop3-login
  }
  service login/pop3 {
    name = pop3
  }
  service replicator-doveadm {
    name = replicator
  }
  service login/stats-writer {
    name = stats
  }
  service submission {
    name = submission-login
  }
  service login/submission {
    name = submission
  }
}
protocol pop3 {
  mail_max_userip_connections = 30
  mail_plugins = quota mailbox_alias acl mail_log notify
  pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
  pop3_uidl_format = %08Xu%08Xv
  service replication-notify-fifo {
    name = aggregator
  }
  service anvil-auth-penalty {
    name = anvil
  }
  service auth-worker {
    name = auth-worker
  }
  service auth-client {
    name = auth
  }
  service config {
    name = config
  }
  service dict-async {
    name = dict-async
  }
  service dict {
    name = dict
  }
  service login/proxy-notify {
    name = director
  }
  service dns-client {
    name = dns-client
  }
  service doveadm-server {
    name = doveadm
  }
  service imap-hibernate {
    name = imap-hibernate
  }
  service imap {
    name = imap-login
  }
  service imap-urlauth {
    name = imap-urlauth-login
  }
  service imap-urlauth-worker {
    name = imap-urlauth-worker
  }
  service token-login/imap-urlauth {
    name = imap-urlauth
  }
  service imap-master {
    name = imap
  }
  service indexer-worker {
    name = indexer-worker
  }
  service indexer {
    name = indexer
  }
  service ipc {
    name = ipc
  }
  service lmtp {
    name = lmtp
  }
  service log-errors {
    name = log
  }
  service sieve {
    name = managesieve-login
  }
  service login/sieve {
    name = managesieve
  }
  service old-stats-mail {
    name = old-stats
  }
  service pop3 {
    name = pop3-login
  }
  service login/pop3 {
    name = pop3
  }
  service replicator-doveadm {
    name = replicator
  }
  service login/stats-writer {
    name = stats
  }
  service submission {
    name = submission-login
  }
  service login/submission {
    name = submission
  }
}

2

Answers


  1. It seems a physic limit on dovecot structure:

    Quota and Shared Namespaces

    Quota plugin considers shared namespaces against owner’s quota, not
    the current user’s. There is a limitation that per-user quota
    configuration is ignored, and the current user’s configuration is
    used.

    Public namespaces are ignored unless there is explicit quota specified
    for it

    Have you tried to use different quota check on specific namespace?

    https://doc.dovecot.org/configuration_manual/quota/#quota-for-private-namespaces

    Login or Signup to reply.
  2. As Alessio wrote, it is indeed possible to set separate quotas on namespaces. However, as you mentioned yourself, shared mailboxes belong to other people (owners) and the quota set for the owner on that mailbox should be enforced. What misses in Dovecot, is sharing this data over IMAP. Quite some time ago, I stumbled upon this problem myself, but I had not discussed the issue with the Dovecot developers nor did I write a patch for it myself.

    In the Dovecot source code, requesting other people’s quota is disallowed explicitly as per the imap-quota plugin (https://github.com/dovecot/core/blob/main/src/plugins/imap-quota/imap-quota-plugin.c#L101-L104):

        if (ns->owner != NULL && ns->owner != client->user) {
            client_send_tagline(cmd, "NO Not showing other users' quota.");
            return TRUE;
        }
    

    Funnily enough, the commit disallowing the use of other users’ quota has message "Quote works now properly with shared mailboxes." See https://github.com/dovecot/core/commit/a443e5aaf632257bfd1e7aa9b3c42c09512bbe43.

    This also leads me to the code referring quota enforcement to the quota of the owner following that commit (https://github.com/dovecot/core/blob/a443e5aaf632257bfd1e7aa9b3c42c09512bbe43/src/plugins/quota/quota-storage.c#L529-L530):

            /* register to owner's quota roots */
            quota = quota_get_mail_user_quota(storage->ns->owner);
    

    Later on, this code has been changed to check for quota enforcement on the source https://github.com/dovecot/core/commit/1f5670bce3e5ec0c1ddf8d95c57ebb428f1339f6.

    Following your example, it seems that that is not working correctly. This might be by design, though. I suspect that setting the same quota settings on the shared namespace (https://doc.dovecot.org/configuration_manual/quota/#quota-for-private-namespaces) would make it work, as then the owner’s quota is checked against the shared namespace (instead of the namespace the mailbox shows up in on their end). This issue would probably be worth sending an e-mail to the user maillist [email protected]. Please let us know whether that brings you to a solution to your issue.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search