Одразу скажу, що нормального вирішення саме по квотам для цілого домену я не знайшов, тому зробив декілька workaround’ів. Я використовую звʼязку exim (+dovecot delivery) + dovecot + postfixadmin.
postfixadmin
Тут все достатньо просто. Вмикаємо підтримку квот в конфігу:
$CONF['quota'] = 'YES'; $CONF['domain_quota'] = 'YES';
dovecot
В конфіг dovecot.conf додаємо відповідні секціїї:
mail_plugins=$mail_plugins quota protocol lda { mail_plugins = $mail_plugins quota ... } protocol imap { mail_plugins = $mail_plugins imap_quota } service dict { unix_listener dict { mode = 0660 user = dovecot group = Debian-exim } } dict { sqluserquota = mysql:/etc/dovecot/dovecot-dict-sql-quota-user.conf } plugin { quota = dict:User Quota::proxy::sqluserquota quota_warning = storage=100%% quota-exceeded 100 %d }
Поясню дещо. По суті, працюють тільки квоти для користувачів, які описуються в /etc/dovecot/dovecot-dict-sql-quota-user.conf, тому описуємо лише їх:
dovecot-dict-sql-quota-user.conf: connect = host=/run/mysqld/mysqld.sock user=mail password=pass dbname=postfixadmin 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 }
Тут ми описуємо таблиці, в яких будуть зберігатися квоти. Як оновлюються квоти? dovecot (якщо використовується саме dovecot delivery) при активності користувача (вході до скриньки чи отриманні листа) автоматично підраховує розмір поштової скриньки і складає ці дані в таблицю. В даному випадку це таблиця quote2.
mysql
Дані про розмір для всього домену будемо підраховувати окремим скриптом, який буде складати оновлені дані в іншу таблицю exim_quota. Створимо її:
mysql>CREATE TABLE `exim_quota` ( `domain` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL, `quota` bigint NOT NULL DEFAULT '0', `overlimit` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`domain`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Таблиця містить такі стовпці: домен, поточний обʼєм і overlimit, який визначає, чи перевищений (значення “1”) ліміт, чи – ні (значення “0”).
Треба видати права для користувача mail@localhost (від якого працює dovecot), щоб dovecot міг оновлювати квоти в таблицях:
mysql>GRANT INSERT, UPDATE, DELETE ON `postfixadmin`.`quota2` TO `mail`@`localhost`; mysql>GRANT INSERT, UPDATE, DELETE ON `postfixadmin`.`quota` TO `mail`@`localhost`;
exim
acl_smtp_mail = acl_check_mail acl_smtp_rcpt = acl_check_rcpt ... acl_check_mail: ... deny log_message = "Quota for outgoing domain <$sender_address_domain> exceeded" message = "Quota for outgoing domain <$sender_address_domain> exceeded" sender_domains = ${lookup mysql{SELECT `domain` FROM `exim_quota` WHERE `domain` = '${quote_mysql:$sender_address_domain}' AND overlimit = '1'}} ... acl_check_rcpt: ... deny log_message = "Quota for incoming domain <$domain> exceeded" message = "Quota for outgoing domain <$domain> exceeded" domains = ${lookup mysql{SELECT `domain` FROM `exim_quota` WHERE `domain` = '${quote_mysql:$domain}' AND overlimit = '1'}}
Додаємо перевірку на використання квот для вихідної пошти (acl_check_mail) і вхідної (acl_check_rcpt). Логіка така: якщо запит mysql повертає домен, то застосовується політика deny. Якщо нічого не повертається, то політика не застосовується. Тобто, exim лише перевіряє поле overlimit. Дані в цьому полі оновлює спеціальний скрипт, щоб при кожному листі не треба було підраховувати заново квоту для всіх доменів.
Для debug’u ACL можна використовувати такий метод:
#exim -v -d-all+acl -bhc 1.1.1.1
і далі як зі звичайним telnet smtp вводити команди, ehlo, mail from, rctp to,…
script
#!/bin/bash MYSQL=/usr/bin/mysql mysql_db=postfixadmin buff=`$MYSQL --login-path=postfixadmin -N -e "select domain, quota from domain where domain <> 'ALL' and active = '1';" $mysql_db` while read line do domain=`echo "${line}"|awk '{print $1}'` quota=`echo "${line}"|awk '{print $2}'` check=`echo "SELECT domain FROM domain WHERE (SELECT SUM(bytes)/1048576 FROM quota2 WHERE username LIKE '%@$domain') > '$quota' AND domain = '$domain';" | $MYSQL --login-path=postfixadmin -N $mysql_db` if [ -z "$check" ] then overlimit=0 else overlimit=1 fi echo "INSERT INTO exim_quota (domain,quota,overlimit) VALUES ('$domain','$quota','$overlimit') ON DUPLICATE KEY UPDATE quota=values(quota), overlimit=values(overlimit);" | $MYSQL --login-path=postfixadmin -N $mysql_db done <<< "$buff"
Його треба додати в cron (раз на хвилину). Звичайно, потрібні відповідні права на цю таблицю.
Нюанси.
Квоти починають рахуватися лише з того моменту, з якого ви їх налаштували. Тобто, поки користувачу не прийде новий лист чи він не ввійде у скриньку, він не буде врахований. Як бути? Треба запустити примусовий перерахунок для всіх користувачів. Ось, як це можна зробити для конкретного домену:
# doveadm -Dv quota recalc -u *@domain.ua
Якщо при запуску отримали помилку:
Error: auth-master: userdb list: User listing returned failure
то треба додати такий рядок до файлу dovecot-sql.conf (або того, де у вас описано підключення дл mysql в dovecot) і перезапустити dovecot і потім заново doveadm…:
iterate_query = SELECT username AS user FROM mailbox