[exim-conf] изменения в ограничениях SMTP аутентификации и рейтлимитах

Victor Ustugov victor на corvax.kiev.ua
Сб Окт 6 19:56:47 EEST 2018


приветствую

за последние дни проведены некоторые изменения в ограничениях SMTP
аутентификации и рейтлимитах.


реализованы более гибкие исключения из требования использовать STARTTLS
на порту 587 (см. confTLS_ENFORCE_SUBMIT_SKIP_HOSTS) с целью согласовать
значение confTLS_ENFORCE_SUBMIT со значением
confSMTP_AUTH_RESTRICT_DISABLE_AUTH_WITHOUT_TLS_SKIP_HOSTS.

попросту говоря, если по каким-то причинам нужно разрешить SMTP
аутентификацию без шифрования с каких-то хостов (сейчас речь не идёт об
обосновании такого исключения) на порту 587, при этом данные хосты
указаны в, скажем, списке trusted_hosts (файл hosts-trusted), то с одной
стороны нужно для включения исключений из запрета приёма писем без
шифрования с SMTP аутентификцией добавить DISABLE_AUTH_WITHOUT_TLS_SKIP
в значение переменной confSMTP_AUTH_RESTRICT и добавить значение
+trusted_hosts в виде элемента списка в
confSMTP_AUTH_RESTRICT_DISABLE_AUTH_WITHOUT_TLS_SKIP_HOSTS, а с другой
стороны для confTLS_ENFORCE_SUBMIT не было варианта сделать
соответствующее исключение.

confTLS_ENFORCE_SUBMIT вообще добавлялся в конфигуратор очень давно,
возможные значения там коряво названы. и вообще, всё это требует
рефакторинга.

на текущий момент времени добавлена переменная
confTLS_ENFORCE_SUBMIT_SKIP_HOSTS, в которой можно перечислить хосты и
сети, для которых на порту 587 не требуется использование STARTTLS при
указании EXTERNAL в значении confTLS_ENFORCE_SUBMIT.

т. о. кроме описанных выше телодвижений с confSMTP_AUTH_RESTRICT и
confSMTP_AUTH_RESTRICT_DISABLE_AUTH_WITHOUT_TLS_SKIP_HOSTS нужно будет
добавить +trusted_hosts в confTLS_ENFORCE_SUBMIT_SKIP_HOSTS:

