Одразу скажу, що нормального вирішення саме по квотам для цілого домену я не знайшов, тому зробив декілька 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