Категорії
Mail systems

[exim] postfixadmin: використовуємо квоти для цілого домену.

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

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Домашняя страничка Andy
Записки молодого админа
Самостоятельная подготовка к Cisco CCNA
Самостоятельная подготовка к Cisco CCNP
Powered by Muff