Простой greylisting в opensmtpd
Данная статья является логическим продолжением статьи «Интеграция почтового анти-спама rspamd с opensmtpd» из-за некоторого вызова, который мне бросили, сказав, что сложно реализовать greylisting в связке opensmtpd+rspamd.
«Историй успеха» интеграции greylisting в opensmtpd я не встречал (если они существуют, то просьба написать в комментариях).
Что реализовать greylisting в моём случае сложно — я не отрицал и не собирался делать данный функционал до того, как в opensmtpd реализуют фильтры, хотя и понимал, что greylisting — одна из неплохих методик борьбы со спамом. Но брошенный вызов и желание ещё немного снизить количество спама и спать спокойно — не дали спать спокойно и заставили сделать это.
Мне удалось реализовать простой способ greylisting’а.
Несколько уточнений:
Способ простой только для того случая, если реализация связки opensmtpd+rspamd сделана через скрипт-прослойку mda.
Реализация greylisting’а для opensmtpd существует через фильтр. Но, как знают, заинтересованные в opensmtpd лица, стабильной реализации фильтров в opensmtpd нет. Кроме того, не хотелось добавлять ещё один демон при наличии встроенного грейлистинга в rspamd.
Реализация грейлистинга для opensmtpd существует для freebsd и spamd. Подробностями реализации не интересовался, т.к. демона spamd под linux нет.
- Реализация является костылём и использованием недокументированных возможностей, но куда мы без них в IT?
Теория
Greylisting — методика отброса спама, основанная на том, что спам-программы хотят разослать как можно больше спама здесь и в данную секунду.
Упрощённая логика грейлистинга: если к нам приходит письмо, то мы отвечаем отправителю временной ошибкой, сами запоминаем отправителя (например, на сутки) для того, чтобы принять от него письмо через некоторое время (например, через 5 минут).
«Правильный» отправитель (согласно стандартам) должен попытаться отправить нам письмо ещё раз через некоторое время. «Неправильный» отправитель этого или не делает или делает, но за прошедшее время отправитель успевает попасть в различные внешние спам-списки и мы его отфильтровываем уже на основании этих спам-списков.
После того, как мы отправителя запомнили, то в течение суток (как пример), проверка на грейлисты не делается повторно. Это позволяет сократить время доставки повторного письма (если это правильный отправитель).
В postfix+amavis+spamasassin greylisting делался, если не ошибаюсь, по доменным именам. В rspamd ключём для greylisting’а сделан IP адрес отправителя с маской /19 (конфигурируемо). Со стороны всеобщей кластеризации и гео-распределённых сервисов — это выглядит как более правильное решение, но и обсуждаемое, с другой стороны.
К сожалению, всё вышесказанное в теории выглядит хорошо, но на практике, когда на чайниках, микроволновках и компьютерах обычных пользователей существует множество ботнетов, которые рассылают спам через обычный Outlook (как он там, существует ещё до сих пор на Windows?) — всё не так замечательно. Но как-то методика работает и отказываться от неё не стоит.
Практика
redis в rpsamd
Включаем поддержку редиса в rpsamd. Модуль грейлистинга хранит в редисе свои данные.
# /etc/rspamd/local.d/redis.conf
servers = "redis.example.com";
password = "example_password";
Запускаем редис (в докере, например, это делается одной командой).
greylisting в rspamd
Включаем модуль грейлистинга (по умолчанию, он выключен). expire — время, на которое отправитель становится доверенным, после прохождения проверки. Я специально подкрутил побольше. По умолчанию — 86400 секунд.
# /etc/rspamd/modules.d/greylist.conf
greylist {
expire = 864000;
.include(try=true,priority=5) "${DBDIR}/dynamic/greylist.conf"
.include(try=true,priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/greylist.conf"
.include(try=true,priority=10) "$LOCAL_CONFDIR/override.d/greylist.conf"
}
→ Пример контейнера с rspamd
greylisting в opensmtpd
Самая неоднозначная часть решения.
После того, как мы отправили письмо демону rspamd через клиент rspamc (см. предыдущую статью) — демон нам на STDOUT отвечает текстом письма с добавленными заголовками.
В случае, если требуется greylisting, то в заголовках будет присутствовать «X-Spam-Action: soft reject». По хорошему, mta или фильтры распознают этот заголовок и отвечают отправителю временной ошибкой.
Но у нас нет поддержки mta или фильтров. Поэтому, мы просто делаем exit 1!
# см. скрипт из предыдущий статьи
greylisted=$( cat $mail_file | fgrep 'X-Spam-Action: soft reject' )
if [ -n "$greylisted" ]; then
exit 1
fi
opensmtpd, получив код возврата 1, понимает, что что-то пошло не так и за нас отвечает отправителю временной ошибкой.
Особенности запуска docker контейнера с opensmtpd: его нужно запускать в режиме сети «host», чтобы демон видел корректные IP адреса отправителей.
→ Пример контейнера с opensmtpd
Итого
Ещё меньше спама.