настройка квоты домена в MDA Dovecot

используя dict + mysql

Постановка задачи: настроить квоты доменов в Dovecot v.2+ с использованием Quota/Dict с учётом хранения пользователей и квот в MySQL…

Много админов сломало копья, пытаясь настроить эту опцию для Dovecot. Скажем прямо — опция так-себе, но бывает нужна например, чтобы ограничить локальных админов доменов (PostfixAdmin) в создании новых ящиков или в желании получать уведомления при приближении/превышении лимита домена. Да и в бэкэнде PostfixAdmin всё уже есть — грех не использовать!

Кстати, в свое время в безысходность уже упёрлись тут и тут. Ещё масла в огонь подлил "закомментированный кусок" конфигурации в /etc/dovecot/conf.d/90-quota.conf:

root@mail:/# cat /etc/dovecot/conf.d/90-quota.conf

# Multiple quota roots are also possible, for example this gives each user
# their own 100MB quota and one shared 1GB quota within the domain:
plugin {
  #quota = dict:user::proxy::quota
  #quota2 = dict:domain:%d:proxy::quota_domain
  #quota_rule = *:storage=102400
  #quota2_rule = *:storage=1048576
}

Ага!!! починили-таки… «Чудно» — воскликнул я и приступил к настройке квот доменов по следующей схеме (ею, кстати, пол-интернета завалено). На примере dovecot-dict-sql.conf.ext создал новый файл dovecot-dict-sql-domain.conf, где хранится информация отображения (маппинга) размера квот домена и ссылается на таблицу домена domain, потом добавил новый словарь и получение квоты из БД mysql…


root@mail:/# vim /etc/dovecot/dovecot-dict-sql-domain.conf

connect = host=localhost dbname=postfixadmin user=postfixadmin password=myVerySecurePasswordFromUserPostfixadmin
map {
    pattern = priv/quota/storage
    table = domain
    username_field = domain
    value_field = quota
}
map {
    pattern = priv/quota/messages
    table = quota2
    username_field = username
    value_field = messages
}


root@mail:/# vim /etc/dovecot/dovecot.conf

dict {
  …
  sqldomainquota = mysql:/etc/dovecot/dovecot-dict-sql-domain.conf
  …
}


root@mail:/# vim /etc/dovecot/conf.d/90-quota.conf

plugin {
  …
  quota2 = dict:Domain Quota:%d:proxy::sqldomainquota
  # quota2_rule =
}

root@mail:/# vim /etc/dovecot/dovecot-sql.conf.ext

user_query = SELECT CONCAT('/home/vmail/',domain.domain) as home, \
                    CONCAT('*:bytes=', IF(mailbox.quota = 0 || mailbox.quota = -1, 0, mailbox.quota)) as quota_rule \
                    CONCAT('*:bytes=', IF(domain.maxquota = 0 || domain.maxquota = -1, 0, domain.maxquota*1048576)) as quota2_rule \
             FROM mailbox, domain \
             WHERE username = '%u' AND mailbox.active = '1' AND \
                   domain.domain = '%d' AND domain.active = '1'

#

Рестарт сервиса.

Попытка обновить квоты для пары ящиков домена приводит к интересным результатам — либо в поле quota таблицы domain заносится индивидульное значение последнего "пересчитанного" юзера, либо запись о домене удаляется вовсе. Включив логирование SQL запросов и более внимательно прочитав документацию Quota/Dict прихожу к выводу, что мы не можем использовать таблицу domain, так как типичное обновление (пересчет) квот происходит не через UPDATE, а через DELETE/INSERT:

root@mail:/# doveadm -Dv quota recalc -u admin@example.com
root@mail:/# doveadm -Dv quota get -u admin@example.com
Quota name   Type    Value  Limit                                                    %
User Quota   STORAGE  6773   8192                                                   82
User Quota   MESSAGE    21      -                                                    0
Domain Quota STORAGE  6773 153600                                                    4
Domain Quota MESSAGE    21      -                                                    0

