Уже третий день подряд мучился с настройкой квот в Dovecot прямо на работающем сервере. Мучился очень аккуратно и имея в запасе бэкапы рабочих конфигураций на начало каждого дня.
Хранение счётчиков квот в MySQL настроил быстро, формирование правил квот по данным из MySQL тоже настроил быстро. Однако после настройки текущие значения счётчиков квот становились отрицательными и со временем только уменьшались. Я сразу подумал, что POP3-сервер обновляет счётчики квот, т.к. его задача лишь забирать почту из ящика, а вот LDA (агент локальной доставки) - не обновляет, т.к. именно он кладёт почту в ящики и должен увеличивать значения счётчиков.
В конечном итоге оказалось, что вместо LDA delivery из Dovecot по-прежнему используется LDA virtual из Postfix. Произошло это потому что действие настройки virtual_transport = dovecot перекрывается настройкой transport_maps = mysql:/etc/postfix/sql/transport.cf, где для тестируемого домена был настроен транспорт virtual. Здесь можно поблагодарить Вьетце Венема за отличную документацию - это я прочитал прямо в man 5 postconf. Dovecot практически не документирован, а вики-страницы с Howto по настройке различных фишек Dovecot за документацию я не считаю.
После исправления значения транспорта в базе данных возникло ещё несколько проблем:
Проблемы решались следующим образом:
Итоговые конфигурационные файлы Dovecot помещаю сюда, чтобы в следующий раз не наступать на те же грабли:
protocols = pop3
disable_plaintext_auth = no
log_timestamp = "%Y-%m-%d %H:%M:%S "
mail_location = maildir:/var/mail/virtual/%Ld/%Ln
first_valid_uid = 999
first_valid_gid = 999
dict {
quotadict = mysql:/etc/dovecot/dovecot-dict-mysql.conf
}
plugin {
quota = dict:user::proxy::quotadict
# Квота по умолчанию - объём ящика 1 гигабайт,
# не более 1000 писем в ящике
# Эта квота заменяется квотой, найденной в БД
quota_rule = *:storage=1G:messages=1000
}
protocol pop3 {
mail_plugins = quota
mail_executable = /etc/dovecot/pop-update-lastlog.sh
}
protocol lda {
# Ящик администратора почтовой системы
postmaster_address = postadmin@domain.tld
mail_plugins = quota
}
# Домен по умолчанию для пользователей, пытающихся
# аутентифицироваться в Dovecot без указания домена
auth_default_realm = domain.tld
auth default {
mechanisms = plain login
passdb sql {
args = /etc/dovecot/dovecot-mysql.conf
}
userdb sql {
args = /etc/dovecot/dovecot-mysql.conf
}
socket listen {
client {
path = /var/spool/postfix/private/auth
mode = 0660
user = postfix
group = postfix
}
master {
path = /var/run/dovecot/auth-master
mode = 0660
user = vmail
group = vmail
}
}
}
Уже знакомый скрипт /etc/dovecot/pop-before-smtp.sh для аутентификации POP before SMTP:
#!/bin/sh mysql -uuser -ppassword -h127.0.0.1 mail <<END UPDATE users SET lasttime = NOW(), lastip='$IP' WHERE login = '$USER'; END exec /usr/lib/dovecot/pop3 "$@"
Файл настроек для хранения счётчиков квот в БД MySQL /etc/dovecot/dovecot-dict-mysq.conf:
connect = host=127.0.0.1 dbname=mail user=user password=password
map {
pattern = priv/quota/storage
table = users
username_field = login
value_field = bytes
}
map {
pattern = priv/quota/messages
table = users
username_field = login
value_field = messages
}
Файл настроек для проверки учётных данных пользователя и его квот по таблице в БД MySQL /etc/dovecot/dovecot-mysql.conf:
driver = mysql
connect = host=127.0.0.1 dbname=mail user=user password=password
default_pass_scheme = CRYPT
password_query = SELECT password \
FROM users \
WHERE login = '%u'
user_query = SELECT CONCAT(SUBSTRING_INDEX(login, '@', -1), '/', SUBSTRING_INDEX(login, '@', 1), '/'), \
999 AS uid, \
999 AS gid, \
CONCAT('*:bytes=', max_bytes, ':messages=', max_messages) AS quota_rule \
FROM users \
WHERE login = '%u'
В настройки postfix добавлен LDA delivery из Dovecot, в файл /etc/postfix/master.cf добавлены две строчки:
dovecot unix - n n - - pipe
flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}
Столбцы со счётчиками и со значениями квот для пользователей добавлены с помощью четырёх SQL-запросов:
ALTER TABLE users ADD COLUMN bytes bigint default 0; ALTER TABLE users ADD COLUMN messages integer default 0; ALTER TABLE users ADD COLUMN max_bytes bigint default 1073741824; ALTER TABLE users ADD COLUMN max_messages bigint default 1000;
Значения квот по умолчанию можете поменять на свои. Я поставил ограничения в 1 гигабайт на общий объём ящика и не более 1000 писем в одном почтовом ящике.
В таком виде Dovecot не будет принимать от Postfix письма для адресатов, превысивших квоту. Но такие письма будут просто попадать в очередь отложенных писем Postfix. Нужно же, чтобы письмо не занимало дисковое пространство ни в почтовом ящике получателя, ни в очереди отложенных писем, а отклонялось сразу же с соответствующим сообщением отправителю о том, что адресат превысил квоту.
Для этого нужно настроить проверку квот при приёме почты самим Postfix'ом. Для этого воспользуемся правилом check_recipient_access mysql:/etc/postfix/sql/quotas.cf, которое нужно поместить в том числе до правил permit_mynetworks и permit_sasl_authenticated. Если поместить его после этих двух проверок, то квоты получателя просто не будут проверяться, если отправитель аутентифицировался на почтовом сервере или он отправляет почту из доверенной сети.
Файл /etc/postfix/sql/quotas.cf для проверки квоты получателя:
user = user
password = password
dbname = mail
hosts = 127.0.0.1
query = SELECT '452 Mailbox is over quota'
FROM users
WHERE login = '%s'
AND ((bytes >= max_bytes AND max_bytes > 0)
OR (messages >= max_messages AND max_messages > 0))
О расширенных статусах REJECT я прочитал в man 5 access, а подходящий код статуса подобрал в RFC2821 на этой странице: http://tools.ietf.org/html/rfc2821.
В случае если квота по объёму или по количеству сообщений равна нулю, то этот SQL-запрос такую квоту не проверяет. Таким образом значения 0 в полях max_bytes или max_messages означают, что ограничение по этому параметру не действует.
При попытке отправить письмо на ящик, превысивший квоту, Outlook Express сообщает следующее:
Не удается отправить сообщение, поскольку сервер отказался принять адрес одного из получателей. В письме был указан адрес: 'user@domain.tld'. Тема 'test1', Учетная запись: 'mail', Сервер: 'domain.tld', Протокол: SMTP, Ответ сервера: '452 4.7.1 <user@domain.tld>: Recipient address rejected: Mailbox is over quota', Порт: 25, Защита (SSL): Нет, Ошибка сервера: 452, Код ошибки: 0x800CCC79