Квотирование представляет собой механизм органичения выделяемых тому или иному пользователю системных ресурсов. В приложениии к электронной почте квоты, как правило, направлены на установление лимита занимаемого почтовым ящиком места на диске. Помимо персональных ограничений для ящика, на практике в мультидоменных серверных конфигурациях используется и квотирование по домену, когда выделенное дисковое пространство ограничивается для всех созданных в данном домене почтовых ящиков.
Два эти подхода могут быть удобны в различных ситуациях. Однако, наиболее гибким вариантом представляется реализация обоих одновременно, когда ограничение ресурсов может регулироваться как персонально, для конкретного почтового ящика, так и для их совокупности в рамках домена.
Рассмотрим механизм реализации такого двухуровневого квотирования используя возможности POP3 / IMAP4 сервера Dovecot 2.
Для демонстрации воспользуемся уже имеющейся системой на базе FreeBSD 11 c установленным серверов Dovecot последней версии.
root@beta:~ # uname -v
FreeBSD 11.0-RELEASE-p2 #0: Mon Oct 24 06:55:27 UTC 2016 root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC
root@beta:~ # dovecot --version
2.2.27 (c0f36b0)
Данная система использует базу виртуальных пользователей — как доменов, так и отдельных почтовых ящиков, хранимую в базе данных MySQL в формате системы управления Postfixadmin.
root@beta:~ # pkg info | grep postfixadmin
postfixadmin-3.0 PHP web-based management tool for Postfix virtual domains and users
Структура таблиц этой базы c именем exim данных следующая.
root@beta:~ # mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 21361
Server version: 5.7.15-log Source distribution
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
root@localhost [(none)]> SHOW TABLES FROM exim;
+-----------------------+
| Tables_in_exim |
+-----------------------+
| admin |
| alias |
| alias_domain |
| awl |
| config |
| domain |
| domain_admins |
| fetchmail |
| log |
| mailbox |
| quota |
| quota2 |
| vacation |
| vacation_notification |
+-----------------------+
14 rows in set (0,00 sec)
...
Ограничения по максимальным размерах дискового пространства, которое может занимать тот или иной почтовый ящик для данного домена или все ящики домена в совокупности хранятся в таблице domain.
...
root@localhost [(none)]> SHOW COLUMNS FROM domain FROM exim;
+-------------+--------------+------+-----+---------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------------------+-------+
| domain | varchar(255) | NO | PRI | NULL | |
| description | varchar(255) | NO | | NULL | |
| aliases | int(10) | NO | | 0 | |
| mailboxes | int(10) | NO | | 0 | |
| maxquota | bigint(20) | NO | | 0 | |
| quota | bigint(20) | NO | | 0 | |
| transport | varchar(255) | NO | | NULL | |
| backupmx | tinyint(1) | NO | | 0 | |
| created | datetime | NO | | 2000-01-01 00:00:00 | |
| modified | datetime | NO | | 2000-01-01 00:00:00 | |
| active | tinyint(1) | NO | | 1 | |
+-------------+--------------+------+-----+---------------------+-------+
...
Поле quota описывает размер квоты для домена, а maxquota размер квоты, которая устанавливается по умолчанию для конкретного ящика в данном домене в мегабайтах (или иных единицах исходя из квантификатора, указанного в конфигурационном файле Postfixadmin).
root@beta:~ # cat /usr/local/www/postfixadmin/config.local.php | grep quota
$CONF['maxquota'] = '0';
$CONF['quota'] = 'YES';
$CONF['quota_multiplier'] = '1048576';
$CONF['used_quotas'] = 'YES';
$CONF['new_quota_table'] = 'YES';
Оба поля могут быть как заданы, так и быть равными 0. Последнее означает отсутствие назначенной квоты.
Само значение квоты для почтового ящика электронной почты, которое, очевидно, может отличаться от значения по умолчанию, хранится в таблице mailbox в поле quota. Причём, в отличие от таблицы domain, это значение сохраняется уже в байтах.
...
root@localhost [(none)]> SHOW COLUMNS FROM mailbox FROM exim;
+------------+--------------+------+-----+---------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------------------+-------+
| username | varchar(255) | NO | PRI | NULL | |
| password | varchar(255) | NO | | NULL | |
| name | varchar(255) | NO | | NULL | |
| maildir | varchar(255) | NO | | NULL | |
| quota | bigint(20) | NO | | 0 | |
| local_part | varchar(255) | NO | | NULL | |
| domain | varchar(255) | NO | MUL | NULL | |
| created | datetime | NO | | 2000-01-01 00:00:00 | |
| modified | datetime | NO | | 2000-01-01 00:00:00 | |
| active | tinyint(1) | NO | | 1 | |
+------------+--------------+------+-----+---------------------+-------+
10 rows in set (0,01 sec)
root@localhost [(none)]> SELECT `username`, `quota` FROM `exim`.`mailbox`;
+-----------------+------------+
| username | quota |
+-----------------+------------+
| box@my.server | 5368709120 |
...
| max@kostikov.co | 0 |
...
+-----------------+------------+
NNN rows in set (0,00 sec)
...
Кроме того, в той же MySQL базе в таблице quota2 данных хранятся и счётчики использованной квоты, т.н. "словари квот".
...
root@localhost [(none)]> SHOW COLUMNS FROM quota2 FROM exim;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| username | varchar(100) | NO | PRI | NULL | |
| bytes | bigint(20) | NO | | 0 | |
| messages | int(11) | NO | | 0 | |
+----------+--------------+------+-----+---------+-------+
3 rows in set (0,00 sec)
...
В поле username может фигурировать как имя ящика (обычно это логин пользователя в данной системе), так и имя домена в случае использования подоменного квотирования. Также, помимо объёма данных в байтах, учитывается и общее количество сообщений.
Настроим механизм двухуровневого квотирования "пользователь" - "домен" средствами Dovecot 2. При этом в случае наличия квоты для данного почтового ящика будет учитываться, в первую очередь, она. Далее будет использоваться уже квота, установленная для домена.
Для этих целей воспользуемся стандартным плагином quota в Dovecot 2. Конфигурация приобретёт следующий вид.
root@beta:~ # cd /usr/local/etc/dovecot/
root@beta:/usr/local/etc/dovecot # cat dovecot.conf
# --- by Max Kostikov (c) 2010...2016 v.20161119
...
mail_location = maildir:/var/mail/%d/%n
...
mail_plugins = acl quota trash
...
passdb {
args = /usr/local/etc/dovecot/dovecot-sql.conf
driver = sql
}
userdb {
args = /usr/local/etc/dovecot/dovecot-sql.conf
driver = sql
}
...
# -- Quota
plugin {
quota = dict:user_quota::proxy::sqluserquota
quota_rule2 = Trash:storage=+10%%
quota_rule3 = Junk:storage=+10%%
quota_warning = storage=100%% quota-exceeded 100 %u
quota_warning2 = storage=95%% quota-warning 95 %u
quota_warning3 = storage=90%% quota-warning 90 %u
quota_warning4 = storage=75%% quota-warning 75 %u
quota2 = dict:domain_quota:%d:proxy::sqldomainquota
}
dict {
sqluserquota = mysql:/usr/local/etc/dovecot/dovecot-dict-sql-user.conf
sqldomainquota = mysql:/usr/local/etc/dovecot/dovecot-dict-sql-domain.conf
}
service dict {
unix_listener dict {
user = mailnull
mode = 0660
}
}
service quota-warning {
executable = script /usr/local/etc/dovecot/quota_warning.sh
unix_listener quota-warning {
user = mailnull
mode = 0660
}
}
# -- Trash
plugin {
trash = /usr/local/etc/dovecot/dovecot-trash.conf
}
...
protocol imap {
mail_plugins = $mail_plugins antispam imap_acl imap_quota
imap_client_workarounds = delay-newmail tb-extra-mailbox-sep
}
...
Здесь в значении параметра mail_plugins включаются плагины quota, который, собственно, и реализует сам механизм, а также плагин trash, который по определённому алгоритму очищает папки в случае достижения размера ограничений с тем, чтобы не терять в этом случае входящую почту. В тех же целях к размеру квоты дополнительно даётся по 10% для папок Junk (спам) и Trash (удалённые) — см. quota_rule. Также для целей информирования пользователей о приближении к исчерпанию квоты используется специальный механизм информирования с интервалами, определёнными в параметрах quota_warning путём рассылки специального сообщения.
Запросы к учётным записям пользователей в базе данных MySQL оформлены в дополнительном конфигурационном файле dovecot-sql.conf. При этом устанавливаются размеры квот для данного пользователя и / или домена.
root@beta:/usr/local/etc/dovecot # cat dovecot-sql.conf
driver = mysql
connect = host=localhost dbname=exim user=user password=password
default_pass_scheme = BLF-CRYPT
password_query = SELECT `username` AS `user`, `password` \
FROM `mailbox` WHERE `username` = LCASE('%u') \
AND `active` = '1'
user_query = SELECT CONCAT('/var/mail/', LCASE(maildir)) AS home, \
CONCAT('*:bytes=', mailbox.quota) AS quota_rule, \
CONCAT('*:bytes=', domain.quota, 'M') AS quota2_rule \
FROM mailbox, domain \
WHERE username = LCASE('%u') AND mailbox.active = '1' \
AND domain.domain = '%d' AND domain.active = '1'
Словари квот также представлены в отдельных файлах конфигурации.
root@beta:/usr/local/etc/dovecot # cat dovecot-dict-sql-domain.conf
connect = host=localhost dbname=exim user=user password=password
map {
pattern = priv/quota/storage
table = quota2
username_field = username
value_field = bytes
}
map {
pattern = priv/quota/messages
table = quota2
username_field = username
value_field = messages
}
root@beta:/usr/local/etc/dovecot # cat dovecot-dict-sql-user.conf
connect = host=localhost dbname=exim user=user password=password
map {
pattern = priv/quota/storage
table = quota2
username_field = username
value_field = bytes
}
map {
pattern = priv/quota/messages
table = quota2
username_field = username
value_field = messages
}
Они, как видно, идентичны.
Скрипт отправки сообщения о приближении к исчерпанию установленной квоты quota_warning.sh.
root@beta:/usr/local/etc/dovecot # ll quota_warning.sh
-rwxr-xr-x 1 mailnull mail 302 4 дек 23:16 quota_warning.sh*
root@beta:/usr/local/etc/dovecot # cat quota_warning.sh
PERCENT=$1
USER=$2
cat << EOF | /usr/local/libexec/dovecot/dovecot-lda -d $USER -o "plugin/quota=maildir:User quota:noenforcing"
From: postmaster@my.server
Subject: Quota warning
X-Priority: 2
Your mailbox is now $PERCENT% full.
Please remove unnecessary information to avoid new mail loss.
EOF
И, наконец, конфигурация плагина автоочистки папок почтового ящика Trash в случае исчерпания квоты содержится в файле dovecot-trash.conf. Он определяет порядок освобождения дискового пространства.
root@beta:/usr/local/etc/dovecot # cat dovecot-trash.conf
1 Trash
2 Junk
3 Draft
Теперь достаточно лишь перезапустит службу Dovecot 2 и наслаждаться результатом.
root@beta:~ # service dovecot restart
Stopping dovecot.
Waiting for PIDS: 74642.
Starting dovecot.
Имейте ввиду, что если вы подключаете мехнизм квотирования для уже работающих почтовых ящиков, то для начальной установки значения квот в словаре вам потребуется вручную пересчитать квоты каждого пользователя при помощи утилиты администрирования Dovecot doveadm quota recalc. При этом следует учитывать, что в настоящее время она некорректно обрабатывает квоту для домена, поэтому начальное значение её для данного случая нужно будет скорректировать непосредственно в базе данных на основании сумм актуальных значений принадлежащих ему почтовых ящиков.
Статья была полезной? Тогда прошу не стесняться и поддерживать деньгами через PayPal или Яндекс.Деньги.