root@mail:/# doveadm -Dv quota recalc -u user1@example.com
root@mail:/# doveadm -Dv quota get -u user1@example.com
Quota name   Type    Value  Limit                                                    %
User Quota   STORAGE   313 204800                                                    0
User Quota   MESSAGE    18      -                                                    0
Domain Quota STORAGE   313 153600                                                    0
Domain Quota MESSAGE    18      -                                                    0

root@mail:/# cat /var/lib/mysql/mail.log | more

  1447 Query     DELETE FROM domain WHERE domain = 'example.com'
  1447 Query     DELETE FROM quota2 WHERE username = 'example.com'
  1447 Query     INSERT INTO domain (quota,domain) VALUES ('6935859','example.com') ON DUPLICATE KEY UPDATE quota='6935859'
  1447 Query     INSERT INTO quota2 (messages,username) VALUES ('21','example.com') ON DUPLICATE KEY UPDATE messages='21'

  1451 Query     DELETE FROM domain WHERE domain = 'example.com'
  1451 Query     DELETE FROM quota2 WHERE username = 'example.com'
  1451 Query     INSERT INTO domain (quota,domain) VALUES ('321060','example.com') ON DUPLICATE KEY UPDATE bytes='321060'
  1451 Query     INSERT INTO quota2 (messages,username) VALUES ('18','example.com') ON DUPLICATE KEY UPDATE messages='18'

…

что соответственно и «выкашивает» из таблицы domain сам домен example.com, который обслуживается нашим почтовым сервером. Итак — не вариант. Естественно, мы можем «нарисовать» следующий маппинг и подсунуть его Dovecot-у:

root@mail:/# vim /etc/dovecot/dovecot-dict-sql-domain.conf

connect = host=localhost dbname=postfixadmin user=postfixadmin password=myVerySecurePasswordFromUserPostfixadmin
map {
    pattern = priv/quota/storage
    table = quota2
    username_field = username
    value_field = messages
}
map {
    pattern = priv/quota/messages
    table = quota2
    username_field = username
    value_field = messages
}

Результат приблизительно тот-же, индивидуальная квота последнего залогиневшегося/пересчитанного юзера попадает в квоту всего домена, что есть баг:

root@mail:/# doveadm -Dv quota recalc -u admin@example.com
root@mail:/# doveadm -Dv quota recalc -u user1@example.com
root@mail:/# cat /var/lib/mysql/mail.log | more

  1462 Query     DELETE FROM quota2 WHERE domain = 'example.com'
  1462 Query     DELETE FROM quota2 WHERE username = 'example.com'
  1462 Query     INSERT INTO quota2 (bytes,username) VALUES ('6935859','example.com') ON DUPLICATE KEY UPDATE quota='6935859'
  1462 Query     INSERT INTO quota2 (messages,username) VALUES ('21','example.com') ON DUPLICATE KEY UPDATE messages='21'

  1464 Query     DELETE FROM quota2 WHERE domain = 'example.com'
  1464 Query     DELETE FROM quota2 WHERE username = 'example.com'
  1464 Query     INSERT INTO quota2 (bytes,username) VALUES ('321060','example.com') ON DUPLICATE KEY UPDATE bytes='321060'
  1464 Query     INSERT INTO quota2 (messages,username) VALUES ('18','example.com') ON DUPLICATE KEY UPDATE messages='18'

…

Диагноз — реализация квоты домена в Dovecot кривая. Допиливать и решать через cron, doveadm+grep+sed сил и желания уже нет. По итогу, в случае с PostfixAdmin 2.3+, Dovecot 2.1+ и виртуальными пользователями в MySQL — воз и ныне там.

У Вас есть элегантное или не очень решение — рад буду выслушать!

Комментарии 1

  1. Дмитрий Владимирович — Jul 23, 2014 at 04:41 PM

    решения так и не нашлось(

  • 1
Разрешённые теги: <b><i><br>