Pull to refresh

Интеграция почтового анти-спама rspamd с opensmtpd

Reading time5 min
Views7.7K

В сети довольно мало информации о подобной интеграции. То, что задача решаема — свидетельствуют редкие комментарии на сайтах, но готового решения я не нашёл. Возможно, это обусловлено тем, большинство используют postfix/exim.


Цели статьи:


  1. Описать методику решения задачи, чтобы было проще тем, кто пойдёт по моим следам;
  2. Отвлечь внимание коммьюнити от старого-доброго-древнего postfix и компании.


Введение


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


Исходная почтовая система разворачивалась в 2012м году. Ansible тогда только зарождался. Системы конфигурирования не были так распространены как сейчас. Автоматизацию работы я тогда любил меньше, чем сейчас, поэтому всё конфигурировалась руками.


Новая система должна была быть построена на докере (что означает максимально подробное описание построения системы "на будущее") и работать в докер кластере.


Конфиги старой системы сохранились, но мне очень не хотелось возвращаться к "dnl" sendmail, куче строк описания логики postfix, непонятному процессу настройки работы amavis+spamasassin, множеству памяти, которое требовалось amavis+spamasassin. Было принято решение поискать альтернативы.


Чтобы никто не говорил "используешь какой-то маргинальный софт потому, что не осилил postfix", я уточню: осилил. Изначальная система состояла из postfix+dovecot+amavisd-new+spamasassin+dkim-proxy+razor+pyzord+dcc.


Забегая вперёд: конечная система состоит из opensmtpd+dbmail+dkim-proxy+rspamd.


Немного теории


opensmtpd — это легковесный почтовый демон, с элементарным процессом конфигурирования от команды OpenBSD. Правила конфигрурированию настолько просты и интуитивно понятны, что сразу запоминаются и позволяют описать десятью строчками полную логику работы почтового сервера.


dbmail — хранилище почты с pop/imap интерфейсом. Основная особенность — хранение почты в базе, что позволяет создавать произвольное число точек входа.


rspamd — почтовая анти-спам система от разработчиков из Рамблера.


Ещё альтернативы? Почему не Zimbra? Почему не ещё какое-то готовое решение вида mail-in-a-box, которое содержит в себе все нужные компоненты?


Zimbra отпадала по причине Java — не хотелось расходовать ресурсы.


mail-in-a-box — опять же все те же postfix и компания.


Описание проблемы


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


Это:


  1. Отсутствие поддержки протокола "milter". Данный протокол (расширение) позволил бы интегрироваться с rspamd стандартными средствами.


  2. Отсутствие поддержки фильтров. Разработчик rspamd для интеграции рекомендует использовать фильтры opensmtpd. Разработка же фильтров идёт уже который год, но до сих пор не в продакшене. Существует даже фильтр для rspamd, но до сих пор в отдельной ветке на github.


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


  3. Отсутствие метода приёма почты кроме как по почтовым протоколам.

Так же, ситуация усложнялась следующими моментами:


  1. Удалённость сервисов друг от друга: демоны работают в докер-контейнерах с разными IP. Фактически, контейнер с opensmtpd и контейнер с rspamd можно представить как работающие на разных серверах.


  2. Разнородность ПО: opensmtpd есть в стабильной версии Alpine Linux (версия linux, которую часто используют в качестве базового образа Docker), но там нет rspamd. И наоборот: rspamd есть в нестабильной версии Alpine, но там не работает opensmtpd.

Итак, в качестве исходной задачи, мы имеем opensmtpd, который на порту 25 ждёт входящую почту и на порту 10029 ждёт почту, которую проверил rspamd.


Решение проблемы


В opensmtpd существуют mda. Вероятно, это расшифровывается как mail delivery agent. Перенаправление письма в mda вызывает запуск внешней программы. Пример правила перенаправления письма:


accept tagged IN from any for domain <domains> userbase <virtual_users> deliver to mda "/usr/local/bin/rspamd.sh %{sender} %{rcpt}" as admin

Здесь мы вызываем некий внешний скрипт, куда передаём адрес отправителя и адрес получателя.


В комплекте с rspamd идёт rspamc — это аналог почтового mta для rspamd. Данная программа позволяет принять на вход письмо, передать его rspamd по HTTP API и выдать ответ.


Интеграция mda и rspamd выглядит следующим образом


/usr/bin/rspamc --mime -h rspamd.example.ru:11333

На STDIN скрипту мы отправляем письмо, указываем адрес rspamd и параметром --mime говорим, что нужно добавить заголовки обработки письма (по которым в дальнейшем будет определяться спам).


После того, как письмо обработано rspamd, его нужно каким-то образом направить обратно в opensmtpd. Но стандартный mta (в комплекте с opensmtpd) умеет отправлять только на порт 25 (мы же ждём проверенное письмо на другом порту).


Решение: формировать общение по почтовому протоколу самим (хорошо, он несложный) и отправлять данные на нужный порт используя netcat.


Итоговый mda скрипт rspamd.sh интеграции opensmtpd и rspamd


#!/usr/bin/env sh

mail_file=$(mktemp)

echo 'HELO localhost' >> $mail_file
echo "MAIL FROM: <$1>" >> $mail_file
echo "RCPT TO: <$2>" >> $mail_file
echo 'DATA' >> $mail_file

/usr/bin/rspamc --mime -h rspamd.example.ru:11333 >> $mail_file

echo '' >> $mail_file
echo '.' >> $mail_file
echo 'QUIT' >> $mail_file

cut_file=$(mktemp)
sed '/Delivered-To/d' $mail_file > $cut_file
rm "$mail_file"

count=0; IFS=''; while read -r line ; do if [ "$count" -gt "5" ]; then sleep 0.05; else sleep 0.1; fi; echo "$line"; count=$((count+1)); done < "$cut_file" | netcat localhost 10029

rm "$cut_file"

Особенности данного скрипта:


  1. Отправлять данные через netcat следует с задержками после каждой строки этапа установки соединения. Для этого мы делаем задержки между отдачей строк.
  2. Из исходного письма следует вырезать заголовок "Delivered-To", который добавляет opensmtpd. Иначе, opensmtpd посчитает данное письмо зациклившимся.

Решение проблемы совмещения opensmtpd и rspamd в одном контейнере: использование Docker образа на основе Debian sid, куда подключается репозиторий rspamd и из него устанавливается свежая версия. Пример установки:


wget -O- https://rspamd.com/apt-stable/gpg.key | apt-key add - \
&& echo "deb http://rspamd.com/apt-stable/ sid main" > /etc/apt/sources.list.d/rspamd.list \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
rspamd

Итоги


В результате перехода с postfix+amavis на opensmtpd+rspamd я получил простоту конфигурации сервиса, малое потребление памяти и меньше спама на почту.


Реализацию итогового Docker контейнера можно посмотреть здесь.

Tags:
Hubs:
+13
Comments11

Articles