define(`confSMTP_AUTH_RESTRICT',confSMTP_AUTH_RESTRICT`
DISABLE_AUTH_WITHOUT_TLS_SKIP')
define(`confSMTP_AUTH_RESTRICT_DISABLE_AUTH_WITHOUT_TLS_SKIP_HOSTS',confSMTP_AUTH_RESTRICT_DISABLE_AUTH_WITHOUT_TLS_SKIP_HOSTS`
: +trusted_hosts')
define(`confTLS_ENFORCE_SUBMIT',`EXTERNAL')
define(`confTLS_ENFORCE_SUBMIT_SKIP_HOSTS',confTLS_ENFORCE_SUBMIT_SKIP_HOSTS`
: +trusted_hosts')


лично мне понадобилось такое исключение, чтобы протестировать получение
писем с несколькими попытками SMTP аутентификации на одно письма и в
одной сессии.
на сколько я понимаю, в swaks штатно такое не реализовано, а патчить его
опять желания особого не было.

поэтому тесты проводились на голом exim -d -bh с вливанием нескольких
команд AUTH LOGIN.

в результате были реализованы два новых ограничения SMTP аутентификации:

- ограничение на количество попыток аутентификации на каждое
письмо с опциональной задержкой перед обрывом соединения.

- ограничение на количество неудачных попыток аутентификации
за одну SMTP сессию с опциональной задержкой перед обрывом соединения.


ограничение на количество попыток аутентификации на каждое
письмо указывается в переменной confAUTH_MAX_ATTEMPTS_PER_MSG:

define(`confAUTH_MAX_ATTEMPTS_PER_MSG', `1')

по умолчанию переменной confAUTH_MAX_ATTEMPTS_PER_MSG значение не
присваивается, т. о. данное ограничение по умолчанию не включено, а выше
показан просто пример.

это связано с тем, что хотя я не вижу особого смысла указывать в
confAUTH_MAX_ATTEMPTS_PER_MSG величину больше, чем 1, ещё не
протестировано поведение достаточного количества почтовых клиентов при
проведении попыток аутентификации при неверно указанных пользователем
логине или пароле.

формально данная переменная ограничивает не совсем количество попыток
SMTP аутентификации на каждое письмо. скорее это количество попыток SMTP
аутентификации до ближайшего acl_check_mail, acl_check_quit или
aclcheck_notquit. т. е. RSET не сбрасывает счётчик попыток
аутентификации, которые будут ограничены данным фильтром.

при срабатывании фильтра производится обрыв SMTP соединения (действие
drop) с опциональной задержкой и возврат сообщения об ошибке SMTP клиенту.

длительность задержки перед обрывом соединения, если количество попыток
SMTP аутентификации для данного письма превысило значение
confAUTH_MAX_ATTEMPTS_PER_MSG:

define(`confAUTH_MAX_ATTEMPTS_PER_MSG_DELAY', `20s')dnl

по умолчанию значение переменной confAUTH_MAX_ATTEMPTS_PER_MSG_DELAY не
указано, т. о. задержка перед обрывом соединения производится не будет.

сообщение SMTP клиенту при превышении ограничения на количество попыток
аутентификации на каждое письмо:

define(`confAUTH_MAX_ATTEMPTS_PER_MSG_MESSAGE', `Access denied. Too many
authentication attempts')

define(`confAUTH_MAX_ATTEMPTS_PER_MSG_MESSAGE', `Access denied.
Authentication is allowed only ${if
eq{confAUTH_MAX_ATTEMPTS_PER_MSG}{1}{once}{confAUTH_MAX_ATTEMPTS_PER_MSG
times}} per message')

первый из указанных выше вариантов является значением по умолчанию.
второй является просто примером.


ограничение на количество неудачных попыток аутентификации
за одну SMTP сессию указывается в переменной
confAUTH_MAX_FAILED_PER_CONNECTION:

define(`confAUTH_MAX_FAILED_PER_CONNECTION', `3')

по умолчанию переменной confAUTH_MAX_FAILED_PER_CONNECTION значение не
присваивается, т. о. данное ограничение по умолчанию не включено, а выше
указан просто пример.

длительность задержки перед обрывом соединения, если количество
неудачных попыток аутентификации в течении данной SMTP сессии превысило
значение confAUTH_MAX_FAILED_PER_CONNECTION, можно указать в переменной
confAUTH_MAX_FAILED_PER_CONNECTION_DELAY:

define(`confAUTH_MAX_FAILED_PER_CONNECTION_DELAY', `20s')

сообщение SMTP клиенту при превышении ограничения на количество
неудачных попыток аутентификации за одну SMTP сессию можно указать в
перменной confAUTH_MAX_FAILED_PER_CONNECTION_MESSAGE:

define(`confAUTH_MAX_FAILED_PER_CONNECTION_MESSAGE', `Access denied. Too
many failed authentication attempts')

данное сообщение будет возвращено по умолчанию.


в качестве развития данного фильтра реализован ratelimit для неудачных
попыток SMTP аутентификации, использование которого включается путём
добавления AUTH_FAILED в значение переменной confRATELIMIT.

настройки ratelimit'ов по неудачным попыткам аутентификации указываются
в файле ratelimit-host-auth-failed в виде:

аргумент : лимит : действие : сообщение

где:
аргумент	- SMTP логин
лимит		- ограничение в виде "количество / период"
действие	- действие, применяемое к письму (может отсутствовать)
сообщение	- сообщение SMTP клиенту

SMTP логины можно указывать в виде полных значений, масок или регулярных
выражений.

"лимит" указывается в виде "количество_сообщений / период_времени".
"период_времени" указывается в виде 5s, 10m30s, 1h10m и т. д.

возможные действия:
ok		- исключение из проверки
skip		- синоним для ok
warn		- вывод предупреждения в файл протокола и добавление в
		письмо поля заголовка
reject		- отказ в приеме сообщения
deny		- синоним для reject
defer		- возврат временной ошибки
greylist=X	- добавление X баллов к счетчику опционального
		грейлистинга
reject=X	- добавление X баллов к счетчику опционального reject'а
delay=zz	- задержка на zz секунд перед продолжением обработки
		сообщения
pause=zz	- синоним delay=zz

при отсутствии явно указанного действия по умолчанию применяется
действие defer.

пример:
user на domain.tld	: 20 / 1h : defer : Try again later


при описании действий можно использовать вычисляемые выражения.
пример:

* : 20 / 10m : defer delay=${eval:${sg{$sender_rate}{\N[\.].*$\N}{}} -
$sender_rate_limit} : Try again later

в качестве сообщения SMTP клиенту указывается не столько само
сообщение, сколько его уточнение, т. е. вторая часть сообщения.
в уточнении сообщения SMTP клиенту можно использовать вычисляемые
выражения, например в нем можно использовать переменные $sender_rate,
$sender_rate_limit и $sender_rate_period.


при реализации данного ratelimit'а были сделаны некоторые допущения.
необходимость этого вызвана тем, как exim обрабатывает попытки SMTP
аутентификации.

дело в том, что внутри acl_check_auth нет возможности узнать о том, была
ли попытка аутентификации успешна, т. к. логин и пароль проверяются уже
после acl_check_auth. а переменной $authentication_failed присваивается
значение 1 до acl_check_auth и в случае успешной аутентификации оно
обнуляется, но уже после acl_check_auth. т. о. значение
$authentication_failed будет актуальным только в acl_check_mail,
acl_check_quit и acl_check_notquit (ну или в любом другом acl, следующем
за acl_check_mail, но это уже неактуально).

т. о. нет простого пути ограничить количество неудачных попыток
аутентификации, если после каждой неудачной попытки выполняется RSET.

так вот, допущение выражается в том, что при ограничении в N неудачных
попыток аутентификации можно считать общее количество попыток (просто
инкрементировать целочисленную переменную в acl_check_auth). тогда если
общее количество попыток аутентификации станет равным N+1, можно
предположить, что предыдущие N попыток были неудачными (вряд ли есть
смысл после удачной попытки аутентификации выполнять RSET и проводить
следующую попытку аутентификации, разве что это какой-то перебор логинов
и паролей, но тогда с ним тоже нужно бороться).

реально же проверка того, сработал ли ratelimit, проводится в
acl_check_mail, acl_check_quit и acl_check_notquit.

а вышеупомянутое допущение больше относится к
confAUTH_MAX_ATTEMPTS_PER_MSG. а уже после срабатывания ограничения на
количество управление будет передано в acl_check_notquit, где будут
обновление счётчики ratelimit.

на самом деле в acl_check_auth таки проверяются счётчики ratelimit, но
это производится до проверки логина и пароля перед первой попыткой
аутентификации, чтобы иметь возможность среагировать на ранее
накопленные значения счётчиков ratelimit.

-- 
Best wishes
Victor Ustugov        mailto:victor на corvax.kiev.ua
public GnuPG/PGP key: https://victor.corvax.kiev.ua/corvax.asc
Skype ID: corvax_nb   JID: victor на corvax.kiev.ua



Подробная информация о списке рассылки exim-conf