Настройка GreyList в Exim + PostgreSQL

Вступление

В качестве основы был взят Fast Gray List Mini Tutorial (PostreSQL Edition). Но этот вариант обладает существенными недостатками.

Во-первых, авторами предлагается делать проверку два раза: в acl_check_rcpt и acl_check_data. Как следствие, для определения отправителя и получателя они вынуждены строить тяжёлые конструкции, и, что более опасно, исходный алгоритм работы приводит к следующей проблеме для, например, перенаправленных писем:

  1. Удалённый почтовый сервер получает письмо от user@domain1 на user@domain2 и пытается переслать его на user@domain3.
  2. Мы заносим в greylist письмо с адресами из конверта письма sender=user@domain1, recipient=user@domain3. В greylist мы попадаем потому, что пересылает письмо нам сервер, ответственный за domain2, а он не является MX-ом для domain1.
  3. По истечении заданного времени сервер повторно приходит и на этапе RCPT TO: получает accept.
  4. Сервер честно передаёт нам письмо после DATA, после чего мы его ОПЯТЬ заносим в greylist. Потому, что $domain на этапе проверки acl_check_data не определён, а в $h_to: у нас будет user@domain2, что не совпадает с предыдущим значением. При этом предыдущая разрешающая запись удаляется.
  5. По истечении заданного времени сервер повторно приходит и его заносим в greylist. Потому как теперь опять проверка идёт на этапе acl_check_rcpt и используются данные из конверта. Возращаемся на пункт 2.

Во-вторых, возникает ощущение, что автор исходной инструкции недостаточно хорошо знаком с PostgreSQL и, например, для хранения адреса с маской использует тип character varying, не смотря на существование типов данных INET и CIDR.

В-третьих, предложенный автором вариант удаления устаревших записей реально если и работает, то очень плохо и на очень сильно нагруженных серверах. Как временное решение у меня удаление происходит при каждой проверке. Для системы со средним числом попыток RCPT TO: около одного в секунду и адекватными проверками HELO/DNSBL/... до проверки GreyList это не является проблемой, но при более высокой нагрузке лучше удаление вынести в отдельный, вызываемый из crontab скрипт.


Результат

Итогом изучения и переделки исходного документа стали:

  1. Файл создания базы
  2. Файл определений, который включается в конфигурационный файл сразу после определения primary_hostname:
    .include /usr/local/etc/exim/greylist/defines
  3. Файл greylist_acl, который включается в конфигурационный файл в начале определения конфигурации acl после строки "begin acl":
    .include /usr/local/etc/exim/greylist/greylist_acl
  4. Файл acl_check_rcpt, который включается в конфигурационный файл перед последним accept секции acl_check_rcpt:
    .include /usr/local/etc/exim/greylist/acl_check_rcpt
Примечания:
  1. Для отладки включено логирование работы в таблицу greylist_log. Для отключения необходимо в файле defines закомментировать строку:
    GREYLIST_ENABLED_LOG            = yes
  2. Для создания "белого списка" можно воспользоваться таким запросом:
    INSERT INTO
      greylist(relay_ip, bounce, sender, recipient, block_expires, manual)
      VALUES('0/0', FALSE, 'sender.domain', 'recipient.domain', '0001-01-01', TRUE);
  3. Главная