Как организовать вспомогательный почтовый узел
2008-06-05 11:00Как догадываются внимательные читатели, я не только тесты рассылаю, но и сервер почтовй поддерживаю :), спам фильтрую и типа того. В этой статье описан наш опыт организации вспомогательных почтовых узлов (MX-серверов), в котором использовалась распределенная база адресов, реализованная посредством SMTP-протокола. Такое решение представляется более разумным, чем настройка еще одной службы (например, LDAP): чем меньше софта и проще решение - тем оно надежнее. Впрочем, читайте подробности в статье и пробуйте сами.
Как организовать вспомогательный почтовый узел
Почтовая служба домена обеспечивает прием и отправку почтовых сообщений адресатами домена. Технически, почтовая служба состоит из основного почтового сервера, который хранит и раздает почту пользователям, и вспомогательных почтовых узлов, которые принимают входящую почту и передают ее на почтовый сервер.
Использование вспомогательных почтовых узлов позволет повысить надежность почтовой службы: отказ почтового узла не отразится на доставке почты, а кратковременный (порядка нескольких часов) отказ почтового сервера приведет к задержке почты, но не к ее потерям.
Кроме того, почтовые узлы позволяют разгрузить почтовый сервер от (ресурсоемкой) первичной обработки почты, связанной с фильтрацией обращений от недобросовестных отправителей.
Введение в проблему
Организация вспомогательного почтового узла - это легкое и простое дело. Почти. Вносится пара записей в DNS (A и MX), поднимается сервер, на нем настраивается передача всей почты домена на основной почтовый сервер и все. Почти.
Вот с этого "почти" и начинаются проблемы. Одна из них - в современном мире почтовый узел не может принять почту, если не уверен, что может доставить ее адресату. Следовательно, почтовому узлу должен быть доступен список всех адресов, существующих на почтовом сервере. В противном случае почтовый узел начнет принимать весь приходящий на него спам, направленный на неизвестные адреса (и создаст большой траффик), а письма "пользователь недоступен" в ответ на этот спам начнет рассылать почтовый сервер, что неминуемо начнет беспокоить пользователей Интернет и сервер попадет в черный список. Если же почтовый узел сразу скажет "нет", то в черный сервер, пытавшийся отправить спам пользователям домена.
Решение при помощи relay policing
Все решения проблемы так или иначе связаны с созданием распределенной базы адресов и ее синхронизацией на всех почтовых узлах, что представляет собой непростую задачу в общем случае. Предлагаемый relay policing позволяет организовать такую базу данных с минимальными усилями:
- База уже существует на почтовом сервере;
2. SMTP-протокол может быть использован как протокол запросов к базе адресов через SMTP-соединение.
Такой подход был опробован на сервере postfix, который позволяет делегировать решение о роутинге почты демонам, работающим вне среды postfix. Сервер postfix взаимодействует с демонами по специальному протоколу, позволяющему в определенный момент сессии передать ее параметры и получить решение о допустимости роутинга почтового сообщения, передаваемого в рамках этой сессии.
Механизмы принятия решения могут быть самыми разным (известны демоны, выполняющие greylisting, SPF и т.п.). В случае relay policing, демон, запущенный на вспомогательном почтовой узле, проверяет существование адреса на основном почтовом сервере.
Упрощенный алгоритм работы
В простейшем случае, для каждого входящего письма, после получения адресов в начале сессии, почтовый узел выполняет фиктивное обращение к основному почтовому серверу, предъявляя ему те же самые адреса, и, в зависимости от его ответа, пропускает или отбрасывает письмо. К сожалению, такой алгоритм работы снижает преимущества использования вспомогательных почтовых узлов, так как надежность системы не повышается: при выходе из строя основного сервера почтовый узел не сможет проверить валидность адресов и принять почту. Но даже в этом случае вспомогательные почтовые узлы сгладят пиковые нагрузки на основной сервер.
Упрощенный алгоритм работы подразумевает, что при невозможности обратится к основному серверу, почтовый узел отказывает в приеме письма со статусом со статусом "Временно недоступен" или аналогичным (4xx).
Кеширующий relay policing
Решить проблему недоступности основного сервера можно при помощи кеширования. При этом резко сокращается количество обращений к основному серверу, а вспомогательный почтовый узел может корректно обрабатывать входящие сообщения даже при отказе основного сервера. Приведем алгоритм работы:
- Кеш
- Кеш запросов к основному почтовому серверу, позволяющий по адресу определить время внесения записи в кеш и логическое значение, истинное в том случае, если адрес существует на основном сервере.
- Таймаут
- Максимальный возраст записи в кеше.
- Для адреса получателя, полученого демоном, найти значение в кеше;
- Если логическое значение из кеша не старее, чем таймаут, то при истинном значении вернуть серверу команду "принять почту", иначе - "отказать в приеме", перейти на 4;
- Если в кеше не существует значения или оно устарело, то адрес валидируется на основном почтовом сервере. Возможный результат валидации: "Адрес существует", "Адрес не существует", "Сервер недоступен";
- 3.1. При результате "Адрес существует", отдать серверу команду
- "принять почту", внести результат в кеш;
- 3.2. При результате "Адрес не существует", отдать серверу команду
- "отказать в приеме", внести результат в кеш;
- 3.3. При результате "Сервер недоступен", отдать серверу команду
- "отложить прием почты".
- Конец алгоритма.
Алгоритм требует, чтобы основной сервер давал ответ о существовании адреса получателся независимо от адреса отправителя. Кроме того, основной сервер никогда не должен отказывать в приеме почты по каким-либо "особым" причинам (например, RBL-листы и т.п.), что достаточно легко обеспечить. Если эти условия выполнить невозможно - алгоритм может быть доработан.
Результаты практической эксплуатации
Опытная эксплуатация relay policing daemon проводилась на postfix-сервере, служащего сервером входящей почты для сервера Microsoft Exchange, выполняющего для него фильтрацию спама (RBL, greylisting, spamassasin) и почты, зараженной вирусами (clamav). Такое решение позволило резко снизить ресурсоемкость Microsoft Exchange (в отсутствии postfix-сервера на сервере MS Exchange исчерпывались ресурсы практически моментально и его работа прекращалась). Изначально, использование такого алгоритма было обусловлено сложностью обеспечения доступа сервера postfix к базе адресов MS Exchange.
Количество адресов в MS Exchange составляло около 200. В процессе эксплуатации использовался таймаут в 24 часа на удачные и неудачные записи, что приводило к небольшой доле (один раз в сутки) фиктивных обращений к серверу Exchange.
Следует особенно отметить, что откешированный список "несуществующих" адресов имел относительно постоянный размер, и эти адреса действительно многократно использовались. Изучение этого списка показало, что основным источником таких адресов являются:
- уволившиеся сотрудники,
- записи, неверно внесенные в адресную книгу отправителем сообщения,
- некий алгоритм, возможно используемый при рассылке спама, для подбора адресов на основе списка известных адресов.
Поэтому кеширование "несуществующих" адресов, хотя и может приводить к неприятным последствиям, является ценным свойством алгоритма.
Очевидный недостаток алгоритма в возможности ложных отказов в приеме писем, из-за ложного отказа основного сервера. При исправном сервере, единсвенным очевидным сценарием возникновения такого отказа является приход первого письма на данный адрес до того, как этот адрес зарегистрирован в основном сервере.
Во время опытной эксплуатации такой сценарий практически не проявлялся: возможно, пользователям почтовой службы свойственно вначале получить адрес от администратора и настроить пользовательскую почтовую программу, а уж затем распространять новый адрес среди других пользователей интернет.
Реализация relay policing демона
Приведем упрощенный фрагмент кода relay policing демона, использованного в нашей опытной эксплуатации:
from smtplib import SMTP, quoteaddr
from bsddb import btopen
from marshal import dumps,loads
def check(cfg,sender,recipient) :
""" Вернуть результат проверки адреса на основном сервере и напечатать
решение демона для почтового узла """
smtp = SMTP()
try :
code, res = smtp.connect(cfg['relay'],cfg['port'])
except Excepton,msg :
# Сервер недоступен
print "action=defer_if_permit Service temporarily unavailable"
return 1
if code == 220 :
code, res = smtp.helo()
if code == 250 :
code, res = smtp.docmd('mail from:',quoteaddr(sender or ""))
if code == 250 :
code, res = smtp.docmd('rcpt to:',quoteaddr(recipient or ""))
if code == 250 :
# Адрес существует
print "action=dunno"
return 0
# Адрес не существует
print "action=reject"
return 2
def cache(db,cfg,sender,recipient) :
""" Кешировать результат проверки адреса """
try :
tb,haveaddr = loads(db[key])
except KeyError :
# Адреса нет в кеше, выполняем проверку
res = self.check(cfg,sender,recipient)
# Вносим адрес в кеш, если удалось обратится к серверу
if res in [0,2] :
db[key] = dumps((t,0==res))
else :
# Адрес есть в кеше
if t - tb > cfg["delay"] :
# Адрес в кеше устарел, выполняем проверку
res = self.check(cfg,sender,recipient)
# Обновляем данные в кеше, если удалось обратится к серверу
if res in [0,2] :
db[key] = dumps((t,0==res))
elif haveaddr :
# Адрес существует
print "action=dunno"
else :
# Адрес не существует
print "action=reject"
Кэш в этом примере хранился в bsddb.
Заключение
Описанный relay policing является удобным инструментом, позволяющим легко масштабировать почтовую службу. Этот подход позволяет стандартными средствами SMTP-протокола предоставить распределенную базу адресов почтовой службы, необходимой для введения вспомогательных почтовых узлов.
Хотя возможны другие подходы для решения этой задачи (LDAP, PostgreSQL+Notify, и т.п.), подход на основе relay policing внушает большую уверенность в своей надежности, так как практичесии не вносит новых функциональных точек в архитектуру почтовой службы.




